Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (23.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, 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.set_param' % rest_pkg)
107
    @patch('%s.get' % rest_pkg, return_value=FR())
108
    def test_servers_get(self, get, SP):
109
        for args in product(
110
                ('', 'vm_id'),
111
                ('', 'cmd'),
112
                (200, 204),
113
                (None, '50m3-d473'),
114
                ({}, {'k': 'v'})):
115
            (srv_id, command, success, changes_since, kwargs) = args
116
            self.client.servers_get(*args[:4], **kwargs)
117
            srv_str = '/%s' % srv_id if srv_id else ''
118
            cmd_str = '/%s' % command if command else ''
119
            self.assertEqual(get.mock_calls[-1], call(
120
                '/servers%s%s' % (srv_str, cmd_str),
121
                success=success,
122
                **kwargs))
123
            if changes_since:
124
                self.assertEqual(
125
                    SP.mock_calls[-1],
126
                    call('changes-since', changes_since, changes_since))
127

    
128
    @patch('%s.get' % rest_pkg, return_value=FR())
129
    def test_networks_get(self, get):
130
        for args in product(
131
                ('', 'net_id'),
132
                ('', 'cmd'),
133
                (200, 204),
134
                ({}, {'k': 'v'})):
135
            (srv_id, command, success, kwargs) = args
136
            self.client.networks_get(*args[:3], **kwargs)
137
            srv_str = '/%s' % srv_id if srv_id else ''
138
            cmd_str = '/%s' % command if command else ''
139
            self.assertEqual(get.mock_calls[-1], call(
140
                '/networks%s%s' % (srv_str, cmd_str),
141
                success=success,
142
                **kwargs))
143

    
144
    @patch('%s.delete' % rest_pkg, return_value=FR())
145
    def test_networks_delete(self, delete):
146
        for args in product(
147
                ('', 'net_id'),
148
                ('', 'cmd'),
149
                (202, 204),
150
                ({}, {'k': 'v'})):
151
            (srv_id, command, success, kwargs) = args
152
            self.client.networks_delete(*args[:3], **kwargs)
153
            srv_str = '/%s' % srv_id if srv_id else ''
154
            cmd_str = '/%s' % command if command else ''
155
            self.assertEqual(delete.mock_calls[-1], call(
156
                '/networks%s%s' % (srv_str, cmd_str),
157
                success=success,
158
                **kwargs))
159

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

    
183
    @patch('%s.set_header' % rest_pkg)
184
    @patch('%s.put' % rest_pkg, return_value=FR())
185
    def test_networks_put(self, put, SH):
186
        for args in product(
187
                ('', 'net_id'),
188
                ('', 'cmd'),
189
                (None, [dict(json="data"), dict(data="json")]),
190
                (202, 204),
191
                ({}, {'k': 'v'})):
192
            (srv_id, command, json_data, success, kwargs) = args
193
            self.client.networks_put(*args[:4], **kwargs)
194
            vm_str = '/%s' % srv_id if srv_id else ''
195
            cmd_str = '/%s' % command if command else ''
196
            if json_data:
197
                json_data = dumps(json_data)
198
                self.assertEqual(SH.mock_calls[-2:], [
199
                    call('Content-Type', 'application/json'),
200
                    call('Content-Length', len(json_data))])
201
            self.assertEqual(put.mock_calls[-1], call(
202
                '/networks%s%s' % (vm_str, cmd_str),
203
                data=json_data, success=success,
204
                **kwargs))
205

    
206
    @patch('%s.get' % rest_pkg, return_value=FR())
207
    def test_floating_ip_pools_get(self, get):
208
        for args in product(
209
                (200, 204),
210
                ({}, {'k': 'v'})):
211
            success, kwargs = args
212
            r = self.client.floating_ip_pools_get(success, **kwargs)
213
            self.assertTrue(isinstance(r, FR))
214
            self.assertEqual(get.mock_calls[-1], call(
215
                '/os-floating-ip-pools', success=success, **kwargs))
216

    
217
    @patch('%s.get' % rest_pkg, return_value=FR())
218
    def test_floating_ips_get(self, get):
219
        for args in product(
220
                ('fip', ''),
221
                (200, 204),
222
                ({}, {'k': 'v'})):
223
            fip, success, kwargs = args
224
            r = self.client.floating_ips_get(fip, success, **kwargs)
225
            self.assertTrue(isinstance(r, FR))
226
            expected = '' if not fip else '/%s' % fip
227
            self.assertEqual(get.mock_calls[-1], call(
228
                '/os-floating-ips%s' % expected, success=success, **kwargs))
229

    
230
    @patch('%s.set_header' % rest_pkg)
231
    @patch('%s.post' % rest_pkg, return_value=FR())
232
    def test_floating_ips_post(self, post, SH):
233
        for args in product(
234
                (None, [dict(json="data"), dict(data="json")]),
235
                ('fip', ''),
236
                (202, 204),
237
                ({}, {'k': 'v'})):
238
            json_data, fip, success, kwargs = args
239
            self.client.floating_ips_post(*args[:3], **kwargs)
240
            if json_data:
241
                json_data = dumps(json_data)
242
                self.assertEqual(SH.mock_calls[-2:], [
243
                    call('Content-Type', 'application/json'),
244
                    call('Content-Length', len(json_data))])
245
            expected = '' if not fip else '/%s' % fip
246
            self.assertEqual(post.mock_calls[-1], call(
247
                '/os-floating-ips%s' % expected,
248
                data=json_data, success=success,
249
                **kwargs))
250

    
251
    @patch('%s.delete' % rest_pkg, return_value=FR())
252
    def test_floating_ips_delete(self, delete):
253
        for args in product(
254
                ('fip1', 'fip2'),
255
                (200, 204),
256
                ({}, {'k': 'v'})):
257
            fip, success, kwargs = args
258
            r = self.client.floating_ips_delete(fip, success, **kwargs)
259
            self.assertTrue(isinstance(r, FR))
260
            self.assertEqual(delete.mock_calls[-1], call(
261
                '/os-floating-ips/%s' % fip, success=success, **kwargs))
262

    
263

    
264
class CycladesClient(TestCase):
265

    
266
    def assert_dicts_are_equal(self, d1, d2):
267
        for k, v in d1.items():
268
            self.assertTrue(k in d2)
269
            if isinstance(v, dict):
270
                self.assert_dicts_are_equal(v, d2[k])
271
            else:
272
                self.assertEqual(unicode(v), unicode(d2[k]))
273

    
274
    """Set up a Cyclades thorough test"""
275
    def setUp(self):
276
        self.url = 'http://cyclades.example.com'
277
        self.token = 'cyc14d3s70k3n'
278
        self.client = cyclades.CycladesClient(self.url, self.token)
279

    
280
    def tearDown(self):
281
        FR.status_code = 200
282
        FR.json = vm_recv
283

    
284
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
285
    def test_list_servers(self, SG):
286
        FR.json = vm_list
287
        for detail, since in ((0, 0), (True, 0), (0, 'd473'), (True, 'd473')):
288
            r = self.client.list_servers(detail=detail, changes_since=since)
289
            self.assertEqual(SG.mock_calls[-1], call(
290
                command='detail' if detail else '',
291
                changes_since=since))
292
            expected = vm_list['servers']
293
            for i, vm in enumerate(r):
294
                self.assert_dicts_are_equal(vm, expected[i])
295
            self.assertEqual(i + 1, len(expected))
296

    
297
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
298
    def test_shutdown_server(self, SP):
299
        vm_id = vm_recv['server']['id']
300
        self.client.shutdown_server(vm_id)
301
        SP.assert_called_once_with(
302
            vm_id, 'action',
303
            json_data=dict(shutdown=dict()), success=202)
304

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

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

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

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

    
350
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
351
    def test_get_server_stats(self, SG):
352
        vm_id = vm_recv['server']['id']
353
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
354
        FR.json = dict(stats=stats)
355
        r = self.client.get_server_stats(vm_id)
356
        SG.assert_called_once_with(vm_id, 'stats')
357
        self.assert_dicts_are_equal(stats, r)
358

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

    
382
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
383
    def test_connect_server(self, NP):
384
        vm_id = vm_recv['server']['id']
385
        net_id = net_recv['network']['id']
386
        self.client.connect_server(vm_id, net_id)
387
        NP.assert_called_once_with(
388
            net_id, 'action',
389
            json_data=dict(add=dict(serverRef=vm_id)))
390

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

    
410
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
411
    def test_list_server_nics(self, SG):
412
        vm_id = vm_recv['server']['id']
413
        nics = dict(attachments=[dict(id='nic1'), dict(id='nic2')])
414
        FR.json = nics
415
        r = self.client.list_server_nics(vm_id)
416
        SG.assert_called_once_with(vm_id, 'ips')
417
        expected = nics['attachments']
418
        for i in range(len(r)):
419
            self.assert_dicts_are_equal(r[i], expected[i])
420
        self.assertEqual(i + 1, len(r))
421

    
422
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
423
    def test_list_networks(self, NG):
424
        FR.json = net_list
425
        expected = net_list['networks']
426
        for detail in ('', 'detail'):
427
            r = self.client.list_networks(detail=True if detail else False)
428
            self.assertEqual(NG.mock_calls[-1], call(command=detail))
429
            for i, net in enumerate(expected):
430
                self.assert_dicts_are_equal(r[i], net)
431
            self.assertEqual(i + 1, len(r))
432

    
433
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
434
    def test_list_network_nics(self, NG):
435
        net_id = net_recv['network']['id']
436
        FR.json = net_recv
437
        r = self.client.list_network_nics(net_id)
438
        NG.assert_called_once_with(network_id=net_id)
439
        expected = net_recv['network']['attachments']
440
        for i in range(len(r)):
441
            self.assert_dicts_are_equal(r[i], expected[i])
442

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

    
458
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
459
    def test_get_network_details(self, NG):
460
        FR.json = net_recv
461
        net_id = net_recv['network']['id']
462
        r = self.client.get_network_details(net_id)
463
        NG.assert_called_once_with(network_id=net_id)
464
        self.assert_dicts_are_equal(r, net_recv['network'])
465

    
466
    @patch('%s.networks_put' % cyclades_pkg, return_value=FR())
467
    def test_update_network_name(self, NP):
468
        net_id = net_recv['network']['id']
469
        new_name = '%s_new' % net_id
470
        self.client.update_network_name(net_id, new_name)
471
        NP.assert_called_once_with(
472
            network_id=net_id,
473
            json_data=dict(network=dict(name=new_name)))
474

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

    
492
    @patch('%s.floating_ip_pools_get' % cyclades_pkg, return_value=FR())
493
    def test_get_floating_ip_pools(self, get):
494
        r = self.client.get_floating_ip_pools()
495
        self.assert_dicts_are_equal(r, FR.json)
496
        self.assertEqual(get.mock_calls[-1], call())
497

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

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

    
525
    @patch('%s.floating_ips_get' % cyclades_pkg, return_value=FR())
526
    def test_get_floating_ip(self, get):
527
        FR.json = dict(floating_ip=dict(
528
            fixed_ip='fip',
529
            id=1,
530
            instance_id='lala',
531
            ip='102.0.0.1',
532
            pool='pisine'))
533
        self.assertRaises(AssertionError, self.client.get_floating_ip, None)
534
        fip = 'fip'
535
        r = self.client.get_floating_ip(fip)
536
        self.assert_dicts_are_equal(r, FR.json['floating_ip'])
537
        self.assertEqual(get.mock_calls[-1], call(fip))
538

    
539
    @patch('%s.floating_ips_delete' % cyclades_pkg, return_value=FR())
540
    def test_delete_floating_ip(self, delete):
541
        self.assertRaises(AssertionError, self.client.delete_floating_ip, None)
542
        fip = 'fip'
543
        r = self.client.delete_floating_ip(fip)
544
        self.assert_dicts_are_equal(r, FR.headers)
545
        self.assertEqual(delete.mock_calls[-1], call(fip))
546

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

    
563
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
564
    def test_disassoc_floating_ip_to_server(self, spost):
565
        vmid, addr = 42, 'anIpAddress'
566
        for err, args in {
567
                ValueError: ['not a server id', addr],
568
                TypeError: [None, addr],
569
                AssertionError: [vmid, None],
570
                AssertionError: [vmid, '']}.items():
571
            self.assertRaises(
572
                err, self.client.disassoc_floating_ip_to_server, *args)
573
        r = self.client.disassoc_floating_ip_to_server(vmid, addr)
574
        self.assert_dicts_are_equal(r, FR.headers)
575
        expected = dict(removeFloatingIp=dict(address=addr))
576
        self.assertEqual(
577
            spost.mock_calls[-1], call(vmid, 'action', json_data=expected))
578

    
579

    
580
if __name__ == '__main__':
581
    from sys import argv
582
    from kamaki.clients.test import runTestCase
583
    not_found = True
584
    if not argv[1:] or argv[1] == 'CycladesClient':
585
        not_found = False
586
        runTestCase(CycladesClient, 'Cyclades Client', argv[2:])
587
    if not argv[1:] or argv[1] == 'CycladesRestClient':
588
        not_found = False
589
        runTestCase(CycladesRestClient, 'CycladesRest Client', argv[2:])
590
    if not_found:
591
        print('TestCase %s not found' % argv[1])