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.
33 from mock import patch, Mock, call
34 from unittest import TestCase
35 from json import loads
37 from kamaki.clients import Client, ClientError
38 from kamaki.clients.cyclades import CycladesClient
39 from kamaki.clients.cyclades_rest_api import CycladesClientApi
41 img_ref = "1m4g3-r3f3r3nc3"
44 vm_send = dict(server=dict(
48 metadata=dict(os="debian", users="root")))
49 vm_recv = dict(server=dict(
51 updated="2013-03-01T10:04:00.637152+00:00",
55 created="2013-03-01T10:04:00.087324+00:00",
57 adminPass="n0n3sh@11p@55",
61 metadata=dict(values=dict(os="debian", users="root"))))
62 img_recv = dict(image=dict(
64 updated="2013-02-26T11:10:14+00:00",
66 created="2013-02-26T11:03:29+00:00",
69 metadata=dict(values=dict(
70 partition_table="msdos",
78 description="Debian 6.0.7 (Squeeze) Base System"))))
79 vm_list = dict(servers=dict(values=[
80 dict(name='n1', id=1),
81 dict(name='n2', id=2)]))
82 flavor_list = dict(flavors=dict(values=[
83 dict(id=41, name="C1R1024D20"),
84 dict(id=42, name="C1R1024D40"),
85 dict(id=43, name="C1R1028D20")]))
86 img_list = dict(images=dict(values=[
87 dict(name="maelstrom", id="0fb03e45-7d5a-4515-bd4e-e6bbf6457f06"),
88 dict(name="edx_saas", id="1357163d-5fd8-488e-a117-48734c526206"),
89 dict(name="Debian_Wheezy_Base", id="1f8454f0-8e3e-4b6c-ab8e-5236b728dffe"),
90 dict(name="CentOS", id="21894b48-c805-4568-ac8b-7d4bb8eb533d"),
91 dict(name="Ubuntu Desktop", id="37bc522c-c479-4085-bfb9-464f9b9e2e31"),
92 dict(name="Ubuntu 12.10", id="3a24fef9-1a8c-47d1-8f11-e07bd5e544fd"),
93 dict(name="Debian Base", id="40ace203-6254-4e17-a5cb-518d55418a7d"),
94 dict(name="ubuntu_bundled", id="5336e265-5c7c-4127-95cb-2bf832a79903")]))
95 net_send = dict(network=dict(dhcp=False, name='someNet'))
96 net_recv = dict(network=dict(
98 updated="2013-03-05T15:04:51.758780+00:00",
100 created="2013-03-05T15:04:51.758728+00:00",
106 cidr="192.168.1.0/24",
109 attachments=dict(values=[dict(name='att1'), dict(name='att2')])))
110 net_list = dict(networks=dict(values=[
111 dict(id=1, name='n1'),
112 dict(id=2, name='n2'),
113 dict(id=3, name='n3')]))
117 """FR stands for Fake Response"""
127 khttp = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
128 cyclades_pkg = 'kamaki.clients.cyclades.CycladesClient'
131 class Cyclades(TestCase):
133 def assert_dicts_are_equal(self, d1, d2):
134 for k, v in d1.items():
135 self.assertTrue(k in d2)
136 if isinstance(v, dict):
137 self.assert_dicts_are_equal(v, d2[k])
139 self.assertEqual(unicode(v), unicode(d2[k]))
141 """Set up a Cyclades thorough test"""
143 self.url = 'http://cyclades.example.com'
144 self.token = 'cyc14d3s70k3n'
145 self.client = CycladesClient(self.url, self.token)
146 from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
147 self.C = KamakiHTTPConnection
153 def test_list_servers(self):
158 return_value=FR()) as perform_req:
159 r = self.client.list_servers()
160 self.assertEqual(self.client.http_client.url, self.url)
161 self.assertEqual(self.client.http_client.path, '/servers')
162 (method, data, a_headers, a_params) = perform_req.call_args[0]
163 self.assert_dicts_are_equal(dict(values=r), vm_list['servers'])
164 r = self.client.list_servers(detail=True)
165 self.assertEqual(self.client.http_client.url, self.url)
166 self.assertEqual(self.client.http_client.path, '/servers/detail')
170 return_value=FR()) as servers_get:
171 self.client.list_servers(changes_since=True)
172 self.assertTrue(servers_get.call_args[1]['changes_since'])
174 @patch('%s.perform_request' % khttp, return_value=FR())
175 def test_list_flavors(self, PR):
176 FR.json = flavor_list
177 r = self.client.list_flavors()
178 self.assertEqual(self.client.http_client.url, self.url)
179 self.assertEqual(self.client.http_client.path, '/flavors')
180 (method, data, a_headers, a_params) = PR.call_args[0]
181 self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
182 r = self.client.list_flavors(detail=True)
183 self.assertEqual(self.client.http_client.url, self.url)
184 self.assertEqual(self.client.http_client.path, '/flavors/detail')
186 @patch('%s.perform_request' % khttp, return_value=FR())
187 def test_get_flavor_details(self, PR):
188 FR.json = dict(flavor=flavor_list['flavors'])
189 r = self.client.get_flavor_details(fid)
190 self.assertEqual(self.client.http_client.url, self.url)
191 self.assertEqual(self.client.http_client.path, '/flavors/%s' % fid)
192 self.assert_dicts_are_equal(r, flavor_list['flavors'])
194 @patch('%s.perform_request' % khttp, return_value=FR())
195 def test_list_images(self, PR):
197 r = self.client.list_images()
198 self.assertEqual(self.client.http_client.url, self.url)
199 self.assertEqual(self.client.http_client.path, '/images')
200 expected = img_list['images']['values']
201 for i in range(len(r)):
202 self.assert_dicts_are_equal(expected[i], r[i])
203 self.client.list_images(detail=True)
204 self.assertEqual(self.client.http_client.url, self.url)
205 self.assertEqual(self.client.http_client.path, '/images/detail')
207 @patch('%s.perform_request' % khttp, return_value=FR())
208 def test_get_image_details(self, PR):
210 r = self.client.get_image_details(img_ref)
211 self.assertEqual(self.client.http_client.url, self.url)
212 self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
213 self.assert_dicts_are_equal(r, img_recv['image'])
215 @patch('%s.images_get' % cyclades_pkg, return_value=FR())
216 def test_get_image_metadata(self, IG):
217 FR.json = dict(metadata=dict(values=img_recv['image']))
218 r = self.client.get_image_metadata(img_ref)
219 self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta'))
220 self.assert_dicts_are_equal(img_recv['image'], r)
221 FR.json = dict(meta=img_recv['image'])
223 self.client.get_image_metadata(img_ref, key)
224 self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta/%s' % key))
226 @patch('%s.perform_request' % khttp, return_value=FR())
227 def test_shutdown_server(self, PR):
228 vm_id = vm_recv['server']['id']
230 self.client.shutdown_server(vm_id)
231 self.assertEqual(self.client.http_client.url, self.url)
233 self.client.http_client.path,
234 '/servers/%s/action' % vm_id)
237 ('post', '{"shutdown": {}}', {}, {}))
239 @patch('%s.perform_request' % khttp, return_value=FR())
240 def test_start_server(self, PR):
241 vm_id = vm_recv['server']['id']
243 self.client.start_server(vm_id)
244 self.assertEqual(self.client.http_client.url, self.url)
246 self.client.http_client.path,
247 '/servers/%s/action' % vm_id)
248 self.assertEqual(PR.call_args[0], ('post', '{"start": {}}', {}, {}))
250 @patch('%s.perform_request' % khttp, return_value=FR())
251 def test_get_server_console(self, PR):
252 cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
254 vm_id = vm_recv['server']['id']
255 r = self.client.get_server_console(vm_id)
256 self.assertEqual(self.client.http_client.url, self.url)
258 self.client.http_client.path,
259 '/servers/%s/action' % vm_id)
260 self.assert_dicts_are_equal(cnsl['console'], r)
263 ('post', '{"console": {"type": "vnc"}}', {}, {}))
265 def test_get_firewall_profile(self):
266 vm_id = vm_recv['server']['id']
268 ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
271 'get_server_details',
272 return_value=ret) as GSD:
273 r = self.client.get_firewall_profile(vm_id)
274 self.assertEqual(r, v)
275 self.assertEqual(GSD.call_args[0], (vm_id,))
276 ret['attachments']['values'][0].pop('firewallProfile')
279 self.client.get_firewall_profile,
282 @patch('%s.perform_request' % khttp, return_value=FR())
283 def test_set_firewall_profile(self, PR):
284 vm_id = vm_recv['server']['id']
287 self.client.set_firewall_profile(vm_id, v)
288 self.assertEqual(self.client.http_client.url, self.url)
290 self.client.http_client.path,
291 '/servers/%s/action' % vm_id)
292 self.assertEqual(PR.call_args[0], (
294 '{"firewallProfile": {"profile": "%s"}}' % v,
298 @patch('%s.perform_request' % khttp, return_value=FR())
299 def test_get_server_stats(self, PR):
300 vm_id = vm_recv['server']['id']
301 stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
302 FR.json = dict(stats=stats)
303 r = self.client.get_server_stats(vm_id)
304 self.assertEqual(self.client.http_client.url, self.url)
306 self.client.http_client.path,
307 '/servers/%s/stats' % vm_id)
308 self.assert_dicts_are_equal(stats, r)
310 @patch('%s.perform_request' % khttp, return_value=FR())
311 def test_create_network(self, PR):
312 net_name = net_send['network']['name']
316 cidr='192.168.0.0/24',
317 gateway='192.168.0.1',
320 test_args = dict(full_args)
321 test_args.update(dict(empty=None, full=None))
322 for arg, val in test_args.items():
323 kwargs = {} if arg == 'empty' else full_args if (
324 arg == 'full') else {arg: val}
325 r = self.client.create_network(net_name, **kwargs)
326 self.assertEqual(self.client.http_client.url, self.url)
328 self.client.http_client.path,
330 self.assert_dicts_are_equal(r, net_recv['network'])
331 data = PR.call_args[0][1]
332 expected = dict(network=dict(net_send['network']))
333 expected['network'].update(kwargs)
334 self.assert_dicts_are_equal(loads(data), expected)
336 @patch('%s.perform_request' % khttp, return_value=FR())
337 def test_connect_server(self, PR):
338 vm_id = vm_recv['server']['id']
339 net_id = net_recv['network']['id']
341 self.client.connect_server(vm_id, net_id)
342 self.assertEqual(self.client.http_client.url, self.url)
344 self.client.http_client.path,
345 '/networks/%s/action' % net_id)
348 ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
350 @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
351 def test_disconnect_server(self, NP):
352 vm_id = vm_recv['server']['id']
353 net_id = net_recv['network']['id']
354 nic_id = 'nic-%s-%s' % (net_id, vm_id)
356 dict(id=nic_id, network_id=net_id),
357 dict(id='another-nic-id', network_id='another-net-id'),
358 dict(id=nic_id * 2, network_id=net_id * 2)]
362 return_value=vm_nics) as LSN:
363 r = self.client.disconnect_server(vm_id, nic_id)
364 self.assertEqual(r, 1)
365 self.assertEqual(LSN.call_args[0], (vm_id,))
366 self.assertEqual(NP.call_args[0], (net_id, 'action'))
369 dict(json_data=dict(remove=dict(attachment=nic_id))))
371 @patch('%s.perform_request' % khttp, return_value=FR())
372 def test_list_server_nics(self, PR):
373 vm_id = vm_recv['server']['id']
374 nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
376 r = self.client.list_server_nics(vm_id)
377 self.assertEqual(self.client.http_client.url, self.url)
379 self.client.http_client.path,
380 '/servers/%s/ips' % vm_id)
381 expected = nics['addresses']['values']
382 for i in range(len(r)):
383 self.assert_dicts_are_equal(r[i], expected[i])
385 @patch('%s.perform_request' % khttp, return_value=FR())
386 def test_list_networks(self, PR):
388 r = self.client.list_networks()
389 self.assertEqual(self.client.http_client.url, self.url)
390 self.assertEqual(self.client.http_client.path, '/networks')
391 expected = net_list['networks']['values']
392 for i in range(len(r)):
393 self.assert_dicts_are_equal(expected[i], r[i])
394 self.client.list_networks(detail=True)
395 self.assertEqual(self.client.http_client.url, self.url)
396 self.assertEqual(self.client.http_client.path, '/networks/detail')
398 @patch('%s.perform_request' % khttp, return_value=FR())
399 def test_list_network_nics(self, PR):
400 net_id = net_recv['network']['id']
402 r = self.client.list_network_nics(net_id)
403 self.assertEqual(self.client.http_client.url, self.url)
405 self.client.http_client.path,
406 '/networks/%s' % net_id)
407 expected = net_recv['network']['attachments']['values']
408 for i in range(len(r)):
409 self.assert_dicts_are_equal(r[i], expected[i])
411 @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
412 def test_disconnect_network_nics(self, NP):
413 net_id = net_recv['network']['id']
414 nics = ['nic1', 'nic2', 'nic3']
418 return_value=nics) as lnn:
419 self.client.disconnect_network_nics(net_id)
420 lnn.assert_called_once_with(net_id)
421 for i in range(len(nics)):
422 expected = call(net_id, 'action', json_data=dict(
423 remove=dict(attachment=nics[i])))
424 self.assertEqual(expected, NP.mock_calls[i])
426 @patch('%s.perform_request' % khttp, return_value=FR())
427 def test_get_network_details(self, PR):
429 net_id = net_recv['network']['id']
430 r = self.client.get_network_details(net_id)
431 self.assertEqual(self.client.http_client.url, self.url)
433 self.client.http_client.path,
434 '/networks/%s' % net_id)
435 self.assert_dicts_are_equal(r, net_recv['network'])
437 @patch('%s.perform_request' % khttp, return_value=FR())
438 def test_update_network_name(self, PR):
439 net_id = net_recv['network']['id']
440 new_name = '%s_new' % net_id
442 self.client.update_network_name(net_id, new_name)
443 self.assertEqual(self.client.http_client.url, self.url)
444 self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
445 (method, data, a_headers, a_params) = PR.call_args[0]
446 self.assert_dicts_are_equal(
447 dict(network=dict(name=new_name)),
450 @patch('%s.perform_request' % khttp, return_value=FR())
451 def test_delete_server(self, PR):
452 vm_id = vm_recv['server']['id']
454 self.client.delete_server(vm_id)
455 self.assertEqual(self.client.http_client.url, self.url)
456 self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
458 @patch('%s.perform_request' % khttp, return_value=FR())
459 def test_delete_image(self, PR):
461 self.client.delete_image(img_ref)
462 self.assertEqual(self.client.http_client.url, self.url)
463 self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
465 @patch('%s.perform_request' % khttp, return_value=FR())
466 def test_delete_network(self, PR):
467 net_id = net_recv['network']['id']
469 self.client.delete_network(net_id)
470 self.assertEqual(self.client.http_client.url, self.url)
471 self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
473 @patch('%s.perform_request' % khttp, return_value=FR())
474 def test_create_image_metadata(self, PR):
475 metadata = dict(m1='v1', m2='v2', m3='v3')
476 FR.json = dict(meta=img_recv['image'])
479 self.client.create_image_metadata,
480 img_ref, 'key', 'value')
482 for k, v in metadata.items():
483 r = self.client.create_image_metadata(img_ref, k, v)
484 self.assertEqual(self.client.http_client.url, self.url)
486 self.client.http_client.path,
487 '/images/%s/meta/%s' % (img_ref, k))
488 (method, data, a_headers, a_params) = PR.call_args[0]
489 self.assertEqual(dict(meta={k: v}), loads(data))
490 self.assert_dicts_are_equal(r, img_recv['image'])
492 @patch('%s.images_post' % cyclades_pkg, return_value=FR())
493 def test_update_image_metadata(self, images_post):
494 metadata = dict(m1='v1', m2='v2', m3='v3')
495 FR.json = dict(metadata=metadata)
496 r = self.client.update_image_metadata(img_ref, **metadata)
497 self.assert_dicts_are_equal(r, metadata)
498 (called_id, cmd) = images_post.call_args[0]
499 self.assertEqual(called_id, img_ref)
500 self.assertEqual(cmd, 'meta')
501 data = images_post.call_args[1]['json_data']
502 self.assert_dicts_are_equal(data, dict(metadata=metadata))
504 @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
505 def test_delete_image_metadata(self, images_delete):
507 self.client.delete_image_metadata(img_ref, key)
509 (img_ref, '/meta/' + key),
510 images_delete.call_args[0])
512 if __name__ == '__main__':
514 from kamaki.clients.test import runTestCase
515 runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])