Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (19.2 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_get_image_details(self, PR):
176
        FR.json = img_recv
177
        r = self.client.get_image_details(img_ref)
178
        self.assertEqual(self.client.http_client.url, self.url)
179
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
180
        self.assert_dicts_are_equal(r, img_recv['image'])
181

    
182
    @patch('%s.images_get' % cyclades_pkg, return_value=FR())
183
    def test_get_image_metadata(self, IG):
184
        FR.json = dict(metadata=dict(values=img_recv['image']))
185
        r = self.client.get_image_metadata(img_ref)
186
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta'))
187
        self.assert_dicts_are_equal(img_recv['image'], r)
188
        FR.json = dict(meta=img_recv['image'])
189
        key = 'somekey'
190
        self.client.get_image_metadata(img_ref, key)
191
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta/%s' % key))
192

    
193
    @patch('%s.perform_request' % khttp, return_value=FR())
194
    def test_shutdown_server(self, PR):
195
        vm_id = vm_recv['server']['id']
196
        FR.status_code = 202
197
        self.client.shutdown_server(vm_id)
198
        self.assertEqual(self.client.http_client.url, self.url)
199
        self.assertEqual(
200
            self.client.http_client.path,
201
            '/servers/%s/action' % vm_id)
202
        self.assertEqual(
203
            PR.call_args[0],
204
            ('post',  '{"shutdown": {}}', {}, {}))
205

    
206
    @patch('%s.perform_request' % khttp, return_value=FR())
207
    def test_start_server(self, PR):
208
        vm_id = vm_recv['server']['id']
209
        FR.status_code = 202
210
        self.client.start_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(PR.call_args[0], ('post',  '{"start": {}}', {}, {}))
216

    
217
    @patch('%s.perform_request' % khttp, return_value=FR())
218
    def test_get_server_console(self, PR):
219
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
220
        FR.json = cnsl
221
        vm_id = vm_recv['server']['id']
222
        r = self.client.get_server_console(vm_id)
223
        self.assertEqual(self.client.http_client.url, self.url)
224
        self.assertEqual(
225
            self.client.http_client.path,
226
            '/servers/%s/action' % vm_id)
227
        self.assert_dicts_are_equal(cnsl['console'], r)
228
        self.assertEqual(
229
            PR.call_args[0],
230
            ('post',  '{"console": {"type": "vnc"}}', {}, {}))
231

    
232
    def test_get_firewall_profile(self):
233
        vm_id = vm_recv['server']['id']
234
        v = 'Some profile'
235
        ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
236
        with patch.object(
237
                CycladesClient,
238
                'get_server_details',
239
                return_value=ret) as GSD:
240
            r = self.client.get_firewall_profile(vm_id)
241
            self.assertEqual(r, v)
242
            self.assertEqual(GSD.call_args[0], (vm_id,))
243
            ret['attachments']['values'][0].pop('firewallProfile')
244
            self.assertRaises(
245
                ClientError,
246
                self.client.get_firewall_profile,
247
                vm_id)
248

    
249
    @patch('%s.perform_request' % khttp, return_value=FR())
250
    def test_set_firewall_profile(self, PR):
251
        vm_id = vm_recv['server']['id']
252
        v = 'Some profile'
253
        FR.status_code = 202
254
        self.client.set_firewall_profile(vm_id, v)
255
        self.assertEqual(self.client.http_client.url, self.url)
256
        self.assertEqual(
257
            self.client.http_client.path,
258
            '/servers/%s/action' % vm_id)
259
        self.assertEqual(PR.call_args[0], (
260
            'post',
261
            '{"firewallProfile": {"profile": "%s"}}' % v,
262
            {},
263
            {}))
264

    
265
    @patch('%s.perform_request' % khttp, return_value=FR())
266
    def test_get_server_stats(self, PR):
267
        vm_id = vm_recv['server']['id']
268
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
269
        FR.json = dict(stats=stats)
270
        r = self.client.get_server_stats(vm_id)
271
        self.assertEqual(self.client.http_client.url, self.url)
272
        self.assertEqual(
273
            self.client.http_client.path,
274
            '/servers/%s/stats' % vm_id)
275
        self.assert_dicts_are_equal(stats, r)
276

    
277
    @patch('%s.perform_request' % khttp, return_value=FR())
278
    def test_create_network(self, PR):
279
        net_name = net_send['network']['name']
280
        FR.json = net_recv
281
        FR.status_code = 202
282
        full_args = dict(
283
                cidr='192.168.0.0/24',
284
                gateway='192.168.0.1',
285
                type='MAC_FILTERED',
286
                dhcp=True)
287
        test_args = dict(full_args)
288
        test_args.update(dict(empty=None, full=None))
289
        for arg, val in test_args.items():
290
            kwargs = {} if arg == 'empty' else full_args if (
291
                arg == 'full') else {arg: val}
292
            r = self.client.create_network(net_name, **kwargs)
293
            self.assertEqual(self.client.http_client.url, self.url)
294
            self.assertEqual(
295
                self.client.http_client.path,
296
                '/networks')
297
            self.assert_dicts_are_equal(r, net_recv['network'])
298
            data = PR.call_args[0][1]
299
            expected = dict(network=dict(net_send['network']))
300
            expected['network'].update(kwargs)
301
            self.assert_dicts_are_equal(loads(data), expected)
302

    
303
    @patch('%s.perform_request' % khttp, return_value=FR())
304
    def test_connect_server(self, PR):
305
        vm_id = vm_recv['server']['id']
306
        net_id = net_recv['network']['id']
307
        FR.status_code = 202
308
        self.client.connect_server(vm_id, net_id)
309
        self.assertEqual(self.client.http_client.url, self.url)
310
        self.assertEqual(
311
            self.client.http_client.path,
312
            '/networks/%s/action' % net_id)
313
        self.assertEqual(
314
            PR.call_args[0],
315
            ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
316

    
317
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
318
    def test_disconnect_server(self, NP):
319
        vm_id = vm_recv['server']['id']
320
        net_id = net_recv['network']['id']
321
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
322
        vm_nics = [
323
            dict(id=nic_id, network_id=net_id),
324
            dict(id='another-nic-id', network_id='another-net-id'),
325
            dict(id=nic_id * 2, network_id=net_id * 2)]
326
        with patch.object(
327
                CycladesClient,
328
                'list_server_nics',
329
                return_value=vm_nics) as LSN:
330
            r = self.client.disconnect_server(vm_id, nic_id)
331
            self.assertEqual(r, 1)
332
            self.assertEqual(LSN.call_args[0], (vm_id,))
333
            self.assertEqual(NP.call_args[0], (net_id, 'action'))
334
            self.assertEqual(
335
                NP.call_args[1],
336
                dict(json_data=dict(remove=dict(attachment=nic_id))))
337

    
338
    @patch('%s.perform_request' % khttp, return_value=FR())
339
    def test_list_server_nics(self, PR):
340
        vm_id = vm_recv['server']['id']
341
        nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
342
        FR.json = nics
343
        r = self.client.list_server_nics(vm_id)
344
        self.assertEqual(self.client.http_client.url, self.url)
345
        self.assertEqual(
346
            self.client.http_client.path,
347
            '/servers/%s/ips' % vm_id)
348
        expected = nics['addresses']['values']
349
        for i in range(len(r)):
350
            self.assert_dicts_are_equal(r[i], expected[i])
351

    
352
    @patch('%s.perform_request' % khttp, return_value=FR())
353
    def test_list_networks(self, PR):
354
        FR.json = net_list
355
        r = self.client.list_networks()
356
        self.assertEqual(self.client.http_client.url, self.url)
357
        self.assertEqual(self.client.http_client.path, '/networks')
358
        expected = net_list['networks']['values']
359
        for i in range(len(r)):
360
            self.assert_dicts_are_equal(expected[i], r[i])
361
        self.client.list_networks(detail=True)
362
        self.assertEqual(self.client.http_client.url, self.url)
363
        self.assertEqual(self.client.http_client.path, '/networks/detail')
364

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

    
378
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
379
    def test_disconnect_network_nics(self, NP):
380
        net_id = net_recv['network']['id']
381
        nics = ['nic1', 'nic2', 'nic3']
382
        with patch.object(
383
                CycladesClient,
384
                'list_network_nics',
385
                return_value=nics) as lnn:
386
            self.client.disconnect_network_nics(net_id)
387
            lnn.assert_called_once_with(net_id)
388
            for i in range(len(nics)):
389
                expected = call(net_id, 'action', json_data=dict(
390
                    remove=dict(attachment=nics[i])))
391
                self.assertEqual(expected, NP.mock_calls[i])
392

    
393
    @patch('%s.perform_request' % khttp, return_value=FR())
394
    def test_get_network_details(self, PR):
395
        FR.json = net_recv
396
        net_id = net_recv['network']['id']
397
        r = self.client.get_network_details(net_id)
398
        self.assertEqual(self.client.http_client.url, self.url)
399
        self.assertEqual(
400
            self.client.http_client.path,
401
            '/networks/%s' % net_id)
402
        self.assert_dicts_are_equal(r, net_recv['network'])
403

    
404
    @patch('%s.perform_request' % khttp, return_value=FR())
405
    def test_update_network_name(self, PR):
406
        net_id = net_recv['network']['id']
407
        new_name = '%s_new' % net_id
408
        FR.status_code = 204
409
        self.client.update_network_name(net_id, new_name)
410
        self.assertEqual(self.client.http_client.url, self.url)
411
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
412
        (method, data, a_headers, a_params) = PR.call_args[0]
413
        self.assert_dicts_are_equal(
414
            dict(network=dict(name=new_name)),
415
            loads(data))
416

    
417
    @patch('%s.perform_request' % khttp, return_value=FR())
418
    def test_delete_server(self, PR):
419
        vm_id = vm_recv['server']['id']
420
        FR.status_code = 204
421
        self.client.delete_server(vm_id)
422
        self.assertEqual(self.client.http_client.url, self.url)
423
        self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
424

    
425
    @patch('%s.perform_request' % khttp, return_value=FR())
426
    def test_delete_image(self, PR):
427
        FR.status_code = 204
428
        self.client.delete_image(img_ref)
429
        self.assertEqual(self.client.http_client.url, self.url)
430
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
431

    
432
    @patch('%s.perform_request' % khttp, return_value=FR())
433
    def test_delete_network(self, PR):
434
        net_id = net_recv['network']['id']
435
        FR.status_code = 204
436
        self.client.delete_network(net_id)
437
        self.assertEqual(self.client.http_client.url, self.url)
438
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
439

    
440
    @patch('%s.perform_request' % khttp, return_value=FR())
441
    def test_create_image_metadata(self, PR):
442
        metadata = dict(m1='v1', m2='v2', m3='v3')
443
        FR.json = dict(meta=img_recv['image'])
444
        self.assertRaises(
445
            ClientError,
446
            self.client.create_image_metadata,
447
            img_ref, 'key', 'value')
448
        FR.status_code = 201
449
        for k, v in metadata.items():
450
            r = self.client.create_image_metadata(img_ref, k, v)
451
            self.assertEqual(self.client.http_client.url, self.url)
452
            self.assertEqual(
453
                self.client.http_client.path,
454
                '/images/%s/meta/%s' % (img_ref, k))
455
            (method, data, a_headers, a_params) = PR.call_args[0]
456
            self.assertEqual(dict(meta={k: v}), loads(data))
457
            self.assert_dicts_are_equal(r, img_recv['image'])
458

    
459
    @patch('%s.images_post' % cyclades_pkg, return_value=FR())
460
    def test_update_image_metadata(self, images_post):
461
        metadata = dict(m1='v1', m2='v2', m3='v3')
462
        FR.json = dict(metadata=metadata)
463
        r = self.client.update_image_metadata(img_ref, **metadata)
464
        self.assert_dicts_are_equal(r, metadata)
465
        (called_id, cmd) = images_post.call_args[0]
466
        self.assertEqual(called_id, img_ref)
467
        self.assertEqual(cmd, 'meta')
468
        data = images_post.call_args[1]['json_data']
469
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
470

    
471
    @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
472
    def test_delete_image_metadata(self, images_delete):
473
        key = 'metakey'
474
        self.client.delete_image_metadata(img_ref, key)
475
        self.assertEqual(
476
            (img_ref, '/meta/' + key),
477
            images_delete.call_args[0])
478

    
479
if __name__ == '__main__':
480
    from sys import argv
481
    from kamaki.clients.test import runTestCase
482
    runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])