Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ 0651cda3

History | View | Annotate | Download (14.8 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, Mock, call
35
from unittest import TestCase
36
from json import loads
37

    
38
from kamaki.clients import Client, ClientError
39
from kamaki.clients.cyclades import CycladesClient
40
from kamaki.clients.cyclades_rest_api import CycladesClientApi
41

    
42

    
43
compute_pkg_pkg = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
44
compute_pkg = 'kamaki.clients.cyclades.CycladesClient'
45

    
46
img_ref = "1m4g3-r3f3r3nc3"
47
vm_name = "my new VM"
48
fid = 42
49
vm_send = dict(server=dict(
50
    flavorRef=fid,
51
    name=vm_name,
52
    imageRef=img_ref,
53
    metadata=dict(os="debian", users="root")))
54
vm_recv = dict(server=dict(
55
    status="BUILD",
56
    updated="2013-03-01T10:04:00.637152+00:00",
57
    hostId="",
58
    name=vm_name,
59
    imageRef=img_ref,
60
    created="2013-03-01T10:04:00.087324+00:00",
61
    flavorRef=fid,
62
    adminPass="n0n3sh@11p@55",
63
    suspended=False,
64
    progress=0,
65
    id=31173,
66
    metadata=dict(values=dict(os="debian", users="root"))))
67
img_recv = dict(image=dict(
68
    status="ACTIVE",
69
    updated="2013-02-26T11:10:14+00:00",
70
    name="Debian Base",
71
    created="2013-02-26T11:03:29+00:00",
72
    progress=100,
73
    id=img_ref,
74
    metadata=dict(values=dict(
75
        partition_table="msdos",
76
        kernel="2.6.32",
77
        osfamily="linux",
78
        users="root",
79
        gui="No GUI",
80
        sortorder="1",
81
        os="debian",
82
        root_partition="1",
83
        description="Debian 6.0.7 (Squeeze) Base System"))))
84
vm_list = dict(servers=dict(values=[
85
    dict(name='n1', id=1),
86
    dict(name='n2', id=2)]))
87
flavor_list = dict(flavors=dict(values=[
88
        dict(id=41, name="C1R1024D20"),
89
        dict(id=42, name="C1R1024D40"),
90
        dict(id=43, name="C1R1028D20")]))
91
img_list = dict(images=dict(values=[
92
    dict(name="maelstrom", id="0fb03e45-7d5a-4515-bd4e-e6bbf6457f06"),
93
    dict(name="edx_saas", id="1357163d-5fd8-488e-a117-48734c526206"),
94
    dict(name="Debian_Wheezy_Base", id="1f8454f0-8e3e-4b6c-ab8e-5236b728dffe"),
95
    dict(name="CentOS", id="21894b48-c805-4568-ac8b-7d4bb8eb533d"),
96
    dict(name="Ubuntu Desktop", id="37bc522c-c479-4085-bfb9-464f9b9e2e31"),
97
    dict(name="Ubuntu 12.10", id="3a24fef9-1a8c-47d1-8f11-e07bd5e544fd"),
98
    dict(name="Debian Base", id="40ace203-6254-4e17-a5cb-518d55418a7d"),
99
    dict(name="ubuntu_bundled", id="5336e265-5c7c-4127-95cb-2bf832a79903")]))
100
net_send = dict(network=dict(dhcp=False, name='someNet'))
101
net_recv = dict(network=dict(
102
    status="PENDING",
103
    updated="2013-03-05T15:04:51.758780+00:00",
104
    name="someNet",
105
    created="2013-03-05T15:04:51.758728+00:00",
106
    cidr6=None,
107
    id="2130",
108
    gateway6=None,
109
    public=False,
110
    dhcp=False,
111
    cidr="192.168.1.0/24",
112
    type="MAC_FILTERED",
113
    gateway=None,
114
    attachments=dict(values=[dict(name='att1'), dict(name='att2')])))
115
net_list = dict(networks=dict(values=[
116
    dict(id=1, name='n1'),
117
    dict(id=2, name='n2'),
118
    dict(id=3, name='n3')]))
119

    
120

    
121
class FR(object):
122
    """FR stands for Fake Response"""
123
    json = vm_recv
124
    headers = {}
125
    content = json
126
    status = None
127
    status_code = 200
128

    
129
    def release(self):
130
        pass
131

    
132

    
133
class Cyclades(TestCase):
134

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

    
143
    """Set up a Cyclades thorough test"""
144
    def setUp(self):
145
        self.url = 'http://cyclades.example.com'
146
        self.token = 'cyc14d3s70k3n'
147
        self.client = CycladesClient(self.url, self.token)
148
        from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
149
        self.C = KamakiHTTPConnection
150

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

    
155
    @patch(
156
        '%s.get_image_details' % compute_pkg,
157
        return_value=img_recv['image'])
158
    def test_create_server(self, GID):
159
        with patch.object(
160
                CycladesClient, 'servers_post',
161
                side_effect=ClientError(
162
                    'REQUEST ENTITY TOO LARGE',
163
                    status=403)):
164
            self.assertRaises(
165
                ClientError,
166
                self.client.create_server,
167
                vm_name, fid, img_ref)
168
        self.assertEqual(GID.mock_calls[-1], call(img_ref))
169

    
170
        with patch.object(
171
                CycladesClient, 'servers_post',
172
                return_value=FR()) as post:
173
            r = self.client.create_server(vm_name, fid, img_ref)
174
            self.assertEqual(r, FR.json['server'])
175
            self.assertEqual(GID.mock_calls[-1], call(img_ref))
176
            self.assertEqual(post.mock_calls[-1], call(json_data=vm_send))
177
            prsn = 'Personality string (does not work with real servers)'
178
            self.client.create_server(vm_name, fid, img_ref, prsn)
179
            expected = dict(server=dict(vm_send['server']))
180
            expected['server']['personality'] = prsn
181
            self.assertEqual(post.mock_calls[-1], call(json_data=expected))
182

    
183
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
184
    def test_list_servers(self, SG):
185
        FR.json = vm_list
186
        for detail in (False, True):
187
            r = self.client.list_servers(detail)
188
            for i, vm in enumerate(vm_list['servers']['values']):
189
                self.assert_dicts_are_equal(r[i], vm)
190
            self.assertEqual(i + 1, len(r))
191
            self.assertEqual(SG.mock_calls[-1], call(
192
                changes_since=None,
193
                command='detail' if detail else ''))
194

    
195
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
196
    def test_get_server_details(self, SG):
197
        vm_id = vm_recv['server']['id']
198
        r = self.client.get_server_details(vm_id)
199
        self.assert_dicts_are_equal(r, vm_recv['server'])
200
        self.assertEqual(SG.mock_calls[-1], call(vm_id))
201

    
202
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
203
    def test_update_server_name(self, SP):
204
        vm_id = vm_recv['server']['id']
205
        new_name = vm_name + '_new'
206
        self.client.update_server_name(vm_id, new_name)
207
        self.assertEqual(SP.mock_calls[-1], call(vm_id, json_data=dict(
208
            server=dict(name=new_name))))
209

    
210
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
211
    def test_reboot_server(self, SP):
212
        vm_id = vm_recv['server']['id']
213
        for hard in (None, True):
214
            self.client.reboot_server(vm_id, hard=hard)
215
            self.assertEqual(SP.mock_calls[-1], call(
216
                vm_id, 'action',
217
                json_data=dict(reboot=dict(type='HARD' if hard else 'SOFT'))))
218

    
219
    @patch('%s.servers_put' % compute_pkg, return_value=FR())
220
    def test_create_server_metadata(self, SP):
221
        vm_id = vm_recv['server']['id']
222
        metadata = dict(m1='v1', m2='v2', m3='v3')
223
        FR.json = dict(meta=vm_recv['server'])
224
        for k, v in metadata.items():
225
            r = self.client.create_server_metadata(vm_id, k, v)
226
            self.assert_dicts_are_equal(r, vm_recv['server'])
227
            self.assertEqual(SP.mock_calls[-1], call(
228
                vm_id, 'meta/%s' % k,
229
                json_data=dict(meta={k: v}), success=201))
230

    
231
    @patch('%s.servers_get' % compute_pkg, return_value=FR())
232
    def test_get_server_metadata(self, SG):
233
        vm_id = vm_recv['server']['id']
234
        metadata = dict(m1='v1', m2='v2', m3='v3')
235
        FR.json = dict(metadata=dict(values=metadata))
236
        r = self.client.get_server_metadata(vm_id)
237
        self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta'))
238
        self.assert_dicts_are_equal(r, metadata)
239

    
240
        for k, v in metadata.items():
241
            FR.json = dict(meta={k: v})
242
            r = self.client.get_server_metadata(vm_id, k)
243
            self.assert_dicts_are_equal(r, {k: v})
244
            self.assertEqual(SG.mock_calls[-1], call(vm_id, '/meta/%s' % k))
245

    
246
    @patch('%s.servers_post' % compute_pkg, return_value=FR())
247
    def test_update_server_metadata(self, SP):
248
        vm_id = vm_recv['server']['id']
249
        metadata = dict(m1='v1', m2='v2', m3='v3')
250
        FR.json = dict(metadata=metadata)
251
        r = self.client.update_server_metadata(vm_id, **metadata)
252
        self.assert_dicts_are_equal(r, metadata)
253
        self.assertEqual(SP.mock_calls[-1], call(
254
            vm_id, 'meta',
255
            json_data=dict(metadata=metadata), success=201))
256

    
257
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
258
    def test_delete_server_metadata(self, SD):
259
        vm_id = vm_recv['server']['id']
260
        key = 'metakey'
261
        self.client.delete_server_metadata(vm_id, key)
262
        self.assertEqual(SD.mock_calls[-1], call(vm_id, 'meta/' + key))
263

    
264
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
265
    def test_list_flavors(self, FG):
266
        FR.json = flavor_list
267
        for cmd in ('', 'detail'):
268
            r = self.client.list_flavors(detail=(cmd == 'detail'))
269
            self.assertEqual(FG.mock_calls[-1], call(command=cmd))
270
            self.assert_dicts_are_equal(dict(values=r), flavor_list['flavors'])
271

    
272
    @patch('%s.flavors_get' % compute_pkg, return_value=FR())
273
    def test_get_flavor_details(self, FG):
274
        FR.json = dict(flavor=flavor_list['flavors'])
275
        r = self.client.get_flavor_details(fid)
276
        self.assertEqual(FG.mock_calls[-1], call(fid))
277
        self.assert_dicts_are_equal(r, flavor_list['flavors'])
278

    
279
    @patch('%s.images_get' % compute_pkg, return_value=FR())
280
    def test_list_images(self, IG):
281
        FR.json = img_list
282
        for cmd in ('', 'detail'):
283
            r = self.client.list_images(detail=(cmd == 'detail'))
284
            self.assertEqual(IG.mock_calls[-1], call(command=cmd))
285
            expected = img_list['images']['values']
286
            for i in range(len(r)):
287
                self.assert_dicts_are_equal(expected[i], r[i])
288

    
289
    @patch('%s.images_get' % compute_pkg, return_value=FR())
290
    def test_get_image_details(self, IG):
291
        FR.json = img_recv
292
        r = self.client.get_image_details(img_ref)
293
        self.assertEqual(IG.mock_calls[-1], call(img_ref))
294
        self.assert_dicts_are_equal(r, img_recv['image'])
295

    
296
    @patch('%s.images_get' % compute_pkg, return_value=FR())
297
    def test_get_image_metadata(self, IG):
298
        FR.json = dict(metadata=dict(values=img_recv['image']))
299
        r = self.client.get_image_metadata(img_ref)
300
        self.assertEqual(IG.mock_calls[-1], call('%s' % img_ref, '/meta'))
301
        self.assert_dicts_are_equal(img_recv['image'], r)
302
        FR.json = dict(meta=img_recv['image'])
303
        key = 'somekey'
304
        r = self.client.get_image_metadata(img_ref, key)
305
        self.assertEqual(
306
            IG.mock_calls[-1],
307
            call('%s' % img_ref, '/meta/%s' % key))
308
        self.assert_dicts_are_equal(img_recv['image'], r)
309

    
310
    @patch('%s.servers_delete' % compute_pkg, return_value=FR())
311
    def test_delete_server(self, SD):
312
        vm_id = vm_recv['server']['id']
313
        self.client.delete_server(vm_id)
314
        self.assertEqual(SD.mock_calls[-1], call(vm_id))
315

    
316
    """
317
    @patch('%s.perform_request' % compute_pkg, return_value=FR())
318
    def test_delete_image(self, PR):
319
        FR.status_code = 204
320
        self.client.delete_image(img_ref)
321
        self.assertEqual(self.client.http_client.url, self.url)
322
        self.assertEqual(self.client.http_client.path, '/images/%s' % img_ref)
323

324
    @patch('%s.perform_request' % compute_pkg, return_value=FR())
325
    def test_delete_network(self, PR):
326
        net_id = net_recv['network']['id']
327
        FR.status_code = 204
328
        self.client.delete_network(net_id)
329
        self.assertEqual(self.client.http_client.url, self.url)
330
        self.assertEqual(self.client.http_client.path, '/networks/%s' % net_id)
331

332
    @patch('%s.perform_request' % compute_pkg, return_value=FR())
333
    def test_create_image_metadata(self, PR):
334
        metadata = dict(m1='v1', m2='v2', m3='v3')
335
        FR.json = dict(meta=img_recv['image'])
336
        self.assertRaises(
337
            ClientError,
338
            self.client.create_image_metadata,
339
            img_ref, 'key', 'value')
340
        FR.status_code = 201
341
        for k, v in metadata.items():
342
            r = self.client.create_image_metadata(img_ref, k, v)
343
            self.assertEqual(self.client.http_client.url, self.url)
344
            self.assertEqual(
345
                self.client.http_client.path,
346
                '/images/%s/meta/%s' % (img_ref, k))
347
            (method, data, a_headers, a_params) = PR.call_args[0]
348
            self.assertEqual(dict(meta={k: v}), loads(data))
349
            self.assert_dicts_are_equal(r, img_recv['image'])
350

351
    @patch('%s.images_post' % compute_pkg, return_value=FR())
352
    def test_update_image_metadata(self, images_post):
353
        metadata = dict(m1='v1', m2='v2', m3='v3')
354
        FR.json = dict(metadata=metadata)
355
        r = self.client.update_image_metadata(img_ref, **metadata)
356
        self.assert_dicts_are_equal(r, metadata)
357
        (called_id, cmd) = images_post.call_args[0]
358
        self.assertEqual(called_id, img_ref)
359
        self.assertEqual(cmd, 'meta')
360
        data = images_post.call_args[1]['json_data']
361
        self.assert_dicts_are_equal(data, dict(metadata=metadata))
362

363
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
364
    def test_delete_image_metadata(self, images_delete):
365
        key = 'metakey'
366
        self.client.delete_image_metadata(img_ref, key)
367
        self.assertEqual(
368
            (img_ref, '/meta/' + key),
369
            images_delete.call_args[0])
370
    """
371

    
372
if __name__ == '__main__':
373
    from sys import argv
374
    from kamaki.clients.test import runTestCase
375
    runTestCase(Cyclades, 'Cyclades (multi) Client', argv[1:])