Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 26ccd6fb

History | View | Annotate | Download (25.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, Mock, call
35
from unittest import TestCase
36
from json import loads
37

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

    
42

    
43
khttp_pkg = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
44
compute_pkg = 'kamaki.clients.cyclades.CycladesClient'
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
net_send = dict(network=dict(dhcp=False, name='someNet'))
101
net_recv = dict(network=dict(
102
    status="PENDING",
103
    updated="2013-03-05T15:04:51.758780+00:00",
104
    name="someNet",
105
    created="2013-03-05T15:04:51.758728+00:00",
106
    cidr6=None,
107
    id="2130",
108
    gateway6=None,
109
    public=False,
110
    dhcp=False,
111
    cidr="192.168.1.0/24",
112
    type="MAC_FILTERED",
113
    gateway=None,
114
    attachments=dict(values=[dict(name='att1'), dict(name='att2')])))
115
net_list = dict(networks=dict(values=[
116
    dict(id=1, name='n1'),
117
    dict(id=2, name='n2'),
118
    dict(id=3, name='n3')]))
119

    
120

    
121
class FR(object):
122
    """FR stands for Fake Response"""
123
    json = vm_recv
124
    headers = {}
125
    content = json
126
    status = None
127
    status_code = 200
128

    
129
    def release(self):
130
        pass
131

    
132

    
133
class Cyclades(TestCase):
134

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

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

    
151
    def tearDown(self):
152
        FR.status_code = 200
153
        FR.json = vm_recv
154

    
155
    @patch(
156
        '%s.get_image_details' % compute_pkg,
157
        return_value=img_recv['image'])
158
    def test_create_server(self, GID):
159
        with patch.object(
160
                CycladesClient, 'servers_post',
161
                side_effect=ClientError(
162
                    'REQUEST ENTITY TOO LARGE',
163
                    status=403)):
164
            self.assertRaises(
165
                ClientError,
166
                self.client.create_server,
167
                vm_name, fid, img_ref)
168
        self.assertEqual(GID.mock_calls[-1], call(img_ref))
169

    
170
        with patch.object(
171
                CycladesClient, 'servers_post',
172
                return_value=FR()) as post:
173
            r = self.client.create_server(vm_name, fid, img_ref)
174
            self.assertEqual(r, FR.json['server'])
175
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
176
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
177
            prsn = 'Personality string (does not work with real servers)'
178
            self.client.create_server(vm_name, fid, img_ref, prsn)
179
            expected = dict(server=dict(vm_send['server']))
180
            expected['server']['personality'] = prsn
181
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
182

    
183
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
184
    def test_list_servers(self, SG):
185
        FR.json = vm_list
186
        for detail in (False, True):
187
            r = self.client.list_servers(detail)
188
            for i, vm in enumerate(vm_list['servers']['values']):
189
                self.assert_dicts_are_equal(r[i], vm)
190
            self.assertEqual(i + 1, len(r))
191
            self.assertEqual(SG.mock_calls[-1], call(
192
                changes_since=None,
193
                command='detail' if detail else ''))
194

    
195
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
196
    def test_get_server_details(self, SG):
197
        vm_id = vm_recv['server']['id']
198
        r = self.client.get_server_details(vm_id)
199
        self.assert_dicts_are_equal(r, vm_recv['server'])
200
        self.assertEqual(SG.mock_calls[-1], call(vm_id))
201

    
202
    """
203
    @patch('%s.perform_request' % khttp, return_value=FR())
204
    def test_update_server_name(self, PR):
205
        vm_id = vm_recv['server']['id']
206
        new_name = vm_name + '_new'
207
        FR.status_code = 204
208
        self.client.update_server_name(vm_id, new_name)
209
        self.assertEqual(self.client.http_client.url, self.url)
210
        self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
211
        (method, data, a_headers, a_params) = PR.call_args[0]
212
        self.assert_dicts_are_equal(
213
            dict(server=dict(name=new_name)),
214
            loads(data))
215

216
    @patch('%s.perform_request' % khttp, return_value=FR())
217
    def test_reboot_server(self, PR):
218
        vm_id = vm_recv['server']['id']
219
        FR.status_code = 202
220
        self.client.reboot_server(vm_id)
221
        self.assertEqual(self.client.http_client.url, self.url)
222
        self.assertEqual(
223
            self.client.http_client.path,
224
            '/servers/%s/action' % vm_id)
225
        (method, data, a_headers, a_params) = PR.call_args[0]
226
        self.assert_dicts_are_equal(
227
            dict(reboot=dict(type='SOFT')),
228
            loads(data))
229

230
    @patch('%s.perform_request' % khttp, return_value=FR())
231
    def test_create_server_metadata(self, PR):
232
        vm_id = vm_recv['server']['id']
233
        metadata = dict(m1='v1', m2='v2', m3='v3')
234
        FR.json = dict(meta=vm_recv['server'])
235
        self.assertRaises(
236
            ClientError,
237
            self.client.create_server_metadata,
238
            vm_id, 'key', 'value')
239
        FR.status_code = 201
240
        for k, v in metadata.items():
241
            r = self.client.create_server_metadata(vm_id, k, v)
242
            self.assertEqual(self.client.http_client.url, self.url)
243
            self.assertEqual(
244
                self.client.http_client.path,
245
                '/servers/%s/meta/%s' % (vm_id, k))
246
            (method, data, a_headers, a_params) = PR.call_args[0]
247
            self.assertEqual(dict(meta={k: v}), loads(data))
248
            self.assert_dicts_are_equal(r, vm_recv['server'])
249

250
    @patch('%s.perform_request' % khttp, return_value=FR())
251
    def test_get_server_metadata(self, PR):
252
        vm_id = vm_recv['server']['id']
253
        metadata = dict(m1='v1', m2='v2', m3='v3')
254
        FR.json = dict(metadata=dict(values=metadata))
255
        r = self.client.get_server_metadata(vm_id)
256
        self.assertEqual(self.client.http_client.url, self.url)
257
        self.assertEqual(
258
            self.client.http_client.path,
259
            '/servers/%s/meta' % vm_id)
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.assertEqual(self.client.http_client.url, self.url)
266
            self.assertEqual(
267
                self.client.http_client.path,
268
                '/servers/%s/meta/%s' % (vm_id, k))
269
            self.assert_dicts_are_equal(r, {k: v})
270

271
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
272
    def test_update_server_metadata(self, servers_post):
273
        vm_id = vm_recv['server']['id']
274
        metadata = dict(m1='v1', m2='v2', m3='v3')
275
        FR.json = dict(metadata=metadata)
276
        r = self.client.update_server_metadata(vm_id, **metadata)
277
        self.assert_dicts_are_equal(r, metadata)
278
        (called_id, cmd) = servers_post.call_args[0]
279
        self.assertEqual(called_id, vm_id)
280
        self.assertEqual(cmd, 'meta')
281
        data = servers_post.call_args[1]['json_data']
282
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
283

284
    @patch('%s.servers_delete' % cyclades_pkg, return_value=FR())
285
    def test_delete_server_metadata(self, servers_delete):
286
        vm_id = vm_recv['server']['id']
287
        key = 'metakey'
288
        self.client.delete_server_metadata(vm_id, key)
289
        self.assertEqual((vm_id, 'meta/' + key), servers_delete.call_args[0])
290

291
    @patch('%s.perform_request' % khttp, return_value=FR())
292
    def test_list_flavors(self, PR):
293
        FR.json = flavor_list
294
        r = self.client.list_flavors()
295
        self.assertEqual(self.client.http_client.url, self.url)
296
        self.assertEqual(self.client.http_client.path, '/flavors')
297
        (method, data, a_headers, a_params) = PR.call_args[0]
298
        self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
299
        r = self.client.list_flavors(detail=True)
300
        self.assertEqual(self.client.http_client.url, self.url)
301
        self.assertEqual(self.client.http_client.path, '/flavors/detail')
302

303
    @patch('%s.perform_request' % khttp, return_value=FR())
304
    def test_get_flavor_details(self, PR):
305
        FR.json = dict(flavor=flavor_list['flavors'])
306
        r = self.client.get_flavor_details(fid)
307
        self.assertEqual(self.client.http_client.url, self.url)
308
        self.assertEqual(self.client.http_client.path, '/flavors/%s' % fid)
309
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
310

311
    @patch('%s.perform_request' % khttp, return_value=FR())
312
    def test_list_images(self, PR):
313
        FR.json = img_list
314
        r = self.client.list_images()
315
        self.assertEqual(self.client.http_client.url, self.url)
316
        self.assertEqual(self.client.http_client.path, '/images')
317
        expected = img_list['images']['values']
318
        for i in range(len(r)):
319
            self.assert_dicts_are_equal(expected[i], r[i])
320
        self.client.list_images(detail=True)
321
        self.assertEqual(self.client.http_client.url, self.url)
322
        self.assertEqual(self.client.http_client.path, '/images/detail')
323

324
    @patch('%s.perform_request' % khttp, return_value=FR())
325
    def test_get_image_details(self, PR):
326
        FR.json = img_recv
327
        r = self.client.get_image_details(img_ref)
328
        self.assertEqual(self.client.http_client.url, self.url)
329
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
330
        self.assert_dicts_are_equal(r, img_recv['image'])
331

332
    @patch('%s.images_get' % cyclades_pkg, return_value=FR())
333
    def test_get_image_metadata(self, IG):
334
        FR.json = dict(metadata=dict(values=img_recv['image']))
335
        r = self.client.get_image_metadata(img_ref)
336
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta'))
337
        self.assert_dicts_are_equal(img_recv['image'], r)
338
        FR.json = dict(meta=img_recv['image'])
339
        key = 'somekey'
340
        self.client.get_image_metadata(img_ref, key)
341
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta/%s' % key))
342

343
    @patch('%s.perform_request' % khttp, return_value=FR())
344
    def test_shutdown_server(self, PR):
345
        vm_id = vm_recv['server']['id']
346
        FR.status_code = 202
347
        self.client.shutdown_server(vm_id)
348
        self.assertEqual(self.client.http_client.url, self.url)
349
        self.assertEqual(
350
            self.client.http_client.path,
351
            '/servers/%s/action' % vm_id)
352
        self.assertEqual(
353
            PR.call_args[0],
354
            ('post',  '{"shutdown": {}}', {}, {}))
355

356
    @patch('%s.perform_request' % khttp, return_value=FR())
357
    def test_start_server(self, PR):
358
        vm_id = vm_recv['server']['id']
359
        FR.status_code = 202
360
        self.client.start_server(vm_id)
361
        self.assertEqual(self.client.http_client.url, self.url)
362
        self.assertEqual(
363
            self.client.http_client.path,
364
            '/servers/%s/action' % vm_id)
365
        self.assertEqual(PR.call_args[0], ('post',  '{"start": {}}', {}, {}))
366

367
    @patch('%s.perform_request' % khttp, return_value=FR())
368
    def test_get_server_console(self, PR):
369
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
370
        FR.json = cnsl
371
        vm_id = vm_recv['server']['id']
372
        r = self.client.get_server_console(vm_id)
373
        self.assertEqual(self.client.http_client.url, self.url)
374
        self.assertEqual(
375
            self.client.http_client.path,
376
            '/servers/%s/action' % vm_id)
377
        self.assert_dicts_are_equal(cnsl['console'], r)
378
        self.assertEqual(
379
            PR.call_args[0],
380
            ('post',  '{"console": {"type": "vnc"}}', {}, {}))
381

382
    def test_get_firewall_profile(self):
383
        vm_id = vm_recv['server']['id']
384
        v = 'Some profile'
385
        ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
386
        with patch.object(
387
                CycladesClient,
388
                'get_server_details',
389
                return_value=ret) as GSD:
390
            r = self.client.get_firewall_profile(vm_id)
391
            self.assertEqual(r, v)
392
            self.assertEqual(GSD.call_args[0], (vm_id,))
393
            ret['attachments']['values'][0].pop('firewallProfile')
394
            self.assertRaises(
395
                ClientError,
396
                self.client.get_firewall_profile,
397
                vm_id)
398

399
    @patch('%s.perform_request' % khttp, return_value=FR())
400
    def test_set_firewall_profile(self, PR):
401
        vm_id = vm_recv['server']['id']
402
        v = 'Some profile'
403
        FR.status_code = 202
404
        self.client.set_firewall_profile(vm_id, v)
405
        self.assertEqual(self.client.http_client.url, self.url)
406
        self.assertEqual(
407
            self.client.http_client.path,
408
            '/servers/%s/action' % vm_id)
409
        self.assertEqual(PR.call_args[0], (
410
            'post',
411
            '{"firewallProfile": {"profile": "%s"}}' % v,
412
            {},
413
            {}))
414

415
    @patch('%s.perform_request' % khttp, return_value=FR())
416
    def test_get_server_stats(self, PR):
417
        vm_id = vm_recv['server']['id']
418
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
419
        FR.json = dict(stats=stats)
420
        r = self.client.get_server_stats(vm_id)
421
        self.assertEqual(self.client.http_client.url, self.url)
422
        self.assertEqual(
423
            self.client.http_client.path,
424
            '/servers/%s/stats' % vm_id)
425
        self.assert_dicts_are_equal(stats, r)
426

427
    @patch('%s.perform_request' % khttp, return_value=FR())
428
    def test_create_network(self, PR):
429
        net_name = net_send['network']['name']
430
        FR.json = net_recv
431
        FR.status_code = 202
432
        full_args = dict(
433
                cidr='192.168.0.0/24',
434
                gateway='192.168.0.1',
435
                type='MAC_FILTERED',
436
                dhcp=True)
437
        test_args = dict(full_args)
438
        test_args.update(dict(empty=None, full=None))
439
        for arg, val in test_args.items():
440
            kwargs = {} if arg == 'empty' else full_args if (
441
                arg == 'full') else {arg: val}
442
            r = self.client.create_network(net_name, **kwargs)
443
            self.assertEqual(self.client.http_client.url, self.url)
444
            self.assertEqual(
445
                self.client.http_client.path,
446
                '/networks')
447
            self.assert_dicts_are_equal(r, net_recv['network'])
448
            data = PR.call_args[0][1]
449
            expected = dict(network=dict(net_send['network']))
450
            expected['network'].update(kwargs)
451
            self.assert_dicts_are_equal(loads(data), expected)
452

453
    @patch('%s.perform_request' % khttp, return_value=FR())
454
    def test_connect_server(self, PR):
455
        vm_id = vm_recv['server']['id']
456
        net_id = net_recv['network']['id']
457
        FR.status_code = 202
458
        self.client.connect_server(vm_id, net_id)
459
        self.assertEqual(self.client.http_client.url, self.url)
460
        self.assertEqual(
461
            self.client.http_client.path,
462
            '/networks/%s/action' % net_id)
463
        self.assertEqual(
464
            PR.call_args[0],
465
            ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
466

467
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
468
    def test_disconnect_server(self, NP):
469
        vm_id = vm_recv['server']['id']
470
        net_id = net_recv['network']['id']
471
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
472
        vm_nics = [
473
            dict(id=nic_id, network_id=net_id),
474
            dict(id='another-nic-id', network_id='another-net-id'),
475
            dict(id=nic_id * 2, network_id=net_id * 2)]
476
        with patch.object(
477
                CycladesClient,
478
                'list_server_nics',
479
                return_value=vm_nics) as LSN:
480
            r = self.client.disconnect_server(vm_id, nic_id)
481
            self.assertEqual(r, 1)
482
            self.assertEqual(LSN.call_args[0], (vm_id,))
483
            self.assertEqual(NP.call_args[0], (net_id, 'action'))
484
            self.assertEqual(
485
                NP.call_args[1],
486
                dict(json_data=dict(remove=dict(attachment=nic_id))))
487

488
    @patch('%s.perform_request' % khttp, return_value=FR())
489
    def test_list_server_nics(self, PR):
490
        vm_id = vm_recv['server']['id']
491
        nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
492
        FR.json = nics
493
        r = self.client.list_server_nics(vm_id)
494
        self.assertEqual(self.client.http_client.url, self.url)
495
        self.assertEqual(
496
            self.client.http_client.path,
497
            '/servers/%s/ips' % vm_id)
498
        expected = nics['addresses']['values']
499
        for i in range(len(r)):
500
            self.assert_dicts_are_equal(r[i], expected[i])
501

502
    @patch('%s.perform_request' % khttp, return_value=FR())
503
    def test_list_networks(self, PR):
504
        FR.json = net_list
505
        r = self.client.list_networks()
506
        self.assertEqual(self.client.http_client.url, self.url)
507
        self.assertEqual(self.client.http_client.path, '/networks')
508
        expected = net_list['networks']['values']
509
        for i in range(len(r)):
510
            self.assert_dicts_are_equal(expected[i], r[i])
511
        self.client.list_networks(detail=True)
512
        self.assertEqual(self.client.http_client.url, self.url)
513
        self.assertEqual(self.client.http_client.path, '/networks/detail')
514

515
    @patch('%s.perform_request' % khttp, return_value=FR())
516
    def test_list_network_nics(self, PR):
517
        net_id = net_recv['network']['id']
518
        FR.json = net_recv
519
        r = self.client.list_network_nics(net_id)
520
        self.assertEqual(self.client.http_client.url, self.url)
521
        self.assertEqual(
522
            self.client.http_client.path,
523
            '/networks/%s' % net_id)
524
        expected = net_recv['network']['attachments']['values']
525
        for i in range(len(r)):
526
            self.assert_dicts_are_equal(r[i], expected[i])
527

528
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
529
    def test_disconnect_network_nics(self, NP):
530
        net_id = net_recv['network']['id']
531
        nics = ['nic1', 'nic2', 'nic3']
532
        with patch.object(
533
                CycladesClient,
534
                'list_network_nics',
535
                return_value=nics) as lnn:
536
            self.client.disconnect_network_nics(net_id)
537
            lnn.assert_called_once_with(net_id)
538
            for i in range(len(nics)):
539
                expected = call(net_id, 'action', json_data=dict(
540
                    remove=dict(attachment=nics[i])))
541
                self.assertEqual(expected, NP.mock_calls[i])
542

543
    @patch('%s.perform_request' % khttp, return_value=FR())
544
    def test_get_network_details(self, PR):
545
        FR.json = net_recv
546
        net_id = net_recv['network']['id']
547
        r = self.client.get_network_details(net_id)
548
        self.assertEqual(self.client.http_client.url, self.url)
549
        self.assertEqual(
550
            self.client.http_client.path,
551
            '/networks/%s' % net_id)
552
        self.assert_dicts_are_equal(r, net_recv['network'])
553

554
    @patch('%s.perform_request' % khttp, return_value=FR())
555
    def test_update_network_name(self, PR):
556
        net_id = net_recv['network']['id']
557
        new_name = '%s_new' % net_id
558
        FR.status_code = 204
559
        self.client.update_network_name(net_id, new_name)
560
        self.assertEqual(self.client.http_client.url, self.url)
561
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
562
        (method, data, a_headers, a_params) = PR.call_args[0]
563
        self.assert_dicts_are_equal(
564
            dict(network=dict(name=new_name)),
565
            loads(data))
566

567
    @patch('%s.perform_request' % khttp, return_value=FR())
568
    def test_delete_server(self, PR):
569
        vm_id = vm_recv['server']['id']
570
        FR.status_code = 204
571
        self.client.delete_server(vm_id)
572
        self.assertEqual(self.client.http_client.url, self.url)
573
        self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
574

575
    @patch('%s.perform_request' % khttp, return_value=FR())
576
    def test_delete_image(self, PR):
577
        FR.status_code = 204
578
        self.client.delete_image(img_ref)
579
        self.assertEqual(self.client.http_client.url, self.url)
580
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
581

582
    @patch('%s.perform_request' % khttp, return_value=FR())
583
    def test_delete_network(self, PR):
584
        net_id = net_recv['network']['id']
585
        FR.status_code = 204
586
        self.client.delete_network(net_id)
587
        self.assertEqual(self.client.http_client.url, self.url)
588
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
589

590
    @patch('%s.perform_request' % khttp, return_value=FR())
591
    def test_create_image_metadata(self, PR):
592
        metadata = dict(m1='v1', m2='v2', m3='v3')
593
        FR.json = dict(meta=img_recv['image'])
594
        self.assertRaises(
595
            ClientError,
596
            self.client.create_image_metadata,
597
            img_ref, 'key', 'value')
598
        FR.status_code = 201
599
        for k, v in metadata.items():
600
            r = self.client.create_image_metadata(img_ref, k, v)
601
            self.assertEqual(self.client.http_client.url, self.url)
602
            self.assertEqual(
603
                self.client.http_client.path,
604
                '/images/%s/meta/%s' % (img_ref, k))
605
            (method, data, a_headers, a_params) = PR.call_args[0]
606
            self.assertEqual(dict(meta={k: v}), loads(data))
607
            self.assert_dicts_are_equal(r, img_recv['image'])
608

609
    @patch('%s.images_post' % cyclades_pkg, return_value=FR())
610
    def test_update_image_metadata(self, images_post):
611
        metadata = dict(m1='v1', m2='v2', m3='v3')
612
        FR.json = dict(metadata=metadata)
613
        r = self.client.update_image_metadata(img_ref, **metadata)
614
        self.assert_dicts_are_equal(r, metadata)
615
        (called_id, cmd) = images_post.call_args[0]
616
        self.assertEqual(called_id, img_ref)
617
        self.assertEqual(cmd, 'meta')
618
        data = images_post.call_args[1]['json_data']
619
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
620

621
    @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
622
    def test_delete_image_metadata(self, images_delete):
623
        key = 'metakey'
624
        self.client.delete_image_metadata(img_ref, key)
625
        self.assertEqual(
626
            (img_ref, '/meta/' + key),
627
            images_delete.call_args[0])
628
    """
629

    
630
if __name__ == '__main__':
631
    from sys import argv
632
    from kamaki.clients.test import runTestCase
633
    runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])