Unittest ComputeRestApi.servers_get
[kamaki] / kamaki / clients / compute / test.py
1 # Copyright 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 mock import patch, call
35 from unittest import TestCase
36 from itertools import product
37
38 from kamaki.clients.compute import ComputeClient, ComputeClientApi
39 from kamaki.clients import ClientError
40
41
42 rest_pkg = 'kamaki.clients.compute.rest_api.ComputeClientApi'
43 compute_pkg = 'kamaki.clients.compute.ComputeClient'
44
45 img_ref = "1m4g3-r3f3r3nc3"
46 vm_name = "my new VM"
47 fid = 42
48 vm_send = dict(server=dict(
49     flavorRef=fid,
50     name=vm_name,
51     imageRef=img_ref,
52     metadata=dict(os="debian", users="root")))
53 vm_recv = dict(server=dict(
54     status="BUILD",
55     updated="2013-03-01T10:04:00.637152+00:00",
56     hostId="",
57     name=vm_name,
58     imageRef=img_ref,
59     created="2013-03-01T10:04:00.087324+00:00",
60     flavorRef=fid,
61     adminPass="n0n3sh@11p@55",
62     suspended=False,
63     progress=0,
64     id=31173,
65     metadata=dict(values=dict(os="debian", users="root"))))
66 img_recv = dict(image=dict(
67     status="ACTIVE",
68     updated="2013-02-26T11:10:14+00:00",
69     name="Debian Base",
70     created="2013-02-26T11:03:29+00:00",
71     progress=100,
72     id=img_ref,
73     metadata=dict(values=dict(
74         partition_table="msdos",
75         kernel="2.6.32",
76         osfamily="linux",
77         users="root",
78         gui="No GUI",
79         sortorder="1",
80         os="debian",
81         root_partition="1",
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")]))
99
100
101 class FR(object):
102     """FR stands for Fake Response"""
103     json = vm_recv
104     headers = {}
105     content = json
106     status = None
107     status_code = 200
108
109     def release(self):
110         pass
111
112
113 class ComputeRestApi(TestCase):
114
115     """Set up a ComputesRestApi thorough test"""
116     def setUp(self):
117         self.url = 'http://cyclades.example.com'
118         self.token = 'cyc14d3s70k3n'
119         self.client = ComputeClientApi(self.url, self.token)
120
121     def tearDown(self):
122         FR.json = vm_recv
123
124     @patch('%s.get' % rest_pkg, return_value=FR())
125     def test_servers_get(self, get):
126         vm_id = vm_recv['server']['id']
127         for args in product(
128                 ('', vm_id),
129                 ('', 'cmd'),
130                 (200, 204),
131                 ({}, {'k': 'v'})):
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),
138                 success=success,
139                 **args[3]))
140
141
142 class Compute(TestCase):
143
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])
149             else:
150                 self.assertEqual(unicode(v), unicode(d2[k]))
151
152     """Set up a Cyclades thorough test"""
153     def setUp(self):
154         self.url = 'http://cyclades.example.com'
155         self.token = 'cyc14d3s70k3n'
156         self.client = ComputeClient(self.url, self.token)
157
158     def tearDown(self):
159         FR.status_code = 200
160         FR.json = vm_recv
161
162     @patch(
163         '%s.get_image_details' % compute_pkg,
164         return_value=img_recv['image'])
165     def test_create_server(self, GID):
166         with patch.object(
167                 ComputeClient, 'servers_post',
168                 side_effect=ClientError(
169                     'REQUEST ENTITY TOO LARGE',
170                     status=403)):
171             self.assertRaises(
172                 ClientError,
173                 self.client.create_server,
174                 vm_name, fid, img_ref)
175
176         with patch.object(
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))
188
189     @patch('%s.servers_get' % compute_pkg, return_value=FR())
190     def test_list_servers(self, SG):
191         FR.json = vm_list
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))
199
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'])
206
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)))
214
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(
221                 vm_id, 'action',
222                 json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
223
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))
235
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)
244
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))
250
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(
259             vm_id, 'meta',
260             json_data=dict(metadata=metadata), success=201)
261
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']
265         key = 'metakey'
266         self.client.delete_server_metadata(vm_id, key)
267         SD.assert_called_once_with(vm_id, 'meta/' + key)
268
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'])
276
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'])
283
284     @patch('%s.images_get' % compute_pkg, return_value=FR())
285     def test_list_images(self, IG):
286         FR.json = img_list
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])
293
294     @patch('%s.images_get' % compute_pkg, return_value=FR())
295     def test_get_image_details(self, IG):
296         FR.json = img_recv
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'])
300
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(
308                 '%s' % img_ref,
309                 '/meta%s' % (('/%s' % key) if key else '')))
310             self.assert_dicts_are_equal(img_recv['image'], r)
311
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)
317
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)
322
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'])
332
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(
339             img_ref, 'meta',
340             json_data=dict(metadata=metadata))
341         self.assert_dicts_are_equal(r, metadata)
342
343     @patch('%s.images_delete' % compute_pkg, return_value=FR())
344     def test_delete_image_metadata(self, ID):
345         key = 'metakey'
346         self.client.delete_image_metadata(img_ref, key)
347         ID.assert_called_once_with(img_ref, '/meta/%s' % key)
348
349
350 if __name__ == '__main__':
351     from sys import argv
352     from kamaki.clients.test import runTestCase
353     not_found = True
354     if not argv[1:] or argv[1] == 'Compute':
355         not_found = False
356         runTestCase(Compute, 'Compute Client', argv[2:])
357     if not argv[1:] or argv[1] == 'ComputeRestApi':
358         not_found = False
359         runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
360     if not_found:
361         print('TestCase %s not found' % argv[1])