Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 6af47576

History | View | Annotate | Download (17.3 kB)

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
from json import dumps
38

    
39
from kamaki.clients.compute import ComputeClient, ComputeClientApi
40
from kamaki.clients import ClientError
41

    
42

    
43
rest_pkg = 'kamaki.clients.compute.rest_api.ComputeClientApi'
44
compute_pkg = 'kamaki.clients.compute.ComputeClient'
45

    
46
img_ref = "1m4g3-r3f3r3nc3"
47
vm_name = "my new VM"
48
fid = 42
49
vm_send = dict(server=dict(
50
    flavorRef=fid,
51
    name=vm_name,
52
    imageRef=img_ref,
53
    metadata=dict(os="debian", users="root")))
54
vm_recv = dict(server=dict(
55
    status="BUILD",
56
    updated="2013-03-01T10:04:00.637152+00:00",
57
    hostId="",
58
    name=vm_name,
59
    imageRef=img_ref,
60
    created="2013-03-01T10:04:00.087324+00:00",
61
    flavorRef=fid,
62
    adminPass="n0n3sh@11p@55",
63
    suspended=False,
64
    progress=0,
65
    id=31173,
66
    metadata=dict(values=dict(os="debian", users="root"))))
67
img_recv = dict(image=dict(
68
    status="ACTIVE",
69
    updated="2013-02-26T11:10:14+00:00",
70
    name="Debian Base",
71
    created="2013-02-26T11:03:29+00:00",
72
    progress=100,
73
    id=img_ref,
74
    metadata=dict(values=dict(
75
        partition_table="msdos",
76
        kernel="2.6.32",
77
        osfamily="linux",
78
        users="root",
79
        gui="No GUI",
80
        sortorder="1",
81
        os="debian",
82
        root_partition="1",
83
        description="Debian 6.0.7 (Squeeze) Base System"))))
84
vm_list = dict(servers=dict(values=[
85
    dict(name='n1', id=1),
86
    dict(name='n2', id=2)]))
87
flavor_list = dict(flavors=dict(values=[
88
    dict(id=41, name="C1R1024D20"),
89
    dict(id=42, name="C1R1024D40"),
90
    dict(id=43, name="C1R1028D20")]))
91
img_list = dict(images=dict(values=[
92
    dict(name="maelstrom", id="0fb03e45-7d5a-4515-bd4e-e6bbf6457f06"),
93
    dict(name="edx_saas", id="1357163d-5fd8-488e-a117-48734c526206"),
94
    dict(name="Debian_Wheezy_Base", id="1f8454f0-8e3e-4b6c-ab8e-5236b728dffe"),
95
    dict(name="CentOS", id="21894b48-c805-4568-ac8b-7d4bb8eb533d"),
96
    dict(name="Ubuntu Desktop", id="37bc522c-c479-4085-bfb9-464f9b9e2e31"),
97
    dict(name="Ubuntu 12.10", id="3a24fef9-1a8c-47d1-8f11-e07bd5e544fd"),
98
    dict(name="Debian Base", id="40ace203-6254-4e17-a5cb-518d55418a7d"),
99
    dict(name="ubuntu_bundled", id="5336e265-5c7c-4127-95cb-2bf832a79903")]))
100

    
101

    
102
class FR(object):
103
    """FR stands for Fake Response"""
104
    json = vm_recv
105
    headers = {}
106
    content = json
107
    status = None
108
    status_code = 200
109

    
110
    def release(self):
111
        pass
112

    
113

    
114
class ComputeRestApi(TestCase):
115

    
116
    """Set up a ComputesRestApi thorough test"""
117
    def setUp(self):
118
        self.url = 'http://cyclades.example.com'
119
        self.token = 'cyc14d3s70k3n'
120
        self.client = ComputeClientApi(self.url, self.token)
121

    
122
    def tearDown(self):
123
        FR.json = vm_recv
124

    
125
    @patch('%s.get' % rest_pkg, return_value=FR())
126
    def test_servers_get(self, get):
127
        vm_id = vm_recv['server']['id']
128
        for args in product(
129
                ('', vm_id),
130
                ('', 'cmd'),
131
                (200, 204),
132
                ({}, {'k': 'v'})):
133
            (server_id, command, success, kwargs) = args
134
            self.client.servers_get(*args[:3], **kwargs)
135
            vm_str = '/%s' % server_id if server_id else ''
136
            cmd_str = '/%s' % command if command else ''
