From 02bcdf36dba361fcc834e8212ccee7256019b03c Mon Sep 17 00:00:00 2001 From: Ken'ichi Ohmichi Date: Fri, 17 Jun 2016 16:41:26 -0700 Subject: [PATCH] Move image_meta_to_headers from images_client Glance v1 images_client contains image_meta_to_headers() which converts a dict to headers. However, most service clients' methods don't convert like that. Then this moves image_meta_to_headers() to common place like the compute module from the service client. Partially implements blueprint consistent-service-method-names Change-Id: Id8e47fd35f7667578854bc439238a4b0f36fbb8f --- .../api/compute/images/test_image_metadata.py | 18 +++++++----- .../compute/images/test_list_image_filters.py | 19 ++++++++----- tempest/api/image/base.py | 19 +++++++++++-- tempest/api/image/v1/test_images.py | 6 ++-- tempest/api/image/v1/test_images_negative.py | 12 ++++---- tempest/common/image.py | 24 ++++++++++++++++ tempest/scenario/manager.py | 1 + .../services/image/v1/json/images_client.py | 28 ++++--------------- tempest/tests/common/test_image.py | 19 +++++++++++++ 9 files changed, 100 insertions(+), 46 deletions(-) diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py index 427a74816..6f8073095 100644 --- a/tempest/api/compute/images/test_image_metadata.py +++ b/tempest/api/compute/images/test_image_metadata.py @@ -16,6 +16,7 @@ import six from tempest.api.compute import base +from tempest.common import image as common_image from tempest.common.utils import data_utils from tempest.common import waiters from tempest import config @@ -55,15 +56,18 @@ class ImagesMetadataTestJSON(base.BaseV2ComputeTest): super(ImagesMetadataTestJSON, cls).resource_setup() cls.image_id = None - name = data_utils.rand_name('image') + params = { + 'name': data_utils.rand_name('image'), + 'container_format': 'bare', + 'disk_format': 'raw' + } if CONF.image_feature_enabled.api_v1: - kwargs = dict(is_public=False) + params.update({'is_public': False}) + params = {'headers': common_image.image_meta_to_headers(**params)} else: - kwargs = dict(visibility='private') - body = cls.glance_client.create_image(name=name, - container_format='bare', - disk_format='raw', - **kwargs) + params.update({'visibility': 'private'}) + + body = cls.glance_client.create_image(**params) body = body['image'] if 'image' in body else body cls.image_id = body['id'] cls.images.append(cls.image_id) diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py index e74ca67d3..901746105 100644 --- a/tempest/api/compute/images/test_list_image_filters.py +++ b/tempest/api/compute/images/test_list_image_filters.py @@ -19,6 +19,7 @@ import six import testtools from tempest.api.compute import base +from tempest.common import image as common_image from tempest.common.utils import data_utils from tempest.common import waiters from tempest import config @@ -58,15 +59,19 @@ class ListImageFiltersTestJSON(base.BaseV2ComputeTest): super(ListImageFiltersTestJSON, cls).resource_setup() def _create_image(): - name = data_utils.rand_name('image') + params = { + 'name': data_utils.rand_name('image'), + 'container_format': 'bare', + 'disk_format': 'raw' + } if CONF.image_feature_enabled.api_v1: - kwargs = dict(is_public=False) + params.update({'is_public': False}) + params = {'headers': + common_image.image_meta_to_headers(**params)} else: - kwargs = dict(visibility='private') - body = cls.glance_client.create_image(name=name, - container_format='bare', - disk_format='raw', - **kwargs) + params.update({'visibility': 'private'}) + + body = cls.glance_client.create_image(**params) body = body['image'] if 'image' in body else body image_id = body['id'] cls.images.append(image_id) diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py index 3fefc81cb..6fd6ea6e2 100644 --- a/tempest/api/image/base.py +++ b/tempest/api/image/base.py @@ -14,6 +14,7 @@ from six import moves +from tempest.common import image as common_image from tempest.common.utils import data_utils from tempest import config from tempest.lib.common.utils import test_utils @@ -55,14 +56,20 @@ class BaseImageTest(tempest.test.BaseTestCase): super(BaseImageTest, cls).resource_cleanup() @classmethod - def create_image(cls, **kwargs): + def create_image(cls, data=None, **kwargs): """Wrapper that returns a test image.""" if 'name' not in kwargs: name = data_utils.rand_name(cls.__name__ + "-instance") kwargs['name'] = name - image = cls.client.create_image(**kwargs) + params = cls._get_create_params(**kwargs) + if data: + # NOTE: On glance v1 API, the data should be passed on + # a header. Then here handles the data separately. + params['data'] = data + + image = cls.client.create_image(**params) # Image objects returned by the v1 client have the image # data inside a dict that is keyed against 'image'. if 'image' in image: @@ -70,6 +77,10 @@ class BaseImageTest(tempest.test.BaseTestCase): cls.created_images.append(image['id']) return image + @classmethod + def _get_create_params(cls, **kwargs): + return kwargs + class BaseV1ImageTest(BaseImageTest): @@ -85,6 +96,10 @@ class BaseV1ImageTest(BaseImageTest): super(BaseV1ImageTest, cls).setup_clients() cls.client = cls.os.image_client + @classmethod + def _get_create_params(cls, **kwargs): + return {'headers': common_image.image_meta_to_headers(**kwargs)} + class BaseV1ImageMembersTest(BaseV1ImageTest): diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py index 59ac64666..def775062 100644 --- a/tempest/api/image/v1/test_images.py +++ b/tempest/api/image/v1/test_images.py @@ -320,8 +320,10 @@ class UpdateImageMetaTest(base.BaseV1ImageTest): metadata = common_image.get_image_meta_from_headers(resp) self.assertEqual(metadata['properties'], {'key1': 'value1'}) metadata['properties'].update(req_metadata) - metadata = self.client.update_image( - self.image_id, properties=metadata['properties'])['image'] + headers = common_image.image_meta_to_headers( + properties=metadata['properties']) + metadata = self.client.update_image(self.image_id, + headers=headers)['image'] resp = self.client.check_image(self.image_id) resp_metadata = common_image.get_image_meta_from_headers(resp) diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py index babee7417..9e67c25d0 100644 --- a/tempest/api/image/v1/test_images_negative.py +++ b/tempest/api/image/v1/test_images_negative.py @@ -27,17 +27,17 @@ class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest): def test_register_with_invalid_container_format(self): # Negative tests for invalid data supplied to POST /images self.assertRaises(lib_exc.BadRequest, self.client.create_image, - name='test', - container_format='wrong', - disk_format='vhd',) + headers={'x-image-meta-name': 'test', + 'x-image-meta-container_format': 'wrong', + 'x-image-meta-disk_format': 'vhd'}) @test.attr(type=['negative']) @test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67') def test_register_with_invalid_disk_format(self): self.assertRaises(lib_exc.BadRequest, self.client.create_image, - name='test', - container_format='bare', - disk_format='wrong',) + headers={'x-image-meta-name': 'test', + 'x-image-meta-container_format': 'bare', + 'x-image-meta-disk_format': 'wrong'}) @test.attr(type=['negative']) @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488') diff --git a/tempest/common/image.py b/tempest/common/image.py index 42ce5ac3f..72e3a7207 100644 --- a/tempest/common/image.py +++ b/tempest/common/image.py @@ -13,6 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + +import six + def get_image_meta_from_headers(resp): meta = {'properties': {}} @@ -36,3 +40,23 @@ def get_image_meta_from_headers(resp): except ValueError: pass return meta + + +def image_meta_to_headers(**metadata): + headers = {} + fields_copy = copy.deepcopy(metadata) + + copy_from = fields_copy.pop('copy_from', None) + if copy_from is not None: + headers['x-glance-api-copy-from'] = copy_from + + for key, value in six.iteritems(fields_copy.pop('properties', {})): + headers['x-image-meta-property-%s' % key] = str(value) + + for key, value in six.iteritems(fields_copy.pop('api', {})): + headers['x-glance-api-property-%s' % key] = str(value) + + for key, value in six.iteritems(fields_copy): + headers['x-image-meta-%s' % key] = str(value) + + return headers diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py index dd6e0e50d..11c96e0b4 100644 --- a/tempest/scenario/manager.py +++ b/tempest/scenario/manager.py @@ -388,6 +388,7 @@ class ScenarioTest(tempest.test.BaseTestCase): if CONF.image_feature_enabled.api_v1: params['is_public'] = 'False' params['properties'] = properties + params = {'headers': common_image.image_meta_to_headers(**params)} else: params['visibility'] = 'private' # Additional properties are flattened out in the v2 API. diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py index ed0a67696..0db98f87c 100644 --- a/tempest/services/image/v1/json/images_client.py +++ b/tempest/services/image/v1/json/images_client.py @@ -13,11 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -import copy import functools from oslo_serialization import jsonutils as json -import six from six.moves.urllib import parse as urllib from tempest.lib.common import rest_client @@ -29,20 +27,6 @@ CHUNKSIZE = 1024 * 64 # 64kB class ImagesClient(rest_client.RestClient): api_version = "v1" - def _image_meta_to_headers(self, fields): - headers = {} - fields_copy = copy.deepcopy(fields) - copy_from = fields_copy.pop('copy_from', None) - if copy_from is not None: - headers['x-glance-api-copy-from'] = copy_from - for key, value in six.iteritems(fields_copy.pop('properties', {})): - headers['x-image-meta-property-%s' % key] = str(value) - for key, value in six.iteritems(fields_copy.pop('api', {})): - headers['x-glance-api-property-%s' % key] = str(value) - for key, value in six.iteritems(fields_copy): - headers['x-image-meta-%s' % key] = str(value) - return headers - def _create_with_data(self, headers, data): # We are going to do chunked transfert, so split the input data # info fixed-sized chunks. @@ -74,14 +58,14 @@ class ImagesClient(rest_client.RestClient): self._http = self._get_http() return self._http - def create_image(self, data=None, **kwargs): + def create_image(self, data=None, headers=None): """Create an image. Available params: http://developer.openstack.org/ api-ref-image-v1.html#createImage-v1 """ - headers = {} - headers.update(self._image_meta_to_headers(kwargs)) + if headers is None: + headers = {} if data is not None: return self._create_with_data(headers, data) @@ -91,14 +75,14 @@ class ImagesClient(rest_client.RestClient): body = json.loads(body) return rest_client.ResponseBody(resp, body) - def update_image(self, image_id, data=None, **kwargs): + def update_image(self, image_id, data=None, headers=None): """Update an image. Available params: http://developer.openstack.org/ api-ref-image-v1.html#updateImage-v1 """ - headers = {} - headers.update(self._image_meta_to_headers(kwargs)) + if headers is None: + headers = {} if data is not None: return self._update_with_data(image_id, headers, data) diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py index fdd0ae8b2..34772a228 100644 --- a/tempest/tests/common/test_image.py +++ b/tempest/tests/common/test_image.py @@ -38,3 +38,22 @@ class TestImage(base.TestCase): 'name': 'New Http Image' } self.assertEqual(expected, observed) + + def test_image_meta_to_headers(self): + observed = image.image_meta_to_headers( + name='test', + container_format='wrong', + disk_format='vhd', + copy_from='http://localhost/images/10', + properties={'foo': 'bar'}, + api={'abc': 'def'}) + + expected = { + 'x-image-meta-name': 'test', + 'x-image-meta-container_format': 'wrong', + 'x-image-meta-disk_format': 'vhd', + 'x-glance-api-copy-from': 'http://localhost/images/10', + 'x-image-meta-property-foo': 'bar', + 'x-glance-api-property-abc': 'def' + } + self.assertEqual(expected, observed)