Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / image / test.py @ 85898ca4

History | View | Annotate | Download (12.5 kB)

1
# Copyright 2012-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
35
from unittest import TestCase
36

    
37
from kamaki.clients import ClientError
38

    
39
example_images = [
40
    {
41
        "status": "available",
42
        "name": "Archlinux",
43
        "disk_format": "diskdump",
44
        "container_format": "bare",
45
        "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
46
        "size": 752782848},
47
    {
48
        "status": "available",
49
        "name": "maelstrom",
50
        "disk_format": "diskdump",
51
        "container_format": "bare",
52
        "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
53
        "size": 2583195644},
54
    {
55
        "status": "available",
56
        "name": "Gardenia",
57
        "disk_format": "diskdump",
58
        "container_format": "bare",
59
        "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
60
        "size": 2589802496}]
61

    
62
example_images_detailed = [
63
    {
64
        "status": "available",
65
        "name": "Archlinux",
66
        "checksum": "1a126aad07475b43cc1959b446344211be13974",
67
        "created_at": "2013-01-28 22:44:54",
68
        "disk_format": "diskdump",
69
        "updated_at": "2013-01-28 22:44:55",
70
        "properties": {
71
            "partition_table": "msdos",
72
            "osfamily": "linux",
73
            "users": "root",
74
            "exclude_task_assignhostname": "yes",
75
            "os": "archlinux",
76
            "root_partition": "1",
77
            "description": "Archlinux base install 2012.12.01"},
78
        "location": "pithos://us3r-I6E-1d/images/archlinux.12.2012",
79
        "container_format": "bare",
80
        "owner": "user163@mail.example.com",
81
        "is_public": True,
82
        "deleted_at": "",
83
        "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
84
        "size": 752782848},
85
    {
86
        "status": "available",
87
        "name": "maelstrom",
88
        "checksum": "b202b8c7030cb22f896c6664ac",
89
        "created_at": "2013-02-13 10:07:42",
90
        "disk_format": "diskdump",
91
        "updated_at": "2013-02-13 10:07:44",
92
        "properties": {
93
            "partition_table": "msdos",
94
            "osfamily": "linux",
95
            "description": "Ubuntu 12.04.1 LTS",
96
            "os": "ubuntu",
97
            "root_partition": "1",
98
            "users": "user"},
99
        "location": "pithos://us3r-@r3n@-1d/images/mls-201302131203.diskdump",
100
        "container_format": "bare",
101
        "owner": "user3@mail.example.com",
102
        "is_public": True,
103
        "deleted_at": "",
104
        "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
105
        "size": 2583195648},
106
    {
107
        "status": "available",
108
        "name": "Gardenia",
109
        "checksum": "06d3099815d1f6fada91e80107638b882",
110
        "created_at": "2013-02-13 12:35:21",
111
        "disk_format": "diskdump",
112
        "updated_at": "2013-02-13 12:35:23",
113
        "properties": {
114
            "partition_table": "msdos",
115
            "osfamily": "linux",
116
            "description": "Ubuntu 12.04.2 LTS",
117
            "os": "ubuntu",
118
            "root_partition": "1",
119
            "users": "user"},
120
        "location": "pithos://us3r-E-1d/images/Gardenia-201302131431.diskdump",
121
        "container_format": "bare",
122
        "owner": "user3@mail.example.com",
123
        "is_public": True,
124
        "deleted_at": "",
125
        "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
126
        "size": 2589802496}]
127

    
128

    
129
class FR(object):
130
    json = example_images
131
    headers = {}
132
    content = json
133
    status = None
134
    status_code = 200
135

    
136
    def release(self):
137
        pass
138

    
139
khttp = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
140

    
141

    
142
class Image(TestCase):
143

    
144
    def assert_dicts_are_deeply_equal(self, d1, d2):
145
        for k, v in d1.items():
146
            self.assertTrue(k in d2)
147
            if isinstance(v, dict):
148
                self.assert_dicts_are_deeply_equal(v, d2[k])
