Handle creating an image from a server with microversion > 2.35

The compute 2.35 API deprecates the compute image proxy APIs
for showing/listing/deleting images.

The compute 2.45 API removes the 'location' header from the createImage
server action response and in that same version, adds a response body
with a single "image_id" key.

This change updates the BaseV2ComputeTest.create_image_from_server
helper method to be aware of the microversion that the test running
this code is using, and adjust the client to use appropriately.

As a result, the compute images client needs to be aware of the
changed createImage response schema so that is added for compute
API version 2.45.

Change-Id: I5551af0064f9cca594ae533379d0b0ae14444f88
This commit is contained in:
Matt Riedemann 2018-01-08 15:03:36 -05:00 committed by Steve Noyes
parent 039740add6
commit f110a4ba1f
5 changed files with 71 additions and 19 deletions

View File

@ -99,6 +99,15 @@ class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
cls.versions_client = cls.os_primary.compute_versions_client
if CONF.service_available.cinder:
cls.volumes_client = cls.os_primary.volumes_client_latest
if CONF.service_available.glance:
if CONF.image_feature_enabled.api_v1:
cls.images_client = cls.os_primary.image_client
elif CONF.image_feature_enabled.api_v2:
cls.images_client = cls.os_primary.image_client_v2
else:
raise lib_exc.InvalidConfiguration(
'Either api_v1 or api_v2 must be True in '
'[image-feature-enabled].')
@classmethod
def resource_setup(cls):
@ -254,7 +263,11 @@ class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
@classmethod
def create_image_from_server(cls, server_id, **kwargs):
"""Wrapper utility that returns an image created from the server."""
"""Wrapper utility that returns an image created from the server.
If compute microversion >= 2.36, the returned image response will
be from the image service API rather than the compute image proxy API.
"""
name = kwargs.pop('name',
data_utils.rand_name(cls.__name__ + "-image"))
wait_until = kwargs.pop('wait_until', None)
@ -267,14 +280,21 @@ class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
image_id = image['image_id']
else:
image_id = data_utils.parse_image_id(image.response['location'])
# The compute image proxy APIs were deprecated in 2.35 so
# use the images client directly if the API microversion being
# used is >=2.36.
if api_version_utils.compare_version_header_to_response(
"OpenStack-API-Version", "compute 2.36", image.response, "lt"):
client = cls.images_client
else:
client = cls.compute_images_client
cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
cls.compute_images_client.delete_image,
image_id)
client.delete_image, image_id)
if wait_until is not None:
try:
waiters.wait_for_image_status(cls.compute_images_client,
image_id, wait_until)
waiters.wait_for_image_status(client, image_id, wait_until)
except lib_exc.NotFound:
if wait_until.upper() == 'ACTIVE':
# If the image is not found after create_image returned
@ -292,7 +312,11 @@ class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
image_id=image_id)
else:
raise
image = cls.compute_images_client.show_image(image_id)['image']
image = client.show_image(image_id)
# Compute image client returns response wrapped in 'image' element
# which is not the case with Glance image client.
if 'image' in image:
image = image['image']
if wait_until.upper() == 'ACTIVE':
if wait_for_server:

View File

@ -30,18 +30,6 @@ CONF = config.CONF
class FlavorsV2NegativeTest(base.BaseV2ComputeTest):
@classmethod
def setup_clients(cls):
super(FlavorsV2NegativeTest, cls).setup_clients()
if CONF.image_feature_enabled.api_v1:
cls.images_client = cls.os_primary.image_client
elif CONF.image_feature_enabled.api_v2:
cls.images_client = cls.os_primary.image_client_v2
else:
raise lib_exc.InvalidConfiguration(
'Either api_v1 or api_v2 must be True in '
'[image-feature-enabled].')
@decorators.attr(type=['negative'])
@utils.services('image')
@decorators.idempotent_id('90f0d93a-91c1-450c-91e6-07d18172cefe')

View File

@ -0,0 +1,32 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# The 2.45 microversion removes the "location" header and adds "image_id"
# to the response body.
create_image = {
'status_code': [202],
'response_body': {
'type': 'object',
'properties': {
'image_id': {'type': 'string'}
},
'additionalProperties': False,
'required': ['image_id']
}
}
# NOTE(mriedem): The compute proxy APIs for showing/listing and deleting
# images were deprecated in microversion 2.35, and the compute proxy APIs for
# working with image metadata were deprecated in microversion 2.39. Therefore,
# client-side code shouldn't rely on those APIs in the compute images client
# past those microversions and should instead use the Glance images client
# directly.

View File

@ -17,6 +17,7 @@ from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import images as schema
from tempest.lib.api_schema.response.compute.v2_45 import images as schemav245
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.compute import base_compute_client
@ -24,6 +25,10 @@ from tempest.lib.services.compute import base_compute_client
class ImagesClient(base_compute_client.BaseComputeClient):
schema_versions_info = [
{'min': None, 'max': '2.44', 'schema': schema},
{'min': '2.45', 'max': None, 'schema': schemav245}]
def create_image(self, server_id, **kwargs):
"""Create an image of the original server.
@ -36,7 +41,10 @@ class ImagesClient(base_compute_client.BaseComputeClient):
post_body = json.dumps(post_body)
resp, body = self.post('servers/%s/action' % server_id,
post_body)
self.validate_response(schema.create_image, resp, body)
_schema = self.get_schema(self.schema_versions_info)
if body:
body = json.loads(body)
self.validate_response(_schema.create_image, resp, body)
return rest_client.ResponseBody(resp, body)
def list_images(self, detail=False, **params):