Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / cyclades / test.py @ 0e27687b

History | View | Annotate | Download (22.9 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from mock import patch, call
35
from unittest import TestCase
36
from itertools import product
37
from json import dumps
38

    
39
from kamaki.clients import ClientError, cyclades
40

    
41
img_ref = "1m4g3-r3f3r3nc3"
42
vm_name = "my new VM"
43
fid = 42
44
vm_recv = dict(server=dict(
45
    status="BUILD",
46
    updated="2013-03-01T10:04:00.637152+00:00",
47
    hostId="",
48
    name=vm_name,
49
    imageRef=img_ref,
50
    created="2013-03-01T10:04:00.087324+00:00",
51
    flavorRef=fid,
52
    adminPass="n0n3sh@11p@55",
53
    suspended=False,
54
    progress=0,
55
    id=31173,
56
    metadata=dict(os="debian", users="root")))
57
vm_list = dict(servers=[
58
    dict(name='n1', id=1),
59
    dict(name='n2', id=2)])
60
net_send = dict(network=dict(dhcp=False, name='someNet'))
61
net_recv = dict(network=dict(
62
    status="PENDING",
63
    updated="2013-03-05T15:04:51.758780+00:00",
64
    name="someNet",
65
    created="2013-03-05T15:04:51.758728+00:00",
66
    cidr6=None,
67
    id="2130",
68
    gateway6=None,
69
    public=False,
70
    dhcp=False,
71
    cidr="192.168.1.0/24",
72
    type="MAC_FILTERED",
73
    gateway=None,
74
    attachments=[dict(name='att1'), dict(name='att2')]))
75
net_list = dict(networks=[
76
    dict(id=1, name='n1'),
77
    dict(id=2, name='n2'),
78
    dict(id=3, name='n3')])
79
firewalls = dict(attachments=[
80
    dict(firewallProfile='50m3_pr0f1L3', otherStuff='57uff')])
81

    
82

    
83
class FR(object):
84
    """FR stands for Fake Response"""
85
    json = vm_recv
86
    headers = {}
87
    content = json
88
    status = None
89
    status_code = 200
90

    
91
rest_pkg = 'kamaki.clients.cyclades.CycladesRestClient'
92
cyclades_pkg = 'kamaki.clients.cyclades.CycladesClient'
93

    
94

    
95
class CycladesRestClient(TestCase):
96

    
97
    """Set up a Cyclades thorough test"""
98
    def setUp(self):
99
        self.url = 'http://cyclades.example.com'
100
        self.token = 'cyc14d3s70k3n'
101
        self.client = cyclades.CycladesRestClient(self.url, self.token)
102

    
103
    def tearDown(self):
104
        FR.json = vm_recv
105

    
106
    @patch('%s.get' % rest_pkg, return_value=FR())
107
    def test_networks_get(self, get):
108
        for args in product(
109
                ('', 'net_id'),
110
                ('', 'cmd'),
111
                (200, 204),
112
                ({}, {'k': 'v'})):
113
            (srv_id, command, success, kwargs) = args
114
            self.client.networks_get(*args[:3], **kwargs)
115
            srv_str = '/%s' % srv_id if srv_id else ''
116
            cmd_str = '/%s' % command if command else ''
117
            self.assertEqual(get.mock_calls[-1], call(
118
                '/networks%s%s' % (srv_str, cmd_str),
119
                success=success,
120
                **kwargs))
121

    
122
    @patch('%s.delete' % rest_pkg, return_value=FR())
123
    def test_networks_delete(self, delete):
124
        for args in product(
125
                ('', 'net_id'),
126
                ('', 'cmd'),
127
                (202, 204),
128
                ({}, {'k': 'v'})):
129
            (srv_id, command, success, kwargs) = args
130
            self.client.networks_delete(*args[:3], **kwargs)
131
            srv_str = '/%s' % srv_id if srv_id else ''
132
            cmd_str = '/%s' % command if command else ''
133
            self.assertEqual(delete.mock_calls[-1], call(
134
                '/networks%s%s' % (srv_str, cmd_str),
135
                success=success,
136
                **kwargs))
137

    
138
    @patch('%s.set_header' % rest_pkg)
139
    @patch('%s.post' % rest_pkg, return_value=FR())
140
    def test_networks_post(self, post, SH):
141
        for args in product(
142
                ('', 'net_id'),
143
                ('', 'cmd'),
144
                (None, [dict(json="data"), dict(data="json")]),
145
                (202, 204),
146
                ({}, {'k': 'v'})):
147
            (srv_id, command, json_data, success, kwargs) = args
148
            self.client.networks_post(*args[:4], **kwargs)
149
            vm_str = '/%s' % srv_id if srv_id else ''
150
            cmd_str = '/%s' % command if command else ''
151
            if json_data:
152
                json_data = dumps(json_data)
153
                self.assertEqual(SH.mock_calls[-2:], [
154
                    call('Content-Type', 'application/json'),
155
                    call('Content-Length', len(json_data))])
156
            self.assertEqual(post.mock_calls[-1], call(
157
                '/networks%s%s' % (vm_str, cmd_str),
158
                data=json_data, success=success,
159
                **kwargs))
160

    
161
    @patch('%s.set_header' % rest_pkg)
162
    @patch('%s.put' % rest_pkg, return_value=FR())
163
    def test_networks_put(self, put, SH):
164
        for args in product(
165
                ('', 'net_id'),
166
                ('', 'cmd'),
167
                (None, [dict(json="data"), dict(data="json")]),
168
                (202, 204),
169
                ({}, {'k': 'v'})):
170
            (srv_id, command, json_data, success, kwargs) = args
171
            self.client.networks_put(*args[:4], **kwargs)
172
            vm_str = '/%s' % srv_id if srv_id else ''
173
            cmd_str = '/%s' % command if command else ''
174
            if json_data:
175
                json_data = dumps(json_data)
176
                self.assertEqual(SH.mock_calls[-2:], [
177
                    call('Content-Type', 'application/json'),
178
                    call('Content-Length', len(json_data))])
179
            self.assertEqual(put.mock_calls[-1], call(
180
                '/networks%s%s' % (vm_str, cmd_str),
181
                data=json_data, success=success,
182
                **kwargs))
183

    
184
    @patch('%s.get' % rest_pkg, return_value=FR())
185
    def test_floating_ip_pools_get(self, get):
186
        for args in product(
187
                (200, 204),
188
                ({}, {'k': 'v'})):
189
            success, kwargs = args
190
            r = self.client.floating_ip_pools_get(success, **kwargs)
191
            self.assertTrue(isinstance(r, FR))
192
            self.assertEqual(get.mock_calls[-1], call(
193
                '/os-floating-ip-pools', success=success, **kwargs))
194

    
195
    @patch('%s.get' % rest_pkg, return_value=FR())
196
    def test_floating_ips_get(self, get):
197
        for args in product(
198
                ('fip', ''),
199
                (200, 204),
200
                ({}, {'k': 'v'})):
201
            fip, success, kwargs = args
202
            r = self.client.floating_ips_get(fip, success, **kwargs)
203
            self.assertTrue(isinstance(r, FR))
204
            expected = '' if not fip else '/%s' % fip
205
            self.assertEqual(get.mock_calls[-1], call(
206
                '/os-floating-ips%s' % expected, success=success, **kwargs))
207

    
208
    @patch('%s.set_header' % rest_pkg)
209
    @patch('%s.post' % rest_pkg, return_value=FR())
210
    def test_floating_ips_post(self, post, SH):
211
        for args in product(
212
                (None, [dict(json="data"), dict(data="json")]),
213
                ('fip', ''),
214
                (202, 204),
215
                ({}, {'k': 'v'})):
216
            json_data, fip, success, kwargs = args
217
            self.client.floating_ips_post(*args[:3], **kwargs)
218
            if json_data:
219
                json_data = dumps(json_data)
220
                self.assertEqual(SH.mock_calls[-2:], [
221
                    call('Content-Type', 'application/json'),
222
                    call('Content-Length', len(json_data))])
223
            expected = '' if not fip else '/%s' % fip
224
            self.assertEqual(post.mock_calls[-1], call(
225
                '/os-floating-ips%s' % expected,
226
                data=json_data, success=success,
227
                **kwargs))
228

    
229
    @patch('%s.delete' % rest_pkg, return_value=FR())
230
    def test_floating_ips_delete(self, delete):
231
        for args in product(
232
                ('fip1', 'fip2'),
233
                (200, 204),
234
                ({}, {'k': 'v'})):
235
            fip, success, kwargs = args
236
            r = self.client.floating_ips_delete(fip, success, **kwargs)
237
            self.assertTrue(isinstance(r, FR))
238
            self.assertEqual(delete.mock_calls[-1], call(
239
                '/os-floating-ips/%s' % fip, success=success, **kwargs))
240

    
241

    
242
class CycladesNetworkClient(TestCase):
243

    
244
    """Set up a ComputesRest thorough test"""
245
    def setUp(self):
246
        self.url = 'http://network.example.com'
247
        self.token = 'n2tw0rk70k3n'
248
        self.client = cyclades.CycladesNetworkClient(self.url, self.token)
249

    
250
    def tearDown(self):
251
        FR.json = vm_recv
252
        del self.client
253

    
254
    @patch('kamaki.clients.Client.get', return_value=FR)
255
    def test_list_networks(self, get):
256
        FR.json = dict(networks='ret val')
257
        for detail in (True, None):
258
            self.assertEqual(self.client.list_networks(detail), 'ret val')
259
            path = '/networks/detail' if detail else '/networks'
260
            self.assertEqual(get.mock_calls[-1], call(path, success=200))
261

    
262
    @patch(
263
        'kamaki.clients.network.rest_api.NetworkRestClient.networks_post',
264
        return_value=FR())
265
    def test_create_network(self, networks_post):
266
        for name, shared in product((None, 'net name'), (None, True)):
267
            FR.json = dict(network='ret val')
268
            type = 'net type'
269
            self.assertEqual(
270
                self.client.create_network(type, name=name, shared=shared),
271
                'ret val')
272
            req = dict(type=type, admin_state_up=True)
273
            if name:
274
                req['name'] = name
275
            if shared:
276
                req['shared'] = shared
277
            expargs = dict(json_data=dict(network=req), success=201)
278
            self.assertEqual(networks_post.mock_calls[-1], call(**expargs))
279

    
280

    
281
class CycladesClient(TestCase):
282

    
283
    def assert_dicts_are_equal(self, d1, d2):
284
        for k, v in d1.items():
285
            self.assertTrue(k in d2)
286
            if isinstance(v, dict):
287
                self.assert_dicts_are_equal(v, d2[k])
288
            else:
289
                self.assertEqual(unicode(v), unicode(d2[k]))
290

    
291
    """Set up a Cyclades thorough test"""
292
    def setUp(self):
293
        self.url = 'http://cyclades.example.com'
294
        self.token = 'cyc14d3s70k3n'
295
        self.client = cyclades.CycladesClient(self.url, self.token)
296

    
297
    def tearDown(self):
298
        FR.status_code = 200
299
        FR.json = vm_recv
300

    
301
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
302
    def test_shutdown_server(self, SP):
303
        vm_id = vm_recv['server']['id']
304
        self.client.shutdown_server(vm_id)
305
        SP.assert_called_once_with(
306
            vm_id, json_data=dict(shutdown=dict()), success=202)
307

    
308
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
309
    def test_start_server(self, SP):
310
        vm_id = vm_recv['server']['id']
311
        self.client.start_server(vm_id)
312
        SP.assert_called_once_with(
313
            vm_id, json_data=dict(start=dict()), success=202)
314

    
315
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
316
    def test_get_server_console(self, SP):
317
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
318
        FR.json = cnsl
319
        vm_id = vm_recv['server']['id']
320
        r = self.client.get_server_console(vm_id)
321
        SP.assert_called_once_with(
322
            vm_id, json_data=dict(console=dict(type='vnc')), success=200)
323
        self.assert_dicts_are_equal(r, cnsl['console'])
324

    
325
    def test_get_firewall_profile(self):
326
        vm_id = vm_recv['server']['id']
327
        v = firewalls['attachments'][0]['firewallProfile']
328
        with patch.object(
329
                cyclades.CycladesClient, 'get_server_details',
330
                return_value=firewalls) as GSD:
331
            r = self.client.get_firewall_profile(vm_id)
332
            GSD.assert_called_once_with(vm_id)
333
            self.assertEqual(r, v)
334
        with patch.object(
335
                cyclades.CycladesClient, 'get_server_details',
336
                return_value=dict()):
337
            self.assertRaises(
338
                ClientError,
339
                self.client.get_firewall_profile,
340
                vm_id)
341

    
342
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
343
    def test_set_firewall_profile(self, SP):
344
        vm_id = vm_recv['server']['id']
345
        v = firewalls['attachments'][0]['firewallProfile']
346
        self.client.set_firewall_profile(vm_id, v)
347
        SP.assert_called_once_with(vm_id, json_data=dict(
348
            firewallProfile=dict(profile=v)), success=202)
349

    
350
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
351
    def test_create_network(self, NP):
352
        net_name = net_send['network']['name']
353
        FR.json = net_recv
354
        full_args = dict(
355
                cidr='192.168.0.0/24',
356
                gateway='192.168.0.1',
357
                type='MAC_FILTERED',
358
                dhcp=True)
359
        test_args = dict(full_args)
360
        test_args.update(dict(empty=None, full=None))
361
        net_exp = dict(dhcp=False, name=net_name, type='MAC_FILTERED')
362
        for arg, val in test_args.items():
363
            kwargs = {} if arg == 'empty' else full_args if (
364
                arg == 'full') else {arg: val}
365
            expected = dict(network=dict(net_exp))
366
            expected['network'].update(kwargs)
367
            r = self.client.create_network(net_name, **kwargs)
368
            self.assertEqual(
369
                NP.mock_calls[-1],
370
                call(json_data=expected, success=202))
371
            self.assert_dicts_are_equal(r, net_recv['network'])
372

    
373
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
374
    def test_connect_server(self, NP):
375
        vm_id = vm_recv['server']['id']
376
        net_id = net_recv['network']['id']
377
        self.client.connect_server(vm_id, net_id)
378
        NP.assert_called_once_with(
379
            net_id, 'action',
380
            json_data=dict(add=dict(serverRef=vm_id)))
381

    
382
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
383
    def test_disconnect_server(self, NP):
384
        net_id, vm_id = net_recv['network']['id'], vm_recv['server']['id']
385
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
386
        vm_nics = [
387
            dict(id=nic_id, network_id=net_id),
388
            dict(id='another-nic-id', network_id='another-net-id'),
389
            dict(id=nic_id * 2, network_id=net_id * 2)]
390
        with patch.object(
391
                cyclades.CycladesClient,
392
                'list_server_nics',
393
                return_value=vm_nics) as LSN:
394
            r = self.client.disconnect_server(vm_id, nic_id)
395
            LSN.assert_called_once_with(vm_id)
396
            NP.assert_called_once_with(
397
                net_id, 'action',
398
                json_data=dict(remove=dict(attachment=nic_id)))
399
            self.assertEqual(r, 1)
400

    
401
    @patch('%s.servers_ips_get' % cyclades_pkg, return_value=FR())
402
    def test_list_server_nics(self, SG):
403
        vm_id = vm_recv['server']['id']
404
        nics = dict(attachments=[dict(id='nic1'), dict(id='nic2')])
405
        FR.json = nics
406
        r = self.client.list_server_nics(vm_id)
407
        SG.assert_called_once_with(vm_id)
408
        expected = nics['attachments']
409
        for i in range(len(r)):
410
            self.assert_dicts_are_equal(r[i], expected[i])
411
        self.assertEqual(i + 1, len(r))
412

    
413
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
414
    def test_list_networks(self, NG):
415
        FR.json = net_list
416
        expected = net_list['networks']
417
        for detail in ('', 'detail'):
418
            r = self.client.list_networks(detail=True if detail else False)
419
            self.assertEqual(NG.mock_calls[-1], call(command=detail))
420
            for i, net in enumerate(expected):
421
                self.assert_dicts_are_equal(r[i], net)
422
            self.assertEqual(i + 1, len(r))
423

    
424
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
425
    def test_list_network_nics(self, NG):
426
        net_id = net_recv['network']['id']
427
        FR.json = net_recv
428
        r = self.client.list_network_nics(net_id)
429
        NG.assert_called_once_with(network_id=net_id)
430
        expected = net_recv['network']['attachments']
431
        for i in range(len(r)):
432
            self.assert_dicts_are_equal(r[i], expected[i])
433

    
434
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
435
    def test_disconnect_network_nics(self, NP):
436
        net_id = net_recv['network']['id']
437
        nics = ['nic1', 'nic2', 'nic3']
438
        with patch.object(
439
                cyclades.CycladesClient,
440
                'list_network_nics',
441
                return_value=nics) as LNN:
442
            self.client.disconnect_network_nics(net_id)
443
            LNN.assert_called_once_with(net_id)
444
            for i in range(len(nics)):
445
                expected = call(net_id, 'action', json_data=dict(
446
                    remove=dict(attachment=nics[i])))
447
                self.assertEqual(expected, NP.mock_calls[i])
448

    
449
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
450
    def test_get_network_details(self, NG):
451
        FR.json = net_recv
452
        net_id = net_recv['network']['id']
453
        r = self.client.get_network_details(net_id)
454
        NG.assert_called_once_with(network_id=net_id)
455
        self.assert_dicts_are_equal(r, net_recv['network'])
456

    
457
    @patch('%s.networks_put' % cyclades_pkg, return_value=FR())
458
    def test_update_network_name(self, NP):
459
        net_id = net_recv['network']['id']
460
        new_name = '%s_new' % net_id
461
        self.client.update_network_name(net_id, new_name)
462
        NP.assert_called_once_with(
463
            network_id=net_id,
464
            json_data=dict(network=dict(name=new_name)))
465

    
466
    def test_delete_network(self):
467
        net_id = net_recv['network']['id']
468
        with patch.object(
469
                cyclades.CycladesClient, 'networks_delete',
470
                return_value=FR()) as ND:
471
            self.client.delete_network(net_id)
472
            ND.assert_called_once_with(net_id)
473
        with patch.object(
474
                cyclades.CycladesClient, 'networks_delete',
475
                side_effect=ClientError('A 421 Error', 421)):
476
            try:
477
                self.client.delete_network(421)
478
            except ClientError as err:
479
                self.assertEqual(err.status, 421)
480
                self.assertEqual(err.details, [
481
                    'Network may be still connected to at least one server'])
482

    
483
    @patch('%s.floating_ip_pools_get' % cyclades_pkg, return_value=FR())
484
    def test_get_floating_ip_pools(self, get):
485
        r = self.client.get_floating_ip_pools()
486
        self.assert_dicts_are_equal(r, FR.json)
487
        self.assertEqual(get.mock_calls[-1], call())
488

    
489
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
490
    def test_get_floating_ips(self, get):
491
        r = self.client.get_floating_ips()
492
        self.assert_dicts_are_equal(r, FR.json)
493
        self.assertEqual(get.mock_calls[-1], call())
494

    
495
    @patch('%s.floating_ips_post' % cyclades_pkg, return_value=FR())
496
    def test_alloc_floating_ip(self, post):
497
        FR.json = dict(floating_ip=dict(
498
            fixed_ip='fip',
499
            id=1,
500
            instance_id='lala',
501
            ip='102.0.0.1',
502
            pool='pisine'))
503
        for args in product(
504
                (None, 'pisine'),
505
                (None, 'Iwannanip')):
506
            r = self.client.alloc_floating_ip(*args)
507
            pool, address = args
508
            self.assert_dicts_are_equal(r, FR.json['floating_ip'])
509
            json_data = dict()
510
            if pool:
511
                json_data['pool'] = pool
512
            if address:
513
                json_data['address'] = address
514
            self.assertEqual(post.mock_calls[-1], call(json_data))
515

    
516
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
517
    def test_get_floating_ip(self, get):
518
        FR.json = dict(floating_ip=dict(
519
            fixed_ip='fip',
520
            id=1,
521
            instance_id='lala',
522
            ip='102.0.0.1',
523
            pool='pisine'))
524
        self.assertRaises(AssertionError, self.client.get_floating_ip, None)
525
        fip = 'fip'
526
        r = self.client.get_floating_ip(fip)
527
        self.assert_dicts_are_equal(r, FR.json['floating_ip'])
528
        self.assertEqual(get.mock_calls[-1], call(fip))
529

    
530
    @patch('%s.floating_ips_delete' % cyclades_pkg, return_value=FR())
531
    def test_delete_floating_ip(self, delete):
532
        self.assertRaises(AssertionError, self.client.delete_floating_ip, None)
533
        fip = 'fip'
534
        r = self.client.delete_floating_ip(fip)
535
        self.assert_dicts_are_equal(r, FR.headers)
536
        self.assertEqual(delete.mock_calls[-1], call(fip))
537

    
538
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
539
    def test_attach_floating_ip(self, spost):
540
        vmid, addr = 42, 'anIpAddress'
541
        for err, args in {
542
                ValueError: ['not a server id', addr],
543
                TypeError: [None, addr],
544
                AssertionError: [vmid, None],
545
                AssertionError: [vmid, '']}.items():
546
            self.assertRaises(
547
                err, self.client.attach_floating_ip, *args)
548
        r = self.client.attach_floating_ip(vmid, addr)
549
        self.assert_dicts_are_equal(r, FR.headers)
550
        expected = dict(addFloatingIp=dict(address=addr))
551
        self.assertEqual(spost.mock_calls[-1], call(vmid, json_data=expected))
552

    
553
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
554
    def test_detach_floating_ip(self, spost):
555
        vmid, addr = 42, 'anIpAddress'
556
        for err, args in {
557
                ValueError: ['not a server id', addr],
558
                TypeError: [None, addr],
559
                AssertionError: [vmid, None],
560
                AssertionError: [vmid, '']}.items():
561
            self.assertRaises(
562
                err, self.client.detach_floating_ip, *args)
563
        r = self.client.detach_floating_ip(vmid, addr)
564
        self.assert_dicts_are_equal(r, FR.headers)
565
        expected = dict(removeFloatingIp=dict(address=addr))
566
        self.assertEqual(spost.mock_calls[-1], call(vmid, json_data=expected))
567

    
568

    
569
if __name__ == '__main__':
570
    from sys import argv
571
    from kamaki.clients.test import runTestCase
572
    not_found = True
573
    if not argv[1:] or argv[1] == 'CycladesClient':
574
        not_found = False
575
        runTestCase(CycladesNetworkClient, 'Cyclades Client', argv[2:])
576
    if not argv[1:] or argv[1] == 'CycladesNetworkClient':
577
        not_found = False
578
        runTestCase(CycladesNetworkClient, 'CycladesNetwork Client', argv[2:])
579
    if not argv[1:] or argv[1] == 'CycladesRestClient':
580
        not_found = False
581
        runTestCase(CycladesRestClient, 'CycladesRest Client', argv[2:])
582
    if not_found:
583
        print('TestCase %s not found' % argv[1])