Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 882c7e33

History | View | Annotate | Download (14.5 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], **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
    @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
                **args[3]))
157

    
158

    
159
class Compute(TestCase):
160

    
161
    def assert_dicts_are_equal(self, d1, d2):
162
        for k, v in d1.items():
163
            self.assertTrue(k in d2)
164
            if isinstance(v, dict):
165
                self.assert_dicts_are_equal(v, d2[k])
166
            else:
167
                self.assertEqual(unicode(v), unicode(d2[k]))
168

    
169
    """Set up a Cyclades thorough test"""
170
    def setUp(self):
171
        self.url = 'http://cyclades.example.com'
172
        self.token = 'cyc14d3s70k3n'
173
        self.client = ComputeClient(self.url, self.token)
174

    
175
    def tearDown(self):
176
        FR.status_code = 200
177
        FR.json = vm_recv
178

    
179
    @patch(
180
        '%s.get_image_details' % compute_pkg,
181
        return_value=img_recv['image'])
182
    def test_create_server(self, GID):
183
        with patch.object(
184
                ComputeClient, 'servers_post',
185
                side_effect=ClientError(
186
                    'REQUEST ENTITY TOO LARGE',
187
                    status=403)):
188
            self.assertRaises(
189
                ClientError,
190
                self.client.create_server,
191
                vm_name, fid, img_ref)
192

    
193
        with patch.object(
194
                ComputeClient, 'servers_post',
195
                return_value=FR()) as post:
196
            r = self.client.create_server(vm_name, fid, img_ref)
197
            self.assertEqual(r, FR.json['server'])
198
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
199
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
200
            prsn = 'Personality string (does not work with real servers)'
201
            self.client.create_server(vm_name, fid, img_ref, prsn)
202
            expected = dict(server=dict(vm_send['server']))
203
            expected['server']['personality'] = prsn
204
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
205

    
206
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
207
    def test_list_servers(self, SG):
208
        FR.json = vm_list
209
        for detail in (False, True):
210
            r = self.client.list_servers(detail)
211
            self.assertEqual(SG.mock_calls[-1], call(
212
                command='detail' if detail else ''))
213
            for i, vm in enumerate(vm_list['servers']['values']):
214
                self.assert_dicts_are_equal(r[i], vm)
215
            self.assertEqual(i + 1, len(r))
216

    
217
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
218
    def test_get_server_details(self, SG):
219
        vm_id = vm_recv['server']['id']
220
        r = self.client.get_server_details(vm_id)
221
        SG.assert_called_once_with(vm_id)
222
        self.assert_dicts_are_equal(r, vm_recv['server'])
223

    
224
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
225
    def test_update_server_name(self, SP):
226
        vm_id = vm_recv['server']['id']
227
        new_name = vm_name + '_new'
228
        self.client.update_server_name(vm_id, new_name)
229
        SP.assert_called_once_with(vm_id, json_data=dict(
230
            server=dict(name=new_name)))
231

    
232
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
233
    def test_reboot_server(self, SP):
234
        vm_id = vm_recv['server']['id']
235
        for hard in (None, True):
236
            self.client.reboot_server(vm_id, hard=hard)
