1 # Copyright 2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
34 from mock import patch, call
35 from unittest import TestCase
36 from itertools import product
38 from kamaki.clients.compute import ComputeClient, ComputeClientApi
39 from kamaki.clients import ClientError
42 rest_pkg = 'kamaki.clients.compute.rest_api.ComputeClientApi'
43 compute_pkg = 'kamaki.clients.compute.ComputeClient'
45 img_ref = "1m4g3-r3f3r3nc3"
48 vm_send = dict(server=dict(
52 metadata=dict(os="debian", users="root")))
53 vm_recv = dict(server=dict(
55 updated="2013-03-01T10:04:00.637152+00:00",
59 created="2013-03-01T10:04:00.087324+00:00",
61 adminPass="n0n3sh@11p@55",
65 metadata=dict(values=dict(os="debian", users="root"))))
66 img_recv = dict(image=dict(
68 updated="2013-02-26T11:10:14+00:00",
70 created="2013-02-26T11:03:29+00:00",
73 metadata=dict(values=dict(
74 partition_table="msdos",
82 description="Debian 6.0.7 (Squeeze) Base System"))))
83 vm_list = dict(servers=dict(values=[
84 dict(name='n1', id=1),
85 dict(name='n2', id=2)]))
86 flavor_list = dict(flavors=dict(values=[
87 dict(id=41, name="C1R1024D20"),
88 dict(id=42, name="C1R1024D40"),
89 dict(id=43, name="C1R1028D20")]))
90 img_list = dict(images=dict(values=[
91 dict(name="maelstrom", id="0fb03e45-7d5a-4515-bd4e-e6bbf6457f06"),
92 dict(name="edx_saas", id="1357163d-5fd8-488e-a117-48734c526206"),
93 dict(name="Debian_Wheezy_Base", id="1f8454f0-8e3e-4b6c-ab8e-5236b728dffe"),
94 dict(name="CentOS", id="21894b48-c805-4568-ac8b-7d4bb8eb533d"),
95 dict(name="Ubuntu Desktop", id="37bc522c-c479-4085-bfb9-464f9b9e2e31"),
96 dict(name="Ubuntu 12.10", id="3a24fef9-1a8c-47d1-8f11-e07bd5e544fd"),
97 dict(name="Debian Base", id="40ace203-6254-4e17-a5cb-518d55418a7d"),
98 dict(name="ubuntu_bundled", id="5336e265-5c7c-4127-95cb-2bf832a79903")]))
102 """FR stands for Fake Response"""
113 class ComputeRestApi(TestCase):
115 """Set up a ComputesRestApi thorough test"""
117 self.url = 'http://cyclades.example.com'
118 self.token = 'cyc14d3s70k3n'
119 self.client = ComputeClientApi(self.url, self.token)
124 @patch('%s.get' % rest_pkg, return_value=FR())
125 def test_servers_get(self, get):
126 vm_id = vm_recv['server']['id']
132 (server_id, command, success, kwargs) = args
133 self.client.servers_get(*args[:3], **args[3])
134 vm_str = '/%s' % server_id if server_id else ''
135 cmd_str = '/%s' % command if command else ''
136 self.assertEqual(get.mock_calls[-1], call(
137 '/servers%s%s' % (vm_str, cmd_str),
142 class Compute(TestCase):
144 def assert_dicts_are_equal(self, d1, d2):
145 for k, v in d1.items():
146 self.assertTrue(k in d2)
147 if isinstance(v, dict):
148 self.assert_dicts_are_equal(v, d2[k])
150 self.assertEqual(unicode(v), unicode(d2[k]))
152 """Set up a Cyclades thorough test"""
154 self.url = 'http://cyclades.example.com'
155 self.token = 'cyc14d3s70k3n'
156 self.client = ComputeClient(self.url, self.token)
163 '%s.get_image_details' % compute_pkg,
164 return_value=img_recv['image'])
165 def test_create_server(self, GID):
167 ComputeClient, 'servers_post',
168 side_effect=ClientError(
169 'REQUEST ENTITY TOO LARGE',
173 self.client.create_server,
174 vm_name, fid, img_ref)
177 ComputeClient, 'servers_post',
178 return_value=FR()) as post:
179 r = self.client.create_server(vm_name, fid, img_ref)
180 self.assertEqual(r, FR.json['server'])
181 self.assertEqual(GID.mock_calls[-1], call(img_ref))
182 self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
183 prsn = 'Personality string (does not work with real servers)'
184 self.client.create_server(vm_name, fid, img_ref, prsn)
185 expected = dict(server=dict(vm_send['server']))
186 expected['server']['personality'] = prsn
187 self.assertEqual(post.mock_calls[-1], call(json_data=expected))
189 @patch('%s.servers_get' % compute_pkg, return_value=FR())
190 def test_list_servers(self, SG):
192 for detail in (False, True):
193 r = self.client.list_servers(detail)
194 self.assertEqual(SG.mock_calls[-1], call(
195 command='detail' if detail else ''))
196 for i, vm in enumerate(vm_list['servers']['values']):
197 self.assert_dicts_are_equal(r[i], vm)
198 self.assertEqual(i + 1, len(r))
200 @patch('%s.servers_get' % compute_pkg, return_value=FR())
201 def test_get_server_details(self, SG):
202 vm_id = vm_recv['server']['id']
203 r = self.client.get_server_details(vm_id)
204 SG.assert_called_once_with(vm_id)
205 self.assert_dicts_are_equal(r, vm_recv['server'])
207 @patch('%s.servers_put' % compute_pkg, return_value=FR())
208 def test_update_server_name(self, SP):
209 vm_id = vm_recv['server']['id']
210 new_name = vm_name + '_new'
211 self.client.update_server_name(vm_id, new_name)
212 SP.assert_called_once_with(vm_id, json_data=dict(
213 server=dict(name=new_name)))
215 @patch('%s.servers_post' % compute_pkg, return_value=FR())
216 def test_reboot_server(self, SP):
217 vm_id = vm_recv['server']['id']
218 for hard in (None, True):
219 self.client.reboot_server(vm_id, hard=hard)
220 self.assertEqual(SP.mock_calls[-1], call(
222 json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
224 @patch('%s.servers_put' % compute_pkg, return_value=FR())
225 def test_create_server_metadata(self, SP):
226 vm_id = vm_recv['server']['id']
227 metadata = dict(m1='v1', m2='v2', m3='v3')
228 FR.json = dict(meta=vm_recv['server'])
229 for k, v in metadata.items():
230 r = self.client.create_server_metadata(vm_id, k, v)
231 self.assert_dicts_are_equal(r, vm_recv['server'])
232 self.assertEqual(SP.mock_calls[-1], call(
233 vm_id, 'meta/%s' % k,
234 json_data=dict(meta={k: v}), success=201))
236 @patch('%s.servers_get' % compute_pkg, return_value=FR())
237 def test_get_server_metadata(self, SG):
238 vm_id = vm_recv['server']['id']
239 metadata = dict(m1='v1', m2='v2', m3='v3')
240 FR.json = dict(metadata=dict(values=metadata))
241 r = self.client.get_server_metadata(vm_id)
242 SG.assert_called_once_with(vm_id, '/meta')
243 self.assert_dicts_are_equal(r, metadata)
245 for k, v in metadata.items():
246 FR.json = dict(meta={k: v})
247 r = self.client.get_server_metadata(vm_id, k)
248 self.assert_dicts_are_equal(r, {k: v})
249 self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
251 @patch('%s.servers_post' % compute_pkg, return_value=FR())
252 def test_update_server_metadata(self, SP):
253 vm_id = vm_recv['server']['id']
254 metadata = dict(m1='v1', m2='v2', m3='v3')
255 FR.json = dict(metadata=metadata)
256 r = self.client.update_server_metadata(vm_id, **metadata)
257 self.assert_dicts_are_equal(r, metadata)
258 SP.assert_called_once_with(
260 json_data=dict(metadata=metadata), success=201)
262 @patch('%s.servers_delete' % compute_pkg, return_value=FR())
263 def test_delete_server_metadata(self, SD):
264 vm_id = vm_recv['server']['id']
266 self.client.delete_server_metadata(vm_id, key)
267 SD.assert_called_once_with(vm_id, 'meta/' + key)
269 @patch('%s.flavors_get' % compute_pkg, return_value=FR())
270 def test_list_flavors(self, FG):
271 FR.json = flavor_list
272 for cmd in ('', 'detail'):
273 r = self.client.list_flavors(detail=(cmd == 'detail'))
274 self.assertEqual(FG.mock_calls[-1], call(command=cmd))
275 self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
277 @patch('%s.flavors_get' % compute_pkg, return_value=FR())
278 def test_get_flavor_details(self, FG):
279 FR.json = dict(flavor=flavor_list['flavors'])
280 r = self.client.get_flavor_details(fid)
281 FG.assert_called_once_with(fid)
282 self.assert_dicts_are_equal(r, flavor_list['flavors'])
284 @patch('%s.images_get' % compute_pkg, return_value=FR())
285 def test_list_images(self, IG):
287 for cmd in ('', 'detail'):
288 r = self.client.list_images(detail=(cmd == 'detail'))
289 self.assertEqual(IG.mock_calls[-1], call(command=cmd))
290 expected = img_list['images']['values']
291 for i in range(len(r)):
292 self.assert_dicts_are_equal(expected[i], r[i])
294 @patch('%s.images_get' % compute_pkg, return_value=FR())
295 def test_get_image_details(self, IG):
297 r = self.client.get_image_details(img_ref)
298 IG.assert_called_once_with(img_ref)
299 self.assert_dicts_are_equal(r, img_recv['image'])
301 @patch('%s.images_get' % compute_pkg, return_value=FR())
302 def test_get_image_metadata(self, IG):
303 for key in ('', '50m3k3y'):
304 FR.json = dict(meta=img_recv['image']) if (
305 key) else dict(metadata=dict(values=img_recv['image']))
306 r = self.client.get_image_metadata(img_ref, key)
307 self.assertEqual(IG.mock_calls[-1], call(
309 '/meta%s' % (('/%s' % key) if key else '')))
310 self.assert_dicts_are_equal(img_recv['image'], r)
312 @patch('%s.servers_delete' % compute_pkg, return_value=FR())
313 def test_delete_server(self, SD):
314 vm_id = vm_recv['server']['id']
315 self.client.delete_server(vm_id)
316 SD.assert_called_once_with(vm_id)
318 @patch('%s.images_delete' % compute_pkg, return_value=FR())
319 def test_delete_image(self, ID):
320 self.client.delete_image(img_ref)
321 ID.assert_called_once_with(img_ref)
323 @patch('%s.images_put' % compute_pkg, return_value=FR())
324 def test_create_image_metadata(self, IP):
325 (key, val) = ('k1', 'v1')
326 FR.json = dict(meta=img_recv['image'])
327 r = self.client.create_image_metadata(img_ref, key, val)
328 IP.assert_called_once_with(
329 img_ref, 'meta/%s' % key,
330 json_data=dict(meta={key: val}))
331 self.assert_dicts_are_equal(r, img_recv['image'])
333 @patch('%s.images_post' % compute_pkg, return_value=FR())
334 def test_update_image_metadata(self, IP):
335 metadata = dict(m1='v1', m2='v2', m3='v3')
336 FR.json = dict(metadata=metadata)
337 r = self.client.update_image_metadata(img_ref, **metadata)
338 IP.assert_called_once_with(
340 json_data=dict(metadata=metadata))
341 self.assert_dicts_are_equal(r, metadata)
343 @patch('%s.images_delete' % compute_pkg, return_value=FR())
344 def test_delete_image_metadata(self, ID):
346 self.client.delete_image_metadata(img_ref, key)
347 ID.assert_called_once_with(img_ref, '/meta/%s' % key)
350 if __name__ == '__main__':
352 from kamaki.clients.test import runTestCase
354 if not argv[1:] or argv[1] == 'Compute':
356 runTestCase(Compute, 'Compute Client', argv[2:])
357 if not argv[1:] or argv[1] == 'ComputeRestApi':
359 runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
361 print('TestCase %s not found' % argv[1])