Revision f5f2dc53
b/docs/installation.rst | ||
---|---|---|
105 | 105 |
Install objpool (was: snf-common) |
106 | 106 |
""""""""""""""""""""""""""""""""" |
107 | 107 |
|
108 |
Since 0.6.2, kamaki is based on python-objpool. The objpool package is easy to install from source (even on windows platforms):
|
|
108 |
Kamaki is based on python-objpool. The objpool package is easy to install from source, even on windows platforms:
|
|
109 | 109 |
|
110 | 110 |
.. code-block:: console |
111 | 111 |
|
... | ... | |
121 | 121 |
|
122 | 122 |
.. code-block:: console |
123 | 123 |
|
124 |
$ tar xvfz kamaki-0.6.2.tar.gz
|
|
124 |
$ tar xvfz kamaki-0.7.tar.gz
|
|
125 | 125 |
|
126 | 126 |
or it can be downloaded directly from the git repository: |
127 | 127 |
|
... | ... | |
156 | 156 |
Windows |
157 | 157 |
------- |
158 | 158 |
|
159 |
Since version 0.6.2 kamaki can run on Windows, either on standard Windows console, or inside an improved command line shell. The present guide presents a tested method for using kamaki in windows
|
|
159 |
Kamaki can run on Windows, either on standard Windows console, or inside an improved command line shell. The present guide presents a tested method for setting up kamaki in windows
|
|
160 | 160 |
|
161 | 161 |
Requirements |
162 | 162 |
^^^^^^^^^^^^ |
... | ... | |
242 | 242 |
$ python setup.py install |
243 | 243 |
running install |
244 | 244 |
... |
245 |
Finished processing dependencies for kamaki==0.6.2 |
|
246 |
|
|
247 |
.. warning:: kamaki version should be 0.6.2 or better, otherwise it will not function. Users can test that by running:: |
|
245 |
Finished processing dependencies for kamaki==0.7 |
|
248 | 246 |
|
249 | 247 |
$ kamaki --version |
b/docs/setup.rst | ||
---|---|---|
29 | 29 |
|
30 | 30 |
* progress |
31 | 31 |
* Attach progress bars to various kamaki commands (e.g. kamaki store upload) |
32 |
* Since version 0.6.1 kamaki "requires" progress version 1.0.2 or better
|
|
32 |
* If desired, progress version should be 1.0.2 or better
|
|
33 | 33 |
|
34 | 34 |
Any of the above features can be installed at any time before or after kamaki installation. |
35 | 35 |
|
... | ... | |
157 | 157 |
Hidden features |
158 | 158 |
^^^^^^^^^^^^^^^ |
159 | 159 |
|
160 |
Since version 0.6.1 kamaki contains a test suite for the kamaki.clients API. The test suite can be activated with the following option on the configuration file:: |
|
160 |
The livetest suite |
|
161 |
"""""""""""""""""" |
|
161 | 162 |
|
162 |
[test] |
|
163 |
cli=test_cli |
|
163 |
Kamaki contains a live test suite for the kamaki.clients API, where "live" means that the tests are performed against active services that up and running. The live test package is named "livetest", it is accessible as kamaki.clients.livetest and it is designed to check the actual relation between kamaki and synnefo services. |
|
164 | 164 |
|
165 |
After that, users can run "kamaki test" commands to unit-test the prepackaged client APIs. Unit-tests are still experimental and there is a high probability of false alarms due to some of the expected values being hard-coded in the testing code.
|
|
165 |
The livetest suite can be activated with the following option on the configuration file::
|
|
166 | 166 |
|
167 |
Since version 0.6.3, a quotaholder client is introduced as an advanced feature. Quotaholder client is mostly used as a client library for accessing a synnefo quota service, but it can also be allowed as a kamaki command set, but setting the quotaholder.cli and quotaholder.url methods: |
|
167 |
[livetest] |
|
168 |
cli=livetest_cli |
|
169 |
|
|
170 |
In most tests, livetest will run as long as an Astakos identity manager service is accessible and kamaki is set up to authenticate a valid token on this server. |
|
171 |
|
|
172 |
In specific, a setup file needs at least the following mandatory settings in the configuration file: |
|
173 |
|
|
174 |
* If authentication information is used for default kamaki clients:: |
|
175 |
|
|
176 |
[astakos] |
|
177 |
url=<Astakos Identity Manager URL> |
|
178 |
token=<A valid user token> |
|
179 |
|
|
180 |
* else if this authentication information is only for testing add this under [livetest]:: |
|
181 |
|
|
182 |
astakos_url=<Astakos Identity Manager URL> |
|
183 |
astakos_token=<A valid user token> |
|
184 |
|
|
185 |
Each service tested in livetest might need some more options under the [livetest] label, as shown bellow: |
|
186 |
|
|
187 |
* kamaki livetest astakos:: |
|
188 |
|
|
189 |
astakos_email = <The valid email of testing user> |
|
190 |
astakos_name = <The exact "real" name of testing user> |
|
191 |
astakos_username = <The username of the testing user> |
|
192 |
astakos_uuid = <The valid unique user id of the testing user> |
|
193 |
|
|
194 |
* kamaki livetest pithos:: |
|
195 |
|
|
196 |
astakos_uuid = <The valid unique user id of the testing user> |
|
197 |
|
|
198 |
* kamaki livetest cyclades / image:: |
|
199 |
|
|
200 |
image_id = <A valid image id used for testing> |
|
201 |
image_local_path = <The local path of the testing image> |
|
202 |
image_details = <A text file containing testing image details in a python dict> |
|
203 |
|
|
204 |
- example image.details content: |
|
205 |
{ |
|
206 |
u'id': u'b3e68235-3abd-4d60-adfe-1379a4f8d3fe', |
|
207 |
u'metadata': { |
|
208 |
u'values': { |
|
209 |
u'description': u'Debian 6.0.6 (Squeeze) Base System', |
|
210 |
u'gui': u'No GUI', |
|
211 |
u'kernel': u'2.6.32', |
|
212 |
u'os': u'debian', |
|
213 |
u'osfamily': u'linux', |
|
214 |
u'root_partition': u'1', |
|
215 |
u'sortorder': u'1', |
|
216 |
u'users': u'root' |
|
217 |
} |
|
218 |
}, |
|
219 |
u'name': u'Debian Base', |
|
220 |
u'progress': u'100', |
|
221 |
u'status': u'ACTIVE', |
|
222 |
u'created': u'2012-11-19T14:54:57+00:00', |
|
223 |
u'updated': u'2012-11-19T15:29:51+00:00' |
|
224 |
} |
|
225 |
|
|
226 |
flavor_details = <A text file containing the testing images' flavor details in a python dict> |
|
227 |
|
|
228 |
- example flavor.details content: |
|
229 |
{ |
|
230 |
u'name': u'C1R1drbd', |
|
231 |
u'ram': 1024, |
|
232 |
u'id': 1, |
|
233 |
u'SNF:disk_template': u'drbd', |
|
234 |
u'disk': 20, |
|
235 |
u'cpu': 1 |
|
236 |
} |
|
237 |
|
|
238 |
After setup, kamaki can run all tests:: |
|
239 |
|
|
240 |
$ kamaki livetest all |
|
241 |
|
|
242 |
a specific test (e.g. astakos):: |
|
243 |
|
|
244 |
$ kamaki livetest astakos |
|
245 |
|
|
246 |
or a specific method from a service (e.g. astakos authenticate):: |
|
247 |
|
|
248 |
$ kamaki livetest astakos authenticate |
|
249 |
|
|
250 |
The quotaholder client |
|
251 |
"""""""""""""""""""""" |
|
252 |
|
|
253 |
A quotaholder client is introduced as an advanced feature. Quotaholder client is mostly used as a client library for accessing a synnefo quota service, but it can also be allowed as a kamaki command set, but setting the quotaholder.cli and quotaholder.url methods:: |
|
168 | 254 |
|
169 | 255 |
[quotaholder] |
170 | 256 |
cli=quotaholder_cli |
171 | 257 |
url=<URL of quotaholder service> |
258 |
|
|
259 |
Quotaholder is not tested in livetest |
b/kamaki/cli/commands/livetest_cli.py | ||
---|---|---|
1 |
# Copyright 2012-2013 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A.command |
|
33 |
|
|
34 |
from kamaki.cli.commands import errors |
|
35 |
from kamaki.cli import command |
|
36 |
from kamaki.cli.commands import _command_init |
|
37 |
from kamaki.cli.command_tree import CommandTree |
|
38 |
from kamaki.clients import livetest |
|
39 |
from kamaki.cli.errors import raiseCLIError |
|
40 |
|
|
41 |
livetest_cmds = CommandTree('livetest', 'Test clients on live servers') |
|
42 |
_commands = [livetest_cmds] |
|
43 |
|
|
44 |
|
|
45 |
class _livetest_init(_command_init): |
|
46 |
|
|
47 |
def _run(self, client, method=None): |
|
48 |
if method: |
|
49 |
livetest._main([client, method], config=self.config) |
|
50 |
else: |
|
51 |
livetest._main([client], config=self.config) |
|
52 |
|
|
53 |
def main(self, client, method=None): |
|
54 |
return self._run(client, method) |
|
55 |
|
|
56 |
|
|
57 |
@command(livetest_cmds) |
|
58 |
class livetest_error(_livetest_init): |
|
59 |
"""Create an error message with optional message""" |
|
60 |
|
|
61 |
@errors.generic.all |
|
62 |
def _run(self, errmsg='', importance=0, index=0): |
|
63 |
l = [1, 2] |
|
64 |
try: |
|
65 |
l[int(index)] |
|
66 |
except Exception as err: |
|
67 |
raiseCLIError(err, errmsg, importance) |
|
68 |
raiseCLIError(None, errmsg, importance) |
|
69 |
|
|
70 |
def main(self, errmsg='', importance=0, index=0): |
|
71 |
self._run(errmsg, importance, index) |
|
72 |
|
|
73 |
|
|
74 |
@command(livetest_cmds) |
|
75 |
class livetest_args(_livetest_init): |
|
76 |
"""Test how arguments are treated by kamaki""" |
|
77 |
|
|
78 |
@errors.generic.all |
|
79 |
def _run(self, *args): |
|
80 |
print(args) |
|
81 |
|
|
82 |
def main(self, *args): |
|
83 |
self._run(args) |
|
84 |
|
|
85 |
|
|
86 |
@command(livetest_cmds) |
|
87 |
class livetest_all(_livetest_init): |
|
88 |
"""test all clients""" |
|
89 |
|
|
90 |
@errors.generic.all |
|
91 |
def _run(self): |
|
92 |
for client in ('pithos', 'cyclades', 'image', 'astakos'): |
|
93 |
super(self.__class__, self)._run(client) |
|
94 |
|
|
95 |
def main(self): |
|
96 |
self._run() |
|
97 |
|
|
98 |
|
|
99 |
@command(livetest_cmds) |
|
100 |
class livetest_pithos(_livetest_init): |
|
101 |
""" test Pithos client""" |
|
102 |
|
|
103 |
@errors.generic.all |
|
104 |
def _run(self, method=None): |
|
105 |
super(self.__class__, self)._run('pithos', method) |
|
106 |
|
|
107 |
def main(self, method=None): |
|
108 |
self._run(method) |
|
109 |
|
|
110 |
|
|
111 |
@command(livetest_cmds) |
|
112 |
class livetest_cyclades(_livetest_init): |
|
113 |
""" test Cyclades client""" |
|
114 |
|
|
115 |
@errors.generic.all |
|
116 |
def _run(self, method=None): |
|
117 |
super(self.__class__, self)._run('cyclades', method) |
|
118 |
|
|
119 |
def main(self, method=None): |
|
120 |
self._run(method) |
|
121 |
|
|
122 |
|
|
123 |
@command(livetest_cmds) |
|
124 |
class livetest_image(_livetest_init): |
|
125 |
""" test Image client""" |
|
126 |
|
|
127 |
@errors.generic.all |
|
128 |
def _run(self, method=None): |
|
129 |
super(self.__class__, self)._run('image', method) |
|
130 |
|
|
131 |
def main(self, method=None): |
|
132 |
self._run(method) |
|
133 |
|
|
134 |
|
|
135 |
@command(livetest_cmds) |
|
136 |
class livetest_astakos(_livetest_init): |
|
137 |
""" test Astakos client""" |
|
138 |
|
|
139 |
@errors.generic.all |
|
140 |
def _run(self, method=None): |
|
141 |
super(self.__class__, self)._run('astakos', method) |
|
142 |
|
|
143 |
def main(self, method=None): |
|
144 |
self._run(method) |
|
145 |
|
|
146 |
|
|
147 |
@command(livetest_cmds) |
|
148 |
class livetest_prints(_livetest_init): |
|
149 |
""" user-test print methods for lists and dicts""" |
|
150 |
|
|
151 |
d1 = {'key0a': 'val0a', 'key0b': 'val0b', 'key0c': 'val0c'} |
|
152 |
|
|
153 |
l1 = [1, 'string', '3', 'many (2 or 3) numbers and strings combined', 5] |
|
154 |
|
|
155 |
d2 = {'id': 'val0a', 'key0b': d1, 'title': l1} |
|
156 |
|
|
157 |
l2 = [d2, l1, d1] |
|
158 |
|
|
159 |
spr_msg = 'long key of size 75 characters is used to check the effects on' |
|
160 |
spr_msg += ' total result for long messages that drive pep8 completely mad' |
|
161 |
d3 = {'dict 1': d1, 'dict 2': d2, 'list2': l2, spr_msg: l1} |
|
162 |
|
|
163 |
@errors.generic.all |
|
164 |
def _run(self): |
|
165 |
from kamaki.cli.utils import print_dict, print_list, print_items |
|
166 |
print('Test simple dict:\n- - -') |
|
167 |
print_dict(self.d1) |
|
168 |
print('- - -\n') |
|
169 |
print('\nTest simple list:\n- - -') |
|
170 |
print_list(self.l1) |
|
171 |
print('- - -\n') |
|
172 |
print('\nTest 2-level dict:\n- - -') |
|
173 |
print_dict(self.d2) |
|
174 |
print('- - -\n') |
|
175 |
print('\nTest non-trivial list:\n- - -') |
|
176 |
print_list(self.l2) |
|
177 |
print('- - -') |
|
178 |
print('\nTest extreme dict:\n- - -') |
|
179 |
print_dict(self.d3) |
|
180 |
print('- - -\n') |
|
181 |
print('Test simple enumerated dict:\n- - -') |
|
182 |
print_dict(self.d1, with_enumeration=True) |
|
183 |
print('- - -\n') |
|
184 |
print('\nTest simple enumerated list:\n- - -') |
|
185 |
print_list(self.l1, with_enumeration=True) |
|
186 |
print('- - -\n') |
|
187 |
print('Test non-trivial deep-enumerated dict:\n- - -') |
|
188 |
print_dict(self.d2, with_enumeration=True, recursive_enumeration=True) |
|
189 |
print('- - -\n') |
|
190 |
print('\nTest non-trivial enumerated list:\n- - -') |
|
191 |
print_list(self.l2, with_enumeration=True) |
|
192 |
print('- - -\n') |
|
193 |
print('\nTest print_items with id:\n- - -') |
|
194 |
print_items([ |
|
195 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
196 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}]) |
|
197 |
print('- - -') |
|
198 |
print('\nTest print_items with id and enumeration:\n- - -') |
|
199 |
print_items( |
|
200 |
[ |
|
201 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
202 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}], |
|
203 |
with_enumeration=True) |
|
204 |
print('- - -') |
|
205 |
print('\nTest print_items with id, title and redundancy:\n- - -') |
|
206 |
print_items( |
|
207 |
[ |
|
208 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
209 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}], |
|
210 |
title=('id', 'title'), |
|
211 |
with_redundancy=True) |
|
212 |
print('- - -') |
|
213 |
print('\nTest print_items with lists- - -') |
|
214 |
print_items([['i00', 'i01', 'i02'], [self.l2, 'i11', self.d1], 3]) |
|
215 |
print('- - -') |
|
216 |
|
|
217 |
def main(self): |
|
218 |
self._run() |
/dev/null | ||
---|---|---|
1 |
# Copyright 2012-2013 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A.command |
|
33 |
|
|
34 |
from kamaki.cli.commands import errors |
|
35 |
from kamaki.cli import command |
|
36 |
from kamaki.cli.commands import _command_init |
|
37 |
from kamaki.cli.command_tree import CommandTree |
|
38 |
from kamaki.clients import tests |
|
39 |
from kamaki.cli.errors import raiseCLIError |
|
40 |
|
|
41 |
test_cmds = CommandTree('test', 'Unitest clients') |
|
42 |
_commands = [test_cmds] |
|
43 |
|
|
44 |
|
|
45 |
class _test_init(_command_init): |
|
46 |
|
|
47 |
def _run(self, client, method=None): |
|
48 |
if method: |
|
49 |
tests._main([client, method], config=self.config) |
|
50 |
else: |
|
51 |
tests._main([client], config=self.config) |
|
52 |
|
|
53 |
def main(self, client, method=None): |
|
54 |
return self._run(client, method) |
|
55 |
|
|
56 |
|
|
57 |
@command(test_cmds) |
|
58 |
class test_error(_test_init): |
|
59 |
"""Create an error message with optional message""" |
|
60 |
|
|
61 |
@errors.generic.all |
|
62 |
def _run(self, errmsg='', importance=0, index=0): |
|
63 |
l = [1, 2] |
|
64 |
try: |
|
65 |
l[int(index)] |
|
66 |
except Exception as err: |
|
67 |
raiseCLIError(err, errmsg, importance) |
|
68 |
raiseCLIError(None, errmsg, importance) |
|
69 |
|
|
70 |
def main(self, errmsg='', importance=0, index=0): |
|
71 |
self._run(errmsg, importance, index) |
|
72 |
|
|
73 |
|
|
74 |
@command(test_cmds) |
|
75 |
class test_args(_test_init): |
|
76 |
"""Test how arguments are treated by kamaki""" |
|
77 |
|
|
78 |
@errors.generic.all |
|
79 |
def _run(self, *args): |
|
80 |
print(args) |
|
81 |
|
|
82 |
def main(self, *args): |
|
83 |
self._run(args) |
|
84 |
|
|
85 |
|
|
86 |
@command(test_cmds) |
|
87 |
class test_all(_test_init): |
|
88 |
"""test all clients""" |
|
89 |
|
|
90 |
@errors.generic.all |
|
91 |
def _run(self): |
|
92 |
for client in ('pithos', 'cyclades', 'image', 'astakos'): |
|
93 |
super(self.__class__, self)._run(client) |
|
94 |
|
|
95 |
def main(self): |
|
96 |
self._run() |
|
97 |
|
|
98 |
|
|
99 |
@command(test_cmds) |
|
100 |
class test_pithos(_test_init): |
|
101 |
""" test Pithos client""" |
|
102 |
|
|
103 |
@errors.generic.all |
|
104 |
def _run(self, method=None): |
|
105 |
super(self.__class__, self)._run('pithos', method) |
|
106 |
|
|
107 |
def main(self, method=None): |
|
108 |
self._run(method) |
|
109 |
|
|
110 |
|
|
111 |
@command(test_cmds) |
|
112 |
class test_cyclades(_test_init): |
|
113 |
""" test Cyclades client""" |
|
114 |
|
|
115 |
@errors.generic.all |
|
116 |
def _run(self, method=None): |
|
117 |
super(self.__class__, self)._run('cyclades', method) |
|
118 |
|
|
119 |
def main(self, method=None): |
|
120 |
self._run(method) |
|
121 |
|
|
122 |
|
|
123 |
@command(test_cmds) |
|
124 |
class test_image(_test_init): |
|
125 |
""" test Image client""" |
|
126 |
|
|
127 |
@errors.generic.all |
|
128 |
def _run(self, method=None): |
|
129 |
super(self.__class__, self)._run('image', method) |
|
130 |
|
|
131 |
def main(self, method=None): |
|
132 |
self._run(method) |
|
133 |
|
|
134 |
|
|
135 |
@command(test_cmds) |
|
136 |
class test_astakos(_test_init): |
|
137 |
""" test Astakos client""" |
|
138 |
|
|
139 |
@errors.generic.all |
|
140 |
def _run(self, method=None): |
|
141 |
super(self.__class__, self)._run('astakos', method) |
|
142 |
|
|
143 |
def main(self, method=None): |
|
144 |
self._run(method) |
|
145 |
|
|
146 |
|
|
147 |
@command(test_cmds) |
|
148 |
class test_prints(_test_init): |
|
149 |
""" user-test print methods for lists and dicts""" |
|
150 |
|
|
151 |
d1 = {'key0a': 'val0a', 'key0b': 'val0b', 'key0c': 'val0c'} |
|
152 |
|
|
153 |
l1 = [1, 'string', '3', 'many (2 or 3) numbers and strings combined', 5] |
|
154 |
|
|
155 |
d2 = {'id': 'val0a', 'key0b': d1, 'title': l1} |
|
156 |
|
|
157 |
l2 = [d2, l1, d1] |
|
158 |
|
|
159 |
spr_msg = 'long key of size 75 characters is used to check the effects on' |
|
160 |
spr_msg += ' total result for long messages that drive pep8 completely mad' |
|
161 |
d3 = {'dict 1': d1, 'dict 2': d2, 'list2': l2, spr_msg: l1} |
|
162 |
|
|
163 |
@errors.generic.all |
|
164 |
def _run(self): |
|
165 |
from kamaki.cli.utils import print_dict, print_list, print_items |
|
166 |
print('Test simple dict:\n- - -') |
|
167 |
print_dict(self.d1) |
|
168 |
print('- - -\n') |
|
169 |
print('\nTest simple list:\n- - -') |
|
170 |
print_list(self.l1) |
|
171 |
print('- - -\n') |
|
172 |
print('\nTest 2-level dict:\n- - -') |
|
173 |
print_dict(self.d2) |
|
174 |
print('- - -\n') |
|
175 |
print('\nTest non-trivial list:\n- - -') |
|
176 |
print_list(self.l2) |
|
177 |
print('- - -') |
|
178 |
print('\nTest extreme dict:\n- - -') |
|
179 |
print_dict(self.d3) |
|
180 |
print('- - -\n') |
|
181 |
print('Test simple enumerated dict:\n- - -') |
|
182 |
print_dict(self.d1, with_enumeration=True) |
|
183 |
print('- - -\n') |
|
184 |
print('\nTest simple enumerated list:\n- - -') |
|
185 |
print_list(self.l1, with_enumeration=True) |
|
186 |
print('- - -\n') |
|
187 |
print('Test non-trivial deep-enumerated dict:\n- - -') |
|
188 |
print_dict(self.d2, with_enumeration=True, recursive_enumeration=True) |
|
189 |
print('- - -\n') |
|
190 |
print('\nTest non-trivial enumerated list:\n- - -') |
|
191 |
print_list(self.l2, with_enumeration=True) |
|
192 |
print('- - -\n') |
|
193 |
print('\nTest print_items with id:\n- - -') |
|
194 |
print_items([ |
|
195 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
196 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}]) |
|
197 |
print('- - -') |
|
198 |
print('\nTest print_items with id and enumeration:\n- - -') |
|
199 |
print_items( |
|
200 |
[ |
|
201 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
202 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}], |
|
203 |
with_enumeration=True) |
|
204 |
print('- - -') |
|
205 |
print('\nTest print_items with id, title and redundancy:\n- - -') |
|
206 |
print_items( |
|
207 |
[ |
|
208 |
{'id': '42', 'title': 'lalakis 1', 'content': self.d1}, |
|
209 |
{'id': '142', 'title': 'lalakis 2', 'content': self.d2}], |
|
210 |
title=('id', 'title'), |
|
211 |
with_redundancy=True) |
|
212 |
print('- - -') |
|
213 |
print('\nTest print_items with lists- - -') |
|
214 |
print_items([['i00', 'i01', 'i02'], [self.l2, 'i11', self.d1], 3]) |
|
215 |
print('- - -') |
|
216 |
|
|
217 |
def main(self): |
|
218 |
self._run() |
b/kamaki/clients/livetest/__init__.py | ||
---|---|---|
1 |
# Copyright 2012-2013 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import inspect |
|
35 |
from unittest import TestCase, TestSuite, TextTestRunner |
|
36 |
from argparse import ArgumentParser |
|
37 |
from sys import stdout |
|
38 |
|
|
39 |
from kamaki.cli.config import Config |
|
40 |
from kamaki.cli.utils import spiner |
|
41 |
|
|
42 |
|
|
43 |
def _add_value(foo, value): |
|
44 |
def wrap(self): |
|
45 |
return foo(self, value) |
|
46 |
return wrap |
|
47 |
|
|
48 |
|
|
49 |
class Generic(TestCase): |
|
50 |
|
|
51 |
_waits = [] |
|
52 |
_cnf = None |
|
53 |
_grp = None |
|
54 |
_fetched = {} |
|
55 |
|
|
56 |
def __init__(self, specific=None, config=None, group=None): |
|
57 |
super(Generic, self).__init__(specific) |
|
58 |
self._cnf = config or Config() |
|
59 |
self._grp = group |
|
60 |
self._waits.append(0.71828) |
|
61 |
for i in range(10): |
|
62 |
self._waits.append(self._waits[-1] * 2.71828) |
|
63 |
|
|
64 |
def __getitem__(self, key): |
|
65 |
key = self._key(key) |
|
66 |
try: |
|
67 |
r = self._fetched[key] |
|
68 |
return r |
|
69 |
return self._fetched[key] |
|
70 |
except KeyError: |
|
71 |
r = self._get_from_cnf(key) |
|
72 |
return r |
|
73 |
return self._get_from_cnf(key) |
|
74 |
|
|
75 |
def _key(self, key): |
|
76 |
return ('', key) if isinstance(key, str)\ |
|
77 |
else ('', key[0]) if len(key) == 1\ |
|
78 |
else key |
|
79 |
|
|
80 |
def _get_from_cnf(self, key): |
|
81 |
val = 0 |
|
82 |
if key[0]: |
|
83 |
keystr = '%s_%s' % key |
|
84 |
val = self._cnf.get('livetest', keystr) or self._cnf.get(*key) |
|
85 |
if not val: |
|
86 |
val = self._cnf.get('livetest', key[1]) or self._cnf.get( |
|
87 |
'global', |
|
88 |
key[1]) |
|
89 |
self._fetched[key] = val |
|
90 |
return val |
|
91 |
|
|
92 |
def _safe_progress_bar(self, msg): |
|
93 |
"""Try to get a progress bar, but do not raise errors""" |
|
94 |
try: |
|
95 |
from progress.bar import ShadyBar |
|
96 |
wait_bar = ShadyBar(msg) |
|
97 |
|
|
98 |
def wait_gen(n): |
|
99 |
for i in wait_bar.iter(range(int(n))): |
|
100 |
yield |
|
101 |
yield |
|
102 |
wait_cb = wait_gen |
|
103 |
except Exception: |
|
104 |
stdout.write('%s:' % msg) |
|
105 |
(wait_bar, wait_cb) = (None, spiner) |
|
106 |
return (wait_bar, wait_cb) |
|
107 |
|
|
108 |
def _safe_progress_bar_finish(self, progress_bar): |
|
109 |
try: |
|
110 |
progress_bar.finish() |
|
111 |
except Exception: |
|
112 |
print('\b DONE') |
|
113 |
|
|
114 |
def do_with_progress_bar(self, action, msg, items): |
|
115 |
if not items: |
|
116 |
print('%s: DONE' % msg) |
|
117 |
return |
|
118 |
(action_bar, action_cb) = self._safe_progress_bar(msg) |
|
119 |
action_gen = action_cb(len(items)) |
|
120 |
try: |
|
121 |
action_gen.next() |
|
122 |
except Exception: |
|
123 |
pass |
|
124 |
for item in items: |
|
125 |
action(item) |
|
126 |
action_gen.next() |
|
127 |
self._safe_progress_bar_finish(action_bar) |
|
128 |
|
|
129 |
def assert_dicts_are_deeply_equal(self, d1, d2): |
|
130 |
(st1, st2) = (set(d1.keys()), set(d2.keys())) |
|
131 |
diff1 = st1.difference(st2) |
|
132 |
diff2 = st2.difference(st1) |
|
133 |
self.assertTrue( |
|
134 |
not (diff1 or diff2), |
|
135 |
'Key differences:\n\td1-d2=%s\n\td2-d1=%s' % (diff1, diff2)) |
|
136 |
for k, v in d1.items(): |
|
137 |
if isinstance(v, dict): |
|
138 |
self.assert_dicts_are_deeply_equal(v, d2[k]) |
|
139 |
else: |
|
140 |
self.assertEqual(unicode(v), unicode(d2[k])) |
|
141 |
|
|
142 |
def test_000(self): |
|
143 |
print('') |
|
144 |
methods = [method for method in inspect.getmembers( |
|
145 |
self, |
|
146 |
predicate=inspect.ismethod) if method[0].startswith('_test_')] |
|
147 |
failures = 0 |
|
148 |
for method in methods: |
|
149 |
stdout.write('Test %s ' % method[0][6:]) |
|
150 |
stdout.flush() |
|
151 |
try: |
|
152 |
method[1]() |
|
153 |
print(' ...ok') |
|
154 |
except AssertionError: |
|
155 |
print(' FAIL: %s (%s)' % (method[0], method[1])) |
|
156 |
failures += 1 |
|
157 |
if failures: |
|
158 |
raise AssertionError('%s failures' % failures) |
|
159 |
|
|
160 |
|
|
161 |
def init_parser(): |
|
162 |
parser = ArgumentParser(add_help=False) |
|
163 |
parser.add_argument( |
|
164 |
'-h', '--help', |
|
165 |
dest='help', |
|
166 |
action='store_true', |
|
167 |
default=False, |
|
168 |
help="Show this help message and exit") |
|
169 |
return parser |
|
170 |
|
|
171 |
|
|
172 |
def main(argv): |
|
173 |
_main(argv, config=None) |
|
174 |
|
|
175 |
|
|
176 |
def _main(argv, config=None): |
|
177 |
suiteFew = TestSuite() |
|
178 |
if len(argv) == 0 or argv[0] == 'pithos': |
|
179 |
from kamaki.clients.livetest.pithos import Pithos |
|
180 |
test_method = 'test_%s' % (argv[1] if len(argv) > 1 else '000') |
|
181 |
suiteFew.addTest(Pithos(test_method, config)) |
|
182 |
if len(argv) == 0 or argv[0] == 'cyclades': |
|
183 |
from kamaki.clients.livetest.cyclades import Cyclades |
|
184 |
test_method = 'test_%s' % (argv[1] if len(argv) > 1 else '000') |
|
185 |
suiteFew.addTest(Cyclades(test_method, config)) |
|
186 |
if len(argv) == 0 or argv[0] == 'image': |
|
187 |
from kamaki.clients.livetest.image import Image |
|
188 |
test_method = 'test_%s' % (argv[1] if len(argv) > 1 else '000') |
|
189 |
suiteFew.addTest(Image(test_method, config)) |
|
190 |
if len(argv) == 0 or argv[0] == 'astakos': |
|
191 |
from kamaki.clients.livetest.astakos import Astakos |
|
192 |
test_method = 'test_%s' % (argv[1] if len(argv) > 1 else '000') |
|
193 |
suiteFew.addTest(Astakos(test_method, config)) |
|
194 |
|
|
195 |
TextTestRunner(verbosity=2).run(suiteFew) |
|
196 |
|
|
197 |
if __name__ == '__main__': |
|
198 |
parser = init_parser() |
|
199 |
args, argv = parser.parse_known_args() |
|
200 |
if len(argv) > 2 or getattr(args, 'help') or len(argv) < 1: |
|
201 |
raise Exception('\tusage: livetest <group> [command]') |
|
202 |
main(argv) |
b/kamaki/clients/livetest/astakos.py | ||
---|---|---|
1 |
# Copyright 2012-2013 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from kamaki.clients import livetest |
|
35 |
from kamaki.clients.astakos import AstakosClient |
|
36 |
|
|
37 |
|
|
38 |
class Astakos(livetest.Generic): |
|
39 |
def setUp(self): |
|
40 |
self.client = AstakosClient( |
|
41 |
self['astakos', 'url'], |
|
42 |
self['astakos', 'token']) |
|
43 |
|
|
44 |
def test_authenticate(self): |
|
45 |
self._test_0010_authenticate() |
|
46 |
|
|
47 |
def _test_0010_authenticate(self): |
|
48 |
r = self.client.authenticate() |
|
49 |
for term in ( |
|
50 |
'name', |
|
51 |
'username', |
|
52 |
'auth_token_expires', |
|
53 |
'auth_token_created', |
|
54 |
'uuid', |
|
55 |
'id', |
|
56 |
'email'): |
|
57 |
self.assertTrue(term in r) |
|
58 |
|
|
59 |
def test_info(self): |
|
60 |
self._test_0020_info() |
|
61 |
|
|
62 |
def _test_0020_info(self): |
|
63 |
self.assertTrue(set([ |
|
64 |
'name', |
|
65 |
'username', |
|
66 |
'uuid']).issubset(self.client.info().keys())) |
|
67 |
|
|
68 |
def test_get(self): |
|
69 |
self._test_0020_get() |
|
70 |
|
|
71 |
def _test_0020_get(self): |
|
72 |
for term in ('uuid', 'name', 'username'): |
|
73 |
self.assertEqual( |
|
74 |
self.client.term(term, self['astakos', 'token']), |
|
75 |
self['astakos', term]) |
|
76 |
self.assertTrue(self['astakos', 'email'] in self.client.term('email')) |
|
77 |
|
|
78 |
def test_list(self): |
|
79 |
self.client.authenticate() |
|
80 |
self._test_0020_list() |
|
81 |
|
|
82 |
def _test_0020_list(self): |
|
83 |
terms = set(['name', 'username', 'uuid', 'email', 'auth_token']) |
|
84 |
uuid = 0 |
|
85 |
for r in self.client.list(): |
|
86 |
self.assertTrue(terms.issubset(r.keys())) |
|
87 |
self.assertTrue(uuid != r['uuid'] if uuid else True) |
|
88 |
uuid = r['uuid'] |
b/kamaki/clients/livetest/cyclades.py | ||
---|---|---|
1 |
# Copyright 2012-2013 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import time |
|
35 |
|
|
36 |
from kamaki.clients import livetest, ClientError |
|
37 |
from kamaki.clients.cyclades import CycladesClient |
|
38 |
|
|
39 |
|
|
40 |
class Cyclades(livetest.Generic): |
|
41 |
"""Set up a Cyclades thorough test""" |
|
42 |
def setUp(self): |
|
43 |
|
|
44 |
with open(self['image', 'details']) as f: |
|
45 |
self.img_details = eval(f.read()) |
|
46 |
self.img = self.img_details['id'] |
|
47 |
with open(self['flavor', 'details']) as f: |
|
48 |
self._flavor_details = eval(f.read()) |
|
49 |
self.PROFILES = ('ENABLED', 'DISABLED', 'PROTECTED') |
|
50 |
|
|
51 |
self.servers = {} |
|
52 |
self.now = time.mktime(time.gmtime()) |
|
53 |
self.servname1 = 'serv' + unicode(self.now) |
|
54 |
self.servname2 = self.servname1 + '_v2' |
|
55 |
self.servname1 += '_v1' |
|
56 |
self.flavorid = 1 |
|
57 |
#servers have to be created at the begining... |
|
58 |
self.networks = {} |
|
59 |
self.netname1 = 'net' + unicode(self.now) |
|
60 |
self.netname2 = 'net' + unicode(self.now) + '_v2' |
|
61 |
|
|
62 |
self.client = CycladesClient(self['compute', 'url'], self['token']) |
|
63 |
|
|
64 |
def tearDown(self): |
|
65 |
"""Destoy servers used in testing""" |
|
66 |
for net in self.networks.keys(): |
|
67 |
self._delete_network(net) |
|
68 |
for server in self.servers.values(): |
|
69 |
self._delete_server(server['id']) |
|
70 |
print('DEL VM %s (%s)' % (server['id'], server['name'])) |
|
71 |
|
|
72 |
def test_000(self): |
|
73 |
"Prepare a full Cyclades test scenario" |
|
74 |
self.server1 = self._create_server( |
|
75 |
self.servname1, |
|
76 |
self.flavorid, |
|
77 |
self.img) |
|
78 |
self.server2 = self._create_server( |
|
79 |
self.servname2, |
|
80 |
self.flavorid + 2, |
|
81 |
self.img) |
|
82 |
super(self.__class__, self).test_000() |
|
83 |
|
|
84 |
def _create_server(self, servername, flavorid, imageid, personality=None): |
|
85 |
server = self.client.create_server( |
|
86 |
servername, |
|
87 |
flavorid, |
|
88 |
imageid, |
|
89 |
personality) |
|
90 |
print('CREATE VM %s (%s)' % (server['id'], server['name'])) |
|
91 |
self.servers[servername] = server |
|
92 |
return server |
|
93 |
|
|
94 |
def _delete_server(self, servid): |
|
95 |
try: |
|
96 |
current_state = self.client.get_server_details(servid) |
|
97 |
current_state = current_state['status'] |
|
98 |
if current_state == 'DELETED': |
|
99 |
return |
|
100 |
self.client.delete_server(servid) |
|
101 |
self._wait_for_status(servid, current_state) |
|
102 |
self.client.delete_server(servid) |
|
103 |
except: |
|
104 |
return |
|
105 |
|
|
106 |
def _create_network(self, netname, **kwargs): |
|
107 |
net = self.client.create_network(netname, **kwargs) |
|
108 |
self.networks[net['id']] = net |
|
109 |
return net |
|
110 |
|
|
111 |
def _delete_network(self, netid): |
|
112 |
if not netid in self.networks: |
|
113 |
return None |
|
114 |
print('Disconnect nics of network %s' % netid) |
|
115 |
self.client.disconnect_network_nics(netid) |
|
116 |
|
|
117 |
def netwait(wait): |
|
118 |
try: |
|
119 |
self.client.delete_network(netid) |
|
120 |
except ClientError: |
|
121 |
time.sleep(wait) |
|
122 |
self.do_with_progress_bar( |
|
123 |
netwait, |
|
124 |
'Delete network %s' % netid, |
|
125 |
self._waits[:7]) |
|
126 |
return self.networks.pop(netid) |
|
127 |
|
|
128 |
def _wait_for_network(self, netid, status): |
|
129 |
|
|
130 |
def netwait(wait): |
|
131 |
r = self.client.get_network_details(netid) |
|
132 |
if r['status'] == status: |
|
133 |
return |
|
134 |
time.sleep(wait) |
|
135 |
self.do_with_progress_bar( |
|
136 |
netwait, |
|
137 |
'Wait network %s to reach status %s' % (netid, status), |
|
138 |
self._waits[:5]) |
|
139 |
|
|
140 |
def _wait_for_nic(self, netid, servid, in_creation=True): |
|
141 |
self._wait_for_network(netid, 'ACTIVE') |
|
142 |
|
|
143 |
def nicwait(wait): |
|
144 |
nics = self.client.list_server_nics(servid) |
|
145 |
for net in nics: |
|
146 |
found_nic = net['network_id'] == netid |
|
147 |
if (in_creation and found_nic) or not ( |
|
148 |
in_creation or found_nic): |
|
149 |
return |
|
150 |
time.sleep(wait) |
|
151 |
self.do_with_progress_bar( |
|
152 |
nicwait, |
|
153 |
'Wait nic-%s-%s to %sconnect' % ( |
|
154 |
netid, |
|
155 |
servid, |
|
156 |
'' if in_creation else 'dis'), |
|
157 |
self._waits[:5]) |
|
158 |
for net in self.client.list_server_nics(servid): |
|
159 |
if netid == net['network_id']: |
|
160 |
return True |
|
161 |
return False |
|
162 |
|
|
163 |
def _has_status(self, servid, status): |
|
164 |
r = self.client.get_server_details(servid) |
|
165 |
return r['status'] == status |
|
166 |
|
|
167 |
def _wait_for_status(self, servid, status): |
|
168 |
(wait_bar, wait_cb) = self._safe_progress_bar( |
|
169 |
'Server %s in %s' % (servid, status)) |
|
170 |
self.client.wait_server(servid, status, wait_cb=wait_cb) |
|
171 |
self._safe_progress_bar_finish(wait_bar) |
|
172 |
|
|
173 |
def test_parallel_creation(self): |
|
174 |
"""test create with multiple threads |
|
175 |
Do not use this in regular livetest |
|
176 |
""" |
|
177 |
from kamaki.clients import SilentEvent |
|
178 |
c1 = SilentEvent( |
|
179 |
self._create_server, |
|
180 |
self.servname1, |
|
181 |
self.flavorid, |
|
182 |
self.img) |
|
183 |
c2 = SilentEvent( |
|
184 |
self._create_server, |
|
185 |
self.servname2, |
|
186 |
self.flavorid + 2, |
|
187 |
self.img) |
|
188 |
c3 = SilentEvent( |
|
189 |
self._create_server, |
|
190 |
self.servname1, |
|
191 |
self.flavorid, |
|
192 |
self.img) |
|
193 |
c4 = SilentEvent( |
|
194 |
self._create_server, |
|
195 |
self.servname2, |
|
196 |
self.flavorid + 2, |
|
197 |
self.img) |
|
198 |
c5 = SilentEvent( |
|
199 |
self._create_server, |
|
200 |
self.servname1, |
|
201 |
self.flavorid, |
|
202 |
self.img) |
|
203 |
c6 = SilentEvent( |
|
204 |
self._create_server, |
|
205 |
self.servname2, |
|
206 |
self.flavorid + 2, |
|
207 |
self.img) |
|
208 |
c7 = SilentEvent( |
|
209 |
self._create_server, |
|
210 |
self.servname1, |
|
211 |
self.flavorid, |
|
212 |
self.img) |
|
213 |
c8 = SilentEvent( |
|
214 |
self._create_server, |
|
215 |
self.servname2, |
|
216 |
self.flavorid + 2, |
|
217 |
self.img) |
|
218 |
c1.start() |
|
219 |
c2.start() |
|
220 |
c3.start() |
|
221 |
c4.start() |
|
222 |
c5.start() |
|
223 |
c6.start() |
|
224 |
c7.start() |
|
225 |
c8.start() |
|
226 |
|
|
227 |
def test_create_server(self): |
|
228 |
"""Test create_server""" |
|
229 |
self.server1 = self._create_server( |
|
230 |
self.servname1, |
|
231 |
self.flavorid, |
|
232 |
self.img) |
|
233 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
234 |
self._test_0010_create_server() |
|
235 |
|
|
236 |
def _test_0010_create_server(self): |
|
237 |
self.assertEqual(self.server1["name"], self.servname1) |
|
238 |
self.assertEqual(self.server1["flavorRef"], self.flavorid) |
|
239 |
self.assertEqual(self.server1["imageRef"], self.img) |
|
240 |
self.assertEqual(self.server1["status"], "BUILD") |
|
241 |
|
|
242 |
def test_list_servers(self): |
|
243 |
"""Test list servers""" |
|
244 |
self.server1 = self._create_server( |
|
245 |
self.servname1, |
|
246 |
self.flavorid, |
|
247 |
self.img) |
|
248 |
self.server2 = self._create_server( |
|
249 |
self.servname2, |
|
250 |
self.flavorid + 2, |
|
251 |
self.img) |
|
252 |
self._test_0020_list_servers() |
|
253 |
|
|
254 |
def _test_0020_list_servers(self): |
|
255 |
servers = self.client.list_servers() |
|
256 |
dservers = self.client.list_servers(detail=True) |
|
257 |
|
|
258 |
"""detailed and simple are same size""" |
|
259 |
self.assertEqual(len(dservers), len(servers)) |
|
260 |
for i in range(len(servers)): |
|
261 |
for field in ( |
|
262 |
'created', |
|
263 |
'flavorRef', |
|
264 |
'hostId', |
|
265 |
'imageRef', |
|
266 |
'progress', |
|
267 |
'status', |
|
268 |
'updated'): |
|
269 |
self.assertFalse(field in servers[i]) |
|
270 |
self.assertTrue(field in dservers[i]) |
|
271 |
|
|
272 |
"""detailed and simple contain same names""" |
|
273 |
names = sorted(map(lambda x: x["name"], servers)) |
|
274 |
dnames = sorted(map(lambda x: x["name"], dservers)) |
|
275 |
self.assertEqual(names, dnames) |
|
276 |
|
|
277 |
def _test_0030_wait_test_servers_to_build(self): |
|
278 |
"""Pseudo-test to wait for VMs to load""" |
|
279 |
print('') |
|
280 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
281 |
self._wait_for_status(self.server2['id'], 'BUILD') |
|
282 |
|
|
283 |
def test_get_server_details(self): |
|
284 |
"""Test get_server_details""" |
|
285 |
self.server1 = self._create_server( |
|
286 |
self.servname1, |
|
287 |
self.flavorid, |
|
288 |
self.img) |
|
289 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
290 |
self._test_0040_get_server_details() |
|
291 |
|
|
292 |
def _test_0040_get_server_details(self): |
|
293 |
r = self.client.get_server_details(self.server1['id']) |
|
294 |
self.assertEqual(r["name"], self.servname1) |
|
295 |
self.assertEqual(r["flavorRef"], self.flavorid) |
|
296 |
self.assertEqual(r["imageRef"], self.img) |
|
297 |
self.assertEqual(r["status"], "ACTIVE") |
|
298 |
|
|
299 |
def test_update_server_name(self): |
|
300 |
"""Test update_server_name""" |
|
301 |
self.server1 = self._create_server( |
|
302 |
self.servname1, |
|
303 |
self.flavorid, |
|
304 |
self.img) |
|
305 |
self._test_0050_update_server_name() |
|
306 |
|
|
307 |
def _test_0050_update_server_name(self): |
|
308 |
new_name = self.servname1 + '_new_name' |
|
309 |
self.client.update_server_name(self.server1['id'], new_name) |
|
310 |
r = self.client.get_server_details( |
|
311 |
self.server1['id'], |
|
312 |
success=(200, 400)) |
|
313 |
self.assertEqual(r['name'], new_name) |
|
314 |
changed = self.servers.pop(self.servname1) |
|
315 |
changed['name'] = new_name |
|
316 |
self.servers[new_name] = changed |
|
317 |
|
|
318 |
def test_reboot_server(self): |
|
319 |
"""Test reboot server""" |
|
320 |
self.server1 = self._create_server( |
|
321 |
self.servname1, |
|
322 |
self.flavorid, |
|
323 |
self.img) |
|
324 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
325 |
self.server2 = self._create_server( |
|
326 |
self.servname2, |
|
327 |
self.flavorid + 1, |
|
328 |
self.img) |
|
329 |
self._wait_for_status(self.server2['id'], 'BUILD') |
|
330 |
self._test_0060_reboot_server() |
|
331 |
self._wait_for_status(self.server1['id'], 'REBOOT') |
|
332 |
self._wait_for_status(self.server2['id'], 'REBOOT') |
|
333 |
|
|
334 |
def _test_0060_reboot_server(self): |
|
335 |
self.client.reboot_server(self.server1['id']) |
|
336 |
self.assertTrue(self._has_status(self.server1['id'], 'REBOOT')) |
|
337 |
self.client.reboot_server(self.server2['id'], hard=True) |
|
338 |
self.assertTrue(self._has_status(self.server2['id'], 'REBOOT')) |
|
339 |
|
|
340 |
def _test_0070_wait_test_servers_to_reboot(self): |
|
341 |
"""Pseudo-test to wait for VMs to load""" |
|
342 |
print('') |
|
343 |
self._wait_for_status(self.server1['id'], 'REBOOT') |
|
344 |
self._wait_for_status(self.server2['id'], 'REBOOT') |
|
345 |
|
|
346 |
def test_create_server_metadata(self): |
|
347 |
"""Test create_server_metadata""" |
|
348 |
self.server1 = self._create_server( |
|
349 |
self.servname1, |
|
350 |
self.flavorid, |
|
351 |
self.img) |
|
352 |
self._test_0080_create_server_metadata() |
|
353 |
|
|
354 |
def _test_0080_create_server_metadata(self): |
|
355 |
r1 = self.client.create_server_metadata( |
|
356 |
self.server1['id'], |
|
357 |
'mymeta', |
|
358 |
'mymeta val') |
|
359 |
self.assertTrue('mymeta' in r1) |
|
360 |
r2 = self.client.get_server_metadata(self.server1['id'], 'mymeta') |
|
361 |
self.assert_dicts_are_deeply_equal(r1, r2) |
|
362 |
|
|
363 |
def test_get_server_metadata(self): |
|
364 |
"""Test get server_metadata""" |
|
365 |
self.server1 = self._create_server( |
|
366 |
self.servname1, |
|
367 |
self.flavorid, |
|
368 |
self.img) |
|
369 |
self._test_0090_get_server_metadata() |
|
370 |
|
|
371 |
def _test_0090_get_server_metadata(self): |
|
372 |
self.client.create_server_metadata( |
|
373 |
self.server1['id'], |
|
374 |
'mymeta_0', |
|
375 |
'val_0') |
|
376 |
r = self.client.get_server_metadata(self.server1['id'], 'mymeta_0') |
|
377 |
self.assertEqual(r['mymeta_0'], 'val_0') |
|
378 |
|
|
379 |
def test_update_server_metadata(self): |
|
380 |
"""Test update_server_metadata""" |
|
381 |
self.server1 = self._create_server( |
|
382 |
self.servname1, |
|
383 |
self.flavorid, |
|
384 |
self.img) |
|
385 |
self._test_0100_update_server_metadata() |
|
386 |
|
|
387 |
def _test_0100_update_server_metadata(self): |
|
388 |
r1 = self.client.create_server_metadata( |
|
389 |
self.server1['id'], |
|
390 |
'mymeta3', |
|
391 |
'val2') |
|
392 |
self.assertTrue('mymeta3'in r1) |
|
393 |
r2 = self.client.update_server_metadata( |
|
394 |
self.server1['id'], |
|
395 |
mymeta3='val3') |
|
396 |
self.assertTrue(r2['mymeta3'], 'val3') |
|
397 |
|
|
398 |
def test_delete_server_metadata(self): |
|
399 |
"""Test delete_server_metadata""" |
|
400 |
self.server1 = self._create_server( |
|
401 |
self.servname1, |
|
402 |
self.flavorid, |
|
403 |
self.img) |
|
404 |
self._test_0110_delete_server_metadata() |
|
405 |
|
|
406 |
def _test_0110_delete_server_metadata(self): |
|
407 |
r1 = self.client.create_server_metadata( |
|
408 |
self.server1['id'], |
|
409 |
'mymeta', |
|
410 |
'val') |
|
411 |
self.assertTrue('mymeta' in r1) |
|
412 |
self.client.delete_server_metadata(self.server1['id'], 'mymeta') |
|
413 |
try: |
|
414 |
self.client.get_server_metadata(self.server1['id'], 'mymeta') |
|
415 |
raise ClientError('Wrong Error', status=100) |
|
416 |
except ClientError as err: |
|
417 |
self.assertEqual(err.status, 404) |
|
418 |
|
|
419 |
def test_list_flavors(self): |
|
420 |
"""Test flavors_get""" |
|
421 |
self._test_0120_list_flavors() |
|
422 |
|
|
423 |
def _test_0120_list_flavors(self): |
|
424 |
r = self.client.list_flavors() |
|
425 |
self.assertTrue(len(r) > 1) |
|
426 |
r = self.client.list_flavors(detail=True) |
|
427 |
self.assertTrue('SNF:disk_template' in r[0]) |
|
428 |
|
|
429 |
def test_get_flavor_details(self): |
|
430 |
"""Test test_get_flavor_details""" |
|
431 |
self._test_0130_get_flavor_details() |
|
432 |
|
|
433 |
def _test_0130_get_flavor_details(self): |
|
434 |
r = self.client.get_flavor_details(self.flavorid) |
|
435 |
self.assert_dicts_are_deeply_equal(self._flavor_details, r) |
|
436 |
|
|
437 |
def test_list_images(self): |
|
438 |
"""Test list_images""" |
|
439 |
self._test_0140_list_images() |
|
440 |
|
|
441 |
def _test_0140_list_images(self): |
|
442 |
r = self.client.list_images() |
|
443 |
self.assertTrue(len(r) > 1) |
|
444 |
r = self.client.list_images(detail=True) |
|
445 |
for detailed_img in r: |
|
446 |
if detailed_img['id'] == self.img: |
|
447 |
break |
|
448 |
self.assert_dicts_are_deeply_equal(detailed_img, self.img_details) |
|
449 |
|
|
450 |
def test_get_image_details(self): |
|
451 |
"""Test image_details""" |
|
452 |
self._test_0150_get_image_details() |
|
453 |
|
|
454 |
def _test_0150_get_image_details(self): |
|
455 |
r = self.client.get_image_details(self.img) |
|
456 |
self.assert_dicts_are_deeply_equal(r, self.img_details) |
|
457 |
|
|
458 |
def test_get_image_metadata(self): |
|
459 |
"""Test get_image_metadata""" |
|
460 |
self._test_0160_get_image_metadata() |
|
461 |
|
|
462 |
def _test_0160_get_image_metadata(self): |
|
463 |
r = self.client.get_image_metadata(self.img) |
|
464 |
self.assert_dicts_are_deeply_equal( |
|
465 |
self.img_details['metadata']['values'], r) |
|
466 |
for key, val in self.img_details['metadata']['values'].items(): |
|
467 |
r = self.client.get_image_metadata(self.img, key) |
|
468 |
self.assertEqual(r[key], val) |
|
469 |
|
|
470 |
def test_shutdown_server(self): |
|
471 |
"""Test shutdown_server""" |
|
472 |
self.server1 = self._create_server( |
|
473 |
self.servname1, |
|
474 |
self.flavorid, |
|
475 |
self.img) |
|
476 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
477 |
self._test_0170_shutdown_server() |
|
478 |
|
|
479 |
def _test_0170_shutdown_server(self): |
|
480 |
self.client.shutdown_server(self.server1['id']) |
|
481 |
self._wait_for_status(self.server1['id'], 'ACTIVE') |
|
482 |
r = self.client.get_server_details(self.server1['id']) |
|
483 |
self.assertEqual(r['status'], 'STOPPED') |
|
484 |
|
|
485 |
def test_start_server(self): |
|
486 |
"""Test start_server""" |
|
487 |
self.server1 = self._create_server( |
|
488 |
self.servname1, |
|
489 |
self.flavorid, |
|
490 |
self.img) |
|
491 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
492 |
self.client.shutdown_server(self.server1['id']) |
|
493 |
self._wait_for_status(self.server1['id'], 'ACTIVE') |
|
494 |
self._test_0180_start_server() |
|
495 |
|
|
496 |
def _test_0180_start_server(self): |
|
497 |
self.client.start_server(self.server1['id']) |
|
498 |
self._wait_for_status(self.server1['id'], 'STOPPED') |
|
499 |
r = self.client.get_server_details(self.server1['id']) |
|
500 |
self.assertEqual(r['status'], 'ACTIVE') |
|
501 |
|
|
502 |
def test_get_server_console(self): |
|
503 |
"""Test get_server_console""" |
|
504 |
self.server2 = self._create_server( |
|
505 |
self.servname2, |
|
506 |
self.flavorid + 2, |
|
507 |
self.img) |
|
508 |
self._wait_for_status(self.server2['id'], 'BUILD') |
|
509 |
self._test_0190_get_server_console() |
|
510 |
|
|
511 |
def _test_0190_get_server_console(self): |
|
512 |
r = self.client.get_server_console(self.server2['id']) |
|
513 |
self.assertTrue('host' in r) |
|
514 |
self.assertTrue('password' in r) |
|
515 |
self.assertTrue('port' in r) |
|
516 |
self.assertTrue('type' in r) |
|
517 |
|
|
518 |
def test_get_firewall_profile(self): |
|
519 |
"""Test get_firewall_profile""" |
|
520 |
self.server1 = self._create_server( |
|
521 |
self.servname1, |
|
522 |
self.flavorid, |
|
523 |
self.img) |
|
524 |
self._test_0200_get_firewall_profile() |
|
525 |
|
|
526 |
def _test_0200_get_firewall_profile(self): |
|
527 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
528 |
fprofile = self.client.get_firewall_profile(self.server1['id']) |
|
529 |
self.assertTrue(fprofile in self.PROFILES) |
|
530 |
|
|
531 |
def test_set_firewall_profile(self): |
|
532 |
"""Test set_firewall_profile""" |
|
533 |
self.server1 = self._create_server( |
|
534 |
self.servname1, |
|
535 |
self.flavorid, |
|
536 |
self.img) |
|
537 |
self._test_0210_set_firewall_profile() |
|
538 |
|
|
539 |
def _test_0210_set_firewall_profile(self): |
|
540 |
|
|
541 |
self._wait_for_status(self.server1['id'], 'BUILD') |
|
542 |
PROFILES = ['DISABLED', 'ENABLED', 'DISABLED', 'PROTECTED'] |
|
543 |
fprofile = self.client.get_firewall_profile(self.server1['id']) |
|
544 |
print('') |
|
545 |
count_success = 0 |
|
546 |
for counter, fprofile in enumerate(PROFILES): |
|
547 |
npos = counter + 1 |
|
548 |
try: |
|
549 |
nprofile = PROFILES[npos] |
|
550 |
except IndexError: |
|
551 |
nprofile = PROFILES[0] |
|
552 |
print('\tprofile swap %s: %s -> %s' % (npos, fprofile, nprofile)) |
|
553 |
self.client.set_firewall_profile(self.server1['id'], nprofile) |
|
554 |
time.sleep(0.5) |
|
555 |
self.client.reboot_server(self.server1['id'], hard=True) |
|
556 |
time.sleep(1) |
|
557 |
self._wait_for_status(self.server1['id'], 'REBOOT') |
|
558 |
time.sleep(0.5) |
|
559 |
changed = self.client.get_firewall_profile(self.server1['id']) |
|
560 |
try: |
|
561 |
self.assertEqual(changed, nprofile) |
|
562 |
except AssertionError as err: |
|
563 |
if count_success: |
|
564 |
print('\tFAIL in swap #%s' % npos) |
|
565 |
break |
|
566 |
else: |
|
567 |
raise err |
|
568 |
count_success += 1 |
|
569 |
|
|
570 |
def test_get_server_stats(self): |
|
571 |
self.server1 = self._create_server( |
|
572 |
self.servname1, |
|
573 |
self.flavorid, |
|
574 |
self.img) |
Also available in: Unified diff