137
            self.assertEqual(get.mock_calls[-1], call(
138
                '/servers%s%s' % (vm_str, cmd_str),
139
                success=success,
140
                **kwargs))
141

    
142
    @patch('%s.delete' % rest_pkg, return_value=FR())
143
    def test_servers_delete(self, delete):
144
        vm_id = vm_recv['server']['id']
145
        for args in product(
146
                ('', vm_id),
147
                ('', 'cmd'),
148
                (204, 208),
149
                ({}, {'k': 'v'})):
150
            (server_id, command, success, kwargs) = args
151
            self.client.servers_delete(*args[:3], **args[3])
152
            vm_str = '/%s' % server_id if server_id else ''
153
            cmd_str = '/%s' % command if command else ''
154
            self.assertEqual(delete.mock_calls[-1], call(
155
                '/servers%s%s' % (vm_str, cmd_str),
156
                success=success,
157
                **kwargs))
158

    
159
    @patch('%s.set_header' % rest_pkg)
160
    @patch('%s.post' % rest_pkg, return_value=FR())
161
    def test_servers_post(self, post, SH):
162
        vm_id = vm_recv['server']['id']
163
        for args in product(
164
                ('', vm_id),
165
                ('', 'cmd'),
166
                (None, [dict(json="data"), dict(data="json")]),
167
                (202, 204),
168
                ({}, {'k': 'v'})):
169
            (server_id, command, json_data, success, kwargs) = args
170

    
171
            self.client.servers_post(*args[:4], **kwargs)
172
            vm_str = '/%s' % server_id if server_id else ''
173
            cmd_str = '/%s' % command if command else ''
174
            if json_data:
175
                json_data = dumps(json_data)
176
                self.assertEqual(SH.mock_calls[-2:], [
177
                    call('Content-Type', 'application/json'),
178
                    call('Content-Length', len(json_data))])
179
            self.assertEqual(post.mock_calls[-1], call(
180
                '/servers%s%s' % (vm_str, cmd_str),
181
                data=json_data, success=success,
182
                **kwargs))
183

    
184
    @patch('%s.set_header' % rest_pkg)
185
    @patch('%s.put' % rest_pkg, return_value=FR())
186
    def test_servers_put(self, put, SH):
187
        vm_id = vm_recv['server']['id']
188
        for args in product(
189
                ('', vm_id),
190
                ('', 'cmd'),
191
                (None, [dict(json="data"), dict(data="json")]),
192
                (204, 504),
193
                ({}, {'k': 'v'})):
194
            (server_id, command, json_data, success, kwargs) = args
195

    
196
            self.client.servers_put(*args[:4], **kwargs)
197
            vm_str = '/%s' % server_id if server_id else ''
198
            cmd_str = '/%s' % command if command else ''
199
            if json_data:
200
                json_data = dumps(json_data)
201
                self.assertEqual(SH.mock_calls[-2:], [
202
                    call('Content-Type', 'application/json'),
203
                    call('Content-Length', len(json_data))])
204
            self.assertEqual(put.mock_calls[-1], call(
205
                '/servers%s%s' % (vm_str, cmd_str),
206
                data=json_data, success=success,
207
                **kwargs))
208

    
209
    @patch('%s.get' % rest_pkg, return_value=FR())
210
    def test_flavors_get(self, get):
211
        vm_id = vm_recv['server']['id']
212
        for args in product(
213
                ('', vm_id),
214
                ('', 'cmd'),
215
                (200, 204),
216
                ({}, {'k': 'v'})):
217
            (server_id, command, success, kwargs) = args
218
            self.client.flavors_get(*args[:3], **kwargs)
219
            vm_str = '/%s' % server_id if server_id else ''
220
            cmd_str = '/%s' % command if command else ''
221
            self.assertEqual(get.mock_calls[-1], call(
222
                '/flavors%s%s' % (vm_str, cmd_str),
223
                success=success,
224
                **kwargs))
225

    
226

    
227
class Compute(TestCase):
228

    
229
    def assert_dicts_are_equal(self, d1, d2):
230
        for k, v in d1.items():
231
            self.assertTrue(k in d2)
232
            if isinstance(v, dict):
233
                self.assert_dicts_are_equal(v, d2[k])
234
            else:
235
                self.assertEqual(unicode(v), unicode(d2[k]))
236

    
237
    """Set up a Cyclades thorough test"""
238
    def setUp(self):
239
        self.url = 'http://cyclades.example.com'
