Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (23.7 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
    @patch(
281
        'kamaki.clients.network.rest_api.NetworkRestClient.ports_post',
282
        return_value=FR)
283
    def test_create_port(self, ports_post):
284
        network_id, device_id, FR.json = 'netid', 'devid', dict(port='ret v')
285
        for security_groups in ([1, 2, 3], None):
286
            self.assertEqual(
287
                self.client.create_port(
288
                    network_id, device_id, security_groups=security_groups),
289
                'ret v')
290
            req = dict(network_id=network_id, device_id=device_id)
291
            if security_groups:
292
                req['security_groups'] = security_groups
293
            expargs = dict(json_data=dict(port=req), success=201)
294
            self.assertEqual(ports_post.mock_calls[-1], call(**expargs))
295

    
296

    
297
class CycladesClient(TestCase):
298

    
299
    def assert_dicts_are_equal(self, d1, d2):
300
        for k, v in d1.items():
301
            self.assertTrue(k in d2)
302
            if isinstance(v, dict):
303
                self.assert_dicts_are_equal(v, d2[k])
304
            else:
305
                self.assertEqual(unicode(v), unicode(d2[k]))
306

    
307
    """Set up a Cyclades thorough test"""
308
    def setUp(self):
309
        self.url = 'http://cyclades.example.com'
310
        self.token = 'cyc14d3s70k3n'
311
        self.client = cyclades.CycladesClient(self.url, self.token)
312

    
313
    def tearDown(self):
314
        FR.status_code = 200
315
        FR.json = vm_recv
316

    
317
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
318
    def test_shutdown_server(self, SP):
319
        vm_id = vm_recv['server']['id']
320
        self.client.shutdown_server(vm_id)
321
        SP.assert_called_once_with(
322
            vm_id, json_data=dict(shutdown=dict()), success=202)
323

    
324
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
325
    def test_start_server(self, SP):
326
        vm_id = vm_recv['server']['id']
327
        self.client.start_server(vm_id)
328
        SP.assert_called_once_with(
329
            vm_id, json_data=dict(start=dict()), success=202)
330

    
331
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
332
    def test_get_server_console(self, SP):
333
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
334
        FR.json = cnsl
335
        vm_id = vm_recv['server']['id']
336
        r = self.client.get_server_console(vm_id)
337
        SP.assert_called_once_with(
338
            vm_id, json_data=dict(console=dict(type='vnc')), success=200)
339
        self.assert_dicts_are_equal(r, cnsl['console'])
340

    
341
    def test_get_firewall_profile(self):
342
        vm_id = vm_recv['server']['id']
343
        v = firewalls['attachments'][0]['firewallProfile']
344
        with patch.object(
345
                cyclades.CycladesClient, 'get_server_details',
346
                return_value=firewalls) as GSD:
347
            r = self.client.get_firewall_profile(vm_id)
348
            GSD.assert_called_once_with(vm_id)
349
            self.assertEqual(r, v)
350
        with patch.object(
351
                cyclades.CycladesClient, 'get_server_details',
352
                return_value=dict()):
353
            self.assertRaises(
354
                ClientError,
355
                self.client.get_firewall_profile,
356
                vm_id)
357

    
358
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
359
    def test_set_firewall_profile(self, SP):
360
        vm_id = vm_recv['server']['id']
361
        v = firewalls['attachments'][0]['firewallProfile']
362
        self.client.set_firewall_profile(vm_id, v)
363
        SP.assert_called_once_with(vm_id, json_data=dict(
364
            firewallProfile=dict(profile=v)), success=202)
365

    
366
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
367
    def test_create_network(self, NP):
368
        net_name = net_send['network']['name']
369
        FR.json = net_recv
370
        full_args = dict(
371
                cidr='192.168.0.0/24',
372
                gateway='192.168.0.1',
373
                type='MAC_FILTERED',
374
                dhcp=True)
375
        test_args = dict(full_args)
376
        test_args.update(dict(empty=None, full=None))
377
        net_exp = dict(dhcp=False, name=net_name, type='MAC_FILTERED')
378
        for arg, val in test_args.items():
379
            kwargs = {} if arg == 'empty' else full_args if (
380
                arg == 'full') else {arg: val}
381
            expected = dict(network=dict(net_exp))
382
            expected['network'].update(kwargs)
383
            r = self.client.create_network(net_name, **kwargs)
384
            self.assertEqual(
385
                NP.mock_calls[-1],
386
                call(json_data=expected, success=202))
387
            self.assert_dicts_are_equal(r, net_recv['network'])
388

    
389
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
390
    def test_connect_server(self, NP):
391
        vm_id = vm_recv['server']['id']
392
        net_id = net_recv['network']['id']
393
        self.client.connect_server(vm_id, net_id)
394
        NP.assert_called_once_with(
395
            net_id, 'action',
396
            json_data=dict(add=dict(serverRef=vm_id)))
397

    
398
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
399
    def test_disconnect_server(self, NP):
400
        net_id, vm_id = net_recv['network']['id'], vm_recv['server']['id']
401
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
402
        vm_nics = [
403
            dict(id=nic_id, network_id=net_id),
404
            dict(id='another-nic-id', network_id='another-net-id'),
405
            dict(id=nic_id * 2, network_id=net_id * 2)]
406
        with patch.object(
407
                cyclades.CycladesClient,
408
                'list_server_nics',
409
                return_value=vm_nics) as LSN:
410
            r = self.client.disconnect_server(vm_id, nic_id)
411
            LSN.assert_called_once_with(vm_id)
412
            NP.assert_called_once_with(
413
                net_id, 'action',
414
                json_data=dict(remove=dict(attachment=nic_id)))
415
            self.assertEqual(r, 1)
416

    
417
    @patch('%s.servers_ips_get' % cyclades_pkg, return_value=FR())
418
    def test_list_server_nics(self, SG):
419
        vm_id = vm_recv['server']['id']
420
        nics = dict(attachments=[dict(id='nic1'), dict(id='nic2')])
421
        FR.json = nics
422
        r = self.client.list_server_nics(vm_id)
423
        SG.assert_called_once_with(vm_id)
424
        expected = nics['attachments']
425
        for i in range(len(r)):
426
            self.assert_dicts_are_equal(r[i], expected[i])
427
        self.assertEqual(i + 1, len(r))
428

    
429
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
430
    def test_list_networks(self, NG):
431
        FR.json = net_list
432
        expected = net_list['networks']
433
        for detail in ('', 'detail'):
434
            r = self.client.list_networks(detail=True if detail else False)
435
            self.assertEqual(NG.mock_calls[-1], call(command=detail))
436
            for i, net in enumerate(expected):
437
                self.assert_dicts_are_equal(r[i], net)
438
            self.assertEqual(i + 1, len(r))
439

    
440
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
441
    def test_list_network_nics(self, NG):
442
        net_id = net_recv['network']['id']
443
        FR.json = net_recv
444
        r = self.client.list_network_nics(net_id)
445
        NG.assert_called_once_with(network_id=net_id)
446
        expected = net_recv['network']['attachments']
447
        for i in range(len(r)):
448
            self.assert_dicts_are_equal(r[i], expected[i])
449

    
450
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
451
    def test_disconnect_network_nics(self, NP):
452
        net_id = net_recv['network']['id']
453
        nics = ['nic1', 'nic2', 'nic3']
454
        with patch.object(
455
                cyclades.CycladesClient,
456
                'list_network_nics',
457
                return_value=nics) as LNN:
458
            self.client.disconnect_network_nics(net_id)
459
            LNN.assert_called_once_with(net_id)
460
            for i in range(len(nics)):
461
                expected = call(net_id, 'action', json_data=dict(
462
                    remove=dict(attachment=nics[i])))
463
                self.assertEqual(expected, NP.mock_calls[i])
464

    
465
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
466
    def test_get_network_details(self, NG):
467
        FR.json = net_recv
468
        net_id = net_recv['network']['id']
469
        r = self.client.get_network_details(net_id)
470
        NG.assert_called_once_with(network_id=net_id)
471
        self.assert_dicts_are_equal(r, net_recv['network'])
472

    
473
    @patch('%s.networks_put' % cyclades_pkg, return_value=FR())
474
    def test_update_network_name(self, NP):
475
        net_id = net_recv['network']['id']
476
        new_name = '%s_new' % net_id
477
        self.client.update_network_name(net_id, new_name)
478
        NP.assert_called_once_with(
479
            network_id=net_id,
480
            json_data=dict(network=dict(name=new_name)))
481

    
482
    def test_delete_network(self):
483
        net_id = net_recv['network']['id']
484
        with patch.object(
485
                cyclades.CycladesClient, 'networks_delete',
486
                return_value=FR()) as ND:
487
            self.client.delete_network(net_id)
488
            ND.assert_called_once_with(net_id)
489
        with patch.object(
490
                cyclades.CycladesClient, 'networks_delete',
491
                side_effect=ClientError('A 421 Error', 421)):
492
            try:
493
                self.client.delete_network(421)
494
            except ClientError as err:
495
                self.assertEqual(err.status, 421)
496
                self.assertEqual(err.details, [
497
                    'Network may be still connected to at least one server'])
498

    
499
    @patch('%s.floating_ip_pools_get' % cyclades_pkg, return_value=FR())
500
    def test_get_floating_ip_pools(self, get):
501
        r = self.client.get_floating_ip_pools()
502
        self.assert_dicts_are_equal(r, FR.json)
503
        self.assertEqual(get.mock_calls[-1], call())
504

    
505
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
506
    def test_get_floating_ips(self, get):
507
        r = self.client.get_floating_ips()
508
        self.assert_dicts_are_equal(r, FR.json)
509
        self.assertEqual(get.mock_calls[-1], call())
510

    
511
    @patch('%s.floating_ips_post' % cyclades_pkg, return_value=FR())
512
    def test_alloc_floating_ip(self, post):
513
        FR.json = dict(floating_ip=dict(
514
            fixed_ip='fip',
515
            id=1,
516
            instance_id='lala',
517
            ip='102.0.0.1',
518
            pool='pisine'))
519
        for args in product(
520
                (None, 'pisine'),
521
                (None, 'Iwannanip')):
522
            r = self.client.alloc_floating_ip(*args)
523
            pool, address = args
524
            self.assert_dicts_are_equal(r, FR.json['floating_ip'])
525
            json_data = dict()
526
            if pool:
527
                json_data['pool'] = pool
528
            if address:
529
                json_data['address'] = address
530
            self.assertEqual(post.mock_calls[-1], call(json_data))
531

    
532
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
533
    def test_get_floating_ip(self, get):
534
        FR.json = dict(floating_ip=dict(
535
            fixed_ip='fip',
536
            id=1,
537
            instance_id='lala',
538
            ip='102.0.0.1',
539
            pool='pisine'))
540
        self.assertRaises(AssertionError, self.client.get_floating_ip, None)
541
        fip = 'fip'
542
        r = self.client.get_floating_ip(fip)
543
        self.assert_dicts_are_equal(r, FR.json['floating_ip'])
544
        self.assertEqual(get.mock_calls[-1], call(fip))
545

    
546
    @patch('%s.floating_ips_delete' % cyclades_pkg, return_value=FR())
547
    def test_delete_floating_ip(self, delete):
548
        self.assertRaises(AssertionError, self.client.delete_floating_ip, None)
549
        fip = 'fip'
550
        r = self.client.delete_floating_ip(fip)
551
        self.assert_dicts_are_equal(r, FR.headers)
552
        self.assertEqual(delete.mock_calls[-1], call(fip))
553

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

    
569
    @patch('%s.servers_action_post' % cyclades_pkg, return_value=FR())
570
    def test_detach_floating_ip(self, spost):
571
        vmid, addr = 42, 'anIpAddress'
572
        for err, args in {
573
                ValueError: ['not a server id', addr],
574
                TypeError: [None, addr],
575
                AssertionError: [vmid, None],
576
                AssertionError: [vmid, '']}.items():
577
            self.assertRaises(
578
                err, self.client.detach_floating_ip, *args)
579
        r = self.client.detach_floating_ip(vmid, addr)
580
        self.assert_dicts_are_equal(r, FR.headers)
581
        expected = dict(removeFloatingIp=dict(address=addr))
582
        self.assertEqual(spost.mock_calls[-1], call(vmid, json_data=expected))
583

    
584

    
585
if __name__ == '__main__':
586
    from sys import argv
587
    from kamaki.clients.test import runTestCase
588
    not_found = True
589
    if not argv[1:] or argv[1] == 'CycladesClient':
590
        not_found = False
591
        runTestCase(CycladesNetworkClient, 'Cyclades Client', argv[2:])
592
    if not argv[1:] or argv[1] == 'CycladesNetworkClient':
593
        not_found = False
594
        runTestCase(CycladesNetworkClient, 'CycladesNetwork Client', argv[2:])
595
    if not argv[1:] or argv[1] == 'CycladesRestClient':
596
        not_found = False
597
        runTestCase(CycladesRestClient, 'CycladesRest Client', argv[2:])
598
    if not_found:
599
        print('TestCase %s not found' % argv[1])