Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 876aae74

History | View | Annotate | Download (25.3 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
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
203
    def test_update_server_name(self, SP):
204
        vm_id = vm_recv['server']['id']
205
        new_name = vm_name + '_new'
206
        self.client.update_server_name(vm_id, new_name)
207
        self.assertEqual(SP.mock_calls[-1], call(vm_id, json_data=dict(
208
            server=dict(name=new_name))))
209

    
210
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
211
    def test_reboot_server(self, SP):
212
        vm_id = vm_recv['server']['id']
213
        for hard in (None, True):
214
            self.client.reboot_server(vm_id, hard=hard)
215
            self.assertEqual(SP.mock_calls[-1], call(
216
                vm_id, 'action',
217
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
218

    
219
    """
220
    @patch('%s.perform_request' % khttp, return_value=FR())
221
    def test_create_server_metadata(self, PR):
222
        vm_id = vm_recv['server']['id']
223
        metadata = dict(m1='v1', m2='v2', m3='v3')
224
        FR.json = dict(meta=vm_recv['server'])
225
        self.assertRaises(
226
            ClientError,
227
            self.client.create_server_metadata,
228
            vm_id, 'key', 'value')
229
        FR.status_code = 201
230
        for k, v in metadata.items():
231
            r = self.client.create_server_metadata(vm_id, k, v)
232
            self.assertEqual(self.client.http_client.url, self.url)
233
            self.assertEqual(
234
                self.client.http_client.path,
235
                '/servers/%s/meta/%s' % (vm_id, k))
236
            (method, data, a_headers, a_params) = PR.call_args[0]
237
            self.assertEqual(dict(meta={k: v}), loads(data))
238
            self.assert_dicts_are_equal(r, vm_recv['server'])
239

240
    @patch('%s.perform_request' % khttp, return_value=FR())
241
    def test_get_server_metadata(self, PR):
242
        vm_id = vm_recv['server']['id']
243
        metadata = dict(m1='v1', m2='v2', m3='v3')
244
        FR.json = dict(metadata=dict(values=metadata))
245
        r = self.client.get_server_metadata(vm_id)
246
        self.assertEqual(self.client.http_client.url, self.url)
247
        self.assertEqual(
248
            self.client.http_client.path,
249
            '/servers/%s/meta' % vm_id)
250
        self.assert_dicts_are_equal(r, metadata)
251

252
        for k, v in metadata.items():
253
            FR.json = dict(meta={k: v})
254
            r = self.client.get_server_metadata(vm_id, k)
255
            self.assertEqual(self.client.http_client.url, self.url)
256
            self.assertEqual(
257
                self.client.http_client.path,
258
                '/servers/%s/meta/%s' % (vm_id, k))
259
            self.assert_dicts_are_equal(r, {k: v})
260

261
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
262
    def test_update_server_metadata(self, servers_post):
263
        vm_id = vm_recv['server']['id']
264
        metadata = dict(m1='v1', m2='v2', m3='v3')
265
        FR.json = dict(metadata=metadata)
266
        r = self.client.update_server_metadata(vm_id, **metadata)
267
        self.assert_dicts_are_equal(r, metadata)
268
        (called_id, cmd) = servers_post.call_args[0]
269
        self.assertEqual(called_id, vm_id)
270
        self.assertEqual(cmd, 'meta')
271
        data = servers_post.call_args[1]['json_data']
272
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
273

274
    @patch('%s.servers_delete' % cyclades_pkg, return_value=FR())
275
    def test_delete_server_metadata(self, servers_delete):
276
        vm_id = vm_recv['server']['id']
277
        key = 'metakey'
278
        self.client.delete_server_metadata(vm_id, key)
279
        self.assertEqual((vm_id, 'meta/' + key), servers_delete.call_args[0])
280

281
    @patch('%s.perform_request' % khttp, return_value=FR())
282
    def test_list_flavors(self, PR):
283
        FR.json = flavor_list
284
        r = self.client.list_flavors()
285
        self.assertEqual(self.client.http_client.url, self.url)
286
        self.assertEqual(self.client.http_client.path, '/flavors')
287
        (method, data, a_headers, a_params) = PR.call_args[0]
288
        self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
289
        r = self.client.list_flavors(detail=True)
290
        self.assertEqual(self.client.http_client.url, self.url)
291
        self.assertEqual(self.client.http_client.path, '/flavors/detail')
292

293
    @patch('%s.perform_request' % khttp, return_value=FR())
294
    def test_get_flavor_details(self, PR):
295
        FR.json = dict(flavor=flavor_list['flavors'])
296
        r = self.client.get_flavor_details(fid)
297
        self.assertEqual(self.client.http_client.url, self.url)
298
        self.assertEqual(self.client.http_client.path, '/flavors/%s' % fid)
299
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
300

301
    @patch('%s.perform_request' % khttp, return_value=FR())
302
    def test_list_images(self, PR):
303
        FR.json = img_list
304
        r = self.client.list_images()
305
        self.assertEqual(self.client.http_client.url, self.url)
306
        self.assertEqual(self.client.http_client.path, '/images')
307
        expected = img_list['images']['values']
308
        for i in range(len(r)):
309
            self.assert_dicts_are_equal(expected[i], r[i])
310
        self.client.list_images(detail=True)
311
        self.assertEqual(self.client.http_client.url, self.url)
312
        self.assertEqual(self.client.http_client.path, '/images/detail')
313

314
    @patch('%s.perform_request' % khttp, return_value=FR())
315
    def test_get_image_details(self, PR):
316
        FR.json = img_recv
317
        r = self.client.get_image_details(img_ref)
318
        self.assertEqual(self.client.http_client.url, self.url)
319
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
320
        self.assert_dicts_are_equal(r, img_recv['image'])
321

322
    @patch('%s.images_get' % cyclades_pkg, return_value=FR())
323
    def test_get_image_metadata(self, IG):
324
        FR.json = dict(metadata=dict(values=img_recv['image']))
325
        r = self.client.get_image_metadata(img_ref)
326
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta'))
327
        self.assert_dicts_are_equal(img_recv['image'], r)
328
        FR.json = dict(meta=img_recv['image'])
329
        key = 'somekey'
330
        self.client.get_image_metadata(img_ref, key)
331
        self.assertEqual(IG.call_args[0], ('%s' % img_ref, '/meta/%s' % key))
332

333
    @patch('%s.perform_request' % khttp, return_value=FR())
334
    def test_shutdown_server(self, PR):
335
        vm_id = vm_recv['server']['id']
336
        FR.status_code = 202
337
        self.client.shutdown_server(vm_id)
338
        self.assertEqual(self.client.http_client.url, self.url)
339
        self.assertEqual(
340
            self.client.http_client.path,
341
            '/servers/%s/action' % vm_id)
342
        self.assertEqual(
343
            PR.call_args[0],
344
            ('post',  '{"shutdown": {}}', {}, {}))
345

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

357
    @patch('%s.perform_request' % khttp, return_value=FR())
358
    def test_get_server_console(self, PR):
359
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
360
        FR.json = cnsl
361
        vm_id = vm_recv['server']['id']
362
        r = self.client.get_server_console(vm_id)
363
        self.assertEqual(self.client.http_client.url, self.url)
364
        self.assertEqual(
365
            self.client.http_client.path,
366
            '/servers/%s/action' % vm_id)
367
        self.assert_dicts_are_equal(cnsl['console'], r)
368
        self.assertEqual(
369
            PR.call_args[0],
370
            ('post',  '{"console": {"type": "vnc"}}', {}, {}))
371

372
    def test_get_firewall_profile(self):
373
        vm_id = vm_recv['server']['id']
374
        v = 'Some profile'
375
        ret = {'attachments': {'values': [{'firewallProfile': v, 1:1}]}}
376
        with patch.object(
377
                CycladesClient,
378
                'get_server_details',
379
                return_value=ret) as GSD:
380
            r = self.client.get_firewall_profile(vm_id)
381
            self.assertEqual(r, v)
382
            self.assertEqual(GSD.call_args[0], (vm_id,))
383
            ret['attachments']['values'][0].pop('firewallProfile')
384
            self.assertRaises(
385
                ClientError,
386
                self.client.get_firewall_profile,
387
                vm_id)
388

389
    @patch('%s.perform_request' % khttp, return_value=FR())
390
    def test_set_firewall_profile(self, PR):
391
        vm_id = vm_recv['server']['id']
392
        v = 'Some profile'
393
        FR.status_code = 202
394
        self.client.set_firewall_profile(vm_id, v)
395
        self.assertEqual(self.client.http_client.url, self.url)
396
        self.assertEqual(
397
            self.client.http_client.path,
398
            '/servers/%s/action' % vm_id)
399
        self.assertEqual(PR.call_args[0], (
400
            'post',
401
            '{"firewallProfile": {"profile": "%s"}}' % v,
402
            {},
403
            {}))
404

405
    @patch('%s.perform_request' % khttp, return_value=FR())
406
    def test_get_server_stats(self, PR):
407
        vm_id = vm_recv['server']['id']
408
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
409
        FR.json = dict(stats=stats)
410
        r = self.client.get_server_stats(vm_id)
411
        self.assertEqual(self.client.http_client.url, self.url)
412
        self.assertEqual(
413
            self.client.http_client.path,
414
            '/servers/%s/stats' % vm_id)
415
        self.assert_dicts_are_equal(stats, r)
416

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

443
    @patch('%s.perform_request' % khttp, return_value=FR())
444
    def test_connect_server(self, PR):
445
        vm_id = vm_recv['server']['id']
446
        net_id = net_recv['network']['id']
447
        FR.status_code = 202
448
        self.client.connect_server(vm_id, net_id)
449
        self.assertEqual(self.client.http_client.url, self.url)
450
        self.assertEqual(
451
            self.client.http_client.path,
452
            '/networks/%s/action' % net_id)
453
        self.assertEqual(
454
            PR.call_args[0],
455
            ('post', '{"add": {"serverRef": %s}}' % vm_id, {}, {}))
456

457
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
458
    def test_disconnect_server(self, NP):
459
        vm_id = vm_recv['server']['id']
460
        net_id = net_recv['network']['id']
461
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
462
        vm_nics = [
463
            dict(id=nic_id, network_id=net_id),
464
            dict(id='another-nic-id', network_id='another-net-id'),
465
            dict(id=nic_id * 2, network_id=net_id * 2)]
466
        with patch.object(
467
                CycladesClient,
468
                'list_server_nics',
469
                return_value=vm_nics) as LSN:
470
            r = self.client.disconnect_server(vm_id, nic_id)
471
            self.assertEqual(r, 1)
472
            self.assertEqual(LSN.call_args[0], (vm_id,))
473
            self.assertEqual(NP.call_args[0], (net_id, 'action'))
474
            self.assertEqual(
475
                NP.call_args[1],
476
                dict(json_data=dict(remove=dict(attachment=nic_id))))
477

478
    @patch('%s.perform_request' % khttp, return_value=FR())
479
    def test_list_server_nics(self, PR):
480
        vm_id = vm_recv['server']['id']
481
        nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
482
        FR.json = nics
483
        r = self.client.list_server_nics(vm_id)
484
        self.assertEqual(self.client.http_client.url, self.url)
485
        self.assertEqual(
486
            self.client.http_client.path,
487
            '/servers/%s/ips' % vm_id)
488
        expected = nics['addresses']['values']
489
        for i in range(len(r)):
490
            self.assert_dicts_are_equal(r[i], expected[i])
491

492
    @patch('%s.perform_request' % khttp, return_value=FR())
493
    def test_list_networks(self, PR):
494
        FR.json = net_list
495
        r = self.client.list_networks()
496
        self.assertEqual(self.client.http_client.url, self.url)
497
        self.assertEqual(self.client.http_client.path, '/networks')
498
        expected = net_list['networks']['values']
499
        for i in range(len(r)):
500
            self.assert_dicts_are_equal(expected[i], r[i])
501
        self.client.list_networks(detail=True)
502
        self.assertEqual(self.client.http_client.url, self.url)
503
        self.assertEqual(self.client.http_client.path, '/networks/detail')
504

505
    @patch('%s.perform_request' % khttp, return_value=FR())
506
    def test_list_network_nics(self, PR):
507
        net_id = net_recv['network']['id']
508
        FR.json = net_recv
509
        r = self.client.list_network_nics(net_id)
510
        self.assertEqual(self.client.http_client.url, self.url)
511
        self.assertEqual(
512
            self.client.http_client.path,
513
            '/networks/%s' % net_id)
514
        expected = net_recv['network']['attachments']['values']
515
        for i in range(len(r)):
516
            self.assert_dicts_are_equal(r[i], expected[i])
517

518
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
519
    def test_disconnect_network_nics(self, NP):
520
        net_id = net_recv['network']['id']
521
        nics = ['nic1', 'nic2', 'nic3']
522
        with patch.object(
523
                CycladesClient,
524
                'list_network_nics',
525
                return_value=nics) as lnn:
526
            self.client.disconnect_network_nics(net_id)
527
            lnn.assert_called_once_with(net_id)
528
            for i in range(len(nics)):
529
                expected = call(net_id, 'action', json_data=dict(
530
                    remove=dict(attachment=nics[i])))
531
                self.assertEqual(expected, NP.mock_calls[i])
532

533
    @patch('%s.perform_request' % khttp, return_value=FR())
534
    def test_get_network_details(self, PR):
535
        FR.json = net_recv
536
        net_id = net_recv['network']['id']
537
        r = self.client.get_network_details(net_id)
538
        self.assertEqual(self.client.http_client.url, self.url)
539
        self.assertEqual(
540
            self.client.http_client.path,
541
            '/networks/%s' % net_id)
542
        self.assert_dicts_are_equal(r, net_recv['network'])
543

544
    @patch('%s.perform_request' % khttp, return_value=FR())
545
    def test_update_network_name(self, PR):
546
        net_id = net_recv['network']['id']
547
        new_name = '%s_new' % net_id
548
        FR.status_code = 204
549
        self.client.update_network_name(net_id, new_name)
550
        self.assertEqual(self.client.http_client.url, self.url)
551
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
552
        (method, data, a_headers, a_params) = PR.call_args[0]
553
        self.assert_dicts_are_equal(
554
            dict(network=dict(name=new_name)),
555
            loads(data))
556

557
    @patch('%s.perform_request' % khttp, return_value=FR())
558
    def test_delete_server(self, PR):
559
        vm_id = vm_recv['server']['id']
560
        FR.status_code = 204
561
        self.client.delete_server(vm_id)
562
        self.assertEqual(self.client.http_client.url, self.url)
563
        self.assertEqual(self.client.http_client.path, '/servers/%s' % vm_id)
564

565
    @patch('%s.perform_request' % khttp, return_value=FR())
566
    def test_delete_image(self, PR):
567
        FR.status_code = 204
568
        self.client.delete_image(img_ref)
569
        self.assertEqual(self.client.http_client.url, self.url)
570
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
571

572
    @patch('%s.perform_request' % khttp, return_value=FR())
573
    def test_delete_network(self, PR):
574
        net_id = net_recv['network']['id']
575
        FR.status_code = 204
576
        self.client.delete_network(net_id)
577
        self.assertEqual(self.client.http_client.url, self.url)
578
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
579

580
    @patch('%s.perform_request' % khttp, return_value=FR())
581
    def test_create_image_metadata(self, PR):
582
        metadata = dict(m1='v1', m2='v2', m3='v3')
583
        FR.json = dict(meta=img_recv['image'])
584
        self.assertRaises(
585
            ClientError,
586
            self.client.create_image_metadata,
587
            img_ref, 'key', 'value')
588
        FR.status_code = 201
589
        for k, v in metadata.items():
590
            r = self.client.create_image_metadata(img_ref, k, v)
591
            self.assertEqual(self.client.http_client.url, self.url)
592
            self.assertEqual(
593
                self.client.http_client.path,
594
                '/images/%s/meta/%s' % (img_ref, k))
595
            (method, data, a_headers, a_params) = PR.call_args[0]
596
            self.assertEqual(dict(meta={k: v}), loads(data))
597
            self.assert_dicts_are_equal(r, img_recv['image'])
598

599
    @patch('%s.images_post' % cyclades_pkg, return_value=FR())
600
    def test_update_image_metadata(self, images_post):
601
        metadata = dict(m1='v1', m2='v2', m3='v3')
602
        FR.json = dict(metadata=metadata)
603
        r = self.client.update_image_metadata(img_ref, **metadata)
604
        self.assert_dicts_are_equal(r, metadata)
605
        (called_id, cmd) = images_post.call_args[0]
606
        self.assertEqual(called_id, img_ref)
607
        self.assertEqual(cmd, 'meta')
608
        data = images_post.call_args[1]['json_data']
609
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
610

611
    @patch('%s.images_delete' % cyclades_pkg, return_value=FR())
612
    def test_delete_image_metadata(self, images_delete):
613
        key = 'metakey'
614
        self.client.delete_image_metadata(img_ref, key)
615
        self.assertEqual(
616
            (img_ref, '/meta/' + key),
617
            images_delete.call_args[0])
618
    """
619

    
620
if __name__ == '__main__':
621
    from sys import argv
622
    from kamaki.clients.test import runTestCase
623
    runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])