61dbb10cfc0ba016e8d19731c9e8819ff0625164
[kamaki] / kamaki / clients / test / image.py
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 import time
36
37 from unittest import TestCase
38 from kamaki.clients import ClientError
39
40 example_images = [
41     {
42         "status": "available",
43         "name": "Archlinux",
44         "disk_format": "diskdump",
45         "container_format": "bare",
46         "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
47         "size": 752782848},
48     {
49         "status": "available",
50         "name": "Debian_Wheezy_Base",
51         "disk_format": "diskdump",
52         "container_format": "bare",
53         "id": "1f8454f0-8e3e-4b6c-ab8e-5236b728dffe",
54         "size": 795107328},
55     {
56         "status": "available",
57         "name": "maelstrom",
58         "disk_format": "diskdump",
59         "container_format": "bare",
60         "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
61         "size": 2583195644},
62     {
63         "status": "available",
64         "name": "Gardenia",
65         "disk_format": "diskdump",
66         "container_format": "bare",
67         "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
68         "size": 2589802496}]
69
70 example_images_detailed = [
71     {
72         "status": "available",
73         "name": "Archlinux",
74         "checksum": "1a126aad07475b43cc1959b446344211be13974",
75         "created_at": "2013-01-28 22:44:54",
76         "disk_format": "diskdump",
77         "updated_at": "2013-01-28 22:44:55",
78         "properties": {
79             "partition_table": "msdos",
80             "osfamily": "linux",
81             "users": "root",
82             "exclude_task_assignhostname": "yes",
83             "os": "archlinux",
84             "root_partition": "1",
85             "description": "Archlinux base install 2012.12.01"},
86         "location": "pithos://us3r-I6E-1d/images/archlinux.12.2012",
87         "container_format": "bare",
88         "owner": "user163@mail.example.com",
89         "is_public": True,
90         "deleted_at": "",
91         "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
92         "size": 752782848},
93     {
94         "status": "available",
95         "name": "Debian_Wheezy_Base",
96         "checksum": "8f96e73ba8886a05de6f9b3705c981",
97         "created_at": "2013-01-29 16:41:13",
98         "disk_format": "diskdump",
99         "updated_at": "2013-01-29 16:41:14",
100         "properties": {
101             "partition_table": "msdos",
102             "osfamily": "linux",
103             "users": "root",
104             "swap": "5:259",
105             "os": "debian",
106             "root_partition": "1",
107             "description": "Debian7.0(Wheezy)Base"},
108         "location": "pithos://us3r-EO2-1d/images/Deb_Whz201301291840.diskdump",
109         "container_format": "bare",
110         "owner": "user302@mail.example.com",
111         "is_public": True,
112         "deleted_at": "",
113         "id": "1f8454f0-8e3e-4b6c-ab8e-5236b728dffe",
114         "size": 795107328},
115     {
116         "status": "available",
117         "name": "maelstrom",
118         "checksum": "b202b8c7030cb22f896c6664ac",
119         "created_at": "2013-02-13 10:07:42",
120         "disk_format": "diskdump",
121         "updated_at": "2013-02-13 10:07:44",
122         "properties": {
123             "partition_table": "msdos",
124             "osfamily": "linux",
125             "description": "Ubuntu 12.04.1 LTS",
126             "os": "ubuntu",
127             "root_partition": "1",
128             "users": "user"},
129         "location": "pithos://us3r-@r3n@-1d/images/mls-201302131203.diskdump",
130         "container_format": "bare",
131         "owner": "user3@mail.example.com",
132         "is_public": True,
133         "deleted_at": "",
134         "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
135         "size": 2583195648},
136     {
137         "status": "available",
138         "name": "Gardenia",
139         "checksum": "06d3099815d1f6fada91e80107638b882",
140         "created_at": "2013-02-13 12:35:21",
141         "disk_format": "diskdump",
142         "updated_at": "2013-02-13 12:35:23",
143         "properties": {
144             "partition_table": "msdos",
145             "osfamily": "linux",
146             "description": "Ubuntu 12.04.2 LTS",
147             "os": "ubuntu",
148             "root_partition": "1",
149             "users": "user"},
150         "location": "pithos://us3r-E-1d/images/Gardenia-201302131431.diskdump",
151         "container_format": "bare",
152         "owner": "user3@mail.example.com",
153         "is_public": True,
154         "deleted_at": "",
155         "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
156         "size": 2589802496}]
157
158
159 class Image(TestCase):
160
161     class FR(object):
162         json = example_images
163         headers = {}
164         content = json
165         status = None
166         status_code = 200
167
168         def release(self):
169             pass
170
171     def setUp(self):
172         self.now = time.mktime(time.gmtime())
173         self.imgname = 'img_%s' % self.now
174         self.url = 'http://image.example.com'
175         self.token = 'an1m@g370k3n=='
176         from kamaki.clients.image import ImageClient
177         self.client = ImageClient(self.url, self.token)
178         self.cyclades_url = 'http://cyclades.example.com'
179         from kamaki.clients.cyclades import CycladesClient
180         self.cyclades = CycladesClient(self.cyclades_url, self.token)
181         from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
182         self.C = KamakiHTTPConnection
183
184     def tearDown(self):
185         self.FR.json = example_images
186
187     def assert_dicts_are_deeply_equal(self, d1, d2):
188         for k, v in d1.items():
189             self.assertTrue(k in d2)
190             if isinstance(v, dict):
191                 self.assert_dicts_are_deeply_equal(v, d2[k])
192             else:
193                 self.assertEqual(unicode(v), unicode(d2[k]))
194
195     def test_list_public(self):
196         with patch.object(
197             self.C,
198             'perform_request',
199             return_value=self.FR()) as perform_req:
200             r = self.client.list_public()
201             self.assertEqual(self.client.http_client.url, self.url)
202             self.assertEqual(self.client.http_client.path, '/images/')
203             params = perform_req.call_args[0][3]
204             self.assertEqual(params['sort_dir'], 'asc')
205             for i in range(len(r)):
206                 self.assert_dicts_are_deeply_equal(r[i], example_images[i])
207
208             r = self.client.list_public(order='-')
209             params = perform_req.call_args[0][3]
210             self.assertEqual(params['sort_dir'], 'desc')
211             self.assertEqual(self.client.http_client.url, self.url)
212             self.assertEqual(self.client.http_client.path, '/images/')
213
214             self.FR.json = example_images_detailed
215             r = self.client.list_public(detail=True)
216             self.assertEqual(self.client.http_client.url, self.url)
217             self.assertEqual(self.client.http_client.path, '/images/detail')
218             for i in range(len(r)):
219                 self.assert_dicts_are_deeply_equal(
220                     r[i],
221                     example_images_detailed[i])
222
223             size_max = 1000000000
224             r = self.client.list_public(filters=dict(size_max=size_max))
225             params = perform_req.call_args[0][3]
226             self.assertEqual(params['size_max'], size_max)
227             self.assertEqual(self.client.http_client.url, self.url)
228             self.assertEqual(self.client.http_client.path, '/images/')
229
230     def test_get_meta(self):
231         img0 = example_images[0]
232         self.FR.json = img0
233         img0_headers = {}
234         for k, v in example_images_detailed[0].items():
235             img0_headers['x-image-meta-%s' % k] = v
236         self.FR.headers = img0_headers
237         with patch.object(self.C, 'perform_request', return_value=self.FR()):
238             r = self.client.get_meta(img0['id'])
239             self.assertEqual(self.client.http_client.url, self.url)
240             expected_path = '/images/%s' % img0['id']
241             self.assertEqual(self.client.http_client.path, expected_path)
242
243             self.assertEqual(r['id'], img0['id'])
244             self.assert_dicts_are_deeply_equal(r, example_images_detailed[0])
245
246     def test_register(self):
247         img0 = example_images_detailed[0]
248         img0_location = img0['location']
249         img0_name = 'A new img0 name'
250         with patch.object(
251                 self.C,
252                 'perform_request',
253                 return_value=self.FR()) as perform_req:
254             self.client.register(img0_name, img0_location)
255             self.assertEqual(self.client.http_client.url, self.url)
256             self.assertEqual(self.client.http_client.path, '/images/')
257             (method, data, headers, params) = perform_req.call_args[0]
258             self.assertEqual(method, 'post')
259             self.assertTrue(0 == len(params))
260
261             val = 'Some random value'
262             param_dict = dict(
263                 id=val,
264                 store=val,
265                 disk_format=val,
266                 container_format=val,
267                 size=val,
268                 checksum=val,
269                 is_public=val,
270                 owner=val)
271             for key in param_dict.keys():
272                 param = {key: val}
273                 self.client.register(img0_name, img0_location, params=param)
274                 (method, data, a_headers, a_params) = perform_req.call_args[0]
275                 key = 'x-image-meta-%s' % key.replace('_', '-')
276                 self.assertEqual(a_headers[key], val)
277             self.client.register(img0_name, img0_location, params=param_dict)
278             (method, data, a_headers, a_params) = perform_req.call_args[0]
279             self.assertEqual(len(param_dict), len(a_headers))
280             for key, val in param_dict.items():
281                 key = 'x-image-meta-%s' % key.replace('_', '-')
282                 self.assertEqual(a_headers[key], val)
283
284             props = dict(key0='val0', key2='val2', key3='val3')
285             self.client.register(img0_name, img0_location, properties=props)
286             (method, data, a_headers, a_params) = perform_req.call_args[0]
287             for k, v in props.items():
288                 self.assertEquals(a_headers['X-Image-Meta-Property-%s' % k], v)
289
290     """
291     def test_set_members(self):
292         ""Test set_members""
293         self._prepare_img()
294         self._test_set_members()
295
296     def _test_set_members(self):
297         members = ['%s@fake.net' % self.now]
298         for img in self._imglist.values():
299             self.client.set_members(img['id'], members)
300             r = self.client.list_members(img['id'])
301             self.assertEqual(r[0]['member_id'], members[0])
302
303     def test_list_members(self):
304         ""Test list_members""
305         self._test_list_members()
306
307     def _test_list_members(self):
308         self._test_set_members()
309
310     def test_remove_members(self):
311         ""Test remove_members - NO CHECK""
312         self._prepare_img()
313         self._test_remove_members()
314
315     def _test_remove_members(self):
316         return
317         members = ['%s@fake.net' % self.now, '%s_v2@fake.net' % self.now]
318         for img in self._imglist.values():
319             self.client.set_members(img['id'], members)
320             r = self.client.list_members(img['id'])
321             self.assertTrue(len(r) > 1)
322             self.client.remove_member(img['id'], members[0])
323             r0 = self.client.list_members(img['id'])
324             self.assertEqual(len(r), 1 + len(r0))
325             self.assertEqual(r0[0]['member_id'], members[1])
326
327     def test_list_shared(self):
328         ""Test list_shared - NOT CHECKED""
329         self._test_list_shared()
330
331     def _test_list_shared(self):
332         #No way to test this, if I dont have member images
333         pass
334     """