Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / cyclades / test.py @ 68fd1bda

History | View | Annotate | Download (14.2 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
from mock import patch, call
34
from unittest import TestCase
35
from itertools import product
36

    
37
from kamaki.clients import ClientError
38
from kamaki.clients.cyclades import CycladesClient, CycladesClientApi
39

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

    
81

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

    
90
    def release(self):
91
        pass
92

    
93
rest_pkg = 'kamaki.clients.cyclades.CycladesClientApi'
94
cyclades_pkg = 'kamaki.clients.cyclades.CycladesClient'
95

    
96

    
97
class CycladesRestApi(TestCase):
98

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

    
105
    def tearDown(self):
106
        FR.json = vm_recv
107

    
108
    @patch('%s.set_param' % rest_pkg)
109
    @patch('%s.get' % rest_pkg, return_value=FR())
110
    def _test_get(self, service, get, SP):
111
        for args in product(
112
                ('', '%s_id' % service),
113
                ('', 'cmd'),
114
                (200, 204),
115
                (None, '50m3-d473'),
116
                ({}, {'k': 'v'})):
117
            (srv_id, command, success, changes_since, kwargs) = args
118
            method = getattr(self.client, '%s_get' % service)
119
            method(*args[:4], **kwargs)
120
            srv_str = '/%s' % srv_id if srv_id else ''
121
            cmd_str = '/%s' % command if command else ''
122
            self.assertEqual(get.mock_calls[-1], call(
123
                '/%s%s%s' % (service, srv_str, cmd_str),
124
                success=success,
125
                **kwargs))
126
            if changes_since:
127
                self.assertEqual(
128
                    SP.mock_calls[-1],
129
                    call('changes-since', changes_since, changes_since))
130

    
131
    def test_servers_get(self):
132
        self._test_get('servers')
133

    
134

    
135
class Cyclades(TestCase):
136

    
137
    def assert_dicts_are_equal(self, d1, d2):
138
        for k, v in d1.items():
139
            self.assertTrue(k in d2)
140
            if isinstance(v, dict):
141
                self.assert_dicts_are_equal(v, d2[k])
142
            else:
143
                self.assertEqual(unicode(v), unicode(d2[k]))
144

    
145
    """Set up a Cyclades thorough test"""
146
    def setUp(self):
147
        self.url = 'http://cyclades.example.com'
148
        self.token = 'cyc14d3s70k3n'
149
        self.client = CycladesClient(self.url, self.token)
150

    
151
    def tearDown(self):
152
        FR.status_code = 200
153
        FR.json = vm_recv
154

    
155
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
156
    def test_list_servers(self, SG):
157
        FR.json = vm_list
158
        for detail, since in ((0, 0), (True, 0), (0, 'd473'), (True, 'd473')):
159
            r = self.client.list_servers(detail=detail, changes_since=since)
160
            self.assertEqual(SG.mock_calls[-1], call(
161
                command='detail' if detail else '',
162
                changes_since=since))
163
            expected = vm_list['servers']['values']
164
            for i, vm in enumerate(r):
165
                self.assert_dicts_are_equal(vm, expected[i])
166
            self.assertEqual(i + 1, len(expected))
167

    
168
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
169
    def test_shutdown_server(self, SP):
170
        vm_id = vm_recv['server']['id']
171
        self.client.shutdown_server(vm_id)
172
        SP.assert_called_once_with(
173
            vm_id, 'action',
174
            json_data=dict(shutdown=dict()), success=202)
175

    
176
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
177
    def test_start_server(self, SP):
178
        vm_id = vm_recv['server']['id']
179
        self.client.start_server(vm_id)
180
        SP.assert_called_once_with(
181
            vm_id, 'action',
182
            json_data=dict(start=dict()), success=202)
183

    
184
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
185
    def test_get_server_console(self, SP):
186
        cnsl = dict(console=dict(info1='i1', info2='i2', info3='i3'))
187
        FR.json = cnsl
188
        vm_id = vm_recv['server']['id']
189
        r = self.client.get_server_console(vm_id)
190
        SP.assert_called_once_with(
191
            vm_id, 'action',
192
            json_data=dict(console=dict(type='vnc')), success=200)
193
        self.assert_dicts_are_equal(r, cnsl['console'])
194

    
195
    def test_get_firewall_profile(self):
196
        vm_id = vm_recv['server']['id']
197
        v = firewalls['attachments']['values'][0]['firewallProfile']
198
        with patch.object(
199
                CycladesClient, 'get_server_details',
200
                return_value=firewalls) as GSD:
201
            r = self.client.get_firewall_profile(vm_id)
202
            GSD.assert_called_once_with(vm_id)
203
            self.assertEqual(r, v)
204
        with patch.object(
205
                CycladesClient, 'get_server_details',
206
                return_value=dict()):
207
            self.assertRaises(
208
                ClientError,
209
                self.client.get_firewall_profile,
210
                vm_id)
211

    
212
    @patch('%s.servers_post' % cyclades_pkg, return_value=FR())
213
    def test_set_firewall_profile(self, SP):
214
        vm_id = vm_recv['server']['id']
215
        v = firewalls['attachments']['values'][0]['firewallProfile']
216
        self.client.set_firewall_profile(vm_id, v)
217
        SP.assert_called_once_with(
218
            vm_id, 'action',
219
            json_data=dict(firewallProfile=dict(profile=v)), success=202)
220

    
221
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
222
    def test_get_server_stats(self, SG):
223
        vm_id = vm_recv['server']['id']
224
        stats = dict(stat1='v1', stat2='v2', stat3='v3', stat4='v4')
225
        FR.json = dict(stats=stats)
226
        r = self.client.get_server_stats(vm_id)
227
        SG.assert_called_once_with(vm_id, 'stats')
228
        self.assert_dicts_are_equal(stats, r)
229

    
230
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
231
    def test_create_network(self, NP):
232
        net_name = net_send['network']['name']
233
        FR.json = net_recv
234
        full_args = dict(
235
                cidr='192.168.0.0/24',
236
                gateway='192.168.0.1',
237
                type='MAC_FILTERED',
238
                dhcp=True)
239
        test_args = dict(full_args)
240
        test_args.update(dict(empty=None, full=None))
241
        net_exp = dict(dhcp=False, name=net_name)
242
        for arg, val in test_args.items():
243
            kwargs = {} if arg == 'empty' else full_args if (
244
                arg == 'full') else {arg: val}
245
            expected = dict(network=dict(net_exp))
246
            expected['network'].update(kwargs)
247
            r = self.client.create_network(net_name, **kwargs)
248
            self.assertEqual(
249
                NP.mock_calls[-1],
250
                call(json_data=expected, success=202))
251
            self.assert_dicts_are_equal(r, net_recv['network'])
252

    
253
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
254
    def test_connect_server(self, NP):
255
        vm_id = vm_recv['server']['id']
256
        net_id = net_recv['network']['id']
257
        self.client.connect_server(vm_id, net_id)
258
        NP.assert_called_once_with(
259
            net_id, 'action',
260
            json_data=dict(add=dict(serverRef=vm_id)))
261

    
262
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
263
    def test_disconnect_server(self, NP):
264
        net_id, vm_id = net_recv['network']['id'], vm_recv['server']['id']
265
        nic_id = 'nic-%s-%s' % (net_id, vm_id)
266
        vm_nics = [
267
            dict(id=nic_id, network_id=net_id),
268
            dict(id='another-nic-id', network_id='another-net-id'),
269
            dict(id=nic_id * 2, network_id=net_id * 2)]
270
        with patch.object(
271
                CycladesClient,
272
                'list_server_nics',
273
                return_value=vm_nics) as LSN:
274
            r = self.client.disconnect_server(vm_id, nic_id)
275
            LSN.assert_called_once_with(vm_id)
276
            NP.assert_called_once_with(
277
                net_id, 'action',
278
                json_data=dict(remove=dict(attachment=nic_id)))
279
            self.assertEqual(r, 1)
280

    
281
    @patch('%s.servers_get' % cyclades_pkg, return_value=FR())
282
    def test_list_server_nics(self, SG):
283
        vm_id = vm_recv['server']['id']
284
        nics = dict(addresses=dict(values=[dict(id='nic1'), dict(id='nic2')]))
285
        FR.json = nics
286
        r = self.client.list_server_nics(vm_id)
287
        SG.assert_called_once_with(vm_id, 'ips')
288
        expected = nics['addresses']['values']
289
        for i in range(len(r)):
290
            self.assert_dicts_are_equal(r[i], expected[i])
291
        self.assertEqual(i + 1, len(r))
292

    
293
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
294
    def test_list_networks(self, NG):
295
        FR.json = net_list
296
        expected = net_list['networks']['values']
297
        for detail in ('', 'detail'):
298
            r = self.client.list_networks(detail=True if detail else False)
299
            self.assertEqual(NG.mock_calls[-1], call(command=detail))
300
            for i, net in enumerate(expected):
301
                self.assert_dicts_are_equal(r[i], net)
302
            self.assertEqual(i + 1, len(r))
303

    
304
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
305
    def test_list_network_nics(self, NG):
306
        net_id = net_recv['network']['id']
307
        FR.json = net_recv
308
        r = self.client.list_network_nics(net_id)
309
        NG.assert_called_once_with(network_id=net_id)
310
        expected = net_recv['network']['attachments']['values']
311
        for i in range(len(r)):
312
            self.assert_dicts_are_equal(r[i], expected[i])
313

    
314
    @patch('%s.networks_post' % cyclades_pkg, return_value=FR())
315
    def test_disconnect_network_nics(self, NP):
316
        net_id = net_recv['network']['id']
317
        nics = ['nic1', 'nic2', 'nic3']
318
        with patch.object(
319
                CycladesClient,
320
                'list_network_nics',
321
                return_value=nics) as LNN:
322
            self.client.disconnect_network_nics(net_id)
323
            LNN.assert_called_once_with(net_id)
324
            for i in range(len(nics)):
325
                expected = call(net_id, 'action', json_data=dict(
326
                    remove=dict(attachment=nics[i])))
327
                self.assertEqual(expected, NP.mock_calls[i])
328

    
329
    @patch('%s.networks_get' % cyclades_pkg, return_value=FR())
330
    def test_get_network_details(self, NG):
331
        FR.json = net_recv
332
        net_id = net_recv['network']['id']
333
        r = self.client.get_network_details(net_id)
334
        NG.assert_called_once_with(network_id=net_id)
335
        self.assert_dicts_are_equal(r, net_recv['network'])
336

    
337
    @patch('%s.networks_put' % cyclades_pkg, return_value=FR())
338
    def test_update_network_name(self, NP):
339
        net_id = net_recv['network']['id']
340
        new_name = '%s_new' % net_id
341
        self.client.update_network_name(net_id, new_name)
342
        NP.assert_called_once_with(
343
            network_id=net_id,
344
            json_data=dict(network=dict(name=new_name)))
345

    
346
    def test_delete_network(self):
347
        net_id = net_recv['network']['id']
348
        with patch.object(
349
                CycladesClient, 'networks_delete',
350
                return_value=FR()) as ND:
351
            self.client.delete_network(net_id)
352
            ND.assert_called_once_with(net_id)
353
        with patch.object(
354
                CycladesClient, 'networks_delete',
355
                side_effect=ClientError('A 421 Error', 421)):
356
            try:
357
                self.client.delete_network(421)
358
            except ClientError as err:
359
                self.assertEqual(err.status, 421)
360
                self.assertEqual(err.details, [
361
                    'Network may be still connected to at least one server'])
362

    
363

    
364
if __name__ == '__main__':
365
    from sys import argv
366
    from kamaki.clients.test import runTestCase
367
    not_found = True
368
    if not argv[1:] or argv[1] == 'Cyclades':
369
        not_found = False
370
        runTestCase(Cyclades, 'Cyclades Client', argv[2:])
371
    if not argv[1:] or argv[1] == 'CycladesRestApi':
372
        not_found = False
373
        runTestCase(CycladesRestApi, 'CycladesRestApi Client', argv[2:])
374
    if not_found:
375
        print('TestCase %s not found' % argv[1])