Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 6695edc9

History | View | Annotate | Download (16.6 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

    
210
class Compute(TestCase):
211

    
212
    def assert_dicts_are_equal(self, d1, d2):
213
        for k, v in d1.items():
214
            self.assertTrue(k in d2)
215
            if isinstance(v, dict):
216
                self.assert_dicts_are_equal(v, d2[k])
217
            else:
218
                self.assertEqual(unicode(v), unicode(d2[k]))
219

    
220
    """Set up a Cyclades thorough test"""
221
    def setUp(self):
222
        self.url = 'http://cyclades.example.com'
223
        self.token = 'cyc14d3s70k3n'
224
        self.client = ComputeClient(self.url, self.token)
225

    
226
    def tearDown(self):
227
        FR.status_code = 200
228
        FR.json = vm_recv
229

    
230
    @patch(
231
        '%s.get_image_details' % compute_pkg,
232
        return_value=img_recv['image'])
233
    def test_create_server(self, GID):
234
        with patch.object(
235
                ComputeClient, 'servers_post',
236
                side_effect=ClientError(
237
                    'REQUEST ENTITY TOO LARGE',
238
                    status=403)):
239
            self.assertRaises(
240
                ClientError,
241
                self.client.create_server,
242
                vm_name, fid, img_ref)
243

    
244
        with patch.object(
245
                ComputeClient, 'servers_post',
246
                return_value=FR()) as post:
247
            r = self.client.create_server(vm_name, fid, img_ref)
248
            self.assertEqual(r, FR.json['server'])
249
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
250
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
251
            prsn = 'Personality string (does not work with real servers)'
252
            self.client.create_server(vm_name, fid, img_ref, prsn)
253
            expected = dict(server=dict(vm_send['server']))
254
            expected['server']['personality'] = prsn
255
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
256

    
257
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
258
    def test_list_servers(self, SG):
259
        FR.json = vm_list
260
        for detail in (False, True):
261
            r = self.client.list_servers(detail)
262
            self.assertEqual(SG.mock_calls[-1], call(
263
                command='detail' if detail else ''))
264
            for i, vm in enumerate(vm_list['servers']['values']):
265
                self.assert_dicts_are_equal(r[i], vm)
266
            self.assertEqual(i + 1, len(r))
267

    
268
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
269
    def test_get_server_details(self, SG):
270
        vm_id = vm_recv['server']['id']
271
        r = self.client.get_server_details(vm_id)
272
        SG.assert_called_once_with(vm_id)
273
        self.assert_dicts_are_equal(r, vm_recv['server'])
274

    
275
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
276
    def test_update_server_name(self, SP):
277
        vm_id = vm_recv['server']['id']
278
        new_name = vm_name + '_new'
279
        self.client.update_server_name(vm_id, new_name)
280
        SP.assert_called_once_with(vm_id, json_data=dict(
281
            server=dict(name=new_name)))
282

    
283
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
284
    def test_reboot_server(self, SP):
285
        vm_id = vm_recv['server']['id']
286
        for hard in (None, True):
287
            self.client.reboot_server(vm_id, hard=hard)
288
            self.assertEqual(SP.mock_calls[-1], call(
289
                vm_id, 'action',
290
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
291

    
292
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
293
    def test_create_server_metadata(self, SP):
294
        vm_id = vm_recv['server']['id']
295
        metadata = dict(m1='v1', m2='v2', m3='v3')
296
        FR.json = dict(meta=vm_recv['server'])
297
        for k, v in metadata.items():
298
            r = self.client.create_server_metadata(vm_id, k, v)
299
            self.assert_dicts_are_equal(r, vm_recv['server'])
300
            self.assertEqual(SP.mock_calls[-1], call(
301
                vm_id, 'meta/%s' % k,
302
                json_data=dict(meta={k: v}), success=201))
303

    
304
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
305
    def test_get_server_metadata(self, SG):
306
        vm_id = vm_recv['server']['id']
307
        metadata = dict(m1='v1', m2='v2', m3='v3')
308
        FR.json = dict(metadata=dict(values=metadata))
309
        r = self.client.get_server_metadata(vm_id)
310
        SG.assert_called_once_with(vm_id, '/meta')
311
        self.assert_dicts_are_equal(r, metadata)
312

    
313
        for k, v in metadata.items():
314
            FR.json = dict(meta={k: v})
315
            r = self.client.get_server_metadata(vm_id, k)
316
            self.assert_dicts_are_equal(r, {k: v})
317
            self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
318

    
319
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
320
    def test_update_server_metadata(self, SP):
321
        vm_id = vm_recv['server']['id']
322
        metadata = dict(m1='v1', m2='v2', m3='v3')
323
        FR.json = dict(metadata=metadata)
324
        r = self.client.update_server_metadata(vm_id, **metadata)
325
        self.assert_dicts_are_equal(r, metadata)
326
        SP.assert_called_once_with(
327
            vm_id, 'meta',
328
            json_data=dict(metadata=metadata), success=201)
329

    
330
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
331
    def test_delete_server_metadata(self, SD):
332
        vm_id = vm_recv['server']['id']
333
        key = 'metakey'
334
        self.client.delete_server_metadata(vm_id, key)
335
        SD.assert_called_once_with(vm_id, 'meta/' + key)
336

    
337
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
338
    def test_list_flavors(self, FG):
339
        FR.json = flavor_list
340
        for cmd in ('', 'detail'):
341
            r = self.client.list_flavors(detail=(cmd == 'detail'))
342
            self.assertEqual(FG.mock_calls[-1], call(command=cmd))
343
            self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
344

    
345
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
346
    def test_get_flavor_details(self, FG):
347
        FR.json = dict(flavor=flavor_list['flavors'])
348
        r = self.client.get_flavor_details(fid)
349
        FG.assert_called_once_with(fid)
350
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
351

    
352
    @patch('%s.images_get' % compute_pkg, return_value=FR())
353
    def test_list_images(self, IG):
354
        FR.json = img_list
355
        for cmd in ('', 'detail'):
356
            r = self.client.list_images(detail=(cmd == 'detail'))
357
            self.assertEqual(IG.mock_calls[-1], call(command=cmd))
358
            expected = img_list['images']['values']
359
            for i in range(len(r)):
360
                self.assert_dicts_are_equal(expected[i], r[i])
361

    
362
    @patch('%s.images_get' % compute_pkg, return_value=FR())
363
    def test_get_image_details(self, IG):
364
        FR.json = img_recv
365
        r = self.client.get_image_details(img_ref)
366
        IG.assert_called_once_with(img_ref)
367
        self.assert_dicts_are_equal(r, img_recv['image'])
368

    
369
    @patch('%s.images_get' % compute_pkg, return_value=FR())
370
    def test_get_image_metadata(self, IG):
371
        for key in ('', '50m3k3y'):
372
            FR.json = dict(meta=img_recv['image']) if (
373
                key) else dict(metadata=dict(values=img_recv['image']))
374
            r = self.client.get_image_metadata(img_ref, key)
375
            self.assertEqual(IG.mock_calls[-1], call(
376
                '%s' % img_ref,
377
                '/meta%s' % (('/%s' % key) if key else '')))
378
            self.assert_dicts_are_equal(img_recv['image'], r)
379

    
380
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
381
    def test_delete_server(self, SD):
382
        vm_id = vm_recv['server']['id']
383
        self.client.delete_server(vm_id)
384
        SD.assert_called_once_with(vm_id)
385

    
386
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
387
    def test_delete_image(self, ID):
388
        self.client.delete_image(img_ref)
389
        ID.assert_called_once_with(img_ref)
390

    
391
    @patch('%s.images_put' % compute_pkg, return_value=FR())
392
    def test_create_image_metadata(self, IP):
393
        (key, val) = ('k1', 'v1')
394
        FR.json = dict(meta=img_recv['image'])
395
        r = self.client.create_image_metadata(img_ref, key, val)
396
        IP.assert_called_once_with(
397
            img_ref, 'meta/%s' % key,
398
            json_data=dict(meta={key: val}))
399
        self.assert_dicts_are_equal(r, img_recv['image'])
400

    
401
    @patch('%s.images_post' % compute_pkg, return_value=FR())
402
    def test_update_image_metadata(self, IP):
403
        metadata = dict(m1='v1', m2='v2', m3='v3')
404
        FR.json = dict(metadata=metadata)
405
        r = self.client.update_image_metadata(img_ref, **metadata)
406
        IP.assert_called_once_with(
407
            img_ref, 'meta',
408
            json_data=dict(metadata=metadata))
409
        self.assert_dicts_are_equal(r, metadata)
410

    
411
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
412
    def test_delete_image_metadata(self, ID):
413
        key = 'metakey'
414
        self.client.delete_image_metadata(img_ref, key)
415
        ID.assert_called_once_with(img_ref, '/meta/%s' % key)
416

    
417

    
418
if __name__ == '__main__':
419
    from sys import argv
420
    from kamaki.clients.test import runTestCase
421
    not_found = True
422
    if not argv[1:] or argv[1] == 'Compute':
423
        not_found = False
424
        runTestCase(Compute, 'Compute Client', argv[2:])
425
    if not argv[1:] or argv[1] == 'ComputeRestApi':
426
        not_found = False
427
        runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
428
    if not_found:
429
        print('TestCase %s not found' % argv[1])