Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / test.py @ f711008c

History | View | Annotate | Download (14.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, 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
    @patch('%s.images_delete' % compute_pkg, return_value=FR())
317
    def test_delete_image(self, ID):
318
        self.client.delete_image(img_ref)
319
        self.assertEqual(ID.mock_calls[-1], call(img_ref))
320

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

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

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

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

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