blob: 2df3f7f29c28197eee856504c81030cddf9f5ff4 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Jay Pipes50677282012-01-06 15:39:20 -05002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import cStringIO as StringIO
Jay Pipes50677282012-01-06 15:39:20 -050017
Sean Dague1937d092013-05-17 16:36:38 -040018from tempest.api.image import base
ivan-zhud1bbe5d2013-12-29 18:32:46 +080019from tempest.common.utils import data_utils
Matthew Treinishbc0e03e2014-01-30 16:51:06 +000020from tempest import config
ivan-zhud1bbe5d2013-12-29 18:32:46 +080021from tempest import test
Jay Pipes50677282012-01-06 15:39:20 -050022
Matthew Treinishbc0e03e2014-01-30 16:51:06 +000023CONF = config.CONF
24
Jay Pipes50677282012-01-06 15:39:20 -050025
Matthew Treinishce3ef922013-03-11 14:02:46 -040026class CreateRegisterImagesTest(base.BaseV1ImageTest):
27 """Here we test the registration and creation of images."""
Jay Pipes50677282012-01-06 15:39:20 -050028
ivan-zhud1bbe5d2013-12-29 18:32:46 +080029 @test.attr(type='gate')
Jay Pipes50677282012-01-06 15:39:20 -050030 def test_register_then_upload(self):
Sean Dague46c4a2b2013-01-03 17:54:17 -050031 # Register, then upload an image
Matthew Treinish72ea4422013-02-07 14:42:49 -050032 properties = {'prop1': 'val1'}
Matthew Treinishce3ef922013-03-11 14:02:46 -040033 resp, body = self.create_image(name='New Name',
34 container_format='bare',
35 disk_format='raw',
Aaron Rosenc7720622014-05-20 10:38:10 -070036 is_public=False,
Matthew Treinishce3ef922013-03-11 14:02:46 -040037 properties=properties)
Attila Fazekase191cb12013-07-29 06:41:52 +020038 self.assertIn('id', body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050039 image_id = body.get('id')
Matthew Treinish72ea4422013-02-07 14:42:49 -050040 self.assertEqual('New Name', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070041 self.assertFalse(body.get('is_public'))
Matthew Treinish72ea4422013-02-07 14:42:49 -050042 self.assertEqual('queued', body.get('status'))
Matthew Treinish72ea4422013-02-07 14:42:49 -050043 for key, val in properties.items():
44 self.assertEqual(val, body.get('properties')[key])
Jay Pipes50677282012-01-06 15:39:20 -050045
46 # Now try uploading an image file
Matthew Treinish72ea4422013-02-07 14:42:49 -050047 image_file = StringIO.StringIO(('*' * 1024))
48 resp, body = self.client.update_image(image_id, data=image_file)
Attila Fazekase191cb12013-07-29 06:41:52 +020049 self.assertIn('size', body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050050 self.assertEqual(1024, body.get('size'))
Jay Pipes50677282012-01-06 15:39:20 -050051
ivan-zhud1bbe5d2013-12-29 18:32:46 +080052 @test.attr(type='gate')
Jay Pipes50677282012-01-06 15:39:20 -050053 def test_register_remote_image(self):
Sean Dague46c4a2b2013-01-03 17:54:17 -050054 # Register a new remote image
Matthew Treinishce3ef922013-03-11 14:02:46 -040055 resp, body = self.create_image(name='New Remote Image',
56 container_format='bare',
Aaron Rosenc7720622014-05-20 10:38:10 -070057 disk_format='raw', is_public=False,
Matthew Treinishce3ef922013-03-11 14:02:46 -040058 location='http://example.com'
afazekas4a96bf82013-03-25 16:07:38 +010059 '/someimage.iso',
60 properties={'key1': 'value1',
61 'key2': 'value2'})
Attila Fazekase191cb12013-07-29 06:41:52 +020062 self.assertIn('id', body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050063 self.assertEqual('New Remote Image', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070064 self.assertFalse(body.get('is_public'))
Matthew Treinish72ea4422013-02-07 14:42:49 -050065 self.assertEqual('active', body.get('status'))
afazekas4a96bf82013-03-25 16:07:38 +010066 properties = body.get('properties')
67 self.assertEqual(properties['key1'], 'value1')
68 self.assertEqual(properties['key2'], 'value2')
Jay Pipes50677282012-01-06 15:39:20 -050069
ivan-zhud1bbe5d2013-12-29 18:32:46 +080070 @test.attr(type='gate')
Attila Fazekase72b7cd2013-03-26 18:34:21 +010071 def test_register_http_image(self):
Attila Fazekase72b7cd2013-03-26 18:34:21 +010072 resp, body = self.create_image(name='New Http Image',
73 container_format='bare',
Aaron Rosenc7720622014-05-20 10:38:10 -070074 disk_format='raw', is_public=False,
Matt Riedemannd3efe902014-02-10 06:46:38 -080075 copy_from=CONF.image.http_image)
Attila Fazekase191cb12013-07-29 06:41:52 +020076 self.assertIn('id', body)
Attila Fazekase72b7cd2013-03-26 18:34:21 +010077 image_id = body.get('id')
Attila Fazekase72b7cd2013-03-26 18:34:21 +010078 self.assertEqual('New Http Image', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070079 self.assertFalse(body.get('is_public'))
Attila Fazekase72b7cd2013-03-26 18:34:21 +010080 self.client.wait_for_image_status(image_id, 'active')
81 resp, body = self.client.get_image(image_id)
82 self.assertEqual(resp['status'], '200')
Attila Fazekase72b7cd2013-03-26 18:34:21 +010083
ivan-zhud1bbe5d2013-12-29 18:32:46 +080084 @test.attr(type='gate')
hi2suresh75a20302013-04-09 09:17:06 +000085 def test_register_image_with_min_ram(self):
86 # Register an image with min ram
87 properties = {'prop1': 'val1'}
88 resp, body = self.create_image(name='New_image_with_min_ram',
89 container_format='bare',
90 disk_format='raw',
Aaron Rosenc7720622014-05-20 10:38:10 -070091 is_public=False,
hi2suresh75a20302013-04-09 09:17:06 +000092 min_ram=40,
93 properties=properties)
Attila Fazekase191cb12013-07-29 06:41:52 +020094 self.assertIn('id', body)
hi2suresh75a20302013-04-09 09:17:06 +000095 self.assertEqual('New_image_with_min_ram', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070096 self.assertFalse(body.get('is_public'))
hi2suresh75a20302013-04-09 09:17:06 +000097 self.assertEqual('queued', body.get('status'))
98 self.assertEqual(40, body.get('min_ram'))
99 for key, val in properties.items():
100 self.assertEqual(val, body.get('properties')[key])
ivan-zhu3867a1c2013-10-11 14:16:58 +0800101 resp, body = self.client.delete_image(body['id'])
102 self.assertEqual('200', resp['status'])
hi2suresh75a20302013-04-09 09:17:06 +0000103
Jay Pipes50677282012-01-06 15:39:20 -0500104
Matthew Treinishce3ef922013-03-11 14:02:46 -0400105class ListImagesTest(base.BaseV1ImageTest):
Jay Pipes50677282012-01-06 15:39:20 -0500106
107 """
108 Here we test the listing of image information
109 """
110
111 @classmethod
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500112 @test.safe_setup
Jay Pipes50677282012-01-06 15:39:20 -0500113 def setUpClass(cls):
Matthew Treinishce3ef922013-03-11 14:02:46 -0400114 super(ListImagesTest, cls).setUpClass()
Jay Pipes50677282012-01-06 15:39:20 -0500115 # We add a few images here to test the listing functionality of
116 # the images API
Attila Fazekas11795b52013-02-24 15:49:08 +0100117 img1 = cls._create_remote_image('one', 'bare', 'raw')
118 img2 = cls._create_remote_image('two', 'ami', 'ami')
119 img3 = cls._create_remote_image('dup', 'bare', 'raw')
120 img4 = cls._create_remote_image('dup', 'bare', 'raw')
121 img5 = cls._create_standard_image('1', 'ami', 'ami', 42)
122 img6 = cls._create_standard_image('2', 'ami', 'ami', 142)
123 img7 = cls._create_standard_image('33', 'bare', 'raw', 142)
124 img8 = cls._create_standard_image('33', 'bare', 'raw', 142)
125 cls.created_set = set(cls.created_images)
126 # 4x-4x remote image
127 cls.remote_set = set((img1, img2, img3, img4))
128 cls.standard_set = set((img5, img6, img7, img8))
129 # 5x bare, 3x ami
130 cls.bare_set = set((img1, img3, img4, img7, img8))
131 cls.ami_set = set((img2, img5, img6))
132 # 1x with size 42
133 cls.size42_set = set((img5,))
134 # 3x with size 142
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800135 cls.size142_set = set((img6, img7, img8,))
Attila Fazekas11795b52013-02-24 15:49:08 +0100136 # dup named
137 cls.dup_set = set((img3, img4))
Jay Pipes50677282012-01-06 15:39:20 -0500138
139 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100140 def _create_remote_image(cls, name, container_format, disk_format):
Jay Pipes50677282012-01-06 15:39:20 -0500141 """
142 Create a new remote image and return the ID of the newly-registered
143 image
144 """
Attila Fazekas11795b52013-02-24 15:49:08 +0100145 name = 'New Remote Image %s' % name
146 location = 'http://example.com/someimage_%s.iso' % name
Matthew Treinishce3ef922013-03-11 14:02:46 -0400147 resp, image = cls.create_image(name=name,
148 container_format=container_format,
149 disk_format=disk_format,
Aaron Rosenc7720622014-05-20 10:38:10 -0700150 is_public=False,
Matthew Treinishce3ef922013-03-11 14:02:46 -0400151 location=location)
Attila Fazekas11795b52013-02-24 15:49:08 +0100152 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500153 return image_id
154
155 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100156 def _create_standard_image(cls, name, container_format,
157 disk_format, size):
Jay Pipes50677282012-01-06 15:39:20 -0500158 """
159 Create a new standard image and return the ID of the newly-registered
160 image. Note that the size of the new image is a random number between
161 1024 and 4096
162 """
Attila Fazekas11795b52013-02-24 15:49:08 +0100163 image_file = StringIO.StringIO('*' * size)
164 name = 'New Standard Image %s' % name
Matthew Treinishce3ef922013-03-11 14:02:46 -0400165 resp, image = cls.create_image(name=name,
166 container_format=container_format,
167 disk_format=disk_format,
Aaron Rosenc7720622014-05-20 10:38:10 -0700168 is_public=False, data=image_file)
Attila Fazekas11795b52013-02-24 15:49:08 +0100169 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500170 return image_id
171
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800172 @test.attr(type='gate')
Jay Pipes50677282012-01-06 15:39:20 -0500173 def test_index_no_params(self):
Sean Dague46c4a2b2013-01-03 17:54:17 -0500174 # Simple test to see all fixture images returned
Matthew Treinish72ea4422013-02-07 14:42:49 -0500175 resp, images_list = self.client.image_list()
176 self.assertEqual(resp['status'], '200')
177 image_list = map(lambda x: x['id'], images_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100178 for image_id in self.created_images:
Attila Fazekase191cb12013-07-29 06:41:52 +0200179 self.assertIn(image_id, image_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100180
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800181 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100182 def test_index_disk_format(self):
183 resp, images_list = self.client.image_list(disk_format='ami')
184 self.assertEqual(resp['status'], '200')
185 for image in images_list:
186 self.assertEqual(image['disk_format'], 'ami')
187 result_set = set(map(lambda x: x['id'], images_list))
188 self.assertTrue(self.ami_set <= result_set)
189 self.assertFalse(self.created_set - self.ami_set <= result_set)
190
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800191 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100192 def test_index_container_format(self):
193 resp, images_list = self.client.image_list(container_format='bare')
194 self.assertEqual(resp['status'], '200')
195 for image in images_list:
196 self.assertEqual(image['container_format'], 'bare')
197 result_set = set(map(lambda x: x['id'], images_list))
198 self.assertTrue(self.bare_set <= result_set)
199 self.assertFalse(self.created_set - self.bare_set <= result_set)
200
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800201 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100202 def test_index_max_size(self):
203 resp, images_list = self.client.image_list(size_max=42)
204 self.assertEqual(resp['status'], '200')
205 for image in images_list:
206 self.assertTrue(image['size'] <= 42)
207 result_set = set(map(lambda x: x['id'], images_list))
208 self.assertTrue(self.size42_set <= result_set)
209 self.assertFalse(self.created_set - self.size42_set <= result_set)
210
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800211 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100212 def test_index_min_size(self):
213 resp, images_list = self.client.image_list(size_min=142)
214 self.assertEqual(resp['status'], '200')
215 for image in images_list:
216 self.assertTrue(image['size'] >= 142)
217 result_set = set(map(lambda x: x['id'], images_list))
218 self.assertTrue(self.size142_set <= result_set)
219 self.assertFalse(self.size42_set <= result_set)
220
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800221 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100222 def test_index_status_active_detail(self):
223 resp, images_list = self.client.image_list_detail(status='active',
224 sort_key='size',
225 sort_dir='desc')
226 self.assertEqual(resp['status'], '200')
227 top_size = images_list[0]['size'] # We have non-zero sized images
228 for image in images_list:
229 size = image['size']
230 self.assertTrue(size <= top_size)
231 top_size = size
232 self.assertEqual(image['status'], 'active')
233
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800234 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100235 def test_index_name(self):
236 resp, images_list = self.client.image_list_detail(
Sean Dague14c68182013-04-14 15:34:30 -0400237 name='New Remote Image dup')
Attila Fazekas11795b52013-02-24 15:49:08 +0100238 self.assertEqual(resp['status'], '200')
239 result_set = set(map(lambda x: x['id'], images_list))
240 for image in images_list:
241 self.assertEqual(image['name'], 'New Remote Image dup')
242 self.assertTrue(self.dup_set <= result_set)
243 self.assertFalse(self.created_set - self.dup_set <= result_set)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800244
245
246class ListSnapshotImagesTest(base.BaseV1ImageTest):
247 @classmethod
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500248 @test.safe_setup
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800249 def setUpClass(cls):
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800250 # This test class only uses nova v3 api to create snapshot
251 # as the similar test which uses nova v2 api already exists
252 # in nova v2 compute images api tests.
253 # Since nova v3 doesn't have images api proxy, this test
254 # class was added in the image api tests.
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800255 if not CONF.compute_feature_enabled.api_v3:
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800256 skip_msg = ("%s skipped as nova v3 api is not available" %
257 cls.__name__)
258 raise cls.skipException(skip_msg)
259 super(ListSnapshotImagesTest, cls).setUpClass()
260 cls.servers_client = cls.os.servers_v3_client
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800261 cls.servers = []
262 # We add a few images here to test the listing functionality of
263 # the images API
264 cls.snapshot = cls._create_snapshot(
265 'snapshot', CONF.compute.image_ref,
266 CONF.compute.flavor_ref)
267 cls.snapshot_set = set((cls.snapshot,))
268
269 image_file = StringIO.StringIO('*' * 42)
270 resp, image = cls.create_image(name="Standard Image",
271 container_format='ami',
272 disk_format='ami',
Aaron Rosenc7720622014-05-20 10:38:10 -0700273 is_public=False, data=image_file)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800274 cls.image_id = image['id']
275 cls.client.wait_for_image_status(image['id'], 'active')
276
277 @classmethod
278 def tearDownClass(cls):
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500279 for server in getattr(cls, "servers", []):
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800280 cls.servers_client.delete_server(server['id'])
281 super(ListSnapshotImagesTest, cls).tearDownClass()
282
283 @classmethod
284 def _create_snapshot(cls, name, image_id, flavor, **kwargs):
285 resp, server = cls.servers_client.create_server(
286 name, image_id, flavor, **kwargs)
287 cls.servers.append(server)
288 cls.servers_client.wait_for_server_status(
289 server['id'], 'ACTIVE')
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800290 resp, _ = cls.servers_client.create_image(server['id'], name)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800291 image_id = data_utils.parse_image_id(resp['location'])
292 cls.created_images.append(image_id)
293 cls.client.wait_for_image_status(image_id,
294 'active')
295 return image_id
296
297 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400298 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800299 def test_index_server_id(self):
300 # The images should contain images filtered by server id
301 resp, images = self.client.image_list_detail(
302 {'instance_uuid': self.servers[0]['id']})
303 self.assertEqual(200, resp.status)
304 result_set = set(map(lambda x: x['id'], images))
305 self.assertEqual(self.snapshot_set, result_set)
306
307 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400308 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800309 def test_index_type(self):
310 # The list of servers should be filtered by image type
311 params = {'image_type': 'snapshot'}
312 resp, images = self.client.image_list_detail(params)
313
314 self.assertEqual(200, resp.status)
315 result_set = set(map(lambda x: x['id'], images))
316 self.assertIn(self.snapshot, result_set)
317
318 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400319 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800320 def test_index_limit(self):
321 # Verify only the expected number of results are returned
322 resp, images = self.client.image_list_detail(limit=1)
323
324 self.assertEqual(200, resp.status)
325 self.assertEqual(1, len(images))
326
327 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400328 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800329 def test_index_by_change_since(self):
330 # Verify an update image is returned
331 # Becoming ACTIVE will modify the updated time
332 # Filter by the image's created time
333 resp, image = self.client.get_image_meta(self.snapshot)
334 self.assertEqual(200, resp.status)
335 self.assertEqual(self.snapshot, image['id'])
336 resp, images = self.client.image_list_detail(
337 changes_since=image['updated_at'])
338
339 self.assertEqual(200, resp.status)
340 result_set = set(map(lambda x: x['id'], images))
341 self.assertIn(self.image_id, result_set)
342 self.assertNotIn(self.snapshot, result_set)
343
344
345class UpdateImageMetaTest(base.BaseV1ImageTest):
346 @classmethod
347 def setUpClass(cls):
348 super(UpdateImageMetaTest, cls).setUpClass()
349 cls.image_id = cls._create_standard_image('1', 'ami', 'ami', 42)
350
351 @classmethod
352 def _create_standard_image(cls, name, container_format,
353 disk_format, size):
354 """
355 Create a new standard image and return the ID of the newly-registered
356 image. Note that the size of the new image is a random number between
357 1024 and 4096
358 """
359 image_file = StringIO.StringIO('*' * size)
360 name = 'New Standard Image %s' % name
361 resp, image = cls.create_image(name=name,
362 container_format=container_format,
363 disk_format=disk_format,
Aaron Rosenc7720622014-05-20 10:38:10 -0700364 is_public=False, data=image_file,
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800365 properties={'key1': 'value1'})
366 image_id = image['id']
367 return image_id
368
369 @test.attr(type='gate')
370 def test_list_image_metadata(self):
371 # All metadata key/value pairs for an image should be returned
372 resp, resp_metadata = self.client.get_image_meta(self.image_id)
373 expected = {'key1': 'value1'}
374 self.assertEqual(expected, resp_metadata['properties'])
375
376 @test.attr(type='gate')
377 def test_update_image_metadata(self):
378 # The metadata for the image should match the updated values
379 req_metadata = {'key1': 'alt1', 'key2': 'value2'}
380 resp, metadata = self.client.get_image_meta(self.image_id)
381 self.assertEqual(200, resp.status)
382 self.assertEqual(metadata['properties'], {'key1': 'value1'})
383 metadata['properties'].update(req_metadata)
384 resp, metadata = self.client.update_image(
385 self.image_id, properties=metadata['properties'])
386
387 resp, resp_metadata = self.client.get_image_meta(self.image_id)
388 expected = {'key1': 'alt1', 'key2': 'value2'}
389 self.assertEqual(expected, resp_metadata['properties'])