Update to microversion 2.27
This enables the use of the OpenStack-API-Version header, in addition to the older X-OpenStack-Nova-API-Version header. If the client knows that it is in a version less than 2.27, it will not send the newer header. If the client knows that is in a version >= 2.27 if it gets a response that does not have the expected response header (the newer one) it will warn. If it is in an older version, it will warn about the other one missing. A server that is 2.27 or beyond will accept both headers and respond with both. Change-Id: I1f7b1ca0fe795e4ecd333de761d96fff117969c0
This commit is contained in:
parent
2253d02266
commit
082ed80369
@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
|
|||||||
# when client supported the max version, and bumped sequentially, otherwise
|
# when client supported the max version, and bumped sequentially, otherwise
|
||||||
# the client may break due to server side new version may include some
|
# the client may break due to server side new version may include some
|
||||||
# backward incompatible change.
|
# backward incompatible change.
|
||||||
API_MAX_VERSION = api_versions.APIVersion("2.26")
|
API_MAX_VERSION = api_versions.APIVersion("2.27")
|
||||||
|
@ -29,7 +29,9 @@ if not LOG.handlers:
|
|||||||
LOG.addHandler(logging.StreamHandler())
|
LOG.addHandler(logging.StreamHandler())
|
||||||
|
|
||||||
|
|
||||||
HEADER_NAME = "X-OpenStack-Nova-API-Version"
|
LEGACY_HEADER_NAME = "X-OpenStack-Nova-API-Version"
|
||||||
|
HEADER_NAME = "OpenStack-API-Version"
|
||||||
|
SERVICE_TYPE = "compute"
|
||||||
# key is a deprecated version and value is an alternative version.
|
# key is a deprecated version and value is an alternative version.
|
||||||
DEPRECATED_VERSIONS = {"1.1": "2"}
|
DEPRECATED_VERSIONS = {"1.1": "2"}
|
||||||
|
|
||||||
@ -317,19 +319,25 @@ def discover_version(client, requested_version):
|
|||||||
|
|
||||||
|
|
||||||
def update_headers(headers, api_version):
|
def update_headers(headers, api_version):
|
||||||
"""Set 'X-OpenStack-Nova-API-Version' header if api_version is not null"""
|
"""Set microversion headers if api_version is not null"""
|
||||||
|
|
||||||
if not api_version.is_null() and api_version.ver_minor != 0:
|
if not api_version.is_null():
|
||||||
headers[HEADER_NAME] = api_version.get_string()
|
version_string = api_version.get_string()
|
||||||
|
if api_version.ver_minor != 0:
|
||||||
|
headers[LEGACY_HEADER_NAME] = version_string
|
||||||
|
if api_version.ver_minor >= 27:
|
||||||
|
headers[HEADER_NAME] = '%s %s' % (SERVICE_TYPE, version_string)
|
||||||
|
|
||||||
|
|
||||||
def check_headers(response, api_version):
|
def check_headers(response, api_version):
|
||||||
"""Checks that 'X-OpenStack-Nova-API-Version' header in response."""
|
"""Checks that microversion header is in response."""
|
||||||
if api_version.ver_minor > 0 and HEADER_NAME not in response.headers:
|
if api_version.ver_minor > 0:
|
||||||
LOG.warning(_LW(
|
if (api_version.ver_minor < 27
|
||||||
"Your request was processed by a Nova API which does not support "
|
and LEGACY_HEADER_NAME not in response.headers):
|
||||||
"microversions (%s header is missing from response). "
|
_warn_missing_microversion_header(LEGACY_HEADER_NAME)
|
||||||
"Warning: Response may be incorrect."), HEADER_NAME)
|
elif (api_version.ver_minor >= 27
|
||||||
|
and HEADER_NAME not in response.headers):
|
||||||
|
_warn_missing_microversion_header(HEADER_NAME)
|
||||||
|
|
||||||
|
|
||||||
def add_substitution(versioned_method):
|
def add_substitution(versioned_method):
|
||||||
@ -378,3 +386,11 @@ def wraps(start_version, end_version=None):
|
|||||||
return substitution
|
return substitution
|
||||||
|
|
||||||
return decor
|
return decor
|
||||||
|
|
||||||
|
|
||||||
|
def _warn_missing_microversion_header(header_name):
|
||||||
|
"""Log a warning about missing microversion response header."""
|
||||||
|
LOG.warning(_LW(
|
||||||
|
"Your request was processed by a Nova API which does not support "
|
||||||
|
"microversions (%s header is missing from response). "
|
||||||
|
"Warning: Response may be incorrect."), header_name)
|
||||||
|
@ -144,6 +144,18 @@ class UpdateHeadersTestCase(utils.TestCase):
|
|||||||
{"X-OpenStack-Nova-API-Version": api_version.get_string()},
|
{"X-OpenStack-Nova-API-Version": api_version.get_string()},
|
||||||
headers)
|
headers)
|
||||||
|
|
||||||
|
def test_api_version_is_gte_27(self):
|
||||||
|
api_version = api_versions.APIVersion("2.27")
|
||||||
|
headers = {}
|
||||||
|
api_versions.update_headers(headers, api_version)
|
||||||
|
self.assertIn('X-OpenStack-Nova-API-Version', headers)
|
||||||
|
self.assertIn('OpenStack-API-Version', headers)
|
||||||
|
self.assertEqual(api_version.get_string(),
|
||||||
|
headers['X-OpenStack-Nova-API-Version'])
|
||||||
|
self.assertEqual('%s %s' % (api_versions.SERVICE_TYPE,
|
||||||
|
api_version.get_string()),
|
||||||
|
headers['OpenStack-API-Version'])
|
||||||
|
|
||||||
|
|
||||||
class CheckHeadersTestCase(utils.TestCase):
|
class CheckHeadersTestCase(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -152,8 +164,9 @@ class CheckHeadersTestCase(utils.TestCase):
|
|||||||
self.mock_log = mock_log_patch.start()
|
self.mock_log = mock_log_patch.start()
|
||||||
self.addCleanup(mock_log_patch.stop)
|
self.addCleanup(mock_log_patch.stop)
|
||||||
|
|
||||||
def test_microversion_is_specified(self):
|
def test_legacy_microversion_is_specified(self):
|
||||||
response = mock.MagicMock(headers={api_versions.HEADER_NAME: ""})
|
response = mock.MagicMock(
|
||||||
|
headers={api_versions.LEGACY_HEADER_NAME: ""})
|
||||||
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
||||||
self.assertFalse(self.mock_log.warning.called)
|
self.assertFalse(self.mock_log.warning.called)
|
||||||
|
|
||||||
@ -161,8 +174,19 @@ class CheckHeadersTestCase(utils.TestCase):
|
|||||||
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
||||||
self.assertTrue(self.mock_log.warning.called)
|
self.assertTrue(self.mock_log.warning.called)
|
||||||
|
|
||||||
|
def test_generic_microversion_is_specified(self):
|
||||||
|
response = mock.MagicMock(
|
||||||
|
headers={api_versions.HEADER_NAME: ""})
|
||||||
|
api_versions.check_headers(response, api_versions.APIVersion("2.27"))
|
||||||
|
self.assertFalse(self.mock_log.warning.called)
|
||||||
|
|
||||||
|
response = mock.MagicMock(headers={})
|
||||||
|
api_versions.check_headers(response, api_versions.APIVersion("2.27"))
|
||||||
|
self.assertTrue(self.mock_log.warning.called)
|
||||||
|
|
||||||
def test_microversion_is_not_specified(self):
|
def test_microversion_is_not_specified(self):
|
||||||
response = mock.MagicMock(headers={api_versions.HEADER_NAME: ""})
|
response = mock.MagicMock(
|
||||||
|
headers={api_versions.LEGACY_HEADER_NAME: ""})
|
||||||
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
|
||||||
self.assertFalse(self.mock_log.warning.called)
|
self.assertFalse(self.mock_log.warning.called)
|
||||||
|
|
||||||
|
@ -2776,6 +2776,9 @@ class ShellTest(utils.TestCase):
|
|||||||
# new microversion, just an additional checks. See
|
# new microversion, just an additional checks. See
|
||||||
# https://review.openstack.org/#/c/233076/ for more details)
|
# https://review.openstack.org/#/c/233076/ for more details)
|
||||||
20, # doesn't require any changes in novaclient
|
20, # doesn't require any changes in novaclient
|
||||||
|
27, # NOTE(cdent): 27 adds support for updated microversion
|
||||||
|
# headers, and is tested in test_api_versions, but is
|
||||||
|
# not explicitly tested via wraps and _SUBSTITUTIONS.
|
||||||
])
|
])
|
||||||
versions_supported = set(range(0,
|
versions_supported = set(range(0,
|
||||||
novaclient.API_MAX_VERSION.ver_minor + 1))
|
novaclient.API_MAX_VERSION.ver_minor + 1))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user