Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (22.5 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(
255
        'kamaki.clients.network.NetworkClient.networks_post',
256
        return_value=FR())
257
    def test_create_network(self, networks_post):
258
        for name, shared in product((None, 'net name'), (None, True)):
259
            FR.json = dict(network='ret val')
260
            type = 'net type'
261
            self.assertEqual(
262
                self.client.create_network(type, name=name, shared=shared),
263
                'ret val')
264
            req = dict(type=type, admin_state_up=True)
265
            if name:
266
                req['name'] = name
267
            if shared:
268
                req['shared'] = shared
269
            expargs = dict(json_data=dict(network=req), success=201)
270
            self.assertEqual(networks_post.mock_calls[-1], call(**expargs))
271

    
272

    
273
class CycladesClient(TestCase):
274

    
275
    def assert_dicts_are_equal(self, d1, d2):
276
        for k, v in d1.items():
277
            self.assertTrue(k in d2)
278
            if isinstance(v, dict):
279
                self.assert_dicts_are_equal(v, d2[k])
280
            else:
281
                self.assertEqual(unicode(v), unicode(d2[k]))
282

    
283
    """Set up a Cyclades thorough test"""
284
    def setUp(self):
285
        self.url = 'http://cyclades.example.com'
286
        self.token = 'cyc14d3s70k3n'
287
        self.client = cyclades.CycladesClient(self.url, self.token)
288

    
289
    def tearDown(self):
290
        FR.status_code = 200
291
        FR.json = vm_recv
292

    
293
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
294
    def test_shutdown_server(self, SP):
295
        vm_id = vm_recv['server']['id']
296
        self.client.shutdown_server(vm_id)
297
        SP.assert_called_once_with(
298
            vm_id, json_data=dict(shutdown=dict()), success=202)
299

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

    
307
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
308
    def test_get_server_console(self, SP):
309
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
310
        FR.json = cnsl
311
        vm_id = vm_recv['server']['id']
312
        r = self.client.get_server_console(vm_id)
313
        SP.assert_called_once_with(
314
            vm_id, json_data=dict(console=dict(type='vnc')), success=200)
315
        self.assert_dicts_are_equal(r, cnsl['console'])
316

    
317
    def test_get_firewall_profile(self):
318
        vm_id = vm_recv['server']['id']
319
        v = firewalls['attachments'][0]['firewallProfile']
320
        with patch.object(
321
                cyclades.CycladesClient, 'get_server_details',
322
                return_value=firewalls) as GSD:
323
            r = self.client.get_firewall_profile(vm_id)
324
            GSD.assert_called_once_with(vm_id)
325
            self.assertEqual(r, v)
326
        with patch.object(
327
                cyclades.CycladesClient, 'get_server_details',
328
                return_value=dict()):
329
            self.assertRaises(
330
                ClientError,
331
                self.client.get_firewall_profile,
332
                vm_id)
333

    
334
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
335
    def test_set_firewall_profile(self, SP):
336
        vm_id = vm_recv['server']['id']
337
        v = firewalls['attachments'][0]['firewallProfile']
338
        self.client.set_firewall_profile(vm_id, v)
339
        SP.assert_called_once_with(vm_id, json_data=dict(
340
            firewallProfile=dict(profile=v)), success=202)
341

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

    
365
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
366
    def test_connect_server(self, NP):
367
        vm_id = vm_recv['server']['id']
368
        net_id = net_recv['network']['id']
369
        self.client.connect_server(vm_id, net_id)
370
        NP.assert_called_once_with(
371
            net_id, 'action',
372
            json_data=dict(add=dict(serverRef=vm_id)))
373

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

    
393
    @patch('%s.servers_ips_get' % cyclades_pkg, return_value=FR())
394
    def test_list_server_nics(self, SG):
395
        vm_id = vm_recv['server']['id']
396
        nics = dict(attachments=[dict(id='nic1'), dict(id='nic2')])
397
        FR.json = nics
398
        r = self.client.list_server_nics(vm_id)
399
        SG.assert_called_once_with(vm_id)
400
        expected = nics['attachments']
401
        for i in range(len(r)):
402
            self.assert_dicts_are_equal(r[i], expected[i])
403
        self.assertEqual(i + 1, len(r))
404

    
405
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
406
    def test_list_networks(self, NG):
407
        FR.json = net_list
408
        expected = net_list['networks']
409
        for detail in ('', 'detail'):
410
            r = self.client.list_networks(detail=True if detail else False)
411
            self.assertEqual(NG.mock_calls[-1], call(command=detail))
412
            for i, net in enumerate(expected):
413
                self.assert_dicts_are_equal(r[i], net)
414
            self.assertEqual(i + 1, len(r))
415

    
416
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
417
    def test_list_network_nics(self, NG):
418
        net_id = net_recv['network']['id']
419
        FR.json = net_recv
420
        r = self.client.list_network_nics(net_id)
421
        NG.assert_called_once_with(network_id=net_id)
422
        expected = net_recv['network']['attachments']
423
        for i in range(len(r)):
424
            self.assert_dicts_are_equal(r[i], expected[i])
425

    
426
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
427
    def test_disconnect_network_nics(self, NP):
428
        net_id = net_recv['network']['id']
429
        nics = ['nic1', 'nic2', 'nic3']
430
        with patch.object(
431
                cyclades.CycladesClient,
432
                'list_network_nics',
433
                return_value=nics) as LNN:
434
            self.client.disconnect_network_nics(net_id)
435
            LNN.assert_called_once_with(net_id)
436
            for i in range(len(nics)):
437
                expected = call(net_id, 'action', json_data=dict(
438
                    remove=dict(attachment=nics[i])))
439
                self.assertEqual(expected, NP.mock_calls[i])
440

    
441
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
442
    def test_get_network_details(self, NG):
443
        FR.json = net_recv
444
        net_id = net_recv['network']['id']
445
        r = self.client.get_network_details(net_id)
446
        NG.assert_called_once_with(network_id=net_id)
447
        self.assert_dicts_are_equal(r, net_recv['network'])
448

    
449
    @patch('%s.networks_put' % cyclades_pkg, return_value=FR())
450
    def test_update_network_name(self, NP):
451
        net_id = net_recv['network']['id']
452
        new_name = '%s_new' % net_id
453
        self.client.update_network_name(net_id, new_name)
454
        NP.assert_called_once_with(
455
            network_id=net_id,
456
            json_data=dict(network=dict(name=new_name)))
457

    
458
    def test_delete_network(self):
459
        net_id = net_recv['network']['id']
460
        with patch.object(
461
                cyclades.CycladesClient, 'networks_delete',
462
                return_value=FR()) as ND:
463
            self.client.delete_network(net_id)
464
            ND.assert_called_once_with(net_id)
465
        with patch.object(
466
                cyclades.CycladesClient, 'networks_delete',
467
                side_effect=ClientError('A 421 Error', 421)):
468
            try:
469
                self.client.delete_network(421)
470
            except ClientError as err:
471
                self.assertEqual(err.status, 421)
472
                self.assertEqual(err.details, [
473
                    'Network may be still connected to at least one server'])
474

    
475
    @patch('%s.floating_ip_pools_get' % cyclades_pkg, return_value=FR())
476
    def test_get_floating_ip_pools(self, get):
477
        r = self.client.get_floating_ip_pools()
478
        self.assert_dicts_are_equal(r, FR.json)
479
        self.assertEqual(get.mock_calls[-1], call())
480

    
481
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
482
    def test_get_floating_ips(self, get):
483
        r = self.client.get_floating_ips()
484
        self.assert_dicts_are_equal(r, FR.json)
485
        self.assertEqual(get.mock_calls[-1], call())
486

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

    
508
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
509
    def test_get_floating_ip(self, get):
510
        FR.json = dict(floating_ip=dict(
511
            fixed_ip='fip',
512
            id=1,
513
            instance_id='lala',
514
            ip='102.0.0.1',
515
            pool='pisine'))
516
        self.assertRaises(AssertionError, self.client.get_floating_ip, None)
517
        fip = 'fip'
518
        r = self.client.get_floating_ip(fip)
519
        self.assert_dicts_are_equal(r, FR.json['floating_ip'])
520
        self.assertEqual(get.mock_calls[-1], call(fip))
521

    
522
    @patch('%s.floating_ips_delete' % cyclades_pkg, return_value=FR())
523
    def test_delete_floating_ip(self, delete):
524
        self.assertRaises(AssertionError, self.client.delete_floating_ip, None)
525
        fip = 'fip'
526
        r = self.client.delete_floating_ip(fip)
527
        self.assert_dicts_are_equal(r, FR.headers)
528
        self.assertEqual(delete.mock_calls[-1], call(fip))
529

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

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

    
560

    
561
if __name__ == '__main__':
562
    from sys import argv
563
    from kamaki.clients.test import runTestCase
564
    not_found = True
565
    if not argv[1:] or argv[1] == 'CycladesClient':
566
        not_found = False
567
        runTestCase(CycladesNetworkClient, 'Cyclades Client', argv[2:])
568
    if not argv[1:] or argv[1] == 'CycladesNetworkClient':
569
        not_found = False
570
        runTestCase(CycladesNetworkClient, 'CycladesNetwork Client', argv[2:])
571
    if not argv[1:] or argv[1] == 'CycladesRestClient':
572
        not_found = False
573
        runTestCase(CycladesRestClient, 'CycladesRest Client', argv[2:])
574
    if not_found:
575
        print('TestCase %s not found' % argv[1])