blob: 8dba311bae840cea9fb0ca8c803e1b37c18801b4 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2013 OpenStack Foundation
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +05302# Copyright 2013 IBM Corp
Matthew Treinisha62347f2013-03-01 16:37:30 -05003# All Rights Reserved.
Matthew Treinisha62347f2013-03-01 16:37:30 -05004#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
songwenping8c3dac12021-02-22 09:12:37 +080017import io
Matthew Treinisha62347f2013-03-01 16:37:30 -050018import random
19
Takashi NATSUME12a48512015-08-10 18:33:16 +090020from oslo_log import log as logging
Sean Dague1937d092013-05-17 16:36:38 -040021from tempest.api.image import base
PranaliD491d63e2020-08-18 13:29:21 +000022from tempest.common import waiters
Takashi NATSUME12a48512015-08-10 18:33:16 +090023from tempest import config
Ken'ichi Ohmichicc01c3e2017-03-10 10:48:14 -080024from tempest.lib.common.utils import data_utils
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -080025from tempest.lib import decorators
lkuchlan32b53c32017-04-20 16:51:08 +030026from tempest.lib import exceptions as lib_exc
Matthew Treinisha62347f2013-03-01 16:37:30 -050027
Takashi NATSUME12a48512015-08-10 18:33:16 +090028CONF = config.CONF
29LOG = logging.getLogger(__name__)
30
Matthew Treinisha62347f2013-03-01 16:37:30 -050031
Abhishek Kekane7cff1302020-07-16 10:30:13 +000032class ImportImagesTest(base.BaseV2ImageTest):
33 """Here we test the import operations for image"""
34
35 @classmethod
36 def skip_checks(cls):
37 super(ImportImagesTest, cls).skip_checks()
38 if not CONF.image_feature_enabled.import_image:
39 skip_msg = (
40 "%s skipped as image import is not available" % cls.__name__)
41 raise cls.skipException(skip_msg)
42
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050043 @classmethod
44 def resource_setup(cls):
45 super(ImportImagesTest, cls).resource_setup()
46 cls.available_import_methods = cls.client.info_import()[
47 'import-methods']['value']
48 if not cls.available_import_methods:
49 raise cls.skipException('Server does not support '
50 'any import method')
Abhishek Kekane7cff1302020-07-16 10:30:13 +000051
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050052 def _create_image(self):
Abhishek Kekane7cff1302020-07-16 10:30:13 +000053 # Create image
54 uuid = '00000000-1111-2222-3333-444455556666'
55 image_name = data_utils.rand_name('image')
56 container_format = CONF.image.container_formats[0]
57 disk_format = CONF.image.disk_formats[0]
58 image = self.create_image(name=image_name,
59 container_format=container_format,
60 disk_format=disk_format,
61 visibility='private',
62 ramdisk_id=uuid)
63 self.assertIn('name', image)
64 self.assertEqual(image_name, image['name'])
65 self.assertIn('visibility', image)
66 self.assertEqual('private', image['visibility'])
67 self.assertIn('status', image)
68 self.assertEqual('queued', image['status'])
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050069 return image
Abhishek Kekane7cff1302020-07-16 10:30:13 +000070
Dan Smithd4bc9a82021-01-12 17:25:07 -080071 def _require_import_method(self, method):
72 if method not in self.available_import_methods:
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050073 raise self.skipException('Server does not support '
Dan Smithd4bc9a82021-01-12 17:25:07 -080074 '%s import method' % method)
75
76 def _stage_and_check(self):
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050077 image = self._create_image()
Abhishek Kekane7cff1302020-07-16 10:30:13 +000078 # Stage image data
79 file_content = data_utils.random_bytes()
songwenping8c3dac12021-02-22 09:12:37 +080080 image_file = io.BytesIO(file_content)
Abhishek Kekane7cff1302020-07-16 10:30:13 +000081 self.client.stage_image_file(image['id'], image_file)
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050082 # Check image status is 'uploading'
83 body = self.client.show_image(image['id'])
84 self.assertEqual(image['id'], body['id'])
85 self.assertEqual('uploading', body['status'])
Dan Smithd4bc9a82021-01-12 17:25:07 -080086 return image['id']
87
88 @decorators.idempotent_id('32ca0c20-e16f-44ac-8590-07869c9b4cc2')
89 def test_image_glance_direct_import(self):
90 """Test 'glance-direct' import functionalities
91
92 Create image, stage image data, import image and verify
93 that import succeeded.
94 """
95 self._require_import_method('glance-direct')
96
97 image_id = self._stage_and_check()
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -050098 # import image from staging to backend
Dan Smithd4bc9a82021-01-12 17:25:07 -080099 self.client.image_import(image_id, method='glance-direct')
100 waiters.wait_for_image_imported_to_stores(self.client, image_id)
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000101
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -0500102 @decorators.idempotent_id('f6feb7a4-b04f-4706-a011-206129f83e62')
103 def test_image_web_download_import(self):
104 """Test 'web-download' import functionalities
105
106 Create image, import image and verify that import
107 succeeded.
108 """
Dan Smithd4bc9a82021-01-12 17:25:07 -0800109 self._require_import_method('web-download')
110
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -0500111 image = self._create_image()
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000112 # Now try to get image details
113 body = self.client.show_image(image['id'])
114 self.assertEqual(image['id'], body['id'])
Ghanshyam Mann1425ecd2020-07-22 21:01:26 -0500115 self.assertEqual('queued', body['status'])
116 # import image from web to backend
117 image_uri = CONF.image.http_image
118 self.client.image_import(image['id'], method='web-download',
119 image_uri=image_uri)
Dan Smithef8e0542021-02-05 13:05:45 -0800120 waiters.wait_for_image_imported_to_stores(self.client, image['id'])
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000121
Dan Smithd4bc9a82021-01-12 17:25:07 -0800122 @decorators.idempotent_id('e04761a1-22af-42c2-b8bc-a34a3f12b585')
123 def test_remote_import(self):
124 """Test image import against a different worker than stage.
125
126 This creates and stages an image against the primary API worker,
127 but then calls import on a secondary worker (if available) to
128 test that distributed image import works (i.e. proxies the import
129 request to the proper worker).
130 """
131 self._require_import_method('glance-direct')
132
133 if not CONF.image.alternate_image_endpoint:
134 raise self.skipException('No image_remote service to test '
135 'against')
136
137 image_id = self._stage_and_check()
138 # import image from staging to backend, but on the alternate worker
139 self.os_primary.image_client_remote.image_import(
140 image_id, method='glance-direct')
141 waiters.wait_for_image_imported_to_stores(self.client, image_id)
142
143 @decorators.idempotent_id('44d60544-1524-42f7-8899-315301105dd8')
144 def test_remote_delete(self):
145 """Test image delete against a different worker than stage.
146
147 This creates and stages an image against the primary API worker,
148 but then calls delete on a secondary worker (if available) to
149 test that distributed image import works (i.e. proxies the delete
150 request to the proper worker).
151 """
152 self._require_import_method('glance-direct')
153
154 if not CONF.image.alternate_image_endpoint:
155 raise self.skipException('No image_remote service to test '
156 'against')
157
158 image_id = self._stage_and_check()
159 # delete image from staging to backend, but on the alternate worker
160 self.os_primary.image_client_remote.delete_image(image_id)
161 self.client.wait_for_resource_deletion(image_id)
162
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000163
PranaliD491d63e2020-08-18 13:29:21 +0000164class MultiStoresImportImagesTest(base.BaseV2ImageTest):
165 """Test importing image in multiple stores"""
166 @classmethod
167 def skip_checks(cls):
168 super(MultiStoresImportImagesTest, cls).skip_checks()
169 if not CONF.image_feature_enabled.import_image:
170 skip_msg = (
171 "%s skipped as image import is not available" % cls.__name__)
172 raise cls.skipException(skip_msg)
173
174 @classmethod
175 def resource_setup(cls):
176 super(MultiStoresImportImagesTest, cls).resource_setup()
177 cls.available_import_methods = cls.client.info_import()[
178 'import-methods']['value']
179 if not cls.available_import_methods:
180 raise cls.skipException('Server does not support '
181 'any import method')
182
183 # NOTE(pdeore): Skip if glance-direct import method and mutlistore
184 # are not enabled/configured, or only one store is configured in
185 # multiple stores setup.
186 cls.available_stores = cls.get_available_stores()
187 if ('glance-direct' not in cls.available_import_methods or
188 not len(cls.available_stores) > 1):
189 raise cls.skipException(
190 'Either glance-direct import method not present in %s or '
191 'None or only one store is '
192 'configured %s' % (cls.available_import_methods,
193 cls.available_stores))
194
195 def _create_and_stage_image(self, all_stores=False):
196 """Create Image & stage image file for glance-direct import method."""
197 image_name = data_utils.rand_name('test-image')
198 container_format = CONF.image.container_formats[0]
199 disk_format = CONF.image.disk_formats[0]
200 image = self.create_image(name=image_name,
201 container_format=container_format,
202 disk_format=disk_format,
203 visibility='private')
204 self.assertEqual('queued', image['status'])
205
206 self.client.stage_image_file(
207 image['id'],
songwenping8c3dac12021-02-22 09:12:37 +0800208 io.BytesIO(data_utils.random_bytes()))
PranaliD491d63e2020-08-18 13:29:21 +0000209 # Check image status is 'uploading'
210 body = self.client.show_image(image['id'])
211 self.assertEqual(image['id'], body['id'])
212 self.assertEqual('uploading', body['status'])
213
214 if all_stores:
215 stores_list = ','.join([store['id']
216 for store in self.available_stores])
217 else:
218 stores = [store['id'] for store in self.available_stores]
219 stores_list = stores[::len(stores) - 1]
220
221 return body, stores_list
222
223 @decorators.idempotent_id('bf04ff00-3182-47cb-833a-f1c6767b47fd')
224 def test_glance_direct_import_image_to_all_stores(self):
225 """Test image is imported in all available stores
226
227 Create image, import image to all available stores using glance-direct
228 import method and verify that import succeeded.
229 """
230 image, stores = self._create_and_stage_image(all_stores=True)
231
232 self.client.image_import(
233 image['id'], method='glance-direct', all_stores=True)
234
235 waiters.wait_for_image_imported_to_stores(self.client,
236 image['id'], stores)
237
238 @decorators.idempotent_id('82fb131a-dd2b-11ea-aec7-340286b6c574')
239 def test_glance_direct_import_image_to_specific_stores(self):
240 """Test image is imported in all available stores
241
242 Create image, import image to specified store(s) using glance-direct
243 import method and verify that import succeeded.
244 """
245 image, stores = self._create_and_stage_image()
246 self.client.image_import(image['id'], method='glance-direct',
247 stores=stores)
248
249 waiters.wait_for_image_imported_to_stores(self.client, image['id'],
250 (','.join(stores)))
251
252
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530253class BasicOperationsImagesTest(base.BaseV2ImageTest):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000254 """Here we test the basic operations of images"""
Matthew Treinisha62347f2013-03-01 16:37:30 -0500255
Jordan Pittier3b46d272017-04-12 16:17:28 +0200256 @decorators.attr(type='smoke')
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800257 @decorators.idempotent_id('139b765e-7f3d-4b3d-8b37-3ca3876ee318')
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530258 def test_register_upload_get_image_file(self):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000259 """Here we test these functionalities
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530260
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000261 Register image, upload the image file, get image and get image
262 file api's
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530263 """
264
Sean Daguec6ec4762014-05-29 08:54:21 -0400265 uuid = '00000000-1111-2222-3333-444455556666'
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530266 image_name = data_utils.rand_name('image')
Takashi NATSUME12a48512015-08-10 18:33:16 +0900267 container_format = CONF.image.container_formats[0]
268 disk_format = CONF.image.disk_formats[0]
lkuchlanb3348792016-09-29 10:42:21 +0300269 image = self.create_image(name=image_name,
270 container_format=container_format,
271 disk_format=disk_format,
272 visibility='private',
273 ramdisk_id=uuid)
lkuchlanb3348792016-09-29 10:42:21 +0300274 self.assertIn('name', image)
275 self.assertEqual(image_name, image['name'])
276 self.assertIn('visibility', image)
277 self.assertEqual('private', image['visibility'])
278 self.assertIn('status', image)
279 self.assertEqual('queued', image['status'])
Matthew Treinisha62347f2013-03-01 16:37:30 -0500280
wangxiyuan99e4dcf2019-09-17 09:51:55 +0800281 # NOTE: This Glance API returns different status codes for image
282 # condition. In this empty data case, Glance should return 204,
283 # so here should check the status code.
284 image_file = self.client.show_image_file(image['id'])
285 self.assertEqual(0, len(image_file.data))
286 self.assertEqual(204, image_file.response.status)
287
Matthew Treinisha62347f2013-03-01 16:37:30 -0500288 # Now try uploading an image file
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -0700289 file_content = data_utils.random_bytes()
songwenping8c3dac12021-02-22 09:12:37 +0800290 image_file = io.BytesIO(file_content)
lkuchlanb3348792016-09-29 10:42:21 +0300291 self.client.store_image_file(image['id'], image_file)
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530292
293 # Now try to get image details
lkuchlanb3348792016-09-29 10:42:21 +0300294 body = self.client.show_image(image['id'])
295 self.assertEqual(image['id'], body['id'])
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530296 self.assertEqual(image_name, body['name'])
Sean Daguec6ec4762014-05-29 08:54:21 -0400297 self.assertEqual(uuid, body['ramdisk_id'])
Attila Fazekase191cb12013-07-29 06:41:52 +0200298 self.assertIn('size', body)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500299 self.assertEqual(1024, body.get('size'))
300
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530301 # Now try get image file
Ken'ichi Ohmichi3b79f172018-03-20 11:31:44 -0700302 # NOTE: This Glance API returns different status codes for image
303 # condition. In this non-empty data case, Glance should return 200,
304 # so here should check the status code.
lkuchlanb3348792016-09-29 10:42:21 +0300305 body = self.client.show_image_file(image['id'])
David Kranzd7e97b42015-02-16 09:37:31 -0500306 self.assertEqual(file_content, body.data)
Ken'ichi Ohmichi3b79f172018-03-20 11:31:44 -0700307 self.assertEqual(200, body.response.status)
Hoisaleshwara Madan V Se50a6f12013-10-23 18:01:01 +0530308
Jordan Pittier3b46d272017-04-12 16:17:28 +0200309 @decorators.attr(type='smoke')
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800310 @decorators.idempotent_id('f848bb94-1c6e-45a4-8726-39e3a5b23535')
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530311 def test_delete_image(self):
zhufle68f4352020-04-21 13:44:55 +0800312 """Test deleting an image by image_id"""
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530313 # Create image
314 image_name = data_utils.rand_name('image')
Takashi NATSUME12a48512015-08-10 18:33:16 +0900315 container_format = CONF.image.container_formats[0]
316 disk_format = CONF.image.disk_formats[0]
Benny Kopilov900fceb2016-11-09 09:45:40 +0200317 image = self.create_image(name=image_name,
318 container_format=container_format,
319 disk_format=disk_format,
320 visibility='private')
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530321 # Delete Image
lkuchlanb3348792016-09-29 10:42:21 +0300322 self.client.delete_image(image['id'])
323 self.client.wait_for_resource_deletion(image['id'])
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530324
325 # Verifying deletion
John Warrenf3b3e952015-08-17 19:28:12 +0000326 images = self.client.list_images()['images']
Sunil G29856a32014-07-17 23:17:58 +0530327 images_id = [item['id'] for item in images]
lkuchlanb3348792016-09-29 10:42:21 +0300328 self.assertNotIn(image['id'], images_id)
Hoisaleshwara Madan V S7cba1082013-11-26 12:43:04 +0530329
Jordan Pittier3b46d272017-04-12 16:17:28 +0200330 @decorators.attr(type='smoke')
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800331 @decorators.idempotent_id('f66891a7-a35c-41a8-b590-a065c2a1caa6')
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400332 def test_update_image(self):
zhufle68f4352020-04-21 13:44:55 +0800333 """Test updating an image by image_id"""
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400334 # Create image
335 image_name = data_utils.rand_name('image')
Takashi NATSUME12a48512015-08-10 18:33:16 +0900336 container_format = CONF.image.container_formats[0]
337 disk_format = CONF.image.disk_formats[0]
Benny Kopilov900fceb2016-11-09 09:45:40 +0200338 image = self.create_image(name=image_name,
339 container_format=container_format,
340 disk_format=disk_format,
341 visibility='private')
lkuchlanb3348792016-09-29 10:42:21 +0300342 self.assertEqual('queued', image['status'])
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400343
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400344 # Update Image
345 new_image_name = data_utils.rand_name('new-image')
zhufl311104e2017-08-17 15:13:18 +0800346 self.client.update_image(image['id'], [
Aaron Rosenc7720622014-05-20 10:38:10 -0700347 dict(replace='/name', value=new_image_name)])
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400348
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400349 # Verifying updating
350
lkuchlanb3348792016-09-29 10:42:21 +0300351 body = self.client.show_image(image['id'])
352 self.assertEqual(image['id'], body['id'])
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400353 self.assertEqual(new_image_name, body['name'])
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +0400354
lkuchlan32b53c32017-04-20 16:51:08 +0300355 @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
356 def test_deactivate_reactivate_image(self):
zhufle68f4352020-04-21 13:44:55 +0800357 """Test deactivating and reactivating an image"""
lkuchlan32b53c32017-04-20 16:51:08 +0300358 # Create image
359 image_name = data_utils.rand_name('image')
360 image = self.create_image(name=image_name,
361 container_format='bare',
362 disk_format='raw',
363 visibility='private')
364
365 # Upload an image file
366 content = data_utils.random_bytes()
songwenping8c3dac12021-02-22 09:12:37 +0800367 image_file = io.BytesIO(content)
lkuchlan32b53c32017-04-20 16:51:08 +0300368 self.client.store_image_file(image['id'], image_file)
369
370 # Deactivate image
371 self.client.deactivate_image(image['id'])
372 body = self.client.show_image(image['id'])
373 self.assertEqual("deactivated", body['status'])
374
375 # User unable to download deactivated image
376 self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
377 image['id'])
378
379 # Reactivate image
380 self.client.reactivate_image(image['id'])
381 body = self.client.show_image(image['id'])
382 self.assertEqual("active", body['status'])
383
384 # User able to download image after reactivation
385 body = self.client.show_image_file(image['id'])
386 self.assertEqual(content, body.data)
387
Matthew Treinisha62347f2013-03-01 16:37:30 -0500388
zhufl6e042bc2017-01-25 10:33:40 +0800389class ListUserImagesTest(base.BaseV2ImageTest):
390 """Here we test the listing of image information"""
Matthew Treinisha62347f2013-03-01 16:37:30 -0500391
392 @classmethod
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100393 def resource_setup(cls):
zhufl6e042bc2017-01-25 10:33:40 +0800394 super(ListUserImagesTest, cls).resource_setup()
Matthew Treinisha62347f2013-03-01 16:37:30 -0500395 # We add a few images here to test the listing functionality of
396 # the images API
Takashi NATSUME12a48512015-08-10 18:33:16 +0900397 container_fmts = CONF.image.container_formats
398 disk_fmts = CONF.image.disk_formats
399 all_pairs = [(container_fmt, disk_fmt)
400 for container_fmt in container_fmts
401 for disk_fmt in disk_fmts]
402
403 for (container_fmt, disk_fmt) in all_pairs[:6]:
Sergey Vilgelm36fdd202018-11-20 16:10:47 -0600404 LOG.debug("Creating an image "
Takashi NATSUME12a48512015-08-10 18:33:16 +0900405 "(Container format: %s, Disk format: %s).",
406 container_fmt, disk_fmt)
407 cls._create_standard_image(container_fmt, disk_fmt)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500408
409 @classmethod
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700410 def _create_standard_image(cls, container_format, disk_format):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000411 """Create a new standard image and return the newly-registered image-id
412
413 Note that the size of the new image is a random number between
Matthew Treinisha62347f2013-03-01 16:37:30 -0500414 1024 and 4096
415 """
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -0700416 size = random.randint(1024, 4096)
songwenping8c3dac12021-02-22 09:12:37 +0800417 image_file = io.BytesIO(data_utils.random_bytes(size))
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700418 tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
zhufl08e42762016-10-18 16:07:56 +0800419 image = cls.create_image(container_format=container_format,
lkuchlanb3348792016-09-29 10:42:21 +0300420 disk_format=disk_format,
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700421 visibility='private',
422 tags=tags)
lkuchlanb3348792016-09-29 10:42:21 +0300423 cls.client.store_image_file(image['id'], data=image_file)
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700424 # Keep the data of one test image so it can be used to filter lists
425 cls.test_data = image
Matthew Treinisha62347f2013-03-01 16:37:30 -0500426
lkuchlanb3348792016-09-29 10:42:21 +0300427 return image['id']
Matthew Treinisha62347f2013-03-01 16:37:30 -0500428
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700429 def _list_by_param_value_and_assert(self, params):
Ken'ichi Ohmichi9e3dac02015-11-19 07:01:07 +0000430 """Perform list action with given params and validates result."""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700431 # Retrieve the list of images that meet the filter
John Warrenf3b3e952015-08-17 19:28:12 +0000432 images_list = self.client.list_images(params=params)['images']
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700433 # Validating params of fetched images
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700434 msg = 'No images were found that met the filter criteria.'
435 self.assertNotEmpty(images_list, msg)
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700436 for image in images_list:
437 for key in params:
438 msg = "Failed to list images by %s" % key
439 self.assertEqual(params[key], image[key], msg)
440
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700441 def _list_sorted_by_image_size_and_assert(self, params, desc=False):
442 """Validate an image list that has been sorted by size
443
444 Perform list action with given params and validates the results are
445 sorted by image size in either ascending or descending order.
446 """
447 # Retrieve the list of images that meet the filter
448 images_list = self.client.list_images(params=params)['images']
449 # Validate that the list was fetched sorted accordingly
450 msg = 'No images were found that met the filter criteria.'
451 self.assertNotEmpty(images_list, msg)
zhufl940a9e22020-11-25 15:36:13 +0800452 sorted_list = [image['size'] for image in images_list
453 if image['size'] is not None]
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700454 msg = 'The list of images was not sorted correctly.'
455 self.assertEqual(sorted(sorted_list, reverse=desc), sorted_list, msg)
456
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800457 @decorators.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
Flavio Percoco7e26be12015-09-15 22:33:19 +0200458 def test_list_no_params(self):
zhufle68f4352020-04-21 13:44:55 +0800459 """Simple test to see all fixture images returned"""
John Warrenf3b3e952015-08-17 19:28:12 +0000460 images_list = self.client.list_images()['images']
Sirushti Murugesan935f2cc2016-07-12 19:48:24 +0530461 image_list = [image['id'] for image in images_list]
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700462
Matthew Treinisha62347f2013-03-01 16:37:30 -0500463 for image in self.created_images:
Attila Fazekase191cb12013-07-29 06:41:52 +0200464 self.assertIn(image, image_list)
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700465
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800466 @decorators.idempotent_id('9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700467 def test_list_images_param_container_format(self):
zhufle68f4352020-04-21 13:44:55 +0800468 """Test to get all images with a specific container_format"""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700469 params = {"container_format": self.test_data['container_format']}
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700470 self._list_by_param_value_and_assert(params)
471
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800472 @decorators.idempotent_id('4a4735a7-f22f-49b6-b0d9-66e1ef7453eb')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700473 def test_list_images_param_disk_format(self):
zhufle68f4352020-04-21 13:44:55 +0800474 """Test to get all images with disk_format = raw"""
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700475 params = {"disk_format": "raw"}
476 self._list_by_param_value_and_assert(params)
477
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800478 @decorators.idempotent_id('7a95bb92-d99e-4b12-9718-7bc6ab73e6d2')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700479 def test_list_images_param_visibility(self):
zhufle68f4352020-04-21 13:44:55 +0800480 """Test to get all images with visibility = private"""
Aaron Rosenc7720622014-05-20 10:38:10 -0700481 params = {"visibility": "private"}
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700482 self._list_by_param_value_and_assert(params)
483
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800484 @decorators.idempotent_id('cf1b9a48-8340-480e-af7b-fe7e17690876')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700485 def test_list_images_param_size(self):
zhufle68f4352020-04-21 13:44:55 +0800486 """Test to get all images by size"""
Takashi NATSUME12a48512015-08-10 18:33:16 +0900487 image_id = self.created_images[0]
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700488 # Get image metadata
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000489 image = self.client.show_image(image_id)
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700490
491 params = {"size": image['size']}
492 self._list_by_param_value_and_assert(params)
493
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800494 @decorators.idempotent_id('4ad8c157-971a-4ba8-aa84-ed61154b1e7f')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700495 def test_list_images_param_min_max_size(self):
zhufle68f4352020-04-21 13:44:55 +0800496 """Test to get all images with min size and max size"""
Takashi NATSUME12a48512015-08-10 18:33:16 +0900497 image_id = self.created_images[0]
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700498 # Get image metadata
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000499 image = self.client.show_image(image_id)
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700500
501 size = image['size']
502 params = {"size_min": size - 500, "size_max": size + 500}
John Warrenf3b3e952015-08-17 19:28:12 +0000503 images_list = self.client.list_images(params=params)['images']
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700504 image_size_list = map(lambda x: x['size'], images_list)
505
506 for image_size in image_size_list:
Béla Vancsics64862f72016-11-08 09:12:31 +0100507 self.assertGreaterEqual(image_size, params['size_min'],
508 "Failed to get images by size_min")
509 self.assertLessEqual(image_size, params['size_max'],
510 "Failed to get images by size_max")
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700511
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800512 @decorators.idempotent_id('7fc9e369-0f58-4d05-9aa5-0969e2d59d15')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700513 def test_list_images_param_status(self):
zhufle68f4352020-04-21 13:44:55 +0800514 """Test to get all active images"""
Anju Tiwarica2249d2014-01-23 17:33:02 +0530515 params = {"status": "active"}
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700516 self._list_by_param_value_and_assert(params)
517
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800518 @decorators.idempotent_id('e914a891-3cc8-4b40-ad32-e0a39ffbddbb')
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700519 def test_list_images_param_limit(self):
zhufle68f4352020-04-21 13:44:55 +0800520 """Test to get images by limit"""
Takashi NATSUME12a48512015-08-10 18:33:16 +0900521 params = {"limit": 1}
John Warrenf3b3e952015-08-17 19:28:12 +0000522 images_list = self.client.list_images(params=params)['images']
Abhijeet Malawadef268d8e2013-09-17 06:20:23 -0700523
524 self.assertEqual(len(images_list), params['limit'],
525 "Failed to get images by limit")
raiesmh08a1ce3542014-03-04 11:58:29 +0530526
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800527 @decorators.idempotent_id('e9a44b91-31c8-4b40-a332-e0a39ffb4dbb')
Li Wei14bf2412016-09-25 15:56:23 +0800528 def test_list_image_param_owner(self):
zhufle68f4352020-04-21 13:44:55 +0800529 """Test to get images by owner"""
Li Wei14bf2412016-09-25 15:56:23 +0800530 image_id = self.created_images[0]
531 # Get image metadata
532 image = self.client.show_image(image_id)
533
534 params = {"owner": image['owner']}
535 self._list_by_param_value_and_assert(params)
536
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800537 @decorators.idempotent_id('55c8f5f5-bfed-409d-a6d5-4caeda985d7b')
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700538 def test_list_images_param_name(self):
zhufle68f4352020-04-21 13:44:55 +0800539 """Test to get images by name"""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700540 params = {'name': self.test_data['name']}
541 self._list_by_param_value_and_assert(params)
542
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800543 @decorators.idempotent_id('aa8ac4df-cff9-418b-8d0f-dd9c67b072c9')
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700544 def test_list_images_param_tag(self):
zhufle68f4352020-04-21 13:44:55 +0800545 """Test to get images matching a tag"""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700546 params = {'tag': self.test_data['tags'][0]}
547 images_list = self.client.list_images(params=params)['images']
548 # Validating properties of fetched images
549 self.assertNotEmpty(images_list)
550 for image in images_list:
551 msg = ("The image {image_name} does not have the expected tag "
552 "{expected_tag} among its tags: {observerd_tags}."
553 .format(image_name=image['name'],
554 expected_tag=self.test_data['tags'][0],
555 observerd_tags=image['tags']))
556 self.assertIn(self.test_data['tags'][0], image['tags'], msg)
557
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800558 @decorators.idempotent_id('eeadce49-04e0-43b7-aec7-52535d903e7a')
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700559 def test_list_images_param_sort(self):
zhufle68f4352020-04-21 13:44:55 +0800560 """Test listing images sorting in descending order"""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700561 params = {'sort': 'size:desc'}
562 self._list_sorted_by_image_size_and_assert(params, desc=True)
563
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800564 @decorators.idempotent_id('9faaa0c2-c3a5-43e1-8f61-61c54b409a49')
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700565 def test_list_images_param_sort_key_dir(self):
zhufle68f4352020-04-21 13:44:55 +0800566 """Test listing images sorting by size in descending order"""
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700567 params = {'sort_key': 'size', 'sort_dir': 'desc'}
568 self._list_sorted_by_image_size_and_assert(params, desc=True)
569
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800570 @decorators.idempotent_id('622b925c-479f-4736-860d-adeaf13bc371')
raiesmh08a1ce3542014-03-04 11:58:29 +0530571 def test_get_image_schema(self):
zhufle68f4352020-04-21 13:44:55 +0800572 """Test to get image schema"""
raiesmh08a1ce3542014-03-04 11:58:29 +0530573 schema = "image"
Ken'ichi Ohmichi190b24e2016-06-07 23:20:09 +0900574 body = self.schemas_client.show_schema(schema)
raiesmh08a1ce3542014-03-04 11:58:29 +0530575 self.assertEqual("image", body['name'])
576
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800577 @decorators.idempotent_id('25c8d7b2-df21-460f-87ac-93130bcdc684')
raiesmh08a1ce3542014-03-04 11:58:29 +0530578 def test_get_images_schema(self):
zhufle68f4352020-04-21 13:44:55 +0800579 """Test to get images schema"""
raiesmh08a1ce3542014-03-04 11:58:29 +0530580 schema = "images"
Ken'ichi Ohmichi190b24e2016-06-07 23:20:09 +0900581 body = self.schemas_client.show_schema(schema)
raiesmh08a1ce3542014-03-04 11:58:29 +0530582 self.assertEqual("images", body['name'])
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700583
584
zhufl6e042bc2017-01-25 10:33:40 +0800585class ListSharedImagesTest(base.BaseV2ImageTest):
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700586 """Here we test the listing of a shared image information"""
587
588 credentials = ['primary', 'alt']
589
590 @classmethod
591 def setup_clients(cls):
592 super(ListSharedImagesTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +0200593 cls.image_member_client = cls.os_primary.image_member_client_v2
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700594 cls.alt_img_client = cls.os_alt.image_client_v2
595
Ken'ichi Ohmichif35efa22017-01-27 17:55:24 -0800596 @decorators.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700597 def test_list_images_param_member_status(self):
zhufle68f4352020-04-21 13:44:55 +0800598 """Test listing images by member_status and visibility"""
Steve Lewis8ac5b972016-12-22 07:41:29 -0800599 # Create an image to be shared using default visibility
songwenping8c3dac12021-02-22 09:12:37 +0800600 image_file = io.BytesIO(data_utils.random_bytes(2048))
Steve Lewis8ac5b972016-12-22 07:41:29 -0800601 container_format = CONF.image.container_formats[0]
602 disk_format = CONF.image.disk_formats[0]
603 image = self.create_image(container_format=container_format,
604 disk_format=disk_format)
605 self.client.store_image_file(image['id'], data=image_file)
606
607 # Share the image created with the alt user
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700608 self.image_member_client.create_image_member(
Steve Lewis8ac5b972016-12-22 07:41:29 -0800609 image_id=image['id'], member=self.alt_img_client.tenant_id)
610
Castulo J. Martineze9c8ce82016-05-16 07:55:53 -0700611 # As an image consumer you need to provide the member_status parameter
612 # along with the visibility=shared parameter in order for it to show
613 # results
614 params = {'member_status': 'pending', 'visibility': 'shared'}
615 fetched_images = self.alt_img_client.list_images(params)['images']
616 self.assertEqual(1, len(fetched_images))
Steve Lewis8ac5b972016-12-22 07:41:29 -0800617 self.assertEqual(image['id'], fetched_images[0]['id'])