240
        self.token = 'cyc14d3s70k3n'
241
        self.client = ComputeClient(self.url, self.token)
242

    
243
    def tearDown(self):
244
        FR.status_code = 200
245
        FR.json = vm_recv
246

    
247
    @patch(
248
        '%s.get_image_details' % compute_pkg,
249
        return_value=img_recv['image'])
250
    def test_create_server(self, GID):
251
        with patch.object(
252
                ComputeClient, 'servers_post',
253
                side_effect=ClientError(
254
                    'REQUEST ENTITY TOO LARGE',
255
                    status=403)):
256
            self.assertRaises(
257
                ClientError,
258
                self.client.create_server,
259
                vm_name, fid, img_ref)
260

    
261
        with patch.object(
262
                ComputeClient, 'servers_post',
263
                return_value=FR()) as post:
264
            r = self.client.create_server(vm_name, fid, img_ref)
265
            self.assertEqual(r, FR.json['server'])
266
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
267
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
268
            prsn = 'Personality string (does not work with real servers)'
269
            self.client.create_server(vm_name, fid, img_ref, prsn)
270
            expected = dict(server=dict(vm_send['server']))
271
            expected['server']['personality'] = prsn
272
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
273

    
274
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
275
    def test_list_servers(self, SG):
276
        FR.json = vm_list
277
        for detail in (False, True):
278
            r = self.client.list_servers(detail)
279
            self.assertEqual(SG.mock_calls[-1], call(
280
                command='detail' if detail else ''))
281
            for i, vm in enumerate(vm_list['servers']['values']):
282
                self.assert_dicts_are_equal(r[i], vm)
283
            self.assertEqual(i + 1, len(r))
284

    
285
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
286
    def test_get_server_details(self, SG):
287
        vm_id = vm_recv['server']['id']
288
        r = self.client.get_server_details(vm_id)
289
        SG.assert_called_once_with(vm_id)
290
        self.assert_dicts_are_equal(r, vm_recv['server'])
291

    
292
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
293
    def test_update_server_name(self, SP):
294
        vm_id = vm_recv['server']['id']
295
        new_name = vm_name + '_new'
296
        self.client.update_server_name(vm_id, new_name)
297
        SP.assert_called_once_with(vm_id, json_data=dict(
298
            server=dict(name=new_name)))
299

    
300
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
301
    def test_reboot_server(self, SP):
302
        vm_id = vm_recv['server']['id']
303
        for hard in (None, True):
304
            self.client.reboot_server(vm_id, hard=hard)
