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
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)

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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)