blob: 8528e42167930e55c826fb679e7554c404f307a8 [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
Matthew Treinish72ea4422013-02-07 14:42:49 -050047 image_file = StringIO.StringIO(('*' * 1024))
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
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500109 @test.safe_setup
Jay Pipes50677282012-01-06 15:39:20 -0500110 def setUpClass(cls):
Matthew Treinishce3ef922013-03-11 14:02:46 -0400111 super(ListImagesTest, cls).setUpClass()
Jay Pipes50677282012-01-06 15:39:20 -0500112 # We add a few images here to test the listing functionality of
113 # the images API
Attila Fazekas11795b52013-02-24 15:49:08 +0100114 img1 = cls._create_remote_image('one', 'bare', 'raw')
115 img2 = cls._create_remote_image('two', 'ami', 'ami')
116 img3 = cls._create_remote_image('dup', 'bare', 'raw')
117 img4 = cls._create_remote_image('dup', 'bare', 'raw')
118 img5 = cls._create_standard_image('1', 'ami', 'ami', 42)
119 img6 = cls._create_standard_image('2', 'ami', 'ami', 142)
120 img7 = cls._create_standard_image('33', 'bare', 'raw', 142)
121 img8 = cls._create_standard_image('33', 'bare', 'raw', 142)
122 cls.created_set = set(cls.created_images)
123 # 4x-4x remote image
124 cls.remote_set = set((img1, img2, img3, img4))
125 cls.standard_set = set((img5, img6, img7, img8))
126 # 5x bare, 3x ami
127 cls.bare_set = set((img1, img3, img4, img7, img8))
128 cls.ami_set = set((img2, img5, img6))
129 # 1x with size 42
130 cls.size42_set = set((img5,))
131 # 3x with size 142
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800132 cls.size142_set = set((img6, img7, img8,))
Attila Fazekas11795b52013-02-24 15:49:08 +0100133 # dup named
134 cls.dup_set = set((img3, img4))
Jay Pipes50677282012-01-06 15:39:20 -0500135
136 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100137 def _create_remote_image(cls, name, container_format, disk_format):
Jay Pipes50677282012-01-06 15:39:20 -0500138 """
139 Create a new remote image and return the ID of the newly-registered
140 image
141 """
Attila Fazekas11795b52013-02-24 15:49:08 +0100142 name = 'New Remote Image %s' % name
Matthew Treinishe81ae692014-06-19 17:41:31 -0400143 location = CONF.image.http_image
David Kranz9c3b3b62014-06-19 16:05:53 -0400144 _, image = cls.create_image(name=name,
145 container_format=container_format,
146 disk_format=disk_format,
147 is_public=False,
148 location=location)
Attila Fazekas11795b52013-02-24 15:49:08 +0100149 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500150 return image_id
151
152 @classmethod
Attila Fazekas11795b52013-02-24 15:49:08 +0100153 def _create_standard_image(cls, name, container_format,
154 disk_format, size):
Jay Pipes50677282012-01-06 15:39:20 -0500155 """
156 Create a new standard image and return the ID of the newly-registered
157 image. Note that the size of the new image is a random number between
158 1024 and 4096
159 """
Attila Fazekas11795b52013-02-24 15:49:08 +0100160 image_file = StringIO.StringIO('*' * size)
161 name = 'New Standard Image %s' % name
David Kranz9c3b3b62014-06-19 16:05:53 -0400162 _, image = cls.create_image(name=name,
163 container_format=container_format,
164 disk_format=disk_format,
165 is_public=False, data=image_file)
Attila Fazekas11795b52013-02-24 15:49:08 +0100166 image_id = image['id']
Jay Pipes50677282012-01-06 15:39:20 -0500167 return image_id
168
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800169 @test.attr(type='gate')
Jay Pipes50677282012-01-06 15:39:20 -0500170 def test_index_no_params(self):
Sean Dague46c4a2b2013-01-03 17:54:17 -0500171 # Simple test to see all fixture images returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400172 _, images_list = self.client.image_list()
Matthew Treinish72ea4422013-02-07 14:42:49 -0500173 image_list = map(lambda x: x['id'], images_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100174 for image_id in self.created_images:
Attila Fazekase191cb12013-07-29 06:41:52 +0200175 self.assertIn(image_id, image_list)
Attila Fazekas11795b52013-02-24 15:49:08 +0100176
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800177 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100178 def test_index_disk_format(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400179 _, images_list = self.client.image_list(disk_format='ami')
Attila Fazekas11795b52013-02-24 15:49:08 +0100180 for image in images_list:
181 self.assertEqual(image['disk_format'], 'ami')
182 result_set = set(map(lambda x: x['id'], images_list))
183 self.assertTrue(self.ami_set <= result_set)
184 self.assertFalse(self.created_set - self.ami_set <= result_set)
185
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800186 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100187 def test_index_container_format(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400188 _, images_list = self.client.image_list(container_format='bare')
Attila Fazekas11795b52013-02-24 15:49:08 +0100189 for image in images_list:
190 self.assertEqual(image['container_format'], 'bare')
191 result_set = set(map(lambda x: x['id'], images_list))
192 self.assertTrue(self.bare_set <= result_set)
193 self.assertFalse(self.created_set - self.bare_set <= result_set)
194
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800195 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100196 def test_index_max_size(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400197 _, images_list = self.client.image_list(size_max=42)
Attila Fazekas11795b52013-02-24 15:49:08 +0100198 for image in images_list:
199 self.assertTrue(image['size'] <= 42)
200 result_set = set(map(lambda x: x['id'], images_list))
201 self.assertTrue(self.size42_set <= result_set)
202 self.assertFalse(self.created_set - self.size42_set <= result_set)
203
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800204 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100205 def test_index_min_size(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400206 _, images_list = self.client.image_list(size_min=142)
Attila Fazekas11795b52013-02-24 15:49:08 +0100207 for image in images_list:
208 self.assertTrue(image['size'] >= 142)
209 result_set = set(map(lambda x: x['id'], images_list))
210 self.assertTrue(self.size142_set <= result_set)
211 self.assertFalse(self.size42_set <= result_set)
212
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800213 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100214 def test_index_status_active_detail(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400215 _, images_list = self.client.image_list_detail(status='active',
216 sort_key='size',
217 sort_dir='desc')
Attila Fazekas11795b52013-02-24 15:49:08 +0100218 top_size = images_list[0]['size'] # We have non-zero sized images
219 for image in images_list:
220 size = image['size']
221 self.assertTrue(size <= top_size)
222 top_size = size
223 self.assertEqual(image['status'], 'active')
224
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800225 @test.attr(type='gate')
Attila Fazekas11795b52013-02-24 15:49:08 +0100226 def test_index_name(self):
David Kranz9c3b3b62014-06-19 16:05:53 -0400227 _, images_list = self.client.image_list_detail(
Sean Dague14c68182013-04-14 15:34:30 -0400228 name='New Remote Image dup')
Attila Fazekas11795b52013-02-24 15:49:08 +0100229 result_set = set(map(lambda x: x['id'], images_list))
230 for image in images_list:
231 self.assertEqual(image['name'], 'New Remote Image dup')
232 self.assertTrue(self.dup_set <= result_set)
233 self.assertFalse(self.created_set - self.dup_set <= result_set)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800234
235
236class ListSnapshotImagesTest(base.BaseV1ImageTest):
237 @classmethod
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500238 @test.safe_setup
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800239 def setUpClass(cls):
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800240 # This test class only uses nova v3 api to create snapshot
241 # as the similar test which uses nova v2 api already exists
242 # in nova v2 compute images api tests.
243 # Since nova v3 doesn't have images api proxy, this test
244 # class was added in the image api tests.
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800245 if not CONF.compute_feature_enabled.api_v3:
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800246 skip_msg = ("%s skipped as nova v3 api is not available" %
247 cls.__name__)
248 raise cls.skipException(skip_msg)
249 super(ListSnapshotImagesTest, cls).setUpClass()
250 cls.servers_client = cls.os.servers_v3_client
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800251 cls.servers = []
252 # We add a few images here to test the listing functionality of
253 # the images API
254 cls.snapshot = cls._create_snapshot(
255 'snapshot', CONF.compute.image_ref,
256 CONF.compute.flavor_ref)
257 cls.snapshot_set = set((cls.snapshot,))
258
259 image_file = StringIO.StringIO('*' * 42)
David Kranz9c3b3b62014-06-19 16:05:53 -0400260 _, image = cls.create_image(name="Standard Image",
261 container_format='ami',
262 disk_format='ami',
263 is_public=False, data=image_file)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800264 cls.image_id = image['id']
265 cls.client.wait_for_image_status(image['id'], 'active')
266
267 @classmethod
268 def tearDownClass(cls):
Zhi Kun Liuca934a42014-03-24 01:30:31 -0500269 for server in getattr(cls, "servers", []):
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800270 cls.servers_client.delete_server(server['id'])
271 super(ListSnapshotImagesTest, cls).tearDownClass()
272
273 @classmethod
274 def _create_snapshot(cls, name, image_id, flavor, **kwargs):
David Kranz9c3b3b62014-06-19 16:05:53 -0400275 _, server = cls.servers_client.create_server(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800276 name, image_id, flavor, **kwargs)
277 cls.servers.append(server)
278 cls.servers_client.wait_for_server_status(
279 server['id'], 'ACTIVE')
Zhi Kun Liu4640b012014-04-23 22:50:40 +0800280 resp, _ = cls.servers_client.create_image(server['id'], name)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800281 image_id = data_utils.parse_image_id(resp['location'])
282 cls.created_images.append(image_id)
283 cls.client.wait_for_image_status(image_id,
284 'active')
285 return image_id
286
287 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400288 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800289 def test_index_server_id(self):
290 # The images should contain images filtered by server id
David Kranz9c3b3b62014-06-19 16:05:53 -0400291 _, images = self.client.image_list_detail(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800292 {'instance_uuid': self.servers[0]['id']})
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800293 result_set = set(map(lambda x: x['id'], images))
294 self.assertEqual(self.snapshot_set, result_set)
295
296 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400297 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800298 def test_index_type(self):
299 # The list of servers should be filtered by image type
300 params = {'image_type': 'snapshot'}
David Kranz9c3b3b62014-06-19 16:05:53 -0400301 _, images = self.client.image_list_detail(params)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800302
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800303 result_set = set(map(lambda x: x['id'], images))
304 self.assertIn(self.snapshot, result_set)
305
306 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400307 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800308 def test_index_limit(self):
309 # Verify only the expected number of results are returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400310 _, images = self.client.image_list_detail(limit=1)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800311
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800312 self.assertEqual(1, len(images))
313
314 @test.attr(type='gate')
Matthew Treinish08c6a012014-05-05 22:04:38 -0400315 @test.services('compute')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800316 def test_index_by_change_since(self):
317 # Verify an update image is returned
318 # Becoming ACTIVE will modify the updated time
319 # Filter by the image's created time
David Kranz9c3b3b62014-06-19 16:05:53 -0400320 _, image = self.client.get_image_meta(self.snapshot)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800321 self.assertEqual(self.snapshot, image['id'])
David Kranz9c3b3b62014-06-19 16:05:53 -0400322 _, images = self.client.image_list_detail(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800323 changes_since=image['updated_at'])
324
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800325 result_set = set(map(lambda x: x['id'], images))
326 self.assertIn(self.image_id, result_set)
327 self.assertNotIn(self.snapshot, result_set)
328
329
330class UpdateImageMetaTest(base.BaseV1ImageTest):
331 @classmethod
332 def setUpClass(cls):
333 super(UpdateImageMetaTest, cls).setUpClass()
334 cls.image_id = cls._create_standard_image('1', 'ami', 'ami', 42)
335
336 @classmethod
337 def _create_standard_image(cls, name, container_format,
338 disk_format, size):
339 """
340 Create a new standard image and return the ID of the newly-registered
341 image. Note that the size of the new image is a random number between
342 1024 and 4096
343 """
344 image_file = StringIO.StringIO('*' * size)
345 name = 'New Standard Image %s' % name
David Kranz9c3b3b62014-06-19 16:05:53 -0400346 _, image = cls.create_image(name=name,
347 container_format=container_format,
348 disk_format=disk_format,
349 is_public=False, data=image_file,
350 properties={'key1': 'value1'})
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800351 image_id = image['id']
352 return image_id
353
354 @test.attr(type='gate')
355 def test_list_image_metadata(self):
356 # All metadata key/value pairs for an image should be returned
David Kranz9c3b3b62014-06-19 16:05:53 -0400357 _, resp_metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800358 expected = {'key1': 'value1'}
359 self.assertEqual(expected, resp_metadata['properties'])
360
361 @test.attr(type='gate')
362 def test_update_image_metadata(self):
363 # The metadata for the image should match the updated values
364 req_metadata = {'key1': 'alt1', 'key2': 'value2'}
David Kranz9c3b3b62014-06-19 16:05:53 -0400365 _, metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800366 self.assertEqual(metadata['properties'], {'key1': 'value1'})
367 metadata['properties'].update(req_metadata)
David Kranz9c3b3b62014-06-19 16:05:53 -0400368 _, metadata = self.client.update_image(
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800369 self.image_id, properties=metadata['properties'])
370
David Kranz9c3b3b62014-06-19 16:05:53 -0400371 _, resp_metadata = self.client.get_image_meta(self.image_id)
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800372 expected = {'key1': 'alt1', 'key2': 'value2'}
373 self.assertEqual(expected, resp_metadata['properties'])