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:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user