blob: 38a623ace3c0fb3bd4cc4bdcbb13084bfb3716da [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'}
David Kranz9c3b3b62014-06-19 16:05:53 -040033 _, body = self.create_image(name='New Name',
34 container_format='bare',
35 disk_format='raw',
36 is_public=False,
37 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
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -070047 image_file = StringIO.StringIO(data_utils.random_bytes())
David Kranz9c3b3b62014-06-19 16:05:53 -040048 _, 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
David Kranz9c3b3b62014-06-19 16:05:53 -040055 _, body = self.create_image(name='New Remote Image',
56 container_format='bare',
57 disk_format='raw', is_public=False,
58 location=CONF.image.http_image,
59 properties={'key1': 'value1',
60 'key2': 'value2'})
Attila Fazekase191cb12013-07-29 06:41:52 +020061 self.assertIn('id', body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050062 self.assertEqual('New Remote Image', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070063 self.assertFalse(body.get('is_public'))
Matthew Treinish72ea4422013-02-07 14:42:49 -050064 self.assertEqual('active', body.get('status'))
afazekas4a96bf82013-03-25 16:07:38 +010065 properties = body.get('properties')
66 self.assertEqual(properties['key1'], 'value1')
67 self.assertEqual(properties['key2'], 'value2')
Jay Pipes50677282012-01-06 15:39:20 -050068
ivan-zhud1bbe5d2013-12-29 18:32:46 +080069 @test.attr(type='gate')
Attila Fazekase72b7cd2013-03-26 18:34:21 +010070 def test_register_http_image(self):
David Kranz9c3b3b62014-06-19 16:05:53 -040071 _, body = self.create_image(name='New Http Image',
72 container_format='bare',
73 disk_format='raw', is_public=False,
74 copy_from=CONF.image.http_image)
Attila Fazekase191cb12013-07-29 06:41:52 +020075 self.assertIn('id', body)
Attila Fazekase72b7cd2013-03-26 18:34:21 +010076 image_id = body.get('id')
Attila Fazekase72b7cd2013-03-26 18:34:21 +010077 self.assertEqual('New Http Image', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070078 self.assertFalse(body.get('is_public'))
Attila Fazekase72b7cd2013-03-26 18:34:21 +010079 self.client.wait_for_image_status(image_id, 'active')
David Kranz9c3b3b62014-06-19 16:05:53 -040080 self.client.get_image(image_id)
Attila Fazekase72b7cd2013-03-26 18:34:21 +010081
ivan-zhud1bbe5d2013-12-29 18:32:46 +080082 @test.attr(type='gate')
hi2suresh75a20302013-04-09 09:17:06 +000083 def test_register_image_with_min_ram(self):
84 # Register an image with min ram
85 properties = {'prop1': 'val1'}
David Kranz9c3b3b62014-06-19 16:05:53 -040086 _, body = self.create_image(name='New_image_with_min_ram',
87 container_format='bare',
88 disk_format='raw',
89 is_public=False,
90 min_ram=40,
91 properties=properties)
Attila Fazekase191cb12013-07-29 06:41:52 +020092 self.assertIn('id', body)
hi2suresh75a20302013-04-09 09:17:06 +000093 self.assertEqual('New_image_with_min_ram', body.get('name'))
Aaron Rosenc7720622014-05-20 10:38:10 -070094 self.assertFalse(body.get('is_public'))
hi2suresh75a20302013-04-09 09:17:06 +000095 self.assertEqual('queued', body.get('status'))
96 self.assertEqual(40, body.get('min_ram'))
97 for key, val in properties.items():
98 self.assertEqual(val, body.get('properties')[key])
David Kranz9c3b3b62014-06-19 16:05:53 -040099 self.client.delete_image(body['id'])
hi2suresh75a20302013-04-09 09:17:06 +0000100
Jay Pipes50677282012-01-06 15:39:20 -0500101
Matthew Treinishce3ef922013-03-11 14:02:46 -0400102class ListImagesTest(base.BaseV1ImageTest):
Jay Pipes50677282012-01-06 15:39:20 -0500103
104 """
105 Here we test the listing of image information
106 """
107
108 @classmethod
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100109 def resource_setup(cls):
110 super(ListImagesTest, cls).resource_setup()
Jay Pipes50677282012-01-06 15:39:20 -0500111 # We add a few images here to test the listing functionality of
112 # the images API
Attila Fazekas11795b52013-02-24 15:49:08 +0100113 img1 = cls._create_remote_image('one', 'bare', 'raw')
114 img2 = cls._create_remote_image('two', 'ami', 'ami')
115 img3 = cls._create_remote_image('dup', 'bare', 'raw')
116 img4 = cls._create_remote_image('dup', 'bare', 'raw')
117 img5 = cls._create_standard_image('1', 'ami', 'ami', 42)
118 img6 = cls._create_standard_image('2', 'ami', 'ami', 142)
119 img7 = cls._create_standard_image('33', 'bare', 'raw', 142)
120 img8 = cls._create_standard_image('33', 'bare', 'raw', 142)
121 cls.created_set = set(cls.created_images)
122 # 4x-4x remote image
123 cls.remote_set = set((img1, img2, img3, img4))
124 cls.standard_set = set((img5, img6, img7, img8))
125 # 5x bare, 3x ami
126 cls.bare_set = set((img1, img3, img4, img7, img8))
127 cls.ami_set = set((img2, img5, img6))
128 # 1x with size 42
129 cls.size42_set = set((img5,))
130 # 3x with size 142
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800131 cls.size142_set = set((img6, img7, img8,))
Attila Fazekas11795b52013-02-24 15:49:08 +0100132 # dup named
133 cls.dup_set = set((img3, img4))
Jay Pipes50677282012-01-06 15:39:20 -0500134
135 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100136 def _create_remote_image(cls, name, container_format, disk_format):
Jay Pipes50677282012-01-06 15:39:20 -0500137 """
138 Create a new remote image and return the ID of the newly-registered
139 image
140 """
Attila Fazekas11795b52013-02-24 15:49:08 +0100141 name = 'New Remote Image %s' % name
Matthew Treinishe81ae692014-06-19 17:41:31 -0400142 location = CONF.image.http_image
David Kranz9c3b3b62014-06-19 16:05:53 -0400143 _, image = cls.create_image(name=name,
144 container_format=container_format,
145 disk_format=disk_format,
146 is_public=False,
147 location=location)
Attila Fazekas11795b52013-02-24 15:49:08 +0100148 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500149 return image_id
150
151 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100152 def _create_standard_image(cls, name, container_format,
153 disk_format, size):
Jay Pipes50677282012-01-06 15:39:20 -0500154 """
155 Create a new standard image and return the ID of the newly-registered
156 image. Note that the size of the new image is a random number between
157 1024 and 4096
158 """
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -0700159 image_file = StringIO.StringIO(data_utils.random_bytes(size))
Attila Fazekas11795b52013-02-24 15:49:08 +0100160 name = 'New Standard Image %s' % name
David Kranz9c3b3b62014-06-19 16:05:53 -0400161 _, image = cls.create_image(name=name,
162 container_format=container_format,
163 disk_format=disk_format,
164 is_public=False, data=image_file)
Attila Fazekas11795b52013-02-24 15:49:08 +0100165 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500166 return image_id
167
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800168 @test.attr(type='gate')
Jay Pipes50677282012-01-06 15:39:20 -0500169 def test_index_no_params(self):
Sean Dague46c4a2b2013-01-03 17:54:17 -0500170 # Simple test to see all fixture images returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400171 _, images_list = self.client.image_list()
Matthew Treinish72ea4422013-02-07 14:42:49 -0500172 image_list = map(lambda x: x['id'], images_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100173 for image_id in self.created_images:
Attila Fazekase191cb12013-07-29 06:41:52 +0200174 self.assertIn(image_id, image_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100175
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800176 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100177 def test_index_disk_format(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400178 _, images_list = self.client.image_list(disk_format='ami')
Attila Fazekas11795b52013-02-24 15:49:08 +0100179 for image in images_list:
180 self.assertEqual(image['disk_format'], 'ami')
181 result_set = set(map(lambda x: x['id'], images_list))
182 self.assertTrue(self.ami_set <= result_set)
183 self.assertFalse(self.created_set - self.ami_set <= result_set)
184
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800185 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100186 def test_index_container_format(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400187 _, images_list = self.client.image_list(container_format='bare')
Attila Fazekas11795b52013-02-24 15:49:08 +0100188 for image in images_list:
189 self.assertEqual(image['container_format'], 'bare')
190 result_set = set(map(lambda x: x['id'], images_list))
191 self.assertTrue(self.bare_set <= result_set)
192 self.assertFalse(self.created_set - self.bare_set <= result_set)
193
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800194 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100195 def test_index_max_size(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400196 _, images_list = self.client.image_list(size_max=42)
Attila Fazekas11795b52013-02-24 15:49:08 +0100197 for image in images_list:
198 self.assertTrue(image['size'] <= 42)
199 result_set = set(map(lambda x: x['id'], images_list))
200 self.assertTrue(self.size42_set <= result_set)
201 self.assertFalse(self.created_set - self.size42_set <= result_set)
202
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800203 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100204 def test_index_min_size(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400205 _, images_list = self.client.image_list(size_min=142)
Attila Fazekas11795b52013-02-24 15:49:08 +0100206 for image in images_list:
207 self.assertTrue(image['size'] >= 142)
208 result_set = set(map(lambda x: x['id'], images_list))
209 self.assertTrue(self.size142_set <= result_set)
210 self.assertFalse(self.size42_set <= result_set)
211
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800212 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100213 def test_index_status_active_detail(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400214 _, images_list = self.client.image_list_detail(status='active',
215 sort_key='size',
216 sort_dir='desc')
Attila Fazekas11795b52013-02-24 15:49:08 +0100217 top_size = images_list[0]['size'] # We have non-zero sized images
218 for image in images_list:
219 size = image['size']
220 self.assertTrue(size <= top_size)
221 top_size = size
222 self.assertEqual(image['status'], 'active')
223
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800224 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100225 def test_index_name(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400226 _, images_list = self.client.image_list_detail(
Sean Dague14c68182013-04-14 15:34:30 -0400227 name='New Remote Image dup')
Attila Fazekas11795b52013-02-24 15:49:08 +0100228 result_set = set(map(lambda x: x['id'], images_list))
229 for image in images_list:
230 self.assertEqual(image['name'], 'New Remote Image dup')
231 self.assertTrue(self.dup_set <= result_set)
232 self.assertFalse(self.created_set - self.dup_set <= result_set)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800233
234
235class ListSnapshotImagesTest(base.BaseV1ImageTest):
236 @classmethod
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100237 def resource_setup(cls):
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800238 # This test class only uses nova v3 api to create snapshot
239 # as the similar test which uses nova v2 api already exists
240 # in nova v2 compute images api tests.
241 # Since nova v3 doesn't have images api proxy, this test
242 # class was added in the image api tests.
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800243 if not CONF.compute_feature_enabled.api_v3:
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800244 skip_msg = ("%s skipped as nova v3 api is not available" %
245 cls.__name__)
246 raise cls.skipException(skip_msg)
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100247 super(ListSnapshotImagesTest, cls).resource_setup()
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800248 cls.servers_client = cls.os.servers_v3_client
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800249 cls.servers = []
250 # We add a few images here to test the listing functionality of
251 # the images API
252 cls.snapshot = cls._create_snapshot(
253 'snapshot', CONF.compute.image_ref,
254 CONF.compute.flavor_ref)
255 cls.snapshot_set = set((cls.snapshot,))
256
257 image_file = StringIO.StringIO('*' * 42)
David Kranz9c3b3b62014-06-19 16:05:53 -0400258 _, image = cls.create_image(name="Standard Image",
259 container_format='ami',
260 disk_format='ami',
261 is_public=False, data=image_file)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800262 cls.image_id = image['id']
263 cls.client.wait_for_image_status(image['id'], 'active')
264
265 @classmethod
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100266 def resource_cleanup(cls):
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500267 for server in getattr(cls, "servers", []):
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800268 cls.servers_client.delete_server(server['id'])
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100269 super(ListSnapshotImagesTest, cls).resource_cleanup()
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800270
271 @classmethod
272 def _create_snapshot(cls, name, image_id, flavor, **kwargs):
David Kranz9c3b3b62014-06-19 16:05:53 -0400273 _, server = cls.servers_client.create_server(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800274 name, image_id, flavor, **kwargs)
275 cls.servers.append(server)
276 cls.servers_client.wait_for_server_status(
277 server['id'], 'ACTIVE')
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800278 resp, _ = cls.servers_client.create_image(server['id'], name)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800279 image_id = data_utils.parse_image_id(resp['location'])
280 cls.created_images.append(image_id)
281 cls.client.wait_for_image_status(image_id,
282 'active')
283 return image_id
284
285 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400286 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800287 def test_index_server_id(self):
288 # The images should contain images filtered by server id
David Kranz9c3b3b62014-06-19 16:05:53 -0400289 _, images = self.client.image_list_detail(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800290 {'instance_uuid': self.servers[0]['id']})
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800291 result_set = set(map(lambda x: x['id'], images))
292 self.assertEqual(self.snapshot_set, result_set)
293
294 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400295 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800296 def test_index_type(self):
297 # The list of servers should be filtered by image type
298 params = {'image_type': 'snapshot'}
David Kranz9c3b3b62014-06-19 16:05:53 -0400299 _, images = self.client.image_list_detail(params)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800300
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800301 result_set = set(map(lambda x: x['id'], images))
302 self.assertIn(self.snapshot, result_set)
303
304 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400305 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800306 def test_index_limit(self):
307 # Verify only the expected number of results are returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400308 _, images = self.client.image_list_detail(limit=1)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800309
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800310 self.assertEqual(1, len(images))
311
312 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400313 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800314 def test_index_by_change_since(self):
315 # Verify an update image is returned
316 # Becoming ACTIVE will modify the updated time
317 # Filter by the image's created time
David Kranz9c3b3b62014-06-19 16:05:53 -0400318 _, image = self.client.get_image_meta(self.snapshot)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800319 self.assertEqual(self.snapshot, image['id'])
David Kranz9c3b3b62014-06-19 16:05:53 -0400320 _, images = self.client.image_list_detail(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800321 changes_since=image['updated_at'])
322
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800323 result_set = set(map(lambda x: x['id'], images))
324 self.assertIn(self.image_id, result_set)
325 self.assertNotIn(self.snapshot, result_set)
326
327
328class UpdateImageMetaTest(base.BaseV1ImageTest):
329 @classmethod
Andrea Frittoli69a6b632014-09-15 13:14:53 +0100330 def resource_setup(cls):
331 super(UpdateImageMetaTest, cls).resource_setup()
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800332 cls.image_id = cls._create_standard_image('1', 'ami', 'ami', 42)
333
334 @classmethod
335 def _create_standard_image(cls, name, container_format,
336 disk_format, size):
337 """
338 Create a new standard image and return the ID of the newly-registered
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -0700339 image.
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800340 """
Mark Washenberger5c3b6fe2014-07-29 13:40:34 -0700341 image_file = StringIO.StringIO(data_utils.random_bytes(size))
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800342 name = 'New Standard Image %s' % name
David Kranz9c3b3b62014-06-19 16:05:53 -0400343 _, image = cls.create_image(name=name,
344 container_format=container_format,
345 disk_format=disk_format,
346 is_public=False, data=image_file,
347 properties={'key1': 'value1'})
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800348 image_id = image['id']
349 return image_id
350
351 @test.attr(type='gate')
352 def test_list_image_metadata(self):
353 # All metadata key/value pairs for an image should be returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400354 _, resp_metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800355 expected = {'key1': 'value1'}
356 self.assertEqual(expected, resp_metadata['properties'])
357
358 @test.attr(type='gate')
359 def test_update_image_metadata(self):
360 # The metadata for the image should match the updated values
361 req_metadata = {'key1': 'alt1', 'key2': 'value2'}
David Kranz9c3b3b62014-06-19 16:05:53 -0400362 _, metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800363 self.assertEqual(metadata['properties'], {'key1': 'value1'})
364 metadata['properties'].update(req_metadata)
David Kranz9c3b3b62014-06-19 16:05:53 -0400365 _, metadata = self.client.update_image(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800366 self.image_id, properties=metadata['properties'])
367
David Kranz9c3b3b62014-06-19 16:05:53 -0400368 _, resp_metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800369 expected = {'key1': 'alt1', 'key2': 'value2'}
370 self.assertEqual(expected, resp_metadata['properties'])