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
37 from kamaki.clients import ClientError
38 from kamaki.clients.cyclades import CycladesClient
41 compute_pkg = 'kamaki.clients.cyclades.CycladesClient'
43 img_ref = "1m4g3-r3f3r3nc3"
46 vm_send = dict(server=dict(
50 metadata=dict(os="debian", users="root")))
51 vm_recv = dict(server=dict(
53 updated="2013-03-01T10:04:00.637152+00:00",
57 created="2013-03-01T10:04:00.087324+00:00",
59 adminPass="n0n3sh@11p@55",
63 metadata=dict(values=dict(os="debian", users="root"))))
64 img_recv = dict(image=dict(
66 updated="2013-02-26T11:10:14+00:00",
68 created="2013-02-26T11:03:29+00:00",
71 metadata=dict(values=dict(
72 partition_table="msdos",
80 description="Debian 6.0.7 (Squeeze) Base System"))))
81 vm_list = dict(servers=dict(values=[
82 dict(name='n1', id=1),
83 dict(name='n2', id=2)]))
84 flavor_list = dict(flavors=dict(values=[
85 dict(id=41, name="C1R1024D20"),
86 dict(id=42, name="C1R1024D40"),
87 dict(id=43, name="C1R1028D20")]))
88 img_list = dict(images=dict(values=[
89 dict(name="maelstrom", id="0fb03e45-7d5a-4515-bd4e-e6bbf6457f06"),
90 dict(name="edx_saas", id="1357163d-5fd8-488e-a117-48734c526206"),
91 dict(name="Debian_Wheezy_Base", id="1f8454f0-8e3e-4b6c-ab8e-5236b728dffe"),
92 dict(name="CentOS", id="21894b48-c805-4568-ac8b-7d4bb8eb533d"),
93 dict(name="Ubuntu Desktop", id="37bc522c-c479-4085-bfb9-464f9b9e2e31"),
94 dict(name="Ubuntu 12.10", id="3a24fef9-1a8c-47d1-8f11-e07bd5e544fd"),
95 dict(name="Debian Base", id="40ace203-6254-4e17-a5cb-518d55418a7d"),
96 dict(name="ubuntu_bundled", id="5336e265-5c7c-4127-95cb-2bf832a79903")]))
100 """FR stands for Fake Response"""
111 class Cyclades(TestCase):
113 def assert_dicts_are_equal(self, d1, d2):
114 for k, v in d1.items():
115 self.assertTrue(k in d2)
116 if isinstance(v, dict):
117 self.assert_dicts_are_equal(v, d2[k])
119 self.assertEqual(unicode(v), unicode(d2[k]))
121 """Set up a Cyclades thorough test"""
123 self.url = 'http://cyclades.example.com'
124 self.token = 'cyc14d3s70k3n'
125 self.client = CycladesClient(self.url, self.token)
132 '%s.get_image_details' % compute_pkg,
133 return_value=img_recv['image'])
134 def test_create_server(self, GID):
136 CycladesClient, 'servers_post',
137 side_effect=ClientError(
138 'REQUEST ENTITY TOO LARGE',
142 self.client.create_server,
143 vm_name, fid, img_ref)
146 CycladesClient, 'servers_post',
147 return_value=FR()) as post:
148 r = self.client.create_server(vm_name, fid, img_ref)
149 self.assertEqual(r, FR.json['server'])
150 self.assertEqual(GID.mock_calls[-1], call(img_ref))
151 self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
152 prsn = 'Personality string (does not work with real servers)'
153 self.client.create_server(vm_name, fid, img_ref, prsn)
154 expected = dict(server=dict(vm_send['server']))
155 expected['server']['personality'] = prsn
156 self.assertEqual(post.mock_calls[-1], call(json_data=expected))
158 @patch('%s.servers_get' % compute_pkg, return_value=FR())
159 def test_list_servers(self, SG):
161 for detail in (False, True):
162 r = self.client.list_servers(detail)
163 self.assertEqual(SG.mock_calls[-1], call(
165 command='detail' if detail else ''))
166 for i, vm in enumerate(vm_list['servers']['values']):
167 self.assert_dicts_are_equal(r[i], vm)
168 self.assertEqual(i + 1, len(r))
170 @patch('%s.servers_get' % compute_pkg, return_value=FR())
171 def test_get_server_details(self, SG):
172 vm_id = vm_recv['server']['id']
173 r = self.client.get_server_details(vm_id)
174 SG.assert_called_once_with(vm_id)
175 self.assert_dicts_are_equal(r, vm_recv['server'])
177 @patch('%s.servers_put' % compute_pkg, return_value=FR())
178 def test_update_server_name(self, SP):
179 vm_id = vm_recv['server']['id']
180 new_name = vm_name + '_new'
181 self.client.update_server_name(vm_id, new_name)
182 SP.assert_called_once_with(vm_id, json_data=dict(
183 server=dict(name=new_name)))
185 @patch('%s.servers_post' % compute_pkg, return_value=FR())
186 def test_reboot_server(self, SP):
187 vm_id = vm_recv['server']['id']
188 for hard in (None, True):
189 self.client.reboot_server(vm_id, hard=hard)
190 self.assertEqual(SP.mock_calls[-1], call(
192 json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
194 @patch('%s.servers_put' % compute_pkg, return_value=FR())
195 def test_create_server_metadata(self, SP):
196 vm_id = vm_recv['server']['id']
197 metadata = dict(m1='v1', m2='v2', m3='v3')
198 FR.json = dict(meta=vm_recv['server'])
199 for k, v in metadata.items():
200 r = self.client.create_server_metadata(vm_id, k, v)
201 self.assert_dicts_are_equal(r, vm_recv['server'])
202 self.assertEqual(SP.mock_calls[-1], call(
203 vm_id, 'meta/%s' % k,
204 json_data=dict(meta={k: v}), success=201))
206 @patch('%s.servers_get' % compute_pkg, return_value=FR())
207 def test_get_server_metadata(self, SG):
208 vm_id = vm_recv['server']['id']
209 metadata = dict(m1='v1', m2='v2', m3='v3')
210 FR.json = dict(metadata=dict(values=metadata))
211 r = self.client.get_server_metadata(vm_id)
212 SG.assert_called_once_with(vm_id, '/meta')
213 self.assert_dicts_are_equal(r, metadata)
215 for k, v in metadata.items():
216 FR.json = dict(meta={k: v})
217 r = self.client.get_server_metadata(vm_id, k)
218 self.assert_dicts_are_equal(r, {k: v})
219 self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
221 @patch('%s.servers_post' % compute_pkg, return_value=FR())
222 def test_update_server_metadata(self, SP):
223 vm_id = vm_recv['server']['id']
224 metadata = dict(m1='v1', m2='v2', m3='v3')
225 FR.json = dict(metadata=metadata)
226 r = self.client.update_server_metadata(vm_id, **metadata)
227 self.assert_dicts_are_equal(r, metadata)
228 SP.assert_called_once_with(
230 json_data=dict(metadata=metadata), success=201)
232 @patch('%s.servers_delete' % compute_pkg, return_value=FR())
233 def test_delete_server_metadata(self, SD):
234 vm_id = vm_recv['server']['id']
236 self.client.delete_server_metadata(vm_id, key)
237 SD.assert_called_once_with(vm_id, 'meta/' + key)
239 @patch('%s.flavors_get' % compute_pkg, return_value=FR())
240 def test_list_flavors(self, FG):
241 FR.json = flavor_list
242 for cmd in ('', 'detail'):
243 r = self.client.list_flavors(detail=(cmd == 'detail'))
244 self.assertEqual(FG.mock_calls[-1], call(command=cmd))
245 self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
247 @patch('%s.flavors_get' % compute_pkg, return_value=FR())
248 def test_get_flavor_details(self, FG):
249 FR.json = dict(flavor=flavor_list['flavors'])
250 r = self.client.get_flavor_details(fid)
251 FG.assert_called_once_with(fid)
252 self.assert_dicts_are_equal(r, flavor_list['flavors'])
254 @patch('%s.images_get' % compute_pkg, return_value=FR())
255 def test_list_images(self, IG):
257 for cmd in ('', 'detail'):
258 r = self.client.list_images(detail=(cmd == 'detail'))
259 self.assertEqual(IG.mock_calls[-1], call(command=cmd))
260 expected = img_list['images']['values']
261 for i in range(len(r)):
262 self.assert_dicts_are_equal(expected[i], r[i])
264 @patch('%s.images_get' % compute_pkg, return_value=FR())
265 def test_get_image_details(self, IG):
267 r = self.client.get_image_details(img_ref)
268 IG.assert_called_once_with(img_ref)
269 self.assert_dicts_are_equal(r, img_recv['image'])
271 @patch('%s.images_get' % compute_pkg, return_value=FR())
272 def test_get_image_metadata(self, IG):
273 for key in ('', '50m3k3y'):
274 FR.json = dict(meta=img_recv['image']) if (
275 key) else dict(metadata=dict(values=img_recv['image']))
276 r = self.client.get_image_metadata(img_ref, key)
277 self.assertEqual(IG.mock_calls[-1], call(
279 '/meta%s' % (('/%s' % key) if key else '')))
280 self.assert_dicts_are_equal(img_recv['image'], r)
282 @patch('%s.servers_delete' % compute_pkg, return_value=FR())
283 def test_delete_server(self, SD):
284 vm_id = vm_recv['server']['id']
285 self.client.delete_server(vm_id)
286 SD.assert_called_once_with(vm_id)
288 @patch('%s.images_delete' % compute_pkg, return_value=FR())
289 def test_delete_image(self, ID):
290 self.client.delete_image(img_ref)
291 ID.assert_called_once_with(img_ref)
293 @patch('%s.images_put' % compute_pkg, return_value=FR())
294 def test_create_image_metadata(self, IP):
295 (key, val) = ('k1', 'v1')
296 FR.json = dict(meta=img_recv['image'])
297 r = self.client.create_image_metadata(img_ref, key, val)
298 IP.assert_called_once_with(
299 img_ref, 'meta/%s' % key,
300 json_data=dict(meta={key: val}))
301 self.assert_dicts_are_equal(r, img_recv['image'])
303 @patch('%s.images_post' % compute_pkg, return_value=FR())
304 def test_update_image_metadata(self, IP):
305 metadata = dict(m1='v1', m2='v2', m3='v3')
306 FR.json = dict(metadata=metadata)
307 r = self.client.update_image_metadata(img_ref, **metadata)
308 IP.assert_called_once_with(
310 json_data=dict(metadata=metadata))
311 self.assert_dicts_are_equal(r, metadata)
313 @patch('%s.images_delete' % compute_pkg, return_value=FR())
314 def test_delete_image_metadata(self, ID):
316 self.client.delete_image_metadata(img_ref, key)
317 ID.assert_called_once_with(img_ref, '/meta/%s' % key)
319 if __name__ == '__main__':
321 from kamaki.clients.test import runTestCase
322 runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])