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_shutdown_server(self, PR):
176 vm_id = vm_recv['server']['id']
178 self.client.shutdown_server(vm_id)
179 self.assertEqual(self.client.http_client.url, self.url)
181 self.client.http_client.path,
182 '/servers/%s/action' % vm_id)
185 ('post', '{"shutdown": {}}', {}, {}))
187 @patch('%s.perform_request' % khttp, return_value=FR())
188 def test_start_server(self, PR):
189 vm_id = vm_recv['server']['id']
191 self.client.start_server(vm_id)
192 self.assertEqual(self.client.http_client.url, self.url)
194 self.client.http_client.path,
195 '/servers/%s/action' % vm_id)
196 self.assertEqual(PR.call_args[0], ('post', '{"start": {}}', {}, {}))
198 @patch('%s.perform_request' % khttp, return_value=FR())
199 def test_get_server_console(self, PR):
200 cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
202 vm_id = vm_recv['server']['id']
203 r = self.client.get_server_console(vm_id)
204 self.assertEqual(self.client.http_client.url, self.url)
206 self.client.http_client.path,
207 '/servers/%s/action' % vm_id)
208 self.assert_dicts_are_equal(cnsl['console'], r)
211 ('post', '{"console": {"type": "vnc"}}', {}, {}))
213 def test_get_firewall_profile(self):
214 vm_id = vm_recv['server']['id']
216 ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
219 'get_server_details',
220 return_value=ret) as GSD:
221 r = self.client.get_firewall_profile(vm_id)
222 self.assertEqual(r, v)
223 self.assertEqual(GSD.call_args[0], (vm_id,))
224 ret['attachments']['values'][0].pop('firewallProfile')
227 self.client.get_firewall_profile,
230 @patch('%s.perform_request' % khttp, return_value=FR())
231 def test_set_firewall_profile(self, PR):
232 vm_id = vm_recv['server']['id']
235 self.client.set_firewall_profile(vm_id, v)
236 self.assertEqual(self.client.http_client.url, self.url)
238 self.client.http_client.path,
239 '/servers/%s/action' % vm_id)
240 self.assertEqual(PR.call_args[0], (
242 '{"firewallProfile": {"profile": "%s"}}' % v,
246 @patch('%s.perform_request' % khttp, return_value=FR())
247 def test_get_server_stats(self, PR):
248 vm_id = vm_recv['server']['id']
249 stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
250 FR.json = dict(stats=stats)
251 r = self.client.get_server_stats(vm_id)
252 self.assertEqual(self.client.http_client.url, self.url)
254 self.client.http_client.path,
255 '/servers/%s/stats' % vm_id)
256 self.assert_dicts_are_equal(stats, r)
258 @patch('%s.perform_request' % khttp, return_value=FR())
259 def test_create_network(self, PR):
260 net_name = net_send['network']['name']
264 cidr='192.168.0.0/24',
265 gateway='192.168.0.1',
268 test_args = dict(full_args)
269 test_args.update(dict(empty=None, full=None))
270 for arg, val in test_args.items():
271 kwargs = {} if arg == 'empty' else full_args if (
272 arg == 'full') else {arg: val}
273 r = self.client.create_network(net_name, **kwargs)
274 self.assertEqual(self.client.http_client.url, self.url)
276 self.client.http_client.path,
278 self.assert_dicts_are_equal(r, net_recv['network'])
279 data = PR.call_args[0][1]
280 expected = dict(network=dict(net_send['network']))
281 expected['network'].update(kwargs)
282 self.assert_dicts_are_equal(loads(data), expected)
284 @patch('%s.perform_request' % khttp, return_value=FR())
285 def test_connect_server(self, PR):
286 vm_id = vm_recv['server']['id']
287 net_id = net_recv['network']['id']
289 self.client.connect_server(vm_id, net_id)
290 self.assertEqual(self.client.http_client.url, self.url)
292 self.client.http_client.path,
293 '/networks/%s/action' % net_id)
296 ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
298 @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
299 def test_disconnect_server(self, NP):
300 vm_id = vm_recv['server']['id']
301 net_id = net_recv['network']['id']
302 nic_id = 'nic-%s-%s' % (net_id, vm_id)
304 dict(id=nic_id, network_id=net_id),
305 dict(id='another-nic-id', network_id='another-net-id'),
306 dict(id=nic_id * 2, network_id=net_id * 2)]
310 return_value=vm_nics) as LSN:
311 r = self.client.disconnect_server(vm_id, nic_id)
312 self.assertEqual(r, 1)
313 self.assertEqual(LSN.call_args[0], (vm_id,))
314 self.assertEqual(NP.call_args[0], (net_id, 'action'))
317 dict(json_data=dict(remove=dict(attachment=nic_id))))
319 @patch('%s.perform_request' % khttp, return_value=FR())
320 def test_list_server_nics(self, PR):
321 vm_id = vm_recv['server']['id']
322 nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
324 r = self.client.list_server_nics(vm_id)
325 self.assertEqual(self.client.http_client.url, self.url)
327 self.client.http_client.path,
328 '/servers/%s/ips' % vm_id)
329 expected = nics['addresses']['values']
330 for i in range(len(r)):
331 self.assert_dicts_are_equal(r[i], expected[i])
333 @patch('%s.perform_request' % khttp, return_value=FR())
334 def test_list_networks(self, PR):
336 r = self.client.list_networks()
337 self.assertEqual(self.client.http_client.url, self.url)
338 self.assertEqual(self.client.http_client.path, '/networks')
339 expected = net_list['networks']['values']
340 for i in range(len(r)):
341 self.assert_dicts_are_equal(expected[i], r[i])
342 self.client.list_networks(detail=True)
343 self.assertEqual(self.client.http_client.url, self.url)
344 self.assertEqual(self.client.http_client.path, '/networks/detail')
346 @patch('%s.perform_request' % khttp, return_value=FR())
347 def test_list_network_nics(self, PR):
348 net_id = net_recv['network']['id']
350 r = self.client.list_network_nics(net_id)
351 self.assertEqual(self.client.http_client.url, self.url)
353 self.client.http_client.path,
354 '/networks/%s' % net_id)
355 expected = net_recv['network']['attachments']['values']
356 for i in range(len(r)):
357 self.assert_dicts_are_equal(r[i], expected[i])
359 @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
360 def test_disconnect_network_nics(self, NP):
361 net_id = net_recv['network']['id']
362 nics = ['nic1', 'nic2', 'nic3']
366 return_value=nics) as lnn:
367 self.client.disconnect_network_nics(net_id)
368 lnn.assert_called_once_with(net_id)
369 for i in range(len(nics)):
370 expected = call(net_id, 'action', json_data=dict(
371 remove=dict(attachment=nics[i])))
372 self.assertEqual(expected, NP.mock_calls[i])
374 @patch('%s.perform_request' % khttp, return_value=FR())
375 def test_get_network_details(self, PR):
377 net_id = net_recv['network']['id']
378 r = self.client.get_network_details(net_id)
379 self.assertEqual(self.client.http_client.url, self.url)
381 self.client.http_client.path,
382 '/networks/%s' % net_id)
383 self.assert_dicts_are_equal(r, net_recv['network'])
385 @patch('%s.perform_request' % khttp, return_value=FR())
386 def test_update_network_name(self, PR):
387 net_id = net_recv['network']['id']
388 new_name = '%s_new' % net_id
390 self.client.update_network_name(net_id, new_name)
391 self.assertEqual(self.client.http_client.url, self.url)
392 self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
393 (method, data, a_headers, a_params) = PR.call_args[0]
394 self.assert_dicts_are_equal(
395 dict(network=dict(name=new_name)),
398 @patch('%s.perform_request' % khttp, return_value=FR())
399 def test_delete_image(self, PR):
401 self.client.delete_image(img_ref)
402 self.assertEqual(self.client.http_client.url, self.url)
403 self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
405 @patch('%s.perform_request' % khttp, return_value=FR())
406 def test_delete_network(self, PR):
407 net_id = net_recv['network']['id']
409 self.client.delete_network(net_id)
410 self.assertEqual(self.client.http_client.url, self.url)
411 self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
413 @patch('%s.perform_request' % khttp, return_value=FR())
414 def test_create_image_metadata(self, PR):
415 metadata = dict(m1='v1', m2='v2', m3='v3')
416 FR.json = dict(meta=img_recv['image'])
419 self.client.create_image_metadata,
420 img_ref, 'key', 'value')
422 for k, v in metadata.items():
423 r = self.client.create_image_metadata(img_ref, k, v)
424 self.assertEqual(self.client.http_client.url, self.url)
426 self.client.http_client.path,
427 '/images/%s/meta/%s' % (img_ref, k))
428 (method, data, a_headers, a_params) = PR.call_args[0]
429 self.assertEqual(dict(meta={k: v}), loads(data))
430 self.assert_dicts_are_equal(r, img_recv['image'])
432 @patch('%s.images_post' % cyclades_pkg, return_value=FR())
433 def test_update_image_metadata(self, images_post):
434 metadata = dict(m1='v1', m2='v2', m3='v3')
435 FR.json = dict(metadata=metadata)
436 r = self.client.update_image_metadata(img_ref, **metadata)
437 self.assert_dicts_are_equal(r, metadata)
438 (called_id, cmd) = images_post.call_args[0]
439 self.assertEqual(called_id, img_ref)
440 self.assertEqual(cmd, 'meta')
441 data = images_post.call_args[1]['json_data']
442 self.assert_dicts_are_equal(data, dict(metadata=metadata))
444 @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
445 def test_delete_image_metadata(self, images_delete):
447 self.client.delete_image_metadata(img_ref, key)
449 (img_ref, '/meta/' + key),
450 images_delete.call_args[0])
452 if __name__ == '__main__':
454 from kamaki.clients.test import runTestCase
455 runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])