Statistics
| Branch: | Tag: | Revision:

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

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

    
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], **kwargs)
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
                **kwargs))
140

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

    
158
    @patch('%s.set_header' % rest_pkg)
159
    @patch('%s.post' % rest_pkg, return_value=FR())
160
    def test_servers_post(self, post, SH):
161
        from json import dumps
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

    
185
class Compute(TestCase):
186

    
187
    def assert_dicts_are_equal(self, d1, d2):
188
        for k, v in d1.items():
189
            self.assertTrue(k in d2)
190
            if isinstance(v, dict):
191
                self.assert_dicts_are_equal(v, d2[k])
192
            else:
193
                self.assertEqual(unicode(v), unicode(d2[k]))
194

    
195
    """Set up a Cyclades thorough test"""
196
    def setUp(self):
197
        self.url = 'http://cyclades.example.com'
198
        self.token = 'cyc14d3s70k3n'
199
        self.client = ComputeClient(self.url, self.token)
200

    
201
    def tearDown(self):
202
        FR.status_code = 200
203
        FR.json = vm_recv
204

    
205
    @patch(
206
        '%s.get_image_details' % compute_pkg,
207
        return_value=img_recv['image'])
208
    def test_create_server(self, GID):
209
        with patch.object(
210
                ComputeClient, 'servers_post',
211
                side_effect=ClientError(
212
                    'REQUEST ENTITY TOO LARGE',
213
                    status=403)):
214
            self.assertRaises(
215
                ClientError,
216
                self.client.create_server,
217
                vm_name, fid, img_ref)
218

    
219
        with patch.object(
220
                ComputeClient, 'servers_post',
221
                return_value=FR()) as post:
222
            r = self.client.create_server(vm_name, fid, img_ref)
223
            self.assertEqual(r, FR.json['server'])
224
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
225
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
226
            prsn = 'Personality string (does not work with real servers)'
227
            self.client.create_server(vm_name, fid, img_ref, prsn)
228
            expected = dict(server=dict(vm_send['server']))
229
            expected['server']['personality'] = prsn
230
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
231

    
232
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
233
    def test_list_servers(self, SG):
234
        FR.json = vm_list
235
        for detail in (False, True):
236
            r = self.client.list_servers(detail)
237
            self.assertEqual(SG.mock_calls[-1], call(
238
                command='detail' if detail else ''))
239
            for i, vm in enumerate(vm_list['servers']['values']):
240
                self.assert_dicts_are_equal(r[i], vm)
241
            self.assertEqual(i + 1, len(r))
242

    
243
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
244
    def test_get_server_details(self, SG):
245
        vm_id = vm_recv['server']['id']
246
        r = self.client.get_server_details(vm_id)
247
        SG.assert_called_once_with(vm_id)
248
        self.assert_dicts_are_equal(r, vm_recv['server'])
249

    
250
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
251
    def test_update_server_name(self, SP):
252
        vm_id = vm_recv['server']['id']
253
        new_name = vm_name + '_new'
254
        self.client.update_server_name(vm_id, new_name)
255
        SP.assert_called_once_with(vm_id, json_data=dict(
256
            server=dict(name=new_name)))
257

    
258
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
259
    def test_reboot_server(self, SP):
260
        vm_id = vm_recv['server']['id']
261
        for hard in (None, True):
262
            self.client.reboot_server(vm_id, hard=hard)
