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
This commit is contained in:
Ken'ichi Ohmichi
2016-06-17 16:41:26 -07:00
parent f9b4068fb1
commit 02bcdf36db
9 changed files with 100 additions and 46 deletions

View File

@@ -16,6 +16,7 @@
import six import six
from tempest.api.compute import base from tempest.api.compute import base
from tempest.common import image as common_image
from tempest.common.utils import data_utils from tempest.common.utils import data_utils
from tempest.common import waiters from tempest.common import waiters
from tempest import config from tempest import config
@@ -55,15 +56,18 @@ class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
super(ImagesMetadataTestJSON, cls).resource_setup() super(ImagesMetadataTestJSON, cls).resource_setup()
cls.image_id = None 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: 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: else:
kwargs = dict(visibility='private') params.update({'visibility': 'private'})
body = cls.glance_client.create_image(name=name,
container_format='bare', body = cls.glance_client.create_image(**params)
disk_format='raw',
**kwargs)
body = body['image'] if 'image' in body else body body = body['image'] if 'image' in body else body
cls.image_id = body['id'] cls.image_id = body['id']
cls.images.append(cls.image_id) cls.images.append(cls.image_id)

View File

@@ -19,6 +19,7 @@ import six
import testtools import testtools
from tempest.api.compute import base from tempest.api.compute import base
from tempest.common import image as common_image
from tempest.common.utils import data_utils from tempest.common.utils import data_utils
from tempest.common import waiters from tempest.common import waiters
from tempest import config from tempest import config
@@ -58,15 +59,19 @@ class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
super(ListImageFiltersTestJSON, cls).resource_setup() super(ListImageFiltersTestJSON, cls).resource_setup()
def _create_image(): 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: 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: else:
kwargs = dict(visibility='private') params.update({'visibility': 'private'})
body = cls.glance_client.create_image(name=name,
container_format='bare', body = cls.glance_client.create_image(**params)
disk_format='raw',
**kwargs)
body = body['image'] if 'image' in body else body body = body['image'] if 'image' in body else body
image_id = body['id'] image_id = body['id']
cls.images.append(image_id) cls.images.append(image_id)

View File

@@ -14,6 +14,7 @@
from six import moves from six import moves
from tempest.common import image as common_image
from tempest.common.utils import data_utils from tempest.common.utils import data_utils
from tempest import config from tempest import config
from tempest.lib.common.utils import test_utils from tempest.lib.common.utils import test_utils
@@ -55,14 +56,20 @@ class BaseImageTest(tempest.test.BaseTestCase):
super(BaseImageTest, cls).resource_cleanup() super(BaseImageTest, cls).resource_cleanup()
@classmethod @classmethod
def create_image(cls, **kwargs): def create_image(cls, data=None, **kwargs):
"""Wrapper that returns a test image.""" """Wrapper that returns a test image."""
if 'name' not in kwargs: if 'name' not in kwargs:
name = data_utils.rand_name(cls.__name__ + "-instance") name = data_utils.rand_name(cls.__name__ + "-instance")
kwargs['name'] = name 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 # Image objects returned by the v1 client have the image
# data inside a dict that is keyed against 'image'. # data inside a dict that is keyed against 'image'.
if 'image' in image: if 'image' in image:
@@ -70,6 +77,10 @@ class BaseImageTest(tempest.test.BaseTestCase):
cls.created_images.append(image['id']) cls.created_images.append(image['id'])
return image return image
@classmethod
def _get_create_params(cls, **kwargs):
return kwargs
class BaseV1ImageTest(BaseImageTest): class BaseV1ImageTest(BaseImageTest):
@@ -85,6 +96,10 @@ class BaseV1ImageTest(BaseImageTest):
super(BaseV1ImageTest, cls).setup_clients() super(BaseV1ImageTest, cls).setup_clients()
cls.client = cls.os.image_client 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): class BaseV1ImageMembersTest(BaseV1ImageTest):

View File

@@ -320,8 +320,10 @@ class UpdateImageMetaTest(base.BaseV1ImageTest):
metadata = common_image.get_image_meta_from_headers(resp) metadata = common_image.get_image_meta_from_headers(resp)
self.assertEqual(metadata['properties'], {'key1': 'value1'}) self.assertEqual(metadata['properties'], {'key1': 'value1'})
metadata['properties'].update(req_metadata) metadata['properties'].update(req_metadata)
metadata = self.client.update_image( headers = common_image.image_meta_to_headers(
self.image_id, properties=metadata['properties'])['image'] properties=metadata['properties'])
metadata = self.client.update_image(self.image_id,
headers=headers)['image']
resp = self.client.check_image(self.image_id) resp = self.client.check_image(self.image_id)
resp_metadata = common_image.get_image_meta_from_headers(resp) resp_metadata = common_image.get_image_meta_from_headers(resp)

