a1511133516dab50a884fc48a1dcbf0e4bc0d6f2
[kamaki] / kamaki / clients / test / 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 from mock import patch, Mock
34 from unittest import TestCase
35 from json import loads
36
37 from kamaki.clients import Client, ClientError
38
39 img_ref = "1m4g3-r3f3r3nc3"
40 vm_name = "my new VM"
41 fid = 42
42 vm_send = dict(server=dict(
43     flavorRef=fid,
44     name=vm_name,
45     imageRef=img_ref,
46     metadata=dict(os="debian", users="root")))
47 vm_recv = dict(server=dict(
48     status="BUILD",
49     updated="2013-03-01T10:04:00.637152+00:00",
50     hostId="",
51     name=vm_name,
52     imageRef=img_ref,
53     created="2013-03-01T10:04:00.087324+00:00",
54     flavorRef=fid,
55     adminPass="n0n3sh@11p@55",
56     suspended=False,
57     progress=0,
58     id=31173,
59     metadata=dict(values=dict(os="debian", users="root"))))
60 img_recv = dict(image=dict(
61     status="ACTIVE",
62     updated="2013-02-26T11:10:14+00:00",
63     name="Debian Base",
64     created="2013-02-26T11:03:29+00:00",
65     progress=100,
66     id=img_ref,
67     metadata=dict(values=dict(
68         partition_table="msdos",
69         kernel="2.6.32",
70         osfamily="linux",
71         users="root",
72         gui="No GUI",
73         sortorder="1",
74         os="debian",
75         root_partition="1",
76         description="Debian 6.0.7 (Squeeze) Base System"))))
77 vm_list = dict(servers=dict(values=[
78     dict(name='n1', id=1),
79     dict(name='n2', id=2)]))
80
81
82 class Cyclades(TestCase):
83
84     def assert_dicts_are_equal(self, d1, d2):
85         for k, v in d1.items():
86             self.assertTrue(k in d2)
87             if isinstance(v, dict):
88                 self.assert_dicts_are_equal(v, d2[k])
89             else:
90                 self.assertEqual(unicode(v), unicode(d2[k]))
91
92     class FR(object):
93         """FR stands for Fake Response"""
94         json = vm_recv
95         headers = {}
96         content = json
97         status = None
98         status_code = 200
99
100         def release(self):
101             pass
102
103     """Set up a Cyclades thorough test"""
104     def setUp(self):
105         self.url = 'http://cyclades.example.com'
106         self.token = 'cyc14d3s70k3n'
107         from kamaki.clients.cyclades import CycladesClient
108         self.client = CycladesClient(self.url, self.token)
109         from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
110         self.C = KamakiHTTPConnection
111
112     def tearDown(self):
113         self.FR.status_code = 200
114         self.FR.json = vm_recv
115
116     def test_create_server(self):
117         self.client.get_image_details = Mock(return_value=img_recv['image'])
118         with patch.object(Client, 'request', side_effect=ClientError(
119                 'REQUEST ENTITY TOO LARGE',
120                 status=403)):
121             self.assertRaises(
122                 ClientError,
123                 self.client.create_server,
124                 vm_name, fid, img_ref)
125
126         with patch.object(
127                 self.C,
128                 'perform_request',
129                 return_value=self.FR()) as perform_req:
130             self.assertRaises(
131                 ClientError,
132                 self.client.create_server,
133                 vm_name, fid, img_ref)
134             self.FR.status_code = 202
135             r = self.client.create_server(vm_name, fid, img_ref)
136             self.assertEqual(self.client.http_client.url, self.url)
137             self.assertEqual(self.client.http_client.path, '/servers')
138             (method, data, a_headers, a_params) = perform_req.call_args[0]
139             self.assert_dicts_are_equal(loads(data), vm_send)
140             self.assert_dicts_are_equal(r, vm_recv['server'])
141             prsn = 'Personality string (does not work with real servers)'
142             self.client.create_server(vm_name, fid, img_ref, prsn)
143             (method, data, a_headers, a_params) = perform_req.call_args[0]
144             data = loads(data)
145             self.assertTrue('personality' in data['server'])
146             self.assertEqual(prsn, data['server']['personality'])
147
148     def test_list_servers(self):
149         self.FR.json = vm_list
150         with patch.object(
151                 self.C,
152                 'perform_request',
153                 return_value=self.FR()) as perform_req:
154             r = self.client.list_servers()
155             self.assertEqual(self.client.http_client.url, self.url)
156             self.assertEqual(self.client.http_client.path, '/servers')
157             (method, data, a_headers, a_params) = perform_req.call_args[0]
158             self.assert_dicts_are_equal(dict(values=r), vm_list['servers'])
159             r = self.client.list_servers(detail=True)
160             self.assertEqual(self.client.http_client.url, self.url)
161             self.assertEqual(self.client.http_client.path, '/servers/detail')
162         from kamaki.clients.cyclades_rest_api import CycladesClientApi
163         with patch.object(
164                 CycladesClientApi,
165                 'servers_get',
166                 return_value=self.FR()) as servers_get:
167             self.client.list_servers(changes_since=True)
168             self.assertTrue(servers_get.call_args[1]['changes_since'])
169
170     def test_get_server_details(self):
171         vm_id = vm_recv['server']['id']
172         with patch.object(self.C, 'perform_request', return_value=self.FR()):
173             r = self.client.get_server_details(vm_id)
174             self.assertEqual(self.client.http_client.url, self.url)
175             self.assertEqual(
176                 self.client.http_client.path,
177                 '/servers/%s' % vm_id)
178             self.assert_dicts_are_equal(r, vm_recv['server'])
179
180     def test_update_server_name(self):
181         vm_id = vm_recv['server']['id']
182         new_name = vm_name + '_new'
183         self.FR.status_code = 204
184         with patch.object(
185                 self.C,
186                 'perform_request',
187                 return_value=self.FR()) as perform_req:
188             self.client.update_server_name(vm_id, new_name)
189             self.assertEqual(self.client.http_client.url, self.url)
190             self.assertEqual(
191                 self.client.http_client.path,
192                 '/servers/%s' % vm_id)
193             (method, data, a_headers, a_params) = perform_req.call_args[0]
194             self.assert_dicts_are_equal(
195                 dict(server=dict(name=new_name)),
196                 loads(data))
197
198     def test_reboot_server(self):
199         vm_id = vm_recv['server']['id']
200         self.FR.status_code = 202
201         with patch.object(
202                 self.C,
203                 'perform_request',
204                 return_value=self.FR()) as perform_req:
205             self.client.reboot_server(vm_id)
206             self.assertEqual(self.client.http_client.url, self.url)
207             self.assertEqual(
208                 self.client.http_client.path,
209                 '/servers/%s/action' % vm_id)
210             (method, data, a_headers, a_params) = perform_req.call_args[0]
211             self.assert_dicts_are_equal(
212                 dict(reboot=dict(type='SOFT')),
213                 loads(data))
214
215     def test_create_server_metadata(self):
216         vm_id = vm_recv['server']['id']
217         metadata = dict(m1='v1', m2='v2', m3='v3')
218         self.FR.json = dict(meta=vm_recv['server'])
219         with patch.object(
220                 self.C,
221                 'perform_request',
222                 return_value=self.FR()) as perform_req:
223             self.assertRaises(
224                 ClientError,
225                 self.client.create_server_metadata,
226                 vm_id, 'key', 'value')
227             self.FR.status_code = 201
228             for k, v in metadata.items():
229                 r = self.client.create_server_metadata(vm_id, k, v)
230                 self.assertEqual(self.client.http_client.url, self.url)
231                 self.assertEqual(
232                     self.client.http_client.path,
233                     '/servers/%s/meta/%s' % (vm_id, k))
234                 (method, data, a_headers, a_params) = perform_req.call_args[0]
235                 self.assertEqual(dict(meta={k: v}), loads(data))
236                 self.assert_dicts_are_equal(r, vm_recv['server'])
237
238     def test_get_server_metadata(self):
239         vm_id = vm_recv['server']['id']
240         metadata = dict(m1='v1', m2='v2', m3='v3')
241         with patch.object(self.C, 'perform_request', return_value=self.FR()):
242             self.FR.json = dict(metadata=dict(values=metadata))
243             r = self.client.get_server_metadata(vm_id)
244             self.assertEqual(self.client.http_client.url, self.url)
245             self.assertEqual(
246                 self.client.http_client.path,
247                 '/servers/%s/meta' % vm_id)
248             self.assert_dicts_are_equal(r, metadata)
249
250             for k, v in metadata.items():
251                 self.FR.json = dict(meta={k: v})
252                 r = self.client.get_server_metadata(vm_id, k)
253                 self.assertEqual(self.client.http_client.url, self.url)
254                 self.assertEqual(
255                     self.client.http_client.path,
256                     '/servers/%s/meta/%s' % (vm_id, k))
257                 self.assert_dicts_are_equal(r, {k: v})
258
259     """
260     def test_update_server_metadata(self):
261         r1 = self.client.create_server_metadata(
262             self.server1['id'],
263             'mymeta3',
264             'val2')
265         self.assertTrue('mymeta3'in r1)
266         r2 = self.client.update_server_metadata(
267             self.server1['id'],
268             mymeta3='val3')
269         self.assertTrue(r2['mymeta3'], 'val3')
270
271     def test_delete_server_metadata(self):
272         r1 = self.client.create_server_metadata(
273             self.server1['id'],
274             'mymeta',
275             'val')
276         self.assertTrue('mymeta' in r1)
277         self.client.delete_server_metadata(self.server1['id'], 'mymeta')
278         try:
279             self.client.get_server_metadata(self.server1['id'], 'mymeta')
280             raise ClientError('Wrong Error', status=100)
281         except ClientError as err:
282             self.assertEqual(err.status, 404)
283
284     def test_list_flavors(self):
285         r = self.client.list_flavors()
286         self.assertTrue(len(r) > 1)
287         r = self.client.list_flavors(detail=True)
288         self.assertTrue('SNF:disk_template' in r[0])
289
290     def test_get_flavor_details(self):
291         r = self.client.get_flavor_details(self.flavorid)
292         self.assert_dicts_are_equal(self._flavor_details, r)
293
294     def test_list_images(self):
295         r = self.client.list_images()
296         self.assertTrue(len(r) > 1)
297         r = self.client.list_images(detail=True)
298         for detailed_img in r:
299             if detailed_img['id'] == self.img:
300                 break
301         self.assert_dicts_are_equal(detailed_img, self.img_details)
302
303     def test_get_image_details(self):
304         r = self.client.get_image_details(self.img)
305         self.assert_dicts_are_equal(r, self.img_details)
306
307     def test_get_image_metadata(self):
308         r = self.client.get_image_metadata(self.img)
309         self.assert_dicts_are_equal(
310             self.img_details['metadata']['values'], r)
311         for key, val in self.img_details['metadata']['values'].items():
312             r = self.client.get_image_metadata(self.img, key)
313             self.assertEqual(r[key], val)
314
315     def test_shutdown_server(self):
316         self.client.shutdown_server(self.server1['id'])
317         self._wait_for_status(self.server1['id'], 'ACTIVE')
318         r = self.client.get_server_details(self.server1['id'])
319         self.assertEqual(r['status'], 'STOPPED')
320
321     def test_start_server(self):
322         self.client.start_server(self.server1['id'])
323         self._wait_for_status(self.server1['id'], 'STOPPED')
324         r = self.client.get_server_details(self.server1['id'])
325         self.assertEqual(r['status'], 'ACTIVE')
326
327     def test_get_server_console(self):
328         r = self.client.get_server_console(self.server2['id'])
329         self.assertTrue('host' in r)
330         self.assertTrue('password' in r)
331         self.assertTrue('port' in r)
332         self.assertTrue('type' in r)
333
334     def test_get_firewall_profile(self):
335         self._wait_for_status(self.server1['id'], 'BUILD')
336         fprofile = self.client.get_firewall_profile(self.server1['id'])
337         self.assertTrue(fprofile in self.PROFILES)
338
339     def test_set_firewall_profile(self):
340         self._wait_for_status(self.server1['id'], 'BUILD')
341         PROFILES = ['DISABLED', 'ENABLED', 'DISABLED', 'PROTECTED']
342         fprofile = self.client.get_firewall_profile(self.server1['id'])
343         print('')
344         count_success = 0
345         for counter, fprofile in enumerate(PROFILES):
346             npos = counter + 1
347             try:
348                 nprofile = PROFILES[npos]
349             except IndexError:
350                 nprofile = PROFILES[0]
351             print('\tprofile swap %s: %s -> %s' % (npos, fprofile, nprofile))
352             self.client.set_firewall_profile(self.server1['id'], nprofile)
353             time.sleep(0.5)
354             self.client.reboot_server(self.server1['id'], hard=True)
355             time.sleep(1)
356             self._wait_for_status(self.server1['id'], 'REBOOT')
357             time.sleep(0.5)
358             changed = self.client.get_firewall_profile(self.server1['id'])
359             try:
360                 self.assertEqual(changed, nprofile)
361             except AssertionError as err:
362                 if count_success:
363                     print('\tFAIL in swap #%s' % npos)
364                     break
365                 else:
366                     raise err
367             count_success += 1
368
369     def test_get_server_stats(self):
370         r = self.client.get_server_stats(self.server1['id'])
371         it = ('cpuBar', 'cpuTimeSeries', 'netBar', 'netTimeSeries', 'refresh')
372         for term in it:
373             self.assertTrue(term in r)
374
375     def test_create_network(self):
376         print('\twith no params')
377         self.network1 = self._create_network(self.netname1)
378         self._wait_for_network(self.network1['id'], 'ACTIVE')
379         n1id = self.network1['id']
380         self.network1 = self.client.get_network_details(n1id)
381         nets = self.client.list_networks(self.network1['id'])
382         chosen = [net for net in nets if net['id'] == n1id][0]
383         chosen.pop('updated')
384         net1 = dict(self.network1)
385         net1.pop('updated')
386         self.assert_dicts_are_equal(chosen, net1)
387         for param, val in dict(
388                 cidr='192.168.0.0/24',
389                 gateway='192.168.0.1',
390                 type='MAC_FILTERED',
391                 dhcp=True).items():
392             print('\tdelete %s to avoid max net limit' % n1id)
393             self._delete_network(n1id)
394             kwargs = {param: val}
395             print('\twith %s=%s' % (param, val))
396             self.network1 = self._create_network(self.netname1, **kwargs)
397             n1id = self.network1['id']
398             self._wait_for_network(n1id, 'ACTIVE')
399             self.network1 = self.client.get_network_details(n1id)
400             self.assertEqual(self.network1[param], val)
401
402     def test_connect_server(self):
403         self.client.connect_server(self.server1['id'], self.network1['id'])
404         self.assertTrue(self._wait_for_nic(
405             self.network1['id'],
406             self.server1['id']))
407
408     def test_disconnect_server(self):
409         self.client.disconnect_server(self.server1['id'], self.network1['id'])
410         self.assertTrue(self._wait_for_nic(
411             self.network1['id'],
412             self.server1['id'],
413             in_creation=False))
414
415     def _test_0260_wait_for_second_network(self):
416         self.server1 = self._create_server(
417             self.servname1,
418             self.flavorid,
419             self.img)
420         self.network2 = self._create_network(self.netname2)
421         self._wait_for_status(self.server1['id'], 'BUILD')
422         self._wait_for_network(self.network2['id'], 'ACTIVE')
423         self._test_0280_list_server_nics()
424
425     def _test_0280_list_server_nics(self):
426         r = self.client.list_server_nics(self.server1['id'])
427         len0 = len(r)
428         self.client.connect_server(self.server1['id'], self.network2['id'])
429         self.assertTrue(self._wait_for_nic(
430             self.network2['id'],
431             self.server1['id']))
432         r = self.client.list_server_nics(self.server1['id'])
433         self.assertTrue(len(r) > len0)
434
435     def test_list_networks(self):
436         r = self.client.list_networks()
437         self.assertTrue(len(r) > 1)
438         ids = [net['id'] for net in r]
439         names = [net['name'] for net in r]
440         self.assertTrue('1' in ids)
441         #self.assertTrue('public' in names)
442         self.assertTrue(self.network1['id'] in ids)
443         self.assertTrue(self.network1['name'] in names)
444
445         r = self.client.list_networks(detail=True)
446         ids = [net['id'] for net in r]
447         names = [net['name'] for net in r]
448         for net in r:
449             self.assertTrue(net['id'] in ids)
450             self.assertTrue(net['name'] in names)
451             for term in ('status', 'updated', 'created'):
452                 self.assertTrue(term in net.keys())
453
454     def test_get_network_details(self):
455         r = self.client.get_network_details(self.network1['id'])
456         net1 = dict(self.network1)
457         net1.pop('status')
458         net1.pop('updated', None)
459         net1.pop('attachments')
460         r.pop('status')
461         r.pop('updated', None)
462         r.pop('attachments')
463         self.assert_dicts_are_equal(net1, r)
464
465     def test_update_network_name(self):
466         updated_name = self.netname2 + '_upd'
467         self.client.update_network_name(self.network2['id'], updated_name)
468
469         def netwait(wait):
470             r = self.client.get_network_details(self.network2['id'])
471             if r['name'] == updated_name:
472                 return
473             time.sleep(wait)
474         self.do_with_progress_bar(
475             netwait,
476             'Network %s name is changing:' % self.network2['id'],
477             self._waits[:5])
478
479         r = self.client.get_network_details(self.network2['id'])
480         self.assertEqual(r['name'], updated_name)
481
482     def test_delete_image(self):
483         images = self.client.list_images()
484         self.client.delete_image(images[2]['id'])
485         try:
486             r = self.client.get_image_details(images[2]['id'], success=(400))
487         except ClientError as err:
488             self.assertEqual(err.status, 404)
489
490     def test_create_image_metadata(self):
491         r = self.client.create_image_metadata(self.img, 'mykey', 'myval')
492         self.assertEqual(r['mykey'], 'myval')
493
494     def test_update_image_metadata(self):
495         r = self.client.create_image_metadata(self.img, 'mykey0', 'myval')
496         r = self.client.update_image_metadata(self.img, 'mykey0', 'myval0')
497         self.assertEqual(r['mykey0'], 'myval0')
498
499     def test_delete_image_metadata(self):
500         self.client.create_image_metadata(self.img, 'mykey1', 'myval1')
501         self.client.delete_image_metadata(self.img, 'mykey1')
502         r = self.client.get_image_metadata(self.img)
503         self.assertNotEqual('mykey1' in r)
504     """