Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / cyclades / test.py @ ae9c3b5f

History | View | Annotate | Download (19.8 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
from mock import patch, Mock, call
34
from unittest import TestCase
35
from json import loads
36

    
37
from kamaki.clients import Client, ClientError
38
from kamaki.clients.cyclades import CycladesClient
39
from kamaki.clients.cyclades_rest_api import CycladesClientApi
40

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

    
115

    
116
class FR(object):
117
    """FR stands for Fake Response"""
118
    json = vm_recv
119
    headers = {}
120
    content = json
121
    status = None
122
    status_code = 200
123

    
124
    def release(self):
125
        pass
126

    
127
khttp = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
128
cyclades_pkg = 'kamaki.clients.cyclades.CycladesClient'
129

    
130

    
131
class Cyclades(TestCase):
132

    
133
    def assert_dicts_are_equal(self, d1, d2):
134
        for k, v in d1.items():
135
            self.assertTrue(k in d2)
136
            if isinstance(v, dict):
137
                self.assert_dicts_are_equal(v, d2[k])
138
            else:
139
                self.assertEqual(unicode(v), unicode(d2[k]))
140

    
141
    """Set up a Cyclades thorough test"""
142
    def setUp(self):
143
        self.url = 'http://cyclades.example.com'
144
        self.token = 'cyc14d3s70k3n'
145
        self.client = CycladesClient(self.url, self.token)
146
        from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
147
        self.C = KamakiHTTPConnection
148

    
149
    def tearDown(self):
150
        FR.status_code = 200
151
        FR.json = vm_recv
152

    
153
    def test_list_servers(self):
154
        FR.json = vm_list
155
        with patch.object(
156
                self.C,
157
                'perform_request',
158
                return_value=FR()) as perform_req:
159
            r = self.client.list_servers()
160
            self.assertEqual(self.client.http_client.url, self.url)
161
            self.assertEqual(self.client.http_client.path, '/servers')
162
            (method, data, a_headers, a_params) = perform_req.call_args[0]
163
            self.assert_dicts_are_equal(dict(values=r), vm_list['servers'])
164
            r = self.client.list_servers(detail=True)
165
            self.assertEqual(self.client.http_client.url, self.url)
166
            self.assertEqual(self.client.http_client.path, '/servers/detail')
167
        with patch.object(
168
                CycladesClientApi,
169
                'servers_get',
170
                return_value=FR()) as servers_get:
171
            self.client.list_servers(changes_since=True)
172
            self.assertTrue(servers_get.call_args[1]['changes_since'])
173

    
174
    @patch('%s.perform_request' % khttp, return_value=FR())
175
    def test_list_images(self, PR):
176
        FR.json = img_list
177
        r = self.client.list_images()
178
        self.assertEqual(self.client.http_client.url, self.url)
179
        self.assertEqual(self.client.http_client.path, '/images')
180
        expected = img_list['images']['values']
181
        for i in range(len(r)):
182
            self.assert_dicts_are_equal(expected[i], r[i])
183
        self.client.list_images(detail=True)
184
        self.assertEqual(self.client.http_client.url, self.url)
185
        self.assertEqual(self.client.http_client.path, '/images/detail')
186

    
187
    @patch('%s.perform_request' % khttp, return_value=FR())
188
    def test_get_image_details(self, PR):
189
        FR.json = img_recv
190
        r = self.client.get_image_details(img_ref)
191
        self.assertEqual(self.client.http_client.url, self.url)
192
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
193
        self.assert_dicts_are_equal(r, img_recv['image'])
194

    
195
    @patch('%s.images_get' % cyclades_pkg, return_value=FR())
196
    def test_get_image_metadata(self, IG):
197
        FR.json = dict(metadata=dict(values=img_recv['image']))
198
        r = self.client.get_image_metadata(img_ref)
199
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta'))
200
        self.assert_dicts_are_equal(img_recv['image'], r)
201
        FR.json = dict(meta=img_recv['image'])
202
        key = 'somekey'
203
        self.client.get_image_metadata(img_ref, key)
204
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta/%s' % key))
205

    
206
    @patch('%s.perform_request' % khttp, return_value=FR())
207
    def test_shutdown_server(self, PR):
208
        vm_id = vm_recv['server']['id']
209
        FR.status_code = 202
210
        self.client.shutdown_server(vm_id)
211
        self.assertEqual(self.client.http_client.url, self.url)
212
        self.assertEqual(
213
            self.client.http_client.path,
214
            '/servers/%s/action' % vm_id)
215
        self.assertEqual(
216
            PR.call_args[0],
217
            ('post',  '{"shutdown": {}}', {}, {}))
218

    
219
    @patch('%s.perform_request' % khttp, return_value=FR())
220
    def test_start_server(self, PR):
221
        vm_id = vm_recv['server']['id']
222
        FR.status_code = 202
223
        self.client.start_server(vm_id)
224
        self.assertEqual(self.client.http_client.url, self.url)
225
        self.assertEqual(
226
            self.client.http_client.path,
227
            '/servers/%s/action' % vm_id)
228
        self.assertEqual(PR.call_args[0], ('post',  '{"start": {}}', {}, {}))
229

    
230
    @patch('%s.perform_request' % khttp, return_value=FR())
231
    def test_get_server_console(self, PR):
232
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
233
        FR.json = cnsl
234
        vm_id = vm_recv['server']['id']
235
        r = self.client.get_server_console(vm_id)
236
        self.assertEqual(self.client.http_client.url, self.url)
237
        self.assertEqual(
238
            self.client.http_client.path,
239
            '/servers/%s/action' % vm_id)
240
        self.assert_dicts_are_equal(cnsl['console'], r)
241
        self.assertEqual(
242
            PR.call_args[0],
243
            ('post',  '{"console": {"type": "vnc"}}', {}, {}))
244

    
245
    def test_get_firewall_profile(self):
246
        vm_id = vm_recv['server']['id']
247
        v = 'Some profile'
248
        ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
249
        with patch.object(
250
                CycladesClient,
251
                'get_server_details',
252
                return_value=ret) as GSD:
253
            r = self.client.get_firewall_profile(vm_id)
254
            self.assertEqual(r, v)
255
            self.assertEqual(GSD.call_args[0], (vm_id,))
256
            ret['attachments']['values'][0].pop('firewallProfile')
257
            self.assertRaises(
258
                ClientError,
259
                self.client.get_firewall_profile,
260
                vm_id)
261

    
262
    @patch('%s.perform_request' % khttp, return_value=FR())
263
    def test_set_firewall_profile(self, PR):
264
        vm_id = vm_recv['server']['id']
265
        v = 'Some profile'
266
        FR.status_code = 202
267
        self.client.set_firewall_profile(vm_id, v)
268
        self.assertEqual(self.client.http_client.url, self.url)
269
        self.assertEqual(
270
            self.client.http_client.path,
271
            '/servers/%s/action' % vm_id)
272
        self.assertEqual(PR.call_args[0], (
273
            'post',
274
            '{"firewallProfile": {"profile": "%s"}}' % v,
275
            {},
276
            {}))
277

    
278
    @patch('%s.perform_request' % khttp, return_value=FR())
279
    def test_get_server_stats(self, PR):
280
        vm_id = vm_recv['server']['id']
281
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
282
        FR.json = dict(stats=stats)
283
        r = self.client.get_server_stats(vm_id)
284
        self.assertEqual(self.client.http_client.url, self.url)
285
        self.assertEqual(
286
            self.client.http_client.path,
287
            '/servers/%s/stats' % vm_id)
288
        self.assert_dicts_are_equal(stats, r)
289

    
290
    @patch('%s.perform_request' % khttp, return_value=FR())
291
    def test_create_network(self, PR):
292
        net_name = net_send['network']['name']
293
        FR.json = net_recv
294
        FR.status_code = 202
295
        full_args = dict(
296
                cidr='192.168.0.0/24',
297
                gateway='192.168.0.1',
298
                type='MAC_FILTERED',
299
                dhcp=True)
300
        test_args = dict(full_args)
301
        test_args.update(dict(empty=None, full=None))
302
        for arg, val in test_args.items():
303
            kwargs = {} if arg == 'empty' else full_args if (
304
                arg == 'full') else {arg: val}
305
            r = self.client.create_network(net_name, **kwargs)
306
            self.assertEqual(self.client.http_client.url, self.url)
307
            self.assertEqual(
308
                self.client.http_client.path,
309
                '/networks')
310
            self.assert_dicts_are_equal(r, net_recv['network'])
311
            data = PR.call_args[0][1]
312
            expected = dict(network=dict(net_send['network']))
313
            expected['network'].update(kwargs)
314
            self.assert_dicts_are_equal(loads(data), expected)
315

    
316
    @patch('%s.perform_request' % khttp, return_value=FR())
317
    def test_connect_server(self, PR):
318
        vm_id = vm_recv['server']['id']
319
        net_id = net_recv['network']['id']
320
        FR.status_code = 202
321
        self.client.connect_server(vm_id, net_id)
322
        self.assertEqual(self.client.http_client.url, self.url)
323
        self.assertEqual(
324
            self.client.http_client.path,
325
            '/networks/%s/action' % net_id)
326
        self.assertEqual(
327
            PR.call_args[0],
328
            ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
329

    
330
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
331
    def test_disconnect_server(self, NP):
332
        vm_id = vm_recv['server']['id']
333
        net_id = net_recv['network']['id']
334
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
335
        vm_nics = [
336
            dict(id=nic_id, network_id=net_id),
337
            dict(id='another-nic-id', network_id='another-net-id'),
338
            dict(id=nic_id * 2, network_id=net_id * 2)]
339
        with patch.object(
340
                CycladesClient,
341
                'list_server_nics',
342
                return_value=vm_nics) as LSN:
343
            r = self.client.disconnect_server(vm_id, nic_id)
344
            self.assertEqual(r, 1)
345
            self.assertEqual(LSN.call_args[0], (vm_id,))
346
            self.assertEqual(NP.call_args[0], (net_id, 'action'))
347
            self.assertEqual(
348
                NP.call_args[1],
349
                dict(json_data=dict(remove=dict(attachment=nic_id))))
350

    
351
    @patch('%s.perform_request' % khttp, return_value=FR())
352
    def test_list_server_nics(self, PR):
353
        vm_id = vm_recv['server']['id']
354
        nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
355
        FR.json = nics
356
        r = self.client.list_server_nics(vm_id)
357
        self.assertEqual(self.client.http_client.url, self.url)
358
        self.assertEqual(
359
            self.client.http_client.path,
360
            '/servers/%s/ips' % vm_id)
361
        expected = nics['addresses']['values']
362
        for i in range(len(r)):
363
            self.assert_dicts_are_equal(r[i], expected[i])
364

    
365
    @patch('%s.perform_request' % khttp, return_value=FR())
366
    def test_list_networks(self, PR):
367
        FR.json = net_list
368
        r = self.client.list_networks()
369
        self.assertEqual(self.client.http_client.url, self.url)
370
        self.assertEqual(self.client.http_client.path, '/networks')
371
        expected = net_list['networks']['values']
372
        for i in range(len(r)):
373
            self.assert_dicts_are_equal(expected[i], r[i])
374
        self.client.list_networks(detail=True)
375
        self.assertEqual(self.client.http_client.url, self.url)
376
        self.assertEqual(self.client.http_client.path, '/networks/detail')
377

    
378
    @patch('%s.perform_request' % khttp, return_value=FR())
379
    def test_list_network_nics(self, PR):
380
        net_id = net_recv['network']['id']
381
        FR.json = net_recv
382
        r = self.client.list_network_nics(net_id)
383
        self.assertEqual(self.client.http_client.url, self.url)
384
        self.assertEqual(
385
            self.client.http_client.path,
386
            '/networks/%s' % net_id)
387
        expected = net_recv['network']['attachments']['values']
388
        for i in range(len(r)):
389
            self.assert_dicts_are_equal(r[i], expected[i])
390

    
391
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
392
    def test_disconnect_network_nics(self, NP):
393
        net_id = net_recv['network']['id']
394
        nics = ['nic1', 'nic2', 'nic3']
395
        with patch.object(
396
                CycladesClient,
397
                'list_network_nics',
398
                return_value=nics) as lnn:
399
            self.client.disconnect_network_nics(net_id)
400
            lnn.assert_called_once_with(net_id)
401
            for i in range(len(nics)):
402
                expected = call(net_id, 'action', json_data=dict(
403
                    remove=dict(attachment=nics[i])))
404
                self.assertEqual(expected, NP.mock_calls[i])
405

    
406
    @patch('%s.perform_request' % khttp, return_value=FR())
407
    def test_get_network_details(self, PR):
408
        FR.json = net_recv
409
        net_id = net_recv['network']['id']
410
        r = self.client.get_network_details(net_id)
411
        self.assertEqual(self.client.http_client.url, self.url)
412
        self.assertEqual(
413
            self.client.http_client.path,
414
            '/networks/%s' % net_id)
415
        self.assert_dicts_are_equal(r, net_recv['network'])
416

    
417
    @patch('%s.perform_request' % khttp, return_value=FR())
418
    def test_update_network_name(self, PR):
419
        net_id = net_recv['network']['id']
420
        new_name = '%s_new' % net_id
421
        FR.status_code = 204
422
        self.client.update_network_name(net_id, new_name)
423
        self.assertEqual(self.client.http_client.url, self.url)
424
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
425
        (method, data, a_headers, a_params) = PR.call_args[0]
426
        self.assert_dicts_are_equal(
427
            dict(network=dict(name=new_name)),
428
            loads(data))
429

    
430
    @patch('%s.perform_request' % khttp, return_value=FR())
431
    def test_delete_server(self, PR):
432
        vm_id = vm_recv['server']['id']
433
        FR.status_code = 204
434
        self.client.delete_server(vm_id)
435
        self.assertEqual(self.client.http_client.url, self.url)
436
        self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
437

    
438
    @patch('%s.perform_request' % khttp, return_value=FR())
439
    def test_delete_image(self, PR):
440
        FR.status_code = 204
441
        self.client.delete_image(img_ref)
442
        self.assertEqual(self.client.http_client.url, self.url)
443
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
444

    
445
    @patch('%s.perform_request' % khttp, return_value=FR())
446
    def test_delete_network(self, PR):
447
        net_id = net_recv['network']['id']
448
        FR.status_code = 204
449
        self.client.delete_network(net_id)
450
        self.assertEqual(self.client.http_client.url, self.url)
451
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
452

    
453
    @patch('%s.perform_request' % khttp, return_value=FR())
454
    def test_create_image_metadata(self, PR):
455
        metadata = dict(m1='v1', m2='v2', m3='v3')
456
        FR.json = dict(meta=img_recv['image'])
457
        self.assertRaises(
458
            ClientError,
459
            self.client.create_image_metadata,
460
            img_ref, 'key', 'value')
461
        FR.status_code = 201
462
        for k, v in metadata.items():
463
            r = self.client.create_image_metadata(img_ref, k, v)
464
            self.assertEqual(self.client.http_client.url, self.url)
465
            self.assertEqual(
466
                self.client.http_client.path,
467
                '/images/%s/meta/%s' % (img_ref, k))
468
            (method, data, a_headers, a_params) = PR.call_args[0]
469
            self.assertEqual(dict(meta={k: v}), loads(data))
470
            self.assert_dicts_are_equal(r, img_recv['image'])
471

    
472
    @patch('%s.images_post' % cyclades_pkg, return_value=FR())
473
    def test_update_image_metadata(self, images_post):
474
        metadata = dict(m1='v1', m2='v2', m3='v3')
475
        FR.json = dict(metadata=metadata)
476
        r = self.client.update_image_metadata(img_ref, **metadata)
477
        self.assert_dicts_are_equal(r, metadata)
478
        (called_id, cmd) = images_post.call_args[0]
479
        self.assertEqual(called_id, img_ref)
480
        self.assertEqual(cmd, 'meta')
481
        data = images_post.call_args[1]['json_data']
482
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
483

    
484
    @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
485
    def test_delete_image_metadata(self, images_delete):
486
        key = 'metakey'
487
        self.client.delete_image_metadata(img_ref, key)
488
        self.assertEqual(
489
            (img_ref, '/meta/' + key),
490
            images_delete.call_args[0])
491

    
492
if __name__ == '__main__':
493
    from sys import argv
494
    from kamaki.clients.test import runTestCase
495
    runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])