149
            else:
150
                self.assertEqual(unicode(v), unicode(d2[k]))
151

    
152
    def setUp(self):
153
        self.url = 'http://image.example.com'
154
        self.token = 'an1m@g370k3n=='
155
        from kamaki.clients.image import ImageClient
156
        self.client = ImageClient(self.url, self.token)
157
        from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
158
        self.C = KamakiHTTPConnection
159

    
160
    def tearDown(self):
161
        FR.json = example_images
162
        FR.status_code = 200
163

    
164
    @patch('%s.perform_request' % khttp, return_value=FR())
165
    def test_list_public(self, PR):
166
        r = self.client.list_public()
167
        self.assertEqual(self.client.http_client.url, self.url)
168
        self.assertEqual(self.client.http_client.path, '/images/')
169
        params = PR.call_args[0][3]
170
        self.assertEqual(params['sort_dir'], 'asc')
171
        for i in range(len(r)):
172
            self.assert_dicts_are_deeply_equal(r[i], example_images[i])
173

    
174
        r = self.client.list_public(order='-')
175
        params = PR.call_args[0][3]
176
        self.assertEqual(params['sort_dir'], 'desc')
177
        self.assertEqual(self.client.http_client.url, self.url)
178
        self.assertEqual(self.client.http_client.path, '/images/')
179

    
180
        FR.json = example_images_detailed
181
        r = self.client.list_public(detail=True)
182
        self.assertEqual(self.client.http_client.url, self.url)
183
        self.assertEqual(self.client.http_client.path, '/images/detail')
184
        for i in range(len(r)):
185
            self.assert_dicts_are_deeply_equal(
186
                r[i],
187
                example_images_detailed[i])
188

    
189
        size_max = 1000000000
190
        r = self.client.list_public(filters=dict(size_max=size_max))
191
        params = PR.call_args[0][3]
192
        self.assertEqual(params['size_max'], size_max)
193
        self.assertEqual(self.client.http_client.url, self.url)
194
        self.assertEqual(self.client.http_client.path, '/images/')
195

    
196
    @patch('%s.perform_request' % khttp, return_value=FR())
197
    def test_get_meta(self, PR):
198
        img0 = example_images[0]
199
        FR.json = img0
200
        img0_headers = {}
201
        for k, v in example_images_detailed[0].items():
202
            img0_headers['x-image-meta-%s' % k] = v
203
        FR.headers = img0_headers
204
        r = self.client.get_meta(img0['id'])
205
        self.assertEqual(self.client.http_client.url, self.url)
206
        expected_path = '/images/%s' % img0['id']
207
        self.assertEqual(self.client.http_client.path, expected_path)
208

    
209
        self.assertEqual(r['id'], img0['id'])
210
        self.assert_dicts_are_deeply_equal(r, example_images_detailed[0])
211

    
212
    @patch('%s.perform_request' % khttp, return_value=FR())
213
    def test_register(self, PR):
214
        img0 = example_images_detailed[0]
215
        img0_location = img0['location']
216
        img0_name = 'A new img0 name'
217
        self.client.register(img0_name, img0_location)
218
        self.assertEqual(self.client.http_client.url, self.url)
219
        self.assertEqual(self.client.http_client.path, '/images/')
220
        (method, data, headers, params) = PR.call_args[0]
221
        self.assertEqual(method, 'post')
222
        self.assertTrue(0 == len(params))
223

    
224
        val = 'Some random value'
225
        param_dict = dict(
226
            id=val,
227
            store=val,
228
            disk_format=val,
229
            container_format=val,
230
            size=val,
231
            checksum=val,
232
            is_public=val,
233
            owner=val)
234
        for key in param_dict.keys():
235
            param = {key: val}
236
            self.client.register(img0_name, img0_location, params=param)
237
            (method, data, a_headers, a_params) = PR.call_args[0]
238
            key = 'x-image-meta-%s' % key.replace('_', '-')
239
            self.assertEqual(a_headers[key], val)