237
            self.assertEqual(SP.mock_calls[-1], call(
238
                vm_id, 'action',
239
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
240

    
241
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
242
    def test_create_server_metadata(self, SP):
243
        vm_id = vm_recv['server']['id']
244
        metadata = dict(m1='v1', m2='v2', m3='v3')
245
        FR.json = dict(meta=vm_recv['server'])
246
        for k, v in metadata.items():
247
            r = self.client.create_server_metadata(vm_id, k, v)
248
            self.assert_dicts_are_equal(r, vm_recv['server'])
249
            self.assertEqual(SP.mock_calls[-1], call(
250
                vm_id, 'meta/%s' % k,
251
                json_data=dict(meta={k: v}), success=201))
252

    
253
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
254
    def test_get_server_metadata(self, SG):
255
        vm_id = vm_recv['server']['id']
256
        metadata = dict(m1='v1', m2='v2', m3='v3')
257
        FR.json = dict(metadata=dict(values=metadata))
258
        r = self.client.get_server_metadata(vm_id)
259
        SG.assert_called_once_with(vm_id, '/meta')
260
        self.assert_dicts_are_equal(r, metadata)
261

    
262
        for k, v in metadata.items():
263
            FR.json = dict(meta={k: v})
264
            r = self.client.get_server_metadata(vm_id, k)
265
            self.assert_dicts_are_equal(r, {k: v})
266
            self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
267

    
268
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
269
    def test_update_server_metadata(self, SP):
270
        vm_id = vm_recv['server']['id']
271
        metadata = dict(m1='v1', m2='v2', m3='v3')
272
        FR.json = dict(metadata=metadata)
273
        r = self.client.update_server_metadata(vm_id, **metadata)
274
        self.assert_dicts_are_equal(r, metadata)
275
        SP.assert_called_once_with(
276
            vm_id, 'meta',
277
            json_data=dict(metadata=metadata), success=201)
278

    
279
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
280
    def test_delete_server_metadata(self, SD):
281
        vm_id = vm_recv['server']['id']
282
        key = 'metakey'
283
        self.client.delete_server_metadata(vm_id, key)
284
        SD.assert_called_once_with(vm_id, 'meta/' + key)
285

    
286
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
287
    def test_list_flavors(self, FG):
288
        FR.json = flavor_list
289
        for cmd in ('', 'detail'):
290
            r = self.client.list_flavors(detail=(cmd == 'detail'))
291
            self.assertEqual(FG.mock_calls[-1], call(command=cmd))
292
            self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
293

    
294
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
295
    def test_get_flavor_details(self, FG):
296
        FR.json = dict(flavor=flavor_list['flavors'])
297
        r = self.client.get_flavor_details(fid)
298
        FG.assert_called_once_with(fid)
299
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
300

    
301
    @patch('%s.images_get' % compute_pkg, return_value=FR())
302
    def test_list_images(self, IG):
303
        FR.json = img_list
304
        for cmd in ('', 'detail'):
305
            r = self.client.list_images(detail=(cmd == 'detail'))
306
            self.assertEqual(IG.mock_calls[-1], call(command=cmd))
307
            expected = img_list['images']['values']
308
            for i in range(len(r)):
309
                self.assert_dicts_are_equal(expected[i], r[i])
310

    
311
    @patch('%s.images_get' % compute_pkg, return_value=FR())
312
    def test_get_image_details(self, IG):
313
        FR.json = img_recv
314
        r = self.client.get_image_details(img_ref)
315
        IG.assert_called_once_with(img_ref)
316
        self.assert_dicts_are_equal(r, img_recv['image'])
317

    
318
    @patch('%s.images_get' % compute_pkg, return_value=FR())
319
    def test_get_image_metadata(self, IG):
320
        for key in ('', '50m3k3y'):
321
            FR.json = dict(meta=img_recv['image']) if (
322
                key) else dict(metadata=dict(values=img_recv['image']))
323
            r = self.client.get_image_metadata(img_ref, key)
324
            self.assertEqual(IG.mock_calls[-1], call(
325
                '%s' % img_ref,
326
                '/meta%s' % (('/%s' % key) if key else '')))
327
            self.assert_dicts_are_equal(img_recv['image'], r)
328

    
329
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
330
    def test_delete_server(self, SD):
331
        vm_id = vm_recv['server']['id']
332
        self.client.delete_server(vm_id)
333
        SD.assert_called_once_with(vm_id)
334

    
335
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
336
    def test_delete_image(self, ID):
337
        self.client.delete_image(img_ref)
338
        ID.assert_called_once_with(img_ref)
339

    
340
    @patch('%s.images_put' % compute_pkg, return_value=FR())
341
    def test_create_image_metadata(self, IP):
342
        (key, val) = ('k1', 'v1')
343
        FR.json = dict(meta=img_recv['image'])
344
        r = self.client.create_image_metadata(img_ref, key, val)
345
        IP.assert_called_once_with(
346
            img_ref, 'meta/%s' % key,
347
            json_data=dict(meta={key: val}))
348
        self.assert_dicts_are_equal(r, img_recv['image'])
349

    
350
    @patch('%s.images_post' % compute_pkg, return_value=FR())
351
    def test_update_image_metadata(self, IP):
352
        metadata = dict(m1='v1', m2='v2', m3='v3')
353
        FR.json = dict(metadata=metadata)
354
        r = self.client.update_image_metadata(img_ref, **metadata)
355
        IP.assert_called_once_with(
356
            img_ref, 'meta',
357
            json_data=dict(metadata=metadata))
358
        self.assert_dicts_are_equal(r, metadata)
359

    
360
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
361
    def test_delete_image_metadata(self, ID):
362
        key = 'metakey'
363
        self.client.delete_image_metadata(img_ref, key)
364
        ID.assert_called_once_with(img_ref, '/meta/%s' % key)
365

    
366

    
367
if __name__ == '__main__':
368
    from sys import argv
369
    from kamaki.clients.test import runTestCase
370
    not_found = True
371
    if not argv[1:] or argv[1] == 'Compute':
372
        not_found = False
373
        runTestCase(Compute, 'Compute Client', argv[2:])
374
    if not argv[1:] or argv[1] == 'ComputeRestApi':
375
        not_found = False
376
        runTestCase(ComputeRestApi, 'ComputeRestApi Client', argv[2:])
377
    if not_found:
378
        print('TestCase %s not found' % argv[1])