View File

@@ -27,17 +27,17 @@ class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest):
def test_register_with_invalid_container_format(self): def test_register_with_invalid_container_format(self):
# Negative tests for invalid data supplied to POST /images # Negative tests for invalid data supplied to POST /images
self.assertRaises(lib_exc.BadRequest, self.client.create_image, self.assertRaises(lib_exc.BadRequest, self.client.create_image,
name='test', headers={'x-image-meta-name': 'test',
container_format='wrong', 'x-image-meta-container_format': 'wrong',
disk_format='vhd',) 'x-image-meta-disk_format': 'vhd'})
@test.attr(type=['negative']) @test.attr(type=['negative'])
@test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67') @test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
def test_register_with_invalid_disk_format(self): def test_register_with_invalid_disk_format(self):
self.assertRaises(lib_exc.BadRequest, self.client.create_image, self.assertRaises(lib_exc.BadRequest, self.client.create_image,
name='test', headers={'x-image-meta-name': 'test',
container_format='bare', 'x-image-meta-container_format': 'bare',
disk_format='wrong',) 'x-image-meta-disk_format': 'wrong'})
@test.attr(type=['negative']) @test.attr(type=['negative'])
@test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488') @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488')

View File

@@ -13,6 +13,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import six
def get_image_meta_from_headers(resp): def get_image_meta_from_headers(resp):
meta = {'properties': {}} meta = {'properties': {}}
@@ -36,3 +40,23 @@ def get_image_meta_from_headers(resp):
except ValueError: except ValueError:
pass pass
return meta 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

View File

@@ -388,6 +388,7 @@ class ScenarioTest(tempest.test.BaseTestCase):
if CONF.image_feature_enabled.api_v1: if CONF.image_feature_enabled.api_v1:
params['is_public'] = 'False' params['is_public'] = 'False'
params['properties'] = properties params['properties'] = properties
params = {'headers': common_image.image_meta_to_headers(**params)}
else: else:
params['visibility'] = 'private' params['visibility'] = 'private'
# Additional properties are flattened out in the v2 API. # Additional properties are flattened out in the v2 API.

View File

@@ -13,11 +13,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import functools import functools
from oslo_serialization import jsonutils as json from oslo_serialization import jsonutils as json
import six
from six.moves.urllib import parse as urllib from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client from tempest.lib.common import rest_client
@@ -29,20 +27,6 @@ CHUNKSIZE = 1024 * 64 # 64kB
class ImagesClient(rest_client.RestClient): class ImagesClient(rest_client.RestClient):
api_version = "v1" 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): def _create_with_data(self, headers, data):
# We are going to do chunked transfert, so split the input data # We are going to do chunked transfert, so split the input data
# info fixed-sized chunks. # info fixed-sized chunks.
@@ -74,14 +58,14 @@ class ImagesClient(rest_client.RestClient):
self._http = self._get_http() self._http = self._get_http()
return self._http return self._http
def create_image(self, data=None, **kwargs): def create_image(self, data=None, headers=None):
"""Create an image. """Create an image.
Available params: http://developer.openstack.org/ Available params: http://developer.openstack.org/
api-ref-image-v1.html#createImage-v1 api-ref-image-v1.html#createImage-v1
""" """
headers = {} if headers is None:
headers.update(self._image_meta_to_headers(kwargs)) headers = {}
if data is not None: if data is not None:
return self._create_with_data(headers, data) return self._create_with_data(headers, data)
@@ -91,14 +75,14 @@ class ImagesClient(rest_client.RestClient):
body = json.loads(body) body = json.loads(body)
return rest_client.ResponseBody(resp, 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. """Update an image.
Available params: http://developer.openstack.org/ Available params: http://developer.openstack.org/
api-ref-image-v1.html#updateImage-v1 api-ref-image-v1.html#updateImage-v1
""" """
headers = {} if headers is None:
headers.update(self._image_meta_to_headers(kwargs)) headers = {}
if data is not None: if data is not None:
return self._update_with_data(image_id, headers, data) return self._update_with_data(image_id, headers, data)

View File

@@ -38,3 +38,22 @@ class TestImage(base.TestCase):
'name': 'New Http Image' 'name': 'New Http Image'
} }
self.assertEqual(expected, observed) 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)