240
        self.client.register(img0_name, img0_location, params=param_dict)
241
        (method, data, a_headers, a_params) = PR.call_args[0]
242
        self.assertEqual(len(param_dict), len(a_headers))
243
        for key, val in param_dict.items():
244
            key = 'x-image-meta-%s' % key.replace('_', '-')
245
            self.assertEqual(a_headers[key], val)
246

    
247
        props = dict(key0='val0', key2='val2', key3='val3')
248
        self.client.register(img0_name, img0_location, properties=props)
249
        (method, data, a_headers, a_params) = PR.call_args[0]
250
        for k, v in props.items():
251
            self.assertEquals(a_headers['X-Image-Meta-Property-%s' % k], v)
252

    
253
    @patch('%s.perform_request' % khttp, return_value=FR())
254
    def test_set_members(self, PR):
255
        img0 = example_images_detailed[0]
256
        members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
257
        self.assertRaises(
258
            ClientError,
259
            self.client.set_members,
260
            img0['id'], members)
261
        FR.status_code = 204
262
        self.client.set_members(img0['id'], members)
263
        self.assertEqual(self.client.http_client.url, self.url)
264
        self.assertEqual(
265
                self.client.http_client.path,
266
            '/images/%s/members' % img0['id'])
267
        (method, data, a_headers, a_params) = PR.call_args[0]
268
        from json import loads
269
        memberships = loads(data)['memberships']
270
        for membership in memberships:
271
            self.assertTrue(membership['member_id'] in members)
272

    
273
    @patch('%s.perform_request' % khttp, return_value=FR())
274
    def test_list_members(self, PR):
275
        img0 = example_images_detailed[0]
276
        members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
277
        FR.json = dict(members=members)
278
        r = self.client.list_members(img0['id'])
279
        self.assertEqual(self.client.http_client.url, self.url)
280
        self.assertEqual(
281
            self.client.http_client.path,
282
            '/images/%s/members' % img0['id'])
283
        self.assertEqual(r, members)
284

    
285
    @patch('%s.perform_request' % khttp, return_value=FR())
286
    def test_add_member(self, PR):
287
        img0 = example_images_detailed[0]
288
        new_member = 'us3r-15-n3w'
289
        self.assertRaises(
290
            ClientError,
291
            self.client.add_member,
292
            img0['id'], new_member)
293
        FR.status_code = 204
294
        self.client.add_member(img0['id'], new_member)
295
        self.assertEqual(self.client.http_client.url, self.url)
296
        self.assertEqual(
297
            self.client.http_client.path,
298
            '/images/%s/members/%s' % (img0['id'], new_member))
299

    
300
    @patch('%s.perform_request' % khttp, return_value=FR())
301
    def test_remove_member(self, PR):
302
        img0 = example_images_detailed[0]
303
        old_member = 'us3r-15-0ld'
304
        self.assertRaises(
305
            ClientError,
306
            self.client.remove_member,
307
            img0['id'], old_member)
308
        FR.status_code = 204
309
        self.client.remove_member(img0['id'], old_member)
310
        self.assertEqual(self.client.http_client.url, self.url)
311
        self.assertEqual(
312
            self.client.http_client.path,
313
            '/images/%s/members/%s' % (img0['id'], old_member))
314

    
315
    @patch('%s.perform_request' % khttp, return_value=FR())
316
    def test_list_shared(self, PR):
317
        img0 = example_images_detailed[0]
318
        FR.json = dict(shared_images=example_images)
319
        r = self.client.list_shared(img0['id'])
320
        self.assertEqual(self.client.http_client.url, self.url)
321
        self.assertEqual(
322
            self.client.http_client.path,
323
            '/shared-images/%s' % img0['id'])
324
        for i in range(len(r)):
325
            self.assert_dicts_are_deeply_equal(r[i], example_images[i])
326

    
327
if __name__ == '__main__':
328
    from sys import argv
329
    from kamaki.clients.test import runTestCase
330
    runTestCase(Image, 'Plankton Client', argv[1:])