305
            self.assertEqual(SP.mock_calls[-1], call(
306
                vm_id, 'action',
307
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
308

    
309
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
310
    def test_create_server_metadata(self, SP):
311
        vm_id = vm_recv['server']['id']
312
        metadata = dict(m1='v1', m2='v2', m3='v3')
313
        FR.json = dict(meta=vm_recv['server'])
314
        for k, v in metadata.items():
315
            r = self.client.create_server_metadata(vm_id, k, v)
316
            self.assert_dicts_are_equal(r, vm_recv['server'])
317
            self.assertEqual(SP.mock_calls[-1], call(
318
                vm_id, 'meta/%s' % k,
319
                json_data=dict(meta={k: v}), success=201))
320

    
321
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
322
    def test_get_server_metadata(self, SG):
323
        vm_id = vm_recv['server']['id']
324
        metadata = dict(m1='v1', m2='v2', m3='v3')
325
        FR.json = dict(metadata=dict(values=metadata))
326
        r = self.client.get_server_metadata(vm_id)
327
        SG.assert_called_once_with(vm_id, '/meta')
328
        self.assert_dicts_are_equal(r, metadata)
329

    
330
        for k, v in metadata.items():
331
            FR.json = dict(meta={k: v})
332
            r = self.client.get_server_metadata(vm_id, k)
333
            self.assert_dicts_are_equal(r, {k: v})
334
            self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
335

    
336
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
337
    def test_update_server_metadata(self, SP):
338
        vm_id = vm_recv['server']['id']
339
        metadata = dict(m1='v1', m2='v2', m3='v3')
340
        FR.json = dict(metadata=metadata)
341
        r = self.client.update_server_metadata(vm_id, **metadata)
342
        self.assert_dicts_are_equal(r, metadata)
343
        SP.assert_called_once_with(
344
            vm_id, 'meta',
345
            json_data=dict(metadata=metadata), success=201)
346

    
347
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
348
    def test_delete_server_metadata(self, SD):
349
        vm_id = vm_recv['server']['id']
350
        key = 'metakey'
351
        self.client.delete_server_metadata(vm_id, key)
352
        SD.assert_called_once_with(vm_id, 'meta/' + key)
353

    
354
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
355
    def test_list_flavors(self, FG):
356
        FR.json = flavor_list
357
        for cmd in ('', 'detail'):
358
            r = self.client.list_flavors(detail=(cmd == 'detail'))
359
            self.assertEqual(FG.mock_calls[-1], call(command=cmd))
360
            self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
361

    
362
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
363
    def test_get_flavor_details(self, FG):
364
        FR.json = dict(flavor=flavor_list['flavors'])
365
        r = self.client.get_flavor_details(fid)
366
        FG.assert_called_once_with(fid)
367
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
368

    
369
    @patch('%s.images_get' % compute_pkg, return_value=FR())
370
    def test_list_images(self, IG):
371
        FR.json = img_list
372
        for cmd in ('', 'detail'):
373
            r = self.client.list_images(detail=(cmd == 'detail'))
374
            self.assertEqual(IG.mock_calls[-1], call(command=cmd))
375
            expected = img_list['images']['values']
376
            for i in range(len(r)):
377
                self.assert_dicts_are_equal(expected[i], r[i])
378

    
379
    @patch('%s.images_get' % compute_pkg, return_value=FR())
380
    def test_get_image_details(self, IG):
381
        FR.json = img_recv
382
        r = self.client.get_image_details(img_ref)
383
        IG.assert_called_once_with(img_ref)
384
        self.assert_dicts_are_equal(r, img_recv['image'])
385

    
386
    @patch('%s.images_get' % compute_pkg, return_value=FR())
387
    def test_get_image_metadata(self, IG):
388
        for key in ('', '50m3k3y'):
389
            FR.json = dict(meta=img_recv['image']) if (
390
                key) else dict(metadata=dict(values=img_recv['image']))
391
            r = self.client.get_image_metadata(img_ref, key)
392
            self.assertEqual(IG.mock_calls[-1], call(
393
                '%s' % img_ref,
394
                '/meta%s' % (('/%s' % key) if key else '')))
395
            self.assert_dicts_are_equal(img_recv['image'], r)
396

    
397
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
398
    def test_delete_server(self, SD):
399
        vm_id = vm_recv['server']['id']
400
        self.client.delete_server(vm_id)
401
        SD.assert_called_once_with(vm_id)
402

    
403
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
404
    def test_delete_image(self, ID):
405
        self.client.delete_image(img_ref)
406
        ID.assert_called_once_with(img_ref)
407

    
408
    @patch('%s.images_put' % compute_pkg, return_value=FR())
409
    def test_create_image_metadata(self, IP):
410
        (key, val) = ('k1', 'v1')
411
        FR.json = dict(meta=img_recv['image'])
412
        r = self.client.create_image_metadata(img_ref, key, val)
413
        IP.assert_called_once_with(
414
            img_ref, 'meta/%s' % key,
415
            json_data=dict(meta={key: val}))
416
        self.assert_dicts_are_equal(r, img_recv['image'])
417

    
418
    @patch('%s.images_post' % compute_pkg, return_value=FR())
419
    def test_update_image_metadata(self, IP):
420
        metadata = dict(m1='v1', m2='v2', m3='v3')
421
        FR.json = dict(metadata=metadata)
422
        r = self.client.update_image_metadata(img_ref, **metadata)
423
        IP.assert_called_once_with(
424
            img_ref, 'meta',
425
            json_data=dict(metadata=metadata))
426
        self.assert_dicts_are_equal(r, metadata)
427

    
428
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
429
    def test_delete_image_metadata(self, ID):
430
        key = 'metakey'
431
        self.client.delete_image_metadata(img_ref, key)
432
        ID.assert_called_once_with(img_ref, '/meta/%s' % key)
433

    
434

    
435
if __name__ == '__main__':
436
    from sys import argv
437
    from kamaki.clients.test import runTestCase
438
    not_found = True
439
    if not argv[1:] or argv[1] == 'Compute':
440
        not_found = False
441
        runTestCase(Compute, 'Compute Client', argv[2:])
442
    if not argv[1:] or argv[1] == 'ComputeRestApi':
443
        not_found = False
444
        runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
445
    if not_found:
446
        print('TestCase %s not found' % argv[1])