Add compare header version function to tempest.lib
The motivation for this commit is that some API responses like backing up a server image return the location of the image_id in either the response body or the response header depending on the microversion, e.g. [0]. In the case of server backup action, image_id is added to response body in microversion 2.45. Add `compare_version_header_to_request` to `api_version_utils` to accept a new kwarg called `operation`. At run time, 'eq' is translated to the __eq__ attribute of `APIVersionRequest`. The other operations include le, lt, gt, ge, and ne. This makes it possible to do for example: if api_version_utils.compare_version_header_to_response( "X-OpenStack-Nova-API-Version", "2.45", resp, "lt"): image1_id = resp['image_id'] else: image1_id = data_utils.parse_image_id(resp['location']) Which means that if "2.45" < "microversion in resp" then we can grab the image_id from the response body -- else we have to grab it from resp.response['location']. This commit: - adds compare_version_header_to_response to api_version_utils allowing to compare the request's header microversion to an expected microversion - modifies test_server_actions to use the new function in tests that always assume that the image_id attribute is in the resp header (not true across all microversions) -- this can be done to other tests in follow-up patch - adds related unit tests for all scenarios [0] https://developer.openstack.org/api-ref/compute/#create-server-back-up-createbackup-action Change-Id: Ib97e65cca468a09bbeaf68fcfe0e8192674a481e
This commit is contained in:
parent
ad75393a99
commit
9ff5c280ab
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add a new function called ``compare_version_header_to_response`` to
|
||||||
|
``tempest.lib.common.api_version_utils``, which compares the API
|
||||||
|
micoversion in the response header to another microversion using the
|
||||||
|
comparators defined in
|
||||||
|
``tempest.lib.common.api_version_request.APIVersionRequest``.
|
||||||
|
|
||||||
|
It is now possible to determine how to retrieve an attribute from a
|
||||||
|
response body of an API call, depending on the returned microversion.
|
||||||
|
|
||||||
|
Add a new exception type called ``InvalidParam`` to
|
||||||
|
``tempest.lib.exceptions``, allowing the possibility of raising an
|
||||||
|
exception if an invalid parameter is passed to a library function.
|
@ -23,6 +23,7 @@ from tempest.common import utils
|
|||||||
from tempest.common.utils.linux import remote_client
|
from tempest.common.utils.linux import remote_client
|
||||||
from tempest.common import waiters
|
from tempest.common import waiters
|
||||||
from tempest import config
|
from tempest import config
|
||||||
|
from tempest.lib.common import api_version_utils
|
||||||
from tempest.lib.common.utils import data_utils
|
from tempest.lib.common.utils import data_utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
from tempest.lib import exceptions as lib_exc
|
from tempest.lib import exceptions as lib_exc
|
||||||
@ -369,7 +370,11 @@ class ServerActionsTestJSON(base.BaseV2ComputeTest):
|
|||||||
"been successful as it should have been "
|
"been successful as it should have been "
|
||||||
"deleted during rotation.", oldest_backup)
|
"deleted during rotation.", oldest_backup)
|
||||||
|
|
||||||
image1_id = data_utils.parse_image_id(resp['location'])
|
if api_version_utils.compare_version_header_to_response(
|
||||||
|
"OpenStack-API-Version", "compute 2.45", resp, "lt"):
|
||||||
|
image1_id = resp['image_id']
|
||||||
|
else:
|
||||||
|
image1_id = data_utils.parse_image_id(resp['location'])
|
||||||
self.addCleanup(_clean_oldest_backup, image1_id)
|
self.addCleanup(_clean_oldest_backup, image1_id)
|
||||||
waiters.wait_for_image_status(glance_client,
|
waiters.wait_for_image_status(glance_client,
|
||||||
image1_id, 'active')
|
image1_id, 'active')
|
||||||
@ -380,7 +385,11 @@ class ServerActionsTestJSON(base.BaseV2ComputeTest):
|
|||||||
backup_type='daily',
|
backup_type='daily',
|
||||||
rotation=2,
|
rotation=2,
|
||||||
name=backup2).response
|
name=backup2).response
|
||||||
image2_id = data_utils.parse_image_id(resp['location'])
|
if api_version_utils.compare_version_header_to_response(
|
||||||
|
"OpenStack-API-Version", "compute 2.45", resp, "lt"):
|
||||||
|
image2_id = resp['image_id']
|
||||||
|
else:
|
||||||
|
image2_id = data_utils.parse_image_id(resp['location'])
|
||||||
self.addCleanup(glance_client.delete_image, image2_id)
|
self.addCleanup(glance_client.delete_image, image2_id)
|
||||||
waiters.wait_for_image_status(glance_client,
|
waiters.wait_for_image_status(glance_client,
|
||||||
image2_id, 'active')
|
image2_id, 'active')
|
||||||
@ -419,7 +428,11 @@ class ServerActionsTestJSON(base.BaseV2ComputeTest):
|
|||||||
backup_type='daily',
|
backup_type='daily',
|
||||||
rotation=2,
|
rotation=2,
|
||||||
name=backup3).response
|
name=backup3).response
|
||||||
image3_id = data_utils.parse_image_id(resp['location'])
|
if api_version_utils.compare_version_header_to_response(
|
||||||
|
"OpenStack-API-Version", "compute 2.45", resp, "lt"):
|
||||||
|
image3_id = resp['image_id']
|
||||||
|
else:
|
||||||
|
image3_id = data_utils.parse_image_id(resp['location'])
|
||||||
self.addCleanup(glance_client.delete_image, image3_id)
|
self.addCleanup(glance_client.delete_image, image3_id)
|
||||||
# the first back up should be deleted
|
# the first back up should be deleted
|
||||||
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
|
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from tempest.lib.common import api_version_request
|
from tempest.lib.common import api_version_request
|
||||||
@ -19,6 +20,7 @@ from tempest.lib import exceptions
|
|||||||
|
|
||||||
|
|
||||||
LATEST_MICROVERSION = 'latest'
|
LATEST_MICROVERSION = 'latest'
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BaseMicroversionTest(object):
|
class BaseMicroversionTest(object):
|
||||||
@ -120,3 +122,60 @@ def assert_version_header_matches_request(api_microversion_header_name,
|
|||||||
api_microversion,
|
api_microversion,
|
||||||
response_header))
|
response_header))
|
||||||
raise exceptions.InvalidHTTPResponseHeader(msg)
|
raise exceptions.InvalidHTTPResponseHeader(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def compare_version_header_to_response(api_microversion_header_name,
|
||||||
|
api_microversion,
|
||||||
|
response_header,
|
||||||
|
operation='eq'):
|
||||||
|
"""Compares API microversion in response header to ``api_microversion``.
|
||||||
|
|
||||||
|
Compare the ``api_microversion`` value in response header if microversion
|
||||||
|
header is present in response, otherwise return false.
|
||||||
|
|
||||||
|
To make this function work for APIs which do not return microversion
|
||||||
|
header in response (example compute v2.0), this function does *not* raise
|
||||||
|
InvalidHTTPResponseHeader.
|
||||||
|
|
||||||
|
:param api_microversion_header_name: Microversion header name. Example:
|
||||||
|
'Openstack-Api-Version'.
|
||||||
|
:param api_microversion: Microversion number. Example:
|
||||||
|
|
||||||
|
* '2.10' for the old-style header name, 'X-OpenStack-Nova-API-Version'
|
||||||
|
* 'Compute 2.10' for the new-style header name, 'Openstack-Api-Version'
|
||||||
|
|
||||||
|
:param response_header: Response header where microversion is
|
||||||
|
expected to be present.
|
||||||
|
:param operation: The boolean operation to use to compare the
|
||||||
|
``api_microversion`` to the microversion in ``response_header``.
|
||||||
|
Can be 'lt', 'eq', 'gt', 'le', 'ne', 'ge'. Default is 'eq'. The
|
||||||
|
operation type should be based on the order of the arguments:
|
||||||
|
``api_microversion`` <operation> ``response_header`` microversion.
|
||||||
|
:returns: True if the comparison is logically true, else False if the
|
||||||
|
comparison is logically false or if ``api_microversion_header_name`` is
|
||||||
|
missing in the ``response_header``.
|
||||||
|
:raises InvalidParam: If the operation is not lt, eq, gt, le, ne or ge.
|
||||||
|
"""
|
||||||
|
api_microversion_header_name = api_microversion_header_name.lower()
|
||||||
|
if api_microversion_header_name not in response_header:
|
||||||
|
return False
|
||||||
|
|
||||||
|
op = getattr(api_version_request.APIVersionRequest,
|
||||||
|
'__%s__' % operation, None)
|
||||||
|
|
||||||
|
if op is None:
|
||||||
|
msg = ("Operation %s is invalid. Valid options include: lt, eq, gt, "
|
||||||
|
"le, ne, ge." % operation)
|
||||||
|
LOG.debug(msg)
|
||||||
|
raise exceptions.InvalidParam(invalid_param=msg)
|
||||||
|
|
||||||
|
# Remove "volume" from "volume <microversion>", for example, so that the
|
||||||
|
# microversion can be converted to `APIVersionRequest`.
|
||||||
|
api_version = api_microversion.split(' ')[-1]
|
||||||
|
resp_version = response_header[api_microversion_header_name].split(' ')[-1]
|
||||||
|
if not op(
|
||||||
|
api_version_request.APIVersionRequest(api_version),
|
||||||
|
api_version_request.APIVersionRequest(resp_version)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -276,3 +276,7 @@ class DeleteErrorException(TempestException):
|
|||||||
|
|
||||||
class InvalidTestResource(TempestException):
|
class InvalidTestResource(TempestException):
|
||||||
message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
|
message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidParam(TempestException):
|
||||||
|
message = ("Invalid Parameter passed: %(invalid_param)s")
|
||||||
|
@ -92,24 +92,106 @@ class TestMicroversionHeaderMatches(base.TestCase):
|
|||||||
def test_header_matches(self):
|
def test_header_matches(self):
|
||||||
microversion_header_name = 'x-openstack-xyz-api-version'
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
request_microversion = '2.1'
|
request_microversion = '2.1'
|
||||||
test_respose = {microversion_header_name: request_microversion}
|
test_response = {microversion_header_name: request_microversion}
|
||||||
api_version_utils.assert_version_header_matches_request(
|
api_version_utils.assert_version_header_matches_request(
|
||||||
microversion_header_name, request_microversion, test_respose)
|
microversion_header_name, request_microversion, test_response)
|
||||||
|
|
||||||
def test_header_does_not_match(self):
|
def test_header_does_not_match(self):
|
||||||
microversion_header_name = 'x-openstack-xyz-api-version'
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
request_microversion = '2.1'
|
request_microversion = '2.1'
|
||||||
test_respose = {microversion_header_name: '2.2'}
|
test_response = {microversion_header_name: '2.2'}
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.InvalidHTTPResponseHeader,
|
exceptions.InvalidHTTPResponseHeader,
|
||||||
api_version_utils.assert_version_header_matches_request,
|
api_version_utils.assert_version_header_matches_request,
|
||||||
microversion_header_name, request_microversion, test_respose)
|
microversion_header_name, request_microversion, test_response)
|
||||||
|
|
||||||
def test_header_not_present(self):
|
def test_header_not_present(self):
|
||||||
microversion_header_name = 'x-openstack-xyz-api-version'
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
request_microversion = '2.1'
|
request_microversion = '2.1'
|
||||||
test_respose = {}
|
test_response = {}
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.InvalidHTTPResponseHeader,
|
exceptions.InvalidHTTPResponseHeader,
|
||||||
api_version_utils.assert_version_header_matches_request,
|
api_version_utils.assert_version_header_matches_request,
|
||||||
microversion_header_name, request_microversion, test_respose)
|
microversion_header_name, request_microversion, test_response)
|
||||||
|
|
||||||
|
def test_compare_versions_less_than(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.2'
|
||||||
|
test_response = {microversion_header_name: '2.1'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"lt"))
|
||||||
|
|
||||||
|
def test_compare_versions_less_than_equal(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.2'
|
||||||
|
test_response = {microversion_header_name: '2.1'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"le"))
|
||||||
|
|
||||||
|
def test_compare_versions_greater_than_equal(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.1'
|
||||||
|
test_response = {microversion_header_name: '2.2'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"ge"))
|
||||||
|
|
||||||
|
def test_compare_versions_greater_than(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.1'
|
||||||
|
test_response = {microversion_header_name: '2.2'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"gt"))
|
||||||
|
|
||||||
|
def test_compare_versions_equal(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.11'
|
||||||
|
test_response = {microversion_header_name: '2.1'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"eq"))
|
||||||
|
|
||||||
|
def test_compare_versions_not_equal(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.1'
|
||||||
|
test_response = {microversion_header_name: '2.1'}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"ne"))
|
||||||
|
|
||||||
|
def test_compare_versions_with_name_in_microversion(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = 'volume 3.1'
|
||||||
|
test_response = {microversion_header_name: 'volume 3.1'}
|
||||||
|
self.assertTrue(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"eq"))
|
||||||
|
|
||||||
|
def test_compare_versions_invalid_operation(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.1'
|
||||||
|
test_response = {microversion_header_name: '2.1'}
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.InvalidParam,
|
||||||
|
api_version_utils.compare_version_header_to_response,
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"foo")
|
||||||
|
|
||||||
|
def test_compare_versions_header_not_present(self):
|
||||||
|
microversion_header_name = 'x-openstack-xyz-api-version'
|
||||||
|
request_microversion = '2.1'
|
||||||
|
test_response = {}
|
||||||
|
self.assertFalse(
|
||||||
|
api_version_utils.compare_version_header_to_response(
|
||||||
|
microversion_header_name, request_microversion, test_response,
|
||||||
|
"eq"))
|
||||||
|
Loading…
Reference in New Issue
Block a user