263
            self.assertEqual(SP.mock_calls[-1], call(
264
                vm_id, 'action',
265
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
266

    
267
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
268
    def test_create_server_metadata(self, SP):
269
        vm_id = vm_recv['server']['id']
270
        metadata = dict(m1='v1', m2='v2', m3='v3')
271
        FR.json = dict(meta=vm_recv['server'])
272
        for k, v in metadata.items():
273
            r = self.client.create_server_metadata(vm_id, k, v)
274
            self.assert_dicts_are_equal(r, vm_recv['server'])
275
            self.assertEqual(SP.mock_calls[-1], call(
276
                vm_id, 'meta/%s' % k,
277
                json_data=dict(meta={k: v}), success=201))
278

    
279
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
280
    def test_get_server_metadata(self, SG):
281
        vm_id = vm_recv['server']['id']
282
        metadata = dict(m1='v1', m2='v2', m3='v3')
283
        FR.json = dict(metadata=dict(values=metadata))
284
        r = self.client.get_server_metadata(vm_id)
285
        SG.assert_called_once_with(vm_id, '/meta')
286
        self.assert_dicts_are_equal(r, metadata)
287

    
288
        for k, v in metadata.items():
289
            FR.json = dict(meta={k: v})
290
            r = self.client.get_server_metadata(vm_id, k)
291
            self.assert_dicts_are_equal(r, {k: v})
292
            self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
293

    
294
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
295
    def test_update_server_metadata(self, SP):
296
        vm_id = vm_recv['server']['id']
297
        metadata = dict(m1='v1', m2='v2', m3='v3')
298
        FR.json = dict(metadata=metadata)
299
        r = self.client.update_server_metadata(vm_id, **metadata)
300
        self.assert_dicts_are_equal(r, metadata)
301
        SP.assert_called_once_with(
302
            vm_id, 'meta',
303
            json_data=dict(metadata=metadata), success=201)
304

    
305
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
306
    def test_delete_server_metadata(self, SD):
307
        vm_id = vm_recv['server']['id']
308
        key = 'metakey'
309
        self.client.delete_server_metadata(vm_id, key)
310
        SD.assert_called_once_with(vm_id, 'meta/' + key)
311

    
312
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
313
    def test_list_flavors(self, FG):
314
        FR.json = flavor_list
315
        for cmd in ('', 'detail'):
316
            r = self.client.list_flavors(detail=(cmd == 'detail'))
317
            self.assertEqual(FG.mock_calls[-1], call(command=cmd))
318
            self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
319

    
320
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
321
    def test_get_flavor_details(self, FG):
322
        FR.json = dict(flavor=flavor_list['flavors'])
323
        r = self.client.get_flavor_details(fid)
324
        FG.assert_called_once_with(fid)
325
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
326

    
327
    @patch('%s.images_get' % compute_pkg, return_value=FR())
328
    def test_list_images(self, IG):
329
        FR.json = img_list
330
        for cmd in ('', 'detail'):
331
            r = self.client.list_images(detail=(cmd == 'detail'))
332
            self.assertEqual(IG.mock_calls[-1], call(command=cmd))
333
            expected = img_list['images']['values']
334
            for i in range(len(r)):
335
                self.assert_dicts_are_equal(expected[i], r[i])
336

    
337
    @patch('%s.images_get' % compute_pkg, return_value=FR())
338
    def test_get_image_details(self, IG):
339
        FR.json = img_recv
340
        r = self.client.get_image_details(img_ref)
341
        IG.assert_called_once_with(img_ref)
342
        self.assert_dicts_are_equal(r, img_recv['image'])
343

    
344
    @patch('%s.images_get' % compute_pkg, return_value=FR())
345
    def test_get_image_metadata(self, IG):
346
        for key in ('', '50m3k3y'):
347
            FR.json = dict(meta=img_recv['image']) if (
348
                key) else dict(metadata=dict(values=img_recv['image']))
349
            r = self.client.get_image_metadata(img_ref, key)
350
            self.assertEqual(IG.mock_calls[-1], call(
351
                '%s' % img_ref,
352
                '/meta%s' % (('/%s' % key) if key else '')))
353
            self.assert_dicts_are_equal(img_recv['image'], r)
354

    
355
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
356
    def test_delete_server(self, SD):
357
        vm_id = vm_recv['server']['id']
358
        self.client.delete_server(vm_id)
359
        SD.assert_called_once_with(vm_id)
360

    
361
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
362
    def test_delete_image(self, ID):
363
        self.client.delete_image(img_ref)
364
        ID.assert_called_once_with(img_ref)
365

    
366
    @patch('%s.images_put' % compute_pkg, return_value=FR())
367
    def test_create_image_metadata(self, IP):
368
        (key, val) = ('k1', 'v1')
369
        FR.json = dict(meta=img_recv['image'])
370
        r = self.client.create_image_metadata(img_ref, key, val)
371
        IP.assert_called_once_with(
372
            img_ref, 'meta/%s' % key,
373
            json_data=dict(meta={key: val}))
374
        self.assert_dicts_are_equal(r, img_recv['image'])
375

    
376
    @patch('%s.images_post' % compute_pkg, return_value=FR())
377
    def test_update_image_metadata(self, IP):
378
        metadata = dict(m1='v1', m2='v2', m3='v3')
379
        FR.json = dict(metadata=metadata)
380
        r = self.client.update_image_metadata(img_ref, **metadata)
381
        IP.assert_called_once_with(
382
            img_ref, 'meta',
383
            json_data=dict(metadata=metadata))
384
        self.assert_dicts_are_equal(r, metadata)
385

    
386
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
387
    def test_delete_image_metadata(self, ID):
388
        key = 'metakey'
389
        self.client.delete_image_metadata(img_ref, key)
390
        ID.assert_called_once_with(img_ref, '/meta/%s' % key)
391

    
392

    
393
if __name__ == '__main__':
394
    from sys import argv
395
    from kamaki.clients.test import runTestCase
396
    not_found = True
397
    if not argv[1:] or argv[1] == 'Compute':
398
        not_found = False
399
        runTestCase(Compute, 'Compute Client', argv[2:])
400
    if not argv[1:] or argv[1] == 'ComputeRestApi':
401
        not_found = False
402
        runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
403
    if not_found:
404
        print('TestCase %s not found' % argv[1])