summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuby Loo <ruby.loo@intel.com>2017-11-07 17:47:40 -0500
committerRuby Loo <ruby.loo@intel.com>2017-12-04 10:17:30 -0500
commitfeac8cfb786f7490743b3f270aa569054795ef4f (patch)
tree22d4571c01351e412ae92d6d0cb4e6fd99e8d8ae
parent638943dbf8e4adbcbe81a2af0f8a1d5ffd9771f6 (diff)
downloadironic-feac8cfb786f7490743b3f270aa569054795ef4f.tar.gz
Pin API version during rolling upgrade
During a rolling upgrade, when the new services are pinned to the old release, the API version will also be pinned to the old release. This will prevent users from accessing new features that may not quite work. The .sample was updated to reflect the change to the help string for the [DEFAULT]/pin_release_version configuration option. The update also pulled in changes for other options, from other (non-ironic) libraries. Change-Id: I38a0f106b589945fb62071f3dfe5bff43c6fee93 Partial-Bug: #1708549
-rw-r--r--etc/ironic/ironic.conf.sample47
-rw-r--r--ironic/api/controllers/root.py4
-rw-r--r--ironic/api/controllers/v1/__init__.py35
-rw-r--r--ironic/api/controllers/v1/versions.py38
-rw-r--r--ironic/common/release_mappings.py6
-rw-r--r--ironic/conf/default.py8
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_chassis.py8
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_node.py89
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_port.py24
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_portgroup.py18
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_ramdisk.py24
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_root.py12
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_versions.py34
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_volume.py4
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_volume_connector.py22
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_volume_target.py20
-rw-r--r--ironic/tests/unit/api/test_root.py5
-rw-r--r--ironic/tests/unit/common/test_release_mappings.py10
-rw-r--r--releasenotes/notes/pin-api-version-029748f7d3be68d1.yaml8
19 files changed, 262 insertions, 154 deletions
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index 90242a0a1..dd0702ddb 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -365,13 +365,12 @@
#host = localhost
# Used for rolling upgrades. Setting this option downgrades
-# (or pins) the internal ironic RPC communication and database
-# objects to their respective versions, so they are compatible
-# with older services. When doing a rolling upgrade from
-# version N to version N+1, set (to pin) this to N. To unpin
-# (default), leave it unset and the latest versions of RPC
-# communication and database objects will be used. (string
-# value)
+# (or pins) the Bare Metal API, the internal ironic RPC
+# communication, and the database objects to their respective
+# versions, so they are compatible with older services. When
+# doing a rolling upgrade from version N to version N+1, set
+# (to pin) this to N. To unpin (default), leave it unset and
+# the latest versions will be used. (string value)
# Allowed values: pike, 9.2, 9.1, 9.0, 8.0
#pin_release_version = <None>
@@ -1025,7 +1024,9 @@
#domain_name = <None>
# Always use this endpoint URL for requests for this client.
-# (string value)
+# NOTE: The unversioned endpoint should be specified here; to
+# request a particular API version, use the `version`, `min-
+# version`, and/or `max-version` options. (string value)
#endpoint_override = <None>
# Verify HTTPS connections. (boolean value)
@@ -1662,7 +1663,9 @@
#domain_name = <None>
# Always use this endpoint URL for requests for this client.
-# (string value)
+# NOTE: The unversioned endpoint should be specified here; to
+# request a particular API version, use the `version`, `min-
+# version`, and/or `max-version` options. (string value)
#endpoint_override = <None>
# DEPRECATED: Allow to perform insecure SSL (https) requests
@@ -1972,7 +1975,9 @@
#enabled = false
# Always use this endpoint URL for requests for this client.
-# (string value)
+# NOTE: The unversioned endpoint should be specified here; to
+# request a particular API version, use the `version`, `min-
+# version`, and/or `max-version` options. (string value)
#endpoint_override = <None>
# Verify HTTPS connections. (boolean value)
@@ -2234,6 +2239,24 @@
# service user utilizes for validating tokens, because normal
# end users may not be able to reach that endpoint. (string
# value)
+# Deprecated group/name - [keystone_authtoken]/auth_uri
+#www_authenticate_uri = <None>
+
+# DEPRECATED: Complete "public" Identity API endpoint. This
+# endpoint should not be an "admin" endpoint, as it should be
+# accessible by all end users. Unauthenticated clients are
+# redirected to this endpoint to authenticate. Although this
+# endpoint should ideally be unversioned, client support in
+# the wild varies. If you're using a versioned v2 endpoint
+# here, then this should *not* be the same endpoint the
+# service user utilizes for validating tokens, because normal
+# end users may not be able to reach that endpoint. This
+# option is deprecated in favor of www_authenticate_uri and
+# will be removed in the S release. (string value)
+# This option is deprecated for removal since Queens.
+# Its value may be silently ignored in the future.
+# Reason: The auth_uri option is deprecated in favor of
+# www_authenticate_uri and will be removed in the S release.
#auth_uri = <None>
# API version of the admin Identity API endpoint. (string
@@ -3800,7 +3823,9 @@
#domain_name = <None>
# Always use this endpoint URL for requests for this client.
-# (string value)
+# NOTE: The unversioned endpoint should be specified here; to
+# request a particular API version, use the `version`, `min-
+# version`, and/or `max-version` options. (string value)
#endpoint_override = <None>
# Verify HTTPS connections. (boolean value)
diff --git a/ironic/api/controllers/root.py b/ironic/api/controllers/root.py
index 1a359b505..5fc14a530 100644
--- a/ironic/api/controllers/root.py
+++ b/ironic/api/controllers/root.py
@@ -85,8 +85,8 @@ class Root(base.APIBase):
root.description = ("Ironic is an OpenStack project which aims to "
"provision baremetal machines.")
root.default_version = Version(ID_VERSION1,
- versions.MIN_VERSION_STRING,
- versions.MAX_VERSION_STRING)
+ versions.min_version_string(),
+ versions.max_version_string())
root.versions = [root.default_version]
return root
diff --git a/ironic/api/controllers/v1/__init__.py b/ironic/api/controllers/v1/__init__.py
index e2ac631a3..a94a14ac9 100644
--- a/ironic/api/controllers/v1/__init__.py
+++ b/ironic/api/controllers/v1/__init__.py
@@ -39,12 +39,17 @@ from ironic.common.i18n import _
BASE_VERSION = versions.BASE_VERSION
-MIN_VER = base.Version(
- {base.Version.string: versions.MIN_VERSION_STRING},
- versions.MIN_VERSION_STRING, versions.MAX_VERSION_STRING)
-MAX_VER = base.Version(
- {base.Version.string: versions.MAX_VERSION_STRING},
- versions.MIN_VERSION_STRING, versions.MAX_VERSION_STRING)
+
+def min_version():
+ return base.Version(
+ {base.Version.string: versions.min_version_string()},
+ versions.min_version_string(), versions.max_version_string())
+
+
+def max_version():
+ return base.Version(
+ {base.Version.string: versions.max_version_string()},
+ versions.min_version_string(), versions.max_version_string())
class MediaType(base.APIBase):
@@ -200,29 +205,29 @@ class Controller(rest.RestController):
"Mutually exclusive versions requested. Version %(ver)s "
"requested but not supported by this service. The supported "
"version range is: [%(min)s, %(max)s].") %
- {'ver': version, 'min': versions.MIN_VERSION_STRING,
- 'max': versions.MAX_VERSION_STRING},
+ {'ver': version, 'min': versions.min_version_string(),
+ 'max': versions.max_version_string()},
headers=headers)
# ensure the minor version is within the supported range
- if version < MIN_VER or version > MAX_VER:
+ if version < min_version() or version > max_version():
raise exc.HTTPNotAcceptable(_(
"Version %(ver)s was requested but the minor version is not "
"supported by this service. The supported version range is: "
"[%(min)s, %(max)s].") %
- {'ver': version, 'min': versions.MIN_VERSION_STRING,
- 'max': versions.MAX_VERSION_STRING},
+ {'ver': version, 'min': versions.min_version_string(),
+ 'max': versions.max_version_string()},
headers=headers)
@pecan.expose()
def _route(self, args, request=None):
- v = base.Version(pecan.request.headers, versions.MIN_VERSION_STRING,
- versions.MAX_VERSION_STRING)
+ v = base.Version(pecan.request.headers, versions.min_version_string(),
+ versions.max_version_string())
# Always set the min and max headers
pecan.response.headers[base.Version.min_string] = (
- versions.MIN_VERSION_STRING)
+ versions.min_version_string())
pecan.response.headers[base.Version.max_string] = (
- versions.MAX_VERSION_STRING)
+ versions.max_version_string())
# assert that requested version is supported
self._check_version(v, pecan.response.headers)
diff --git a/ironic/api/controllers/v1/versions.py b/ironic/api/controllers/v1/versions.py
index 9e2340f08..c49638928 100644
--- a/ironic/api/controllers/v1/versions.py
+++ b/ironic/api/controllers/v1/versions.py
@@ -13,6 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_config import cfg
+
+from ironic.common import release_mappings
+
+CONF = cfg.CONF
+
# This is the version 1 API
BASE_VERSION = 1
@@ -104,11 +110,33 @@ MINOR_33_STORAGE_INTERFACE = 33
MINOR_34_PORT_PHYSICAL_NETWORK = 34
MINOR_35_REBUILD_CONFIG_DRIVE = 35
-# When adding another version, update MINOR_MAX_VERSION and also update
-# doc/source/dev/webapi-version-history.rst with a detailed explanation of
-# what the version has changed.
+# When adding another version, update:
+# - MINOR_MAX_VERSION
+# - doc/source/dev/webapi-version-history.rst with a detailed explanation of
+# what changed in the new version
+# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
+
MINOR_MAX_VERSION = MINOR_35_REBUILD_CONFIG_DRIVE
# String representations of the minor and maximum versions
-MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
-MAX_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_MAX_VERSION)
+_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
+_MAX_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_MAX_VERSION)
+
+
+def min_version_string():
+ """Returns the minimum supported API version (as a string)"""
+ return _MIN_VERSION_STRING
+
+
+def max_version_string():
+ """Returns the maximum supported API version (as a string).
+
+ If the service is pinned, the maximum API version is the pinned
+ version. Otherwise, it is the maximum supported API version.
+ """
+ release_ver = release_mappings.RELEASE_MAPPING.get(
+ CONF.pin_release_version)
+ if release_ver:
+ return release_ver['api']
+ else:
+ return _MAX_VERSION_STRING
diff --git a/ironic/common/release_mappings.py b/ironic/common/release_mappings.py
index 837de2e0f..2f6a9ecac 100644
--- a/ironic/common/release_mappings.py
+++ b/ironic/common/release_mappings.py
@@ -26,6 +26,7 @@
# NOTE(xek): The format of this dict is:
# { '<release version>': {
+# 'api': '<Bare Metal API version>',
# 'rpc': '<RPC API version>',
# 'objects': {
# '<object class name>': ['<object version>'],
@@ -55,6 +56,7 @@
RELEASE_MAPPING = {
'8.0': {
+ 'api': '1.31',
'rpc': '1.40',
'objects': {
'Node': ['1.21'],
@@ -67,6 +69,7 @@ RELEASE_MAPPING = {
}
},
'9.0': {
+ 'api': '1.34',
'rpc': '1.41',
'objects': {
'Node': ['1.21'],
@@ -79,6 +82,7 @@ RELEASE_MAPPING = {
}
},
'9.1': {
+ 'api': '1.34',
'rpc': '1.41',
'objects': {
'Node': ['1.21'],
@@ -92,6 +96,7 @@ RELEASE_MAPPING = {
},
'9.2': {
'rpc': '1.41',
+ 'api': '1.35',
'objects': {
'Node': ['1.21'],
'Conductor': ['1.2'],
@@ -103,6 +108,7 @@ RELEASE_MAPPING = {
}
},
'master': {
+ 'api': '1.35',
'rpc': '1.41',
'objects': {
'Node': ['1.21'],
diff --git a/ironic/conf/default.py b/ironic/conf/default.py
index 23fc18793..58cd7753a 100644
--- a/ironic/conf/default.py
+++ b/ironic/conf/default.py
@@ -275,13 +275,13 @@ service_opts = [
choices=versions.RELEASE_VERSIONS,
# TODO(xek): mutable=True,
help=_('Used for rolling upgrades. Setting this option '
- 'downgrades (or pins) the internal ironic RPC '
- 'communication and database objects to their respective '
+ 'downgrades (or pins) the Bare Metal API, '
+ 'the internal ironic RPC communication, and '
+ 'the database objects to their respective '
'versions, so they are compatible with older services. '
'When doing a rolling upgrade from version N to version '
'N+1, set (to pin) this to N. To unpin (default), leave '
- 'it unset and the latest versions of RPC communication '
- 'and database objects will be used.')),
+ 'it unset and the latest versions will be used.')),
]
utils_opts = [
diff --git a/ironic/tests/unit/api/controllers/v1/test_chassis.py b/ironic/tests/unit/api/controllers/v1/test_chassis.py
index 87412e1d8..94e278c57 100644
--- a/ironic/tests/unit/api/controllers/v1/test_chassis.py
+++ b/ironic/tests/unit/api/controllers/v1/test_chassis.py
@@ -77,7 +77,7 @@ class TestListChassis(test_api_base.BaseApiTest):
fields = 'extra,description'
data = self.get_json(
'/chassis/%s?fields=%s' % (chassis.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
# We always append "links"
self.assertItemsEqual(['description', 'extra', 'links'], data)
@@ -89,7 +89,7 @@ class TestListChassis(test_api_base.BaseApiTest):
data = self.get_json(
'/chassis?fields=%s' % fields,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(3, len(data['chassis']))
for ch in data['chassis']:
@@ -101,7 +101,7 @@ class TestListChassis(test_api_base.BaseApiTest):
fields = 'uuid,spongebob'
response = self.get_json(
'/chassis/%s?fields=%s' % (chassis.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
@@ -112,7 +112,7 @@ class TestListChassis(test_api_base.BaseApiTest):
fields = 'uuid,extra'
response = self.get_json(
'/chassis/%s?fields=%s' % (chassis.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
diff --git a/ironic/tests/unit/api/controllers/v1/test_node.py b/ironic/tests/unit/api/controllers/v1/test_node.py
index c183c5d30..be6faf14e 100644
--- a/ironic/tests/unit/api/controllers/v1/test_node.py
+++ b/ironic/tests/unit/api/controllers/v1/test_node.py
@@ -90,7 +90,8 @@ class TestListNodes(test_api_base.BaseApiTest):
node = obj_utils.create_test_node(self.context,
chassis_id=self.chassis.id)
data = self.get_json(
- '/nodes', headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ '/nodes',
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertIn('instance_uuid', data['nodes'][0])
self.assertIn('maintenance', data['nodes'][0])
self.assertIn('power_state', data['nodes'][0])
@@ -125,7 +126,7 @@ class TestListNodes(test_api_base.BaseApiTest):
chassis_id=self.chassis.id)
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(node.uuid, data['uuid'])
self.assertIn('driver', data)
self.assertIn('driver_info', data)
@@ -184,7 +185,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'extra,instance_info'
data = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
# We always append "links"
self.assertItemsEqual(['extra', 'instance_info', 'links'], data)
@@ -197,7 +198,7 @@ class TestListNodes(test_api_base.BaseApiTest):
data = self.get_json(
'/nodes?fields=%s' % fields,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(3, len(data['nodes']))
for node in data['nodes']:
@@ -210,7 +211,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'uuid,spongebob'
response = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
@@ -222,7 +223,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'uuid,extra'
response = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
@@ -233,7 +234,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'driver_info'
data = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
# We always append "links"
self.assertItemsEqual(['driver_info', 'links'], data)
self.assertEqual('******', data['driver_info']['fake_password'])
@@ -254,7 +255,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'network_interface'
response = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertIn('network_interface', response)
def test_get_all_interface_fields_invalid_api_version(self):
@@ -273,7 +274,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields_arg = ','.join(api_utils.V31_FIELDS)
response = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields_arg),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
for field in api_utils.V31_FIELDS:
self.assertIn(field, response)
@@ -293,7 +294,7 @@ class TestListNodes(test_api_base.BaseApiTest):
fields = 'storage_interface'
response = self.get_json(
'/nodes/%s?fields=%s' % (node.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertIn('storage_interface', response)
def test_detail(self):
@@ -301,7 +302,7 @@ class TestListNodes(test_api_base.BaseApiTest):
chassis_id=self.chassis.id)
data = self.get_json(
'/nodes/detail',
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(node.uuid, data['nodes'][0]["uuid"])
self.assertIn('name', data['nodes'][0])
self.assertIn('driver', data['nodes'][0])
@@ -339,7 +340,7 @@ class TestListNodes(test_api_base.BaseApiTest):
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)})
+ headers={api_base.Version.string: str(api_v1.min_version())})
self.assertEqual(states.NOSTATE, data['provision_state'])
data = self.get_json('/nodes/%s' % node.uuid,
@@ -351,7 +352,7 @@ class TestListNodes(test_api_base.BaseApiTest):
driver_internal_info={"foo": "bar"})
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)})
+ headers={api_base.Version.string: str(api_v1.min_version())})
self.assertNotIn('driver_internal_info', data)
data = self.get_json('/nodes/%s' % node.uuid,
@@ -375,7 +376,7 @@ class TestListNodes(test_api_base.BaseApiTest):
inspection_started_at=some_time)
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)})
+ headers={api_base.Version.string: str(api_v1.min_version())})
self.assertNotIn('inspection_finished_at', data)
self.assertNotIn('inspection_started_at', data)
@@ -391,7 +392,7 @@ class TestListNodes(test_api_base.BaseApiTest):
clean_step={"foo": "bar"})
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)})
+ headers={api_base.Version.string: str(api_v1.min_version())})
self.assertNotIn('clean_step', data)
data = self.get_json('/nodes/%s' % node.uuid,
@@ -737,14 +738,14 @@ class TestListNodes(test_api_base.BaseApiTest):
data = self.get_json(
'/nodes/%s/volume/connectors' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(2, len(data['connectors']))
self.assertNotIn('next', data)
# Test collection pagination
data = self.get_json(
'/nodes/%s/volume/connectors?limit=1' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(1, len(data['connectors']))
self.assertIn('next', data)
@@ -755,7 +756,7 @@ class TestListNodes(test_api_base.BaseApiTest):
response = self.get_json(
'/nodes/volume/connectors',
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.NOT_FOUND, response.status_int)
def test_volume_connectors_subresource_node_not_found(self):
@@ -763,7 +764,7 @@ class TestListNodes(test_api_base.BaseApiTest):
response = self.get_json(
'/nodes/%s/volume/connectors' % non_existent_uuid,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.NOT_FOUND, response.status_int)
def test_volume_targets_subresource(self):
@@ -776,14 +777,14 @@ class TestListNodes(test_api_base.BaseApiTest):
data = self.get_json(
'/nodes/%s/volume/targets' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(2, len(data['targets']))
self.assertNotIn('next', data)
# Test collection pagination
data = self.get_json(
'/nodes/%s/volume/targets?limit=1' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(1, len(data['targets']))
self.assertIn('next', data)
@@ -794,7 +795,7 @@ class TestListNodes(test_api_base.BaseApiTest):
response = self.get_json(
'/nodes/volume/targets',
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.NOT_FOUND, response.status_int)
def test_volume_targets_subresource_node_not_found(self):
@@ -802,7 +803,7 @@ class TestListNodes(test_api_base.BaseApiTest):
response = self.get_json(
'/nodes/%s/volume/targets' % non_existent_uuid,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@mock.patch.object(timeutils, 'utcnow')
@@ -1085,7 +1086,7 @@ class TestListNodes(test_api_base.BaseApiTest):
def test_get_nodes_by_driver_invalid_api_version(self):
response = self.get_json(
'/nodes?driver=fake',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
self.assertTrue(response.json['error_message'])
@@ -1147,7 +1148,7 @@ class TestListNodes(test_api_base.BaseApiTest):
response = self.get_json(
base_url % 'fake',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
self.assertTrue(response.json['error_message'])
@@ -1307,7 +1308,7 @@ class TestListNodes(test_api_base.BaseApiTest):
driver_info=driver_info)
data = self.get_json(
'/nodes/%s' % node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual("******", data["driver_info"]["ssh_password"])
self.assertEqual("******", data["driver_info"]["ssh_key_contents"])
@@ -1562,7 +1563,7 @@ class TestPatch(test_api_base.BaseApiTest):
'/nodes/%s/volume/connectors' % self.node.uuid,
[{'path': '/extra/foo', 'value': 'bar', 'op': 'add'}],
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
def test_patch_volume_connectors_subresource(self):
@@ -1574,7 +1575,7 @@ class TestPatch(test_api_base.BaseApiTest):
connector.uuid),
[{'path': '/extra/foo', 'value': 'bar', 'op': 'add'}],
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
def test_patch_volume_targets_subresource(self):
@@ -1585,7 +1586,7 @@ class TestPatch(test_api_base.BaseApiTest):
target.uuid),
[{'path': '/extra/foo', 'value': 'bar', 'op': 'add'}],
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
def test_remove_uuid(self):
@@ -1943,7 +1944,7 @@ class TestPatch(test_api_base.BaseApiTest):
uuid=uuidutils.generate_uuid())
self.mock_update_node.return_value = node
network_interface = 'flat'
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
response = self.patch_json('/nodes/%s' % node.uuid,
[{'path': '/network_interface',
'value': network_interface,
@@ -2029,7 +2030,7 @@ class TestPatch(test_api_base.BaseApiTest):
node = obj_utils.create_test_node(self.context,
uuid=uuidutils.generate_uuid())
self.mock_update_node.return_value = node
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
for field in api_utils.V31_FIELDS:
response = self.patch_json('/nodes/%s' % node.uuid,
[{'path': '/%s' % field,
@@ -2075,7 +2076,7 @@ class TestPatch(test_api_base.BaseApiTest):
uuid=uuidutils.generate_uuid())
self.mock_update_node.return_value = node
storage_interface = 'cinder'
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
response = self.patch_json('/nodes/%s' % node.uuid,
[{'path': '/storage_interface',
'value': storage_interface,
@@ -2473,7 +2474,7 @@ class TestPost(test_api_base.BaseApiTest):
response = self.post_json(
'/nodes/volume/connectors', pdict,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.NOT_FOUND, response.status_int)
def test_post_volume_connectors_subresource(self):
@@ -2483,7 +2484,7 @@ class TestPost(test_api_base.BaseApiTest):
response = self.post_json(
'/nodes/%s/volume/connectors' % node.uuid, pdict,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
def test_post_volume_targets_subresource(self):
@@ -2493,7 +2494,7 @@ class TestPost(test_api_base.BaseApiTest):
response = self.post_json(
'/nodes/%s/volume/targets' % node.uuid, pdict,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
def test_create_node_no_mandatory_field_driver(self):
@@ -2589,11 +2590,11 @@ class TestPost(test_api_base.BaseApiTest):
network_interface='flat')
response = self.post_json('/nodes', ndict,
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual(http_client.CREATED, response.status_int)
result = self.get_json('/nodes/%s' % ndict['uuid'],
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual('flat', result['network_interface'])
def test_create_node_network_interface_old_api_version(self):
@@ -2608,7 +2609,7 @@ class TestPost(test_api_base.BaseApiTest):
network_interface='foo')
response = self.post_json('/nodes', ndict, expect_errors=True,
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
@@ -2617,11 +2618,11 @@ class TestPost(test_api_base.BaseApiTest):
resource_class='foo')
response = self.post_json('/nodes', ndict,
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual(http_client.CREATED, response.status_int)
result = self.get_json('/nodes/%s' % ndict['uuid'],
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual('foo', result['resource_class'])
def test_create_node_resource_class_old_api_version(self):
@@ -2643,7 +2644,7 @@ class TestPost(test_api_base.BaseApiTest):
ndict = test_api_utils.post_get_test_node(storage_interface='foo')
response = self.post_json('/nodes', ndict, expect_errors=True,
headers={api_base.Version.string:
- str(api_v1.MAX_VER)})
+ str(api_v1.max_version())})
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
@@ -2750,7 +2751,7 @@ class TestDelete(test_api_base.BaseApiTest):
response = self.delete(
'/nodes/%s/volume/connectors' % node.uuid,
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
def test_delete_volume_connectors_subresource(self):
@@ -2760,7 +2761,7 @@ class TestDelete(test_api_base.BaseApiTest):
response = self.delete(
'/nodes/%s/volume/connectors/%s' % (node.uuid, connector.uuid),
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
def test_delete_volume_targets_subresource(self):
@@ -2770,7 +2771,7 @@ class TestDelete(test_api_base.BaseApiTest):
response = self.delete(
'/nodes/%s/volume/targets/%s' % (node.uuid, target.uuid),
expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.FORBIDDEN, response.status_int)
@mock.patch.object(notification_utils, '_emit_api_notification')
diff --git a/ironic/tests/unit/api/controllers/v1/test_port.py b/ironic/tests/unit/api/controllers/v1/test_port.py
index 09a01a89b..183450aca 100644
--- a/ironic/tests/unit/api/controllers/v1/test_port.py
+++ b/ironic/tests/unit/api/controllers/v1/test_port.py
@@ -233,7 +233,7 @@ class TestListPorts(test_api_base.BaseApiTest):
fields = 'address,extra'
data = self.get_json(
'/ports/%s?fields=%s' % (port.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
# We always append "links"
self.assertItemsEqual(['address', 'extra', 'links'], data)
@@ -242,7 +242,7 @@ class TestListPorts(test_api_base.BaseApiTest):
internal_info={"foo": "bar"})
data = self.get_json(
'/ports/%s' % port.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)})
+ headers={api_base.Version.string: str(api_v1.min_version())})
self.assertNotIn('internal_info', data)
data = self.get_json('/ports/%s' % port.uuid,
@@ -313,7 +313,7 @@ class TestListPorts(test_api_base.BaseApiTest):
data = self.get_json(
'/ports?fields=%s' % fields,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(3, len(data['ports']))
for port in data['ports']:
@@ -325,7 +325,7 @@ class TestListPorts(test_api_base.BaseApiTest):
fields = 'uuid,spongebob'
response = self.get_json(
'/ports/%s?fields=%s' % (port.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
@@ -336,7 +336,7 @@ class TestListPorts(test_api_base.BaseApiTest):
fields = 'uuid,extra'
response = self.get_json(
'/ports/%s?fields=%s' % (port.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
@@ -380,7 +380,7 @@ class TestListPorts(test_api_base.BaseApiTest):
physical_network='physnet1')
data = self.get_json(
'/ports/detail',
- headers={api_base.Version.string: str(api_v1.MAX_VER)}
+ headers={api_base.Version.string: str(api_v1.max_version())}
)
self.assertEqual(port.uuid, data['ports'][0]["uuid"])
self.assertIn('extra', data['ports'][0])
@@ -519,7 +519,7 @@ class TestListPorts(test_api_base.BaseApiTest):
for invalid_key in invalid_keys_list:
response = self.get_json(
'/ports?sort_key=%s' % invalid_key, expect_errors=True,
- headers={api_base.Version.string: str(api_v1.MAX_VER)}
+ headers={api_base.Version.string: str(api_v1.max_version())}
)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
@@ -535,7 +535,7 @@ class TestListPorts(test_api_base.BaseApiTest):
address='52:54:00:cf:2d:3%s' % id_,
pxe_enabled=id_ % 2)
port_uuids.append(port.uuid)
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
detail_str = '/detail' if detail else ''
data = self.get_json('/ports%s?sort_key=pxe_enabled' % detail_str,
headers=headers)
@@ -1268,7 +1268,7 @@ class TestPatch(test_api_base.BaseApiTest):
def test_invalid_physnet_non_text(self, mock_upd):
physnet = 1234
- headers = {api_base.Version.string: versions.MAX_VERSION_STRING}
+ headers = {api_base.Version.string: versions.max_version_string()}
response = self.patch_json('/ports/%s' % self.port.uuid,
[{'path': '/physical_network',
'value': physnet,
@@ -1281,7 +1281,7 @@ class TestPatch(test_api_base.BaseApiTest):
def test_invalid_physnet_too_long(self, mock_upd):
physnet = 'p' * 65
- headers = {api_base.Version.string: versions.MAX_VERSION_STRING}
+ headers = {api_base.Version.string: versions.max_version_string()}
response = self.patch_json('/ports/%s' % self.port.uuid,
[{'path': '/physical_network',
'value': physnet,
@@ -1319,7 +1319,7 @@ class TestPost(test_api_base.BaseApiTest):
self.portgroup = obj_utils.create_test_portgroup(self.context,
node_id=self.node.id)
self.headers = {api_base.Version.string: str(
- versions.MAX_VERSION_STRING)}
+ versions.max_version_string())}
p = mock.patch.object(rpcapi.ConductorAPI, 'get_topic_for')
self.mock_gtf = p.start()
@@ -1370,7 +1370,7 @@ class TestPost(test_api_base.BaseApiTest):
pdict.pop('pxe_enabled')
pdict.pop('extra')
pdict.pop('physical_network')
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.post_json('/ports', pdict, headers=headers)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.CREATED, response.status_int)
diff --git a/ironic/tests/unit/api/controllers/v1/test_portgroup.py b/ironic/tests/unit/api/controllers/v1/test_portgroup.py
index 57f0b5284..48b301f55 100644
--- a/ironic/tests/unit/api/controllers/v1/test_portgroup.py
+++ b/ironic/tests/unit/api/controllers/v1/test_portgroup.py
@@ -51,7 +51,7 @@ class TestPortgroupObject(base.TestCase):
class TestListPortgroups(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestListPortgroups, self).setUp()
@@ -152,7 +152,7 @@ class TestListPortgroups(test_api_base.BaseApiTest):
node_id=self.node.id)
response = self.get_json(
'/portgroups/%s' % (portgroup.uuid),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -170,7 +170,7 @@ class TestListPortgroups(test_api_base.BaseApiTest):
def test_detail_invalid_api_version(self):
response = self.get_json(
'/portgroups/detail',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -274,14 +274,14 @@ class TestListPortgroups(test_api_base.BaseApiTest):
# Test get one old api version, /portgroups controller not allowed
response = self.get_json('/portgroups/%s/ports/%s' % (
pg.uuid, uuidutils.generate_uuid()),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
# Test get one not allowed to access to /portgroups/<uuid>/ports/<uuid>
response = self.get_json(
'/portgroups/%s/ports/%s' % (pg.uuid, uuidutils.generate_uuid()),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.FORBIDDEN, response.status_int)
@@ -471,7 +471,7 @@ class TestListPortgroups(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'update_portgroup')
class TestPatch(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPatch, self).setUp()
@@ -865,7 +865,7 @@ class TestPatch(test_api_base.BaseApiTest):
'op': 'replace'}],
expect_errors=True,
headers={api_base.Version.string:
- str(api_v1.MIN_VER)})
+ str(api_v1.min_version())})
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
self.assertTrue(response.json['error_message'])
@@ -929,7 +929,7 @@ class TestPatch(test_api_base.BaseApiTest):
class TestPost(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPost, self).setUp()
@@ -1205,7 +1205,7 @@ class TestPost(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'destroy_portgroup')
class TestDelete(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestDelete, self).setUp()
diff --git a/ironic/tests/unit/api/controllers/v1/test_ramdisk.py b/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
index 8d4ebc6d7..d49d7dbed 100644
--- a/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
+++ b/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
@@ -65,14 +65,14 @@ class TestLookup(test_api_base.BaseApiTest):
def test_nothing_provided(self):
response = self.get_json(
'/lookup',
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
def test_not_found(self):
response = self.get_json(
'/lookup?addresses=%s' % ','.join(self.addresses),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -83,7 +83,7 @@ class TestLookup(test_api_base.BaseApiTest):
response = self.get_json(
'/lookup?addresses=%s' % ','.join(self.addresses),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -94,7 +94,7 @@ class TestLookup(test_api_base.BaseApiTest):
data = self.get_json(
'/lookup?addresses=%s' % ','.join(self.addresses),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(self.node.uuid, data['node']['uuid'])
self.assertEqual(set(ramdisk._LOOKUP_RETURN_FIELDS) | {'links'},
set(data['node']))
@@ -110,7 +110,7 @@ class TestLookup(test_api_base.BaseApiTest):
':f4:52:14:03:00:54:06:c2,' + ','.join(self.addresses))
data = self.get_json(
'/lookup?addresses=%s' % addresses,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(self.node.uuid, data['node']['uuid'])
self.assertEqual(set(ramdisk._LOOKUP_RETURN_FIELDS) | {'links'},
set(data['node']))
@@ -121,7 +121,7 @@ class TestLookup(test_api_base.BaseApiTest):
data = self.get_json(
'/lookup?addresses=%s&node_uuid=%s' %
(','.join(self.addresses), self.node.uuid),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(self.node.uuid, data['node']['uuid'])
self.assertEqual(set(ramdisk._LOOKUP_RETURN_FIELDS) | {'links'},
set(data['node']))
@@ -130,7 +130,7 @@ class TestLookup(test_api_base.BaseApiTest):
def test_found_by_only_uuid(self):
data = self.get_json(
'/lookup?node_uuid=%s' % self.node.uuid,
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(self.node.uuid, data['node']['uuid'])
self.assertEqual(set(ramdisk._LOOKUP_RETURN_FIELDS) | {'links'},
set(data['node']))
@@ -140,7 +140,7 @@ class TestLookup(test_api_base.BaseApiTest):
response = self.get_json(
'/lookup?addresses=%s&node_uuid=%s' %
(','.join(self.addresses), self.node2.uuid),
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -149,7 +149,7 @@ class TestLookup(test_api_base.BaseApiTest):
data = self.get_json(
'/lookup?addresses=%s&node_uuid=%s' %
(','.join(self.addresses), self.node2.uuid),
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(self.node2.uuid, data['node']['uuid'])
self.assertEqual(set(ramdisk._LOOKUP_RETURN_FIELDS) | {'links'},
set(data['node']))
@@ -163,7 +163,7 @@ class TestHeartbeat(test_api_base.BaseApiTest):
response = self.post_json(
'/heartbeat/%s' % uuidutils.generate_uuid(),
{'callback_url': 'url'},
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -171,7 +171,7 @@ class TestHeartbeat(test_api_base.BaseApiTest):
response = self.post_json(
'/heartbeat/%s' % uuidutils.generate_uuid(),
{'callback_url': 'url'},
- headers={api_base.Version.string: str(api_v1.MAX_VER)},
+ headers={api_base.Version.string: str(api_v1.max_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -181,7 +181,7 @@ class TestHeartbeat(test_api_base.BaseApiTest):
response = self.post_json(
'/heartbeat/%s' % node.uuid,
{'callback_url': 'url'},
- headers={api_base.Version.string: str(api_v1.MAX_VER)})
+ headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(http_client.ACCEPTED, response.status_int)
self.assertEqual(b'', response.body)
mock_heartbeat.assert_called_once_with(mock.ANY, mock.ANY,
diff --git a/ironic/tests/unit/api/controllers/v1/test_root.py b/ironic/tests/unit/api/controllers/v1/test_root.py
index 817d2b7d2..449c65def 100644
--- a/ironic/tests/unit/api/controllers/v1/test_root.py
+++ b/ironic/tests/unit/api/controllers/v1/test_root.py
@@ -40,7 +40,7 @@ class TestCheckVersions(test_base.TestCase):
def test_check_version_invalid_major_version(self):
self.version.major = v1_api.BASE_VERSION + 1
- self.version.minor = v1_api.MIN_VER.minor
+ self.version.minor = v1_api.min_version().minor
self.assertRaises(
webob_exc.HTTPNotAcceptable,
v1_api.Controller()._check_version,
@@ -48,7 +48,7 @@ class TestCheckVersions(test_base.TestCase):
def test_check_version_too_low(self):
self.version.major = v1_api.BASE_VERSION
- self.version.minor = v1_api.MIN_VER.minor - 1
+ self.version.minor = v1_api.min_version().minor - 1
self.assertRaises(
webob_exc.HTTPNotAcceptable,
v1_api.Controller()._check_version,
@@ -56,14 +56,14 @@ class TestCheckVersions(test_base.TestCase):
def test_check_version_too_high(self):
self.version.major = v1_api.BASE_VERSION
- self.version.minor = v1_api.MAX_VER.minor + 1
+ self.version.minor = v1_api.max_version().minor + 1
e = self.assertRaises(
webob_exc.HTTPNotAcceptable,
v1_api.Controller()._check_version,
- self.version, {'fake-headers': v1_api.MAX_VER.minor})
- self.assertEqual(v1_api.MAX_VER.minor, e.headers['fake-headers'])
+ self.version, {'fake-headers': v1_api.max_version().minor})
+ self.assertEqual(v1_api.max_version().minor, e.headers['fake-headers'])
def test_check_version_ok(self):
self.version.major = v1_api.BASE_VERSION
- self.version.minor = v1_api.MIN_VER.minor
+ self.version.minor = v1_api.min_version().minor
v1_api.Controller()._check_version(self.version)
diff --git a/ironic/tests/unit/api/controllers/v1/test_versions.py b/ironic/tests/unit/api/controllers/v1/test_versions.py
index f5ac5672c..9f265b5ec 100644
--- a/ironic/tests/unit/api/controllers/v1/test_versions.py
+++ b/ironic/tests/unit/api/controllers/v1/test_versions.py
@@ -17,7 +17,11 @@ Tests for the versions constants and methods.
import re
+import mock
+
from ironic.api.controllers.v1 import versions
+from ironic.common import release_mappings
+from ironic.conf import CONF
from ironic.tests import base
@@ -36,16 +40,16 @@ class TestVersionConstants(base.TestCase):
self.minor_consts.sort(key=minor_key)
def test_max_ver_str(self):
- # Test to make sure MAX_VERSION_STRING corresponds with the largest
+ # Test to make sure _MAX_VERSION_STRING corresponds with the largest
# MINOR_ constant
max_ver = '1.{}'.format(getattr(versions, self.minor_consts[-1]))
- self.assertEqual(max_ver, versions.MAX_VERSION_STRING)
+ self.assertEqual(max_ver, versions._MAX_VERSION_STRING)
def test_min_ver_str(self):
- # Try to make sure someone doesn't change the MIN_VERSION_STRING by
+ # Try to make sure someone doesn't change the _MIN_VERSION_STRING by
# accident and make sure it exists
- self.assertEqual('1.1', versions.MIN_VERSION_STRING)
+ self.assertEqual('1.1', versions._MIN_VERSION_STRING)
def test_name_value_match(self):
# Test to make sure variable name matches the value. For example
@@ -67,3 +71,25 @@ class TestVersionConstants(base.TestCase):
value, seen_values,
'The value {} has been used more than once'.format(value))
seen_values.add(value)
+
+
+class TestMaxVersionString(base.TestCase):
+
+ def test_max_version_not_pinned(self):
+ CONF.set_override('pin_release_version', None)
+ self.assertEqual(versions._MAX_VERSION_STRING,
+ versions.max_version_string())
+
+ @mock.patch('ironic.common.release_mappings.RELEASE_MAPPING',
+ autospec=True)
+ def test_max_version_pinned(self, mock_release_mapping):
+ CONF.set_override('pin_release_version',
+ release_mappings.RELEASE_VERSIONS[-1])
+ mock_release_mapping.get.return_value = {
+ 'api': '1.5',
+ 'rpc': '1.4',
+ 'objects': {
+ 'MyObj': ['1.4'],
+ }
+ }
+ self.assertEqual('1.5', versions.max_version_string())
diff --git a/ironic/tests/unit/api/controllers/v1/test_volume.py b/ironic/tests/unit/api/controllers/v1/test_volume.py
index 16752fe09..13f17ac27 100644
--- a/ironic/tests/unit/api/controllers/v1/test_volume.py
+++ b/ironic/tests/unit/api/controllers/v1/test_volume.py
@@ -32,7 +32,7 @@ class TestGetVolume(test_api_base.BaseApiTest):
headers=headers))
def test_get_volume(self):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
data = self.get_json('/volume/', headers=headers)
for key in ['links', 'connectors', 'targets']:
self._test_links(data, key, headers)
@@ -46,7 +46,7 @@ class TestGetVolume(test_api_base.BaseApiTest):
data['targets'][1]['href'])
def test_get_volume_invalid_api_version(self):
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.get_json('/volume/', headers=headers,
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
diff --git a/ironic/tests/unit/api/controllers/v1/test_volume_connector.py b/ironic/tests/unit/api/controllers/v1/test_volume_connector.py
index bdeef82dc..575c86bc1 100644
--- a/ironic/tests/unit/api/controllers/v1/test_volume_connector.py
+++ b/ironic/tests/unit/api/controllers/v1/test_volume_connector.py
@@ -59,7 +59,7 @@ class TestVolumeConnectorObject(base.TestCase):
class TestListVolumeConnectors(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestListVolumeConnectors, self).setUp()
@@ -83,7 +83,7 @@ class TestListVolumeConnectors(test_api_base.BaseApiTest):
node_id=self.node.id)
response = self.get_json(
'/volume/connectors',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -103,7 +103,7 @@ class TestListVolumeConnectors(test_api_base.BaseApiTest):
self.context, node_id=self.node.id)
response = self.get_json(
'/volume/connectors/%s' % connector.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -151,7 +151,7 @@ class TestListVolumeConnectors(test_api_base.BaseApiTest):
fields = 'uuid,extra'
response = self.get_json(
'/volume/connectors/%s?fields=%s' % (connector.uuid, fields),
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -181,7 +181,7 @@ class TestListVolumeConnectors(test_api_base.BaseApiTest):
node_id=self.node.id)
response = self.get_json(
'/volume/connectors?detail=True',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -348,7 +348,7 @@ class TestListVolumeConnectors(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'update_volume_connector')
class TestPatch(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPatch, self).setUp()
@@ -388,7 +388,7 @@ class TestPatch(test_api_base.BaseApiTest):
node_uuid=self.node.uuid)])
def test_update_invalid_api_version(self, mock_upd):
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.patch_json('/volume/connectors/%s'
% self.connector.uuid,
[{'path': '/extra/foo',
@@ -714,7 +714,7 @@ class TestPatch(test_api_base.BaseApiTest):
class TestPost(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPost, self).setUp()
@@ -754,7 +754,7 @@ class TestPost(test_api_base.BaseApiTest):
pdict = post_get_test_volume_connector()
response = self.post_json(
'/volume/connectors', pdict,
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -879,7 +879,7 @@ class TestPost(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'destroy_volume_connector')
class TestDelete(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestDelete, self).setUp()
@@ -907,7 +907,7 @@ class TestDelete(test_api_base.BaseApiTest):
node_uuid=self.node.uuid)])
def test_delete_volume_connector_byid_invalid_api_version(self, mock_dvc):
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.delete('/volume/connectors/%s' % self.connector.uuid,
expect_errors=True, headers=headers)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
diff --git a/ironic/tests/unit/api/controllers/v1/test_volume_target.py b/ironic/tests/unit/api/controllers/v1/test_volume_target.py
index fa1028694..64b9ddc16 100644
--- a/ironic/tests/unit/api/controllers/v1/test_volume_target.py
+++ b/ironic/tests/unit/api/controllers/v1/test_volume_target.py
@@ -59,7 +59,7 @@ class TestVolumeTargetObject(base.TestCase):
class TestListVolumeTargets(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestListVolumeTargets, self).setUp()
@@ -83,7 +83,7 @@ class TestListVolumeTargets(test_api_base.BaseApiTest):
self.context, node_id=self.node.id)
response = self.get_json(
'/volume/targets',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -103,7 +103,7 @@ class TestListVolumeTargets(test_api_base.BaseApiTest):
node_id=self.node.id)
response = self.get_json(
'/volume/targets/%s' % target.uuid,
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -170,7 +170,7 @@ class TestListVolumeTargets(test_api_base.BaseApiTest):
node_id=self.node.id)
response = self.get_json(
'/volume/targets?detail=True',
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -328,7 +328,7 @@ class TestListVolumeTargets(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'update_volume_target')
class TestPatch(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPatch, self).setUp()
@@ -368,7 +368,7 @@ class TestPatch(test_api_base.BaseApiTest):
node_uuid=self.node.uuid)])
def test_update_byid_invalid_api_version(self, mock_upd):
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.patch_json('/volume/targets/%s'
% self.target.uuid,
[{'path': '/extra/foo',
@@ -702,7 +702,7 @@ class TestPatch(test_api_base.BaseApiTest):
class TestPost(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestPost, self).setUp()
@@ -742,7 +742,7 @@ class TestPost(test_api_base.BaseApiTest):
pdict = post_get_test_volume_target()
response = self.post_json(
'/volume/targets', pdict,
- headers={api_base.Version.string: str(api_v1.MIN_VER)},
+ headers={api_base.Version.string: str(api_v1.min_version())},
expect_errors=True)
self.assertEqual(http_client.NOT_FOUND, response.status_int)
@@ -860,7 +860,7 @@ class TestPost(test_api_base.BaseApiTest):
@mock.patch.object(rpcapi.ConductorAPI, 'destroy_volume_target')
class TestDelete(test_api_base.BaseApiTest):
- headers = {api_base.Version.string: str(api_v1.MAX_VER)}
+ headers = {api_base.Version.string: str(api_v1.max_version())}
def setUp(self):
super(TestDelete, self).setUp()
@@ -889,7 +889,7 @@ class TestDelete(test_api_base.BaseApiTest):
node_uuid=self.node.uuid)])
def test_delete_volume_target_byid_invalid_api_version(self, mock_dvc):
- headers = {api_base.Version.string: str(api_v1.MIN_VER)}
+ headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.delete('/volume/targets/%s' % self.target.uuid,
headers=headers,
expect_errors=True)
diff --git a/ironic/tests/unit/api/test_root.py b/ironic/tests/unit/api/test_root.py
index a4dade70a..118a4cca3 100644
--- a/ironic/tests/unit/api/test_root.py
+++ b/ironic/tests/unit/api/test_root.py
@@ -31,8 +31,9 @@ class TestRoot(base.BaseApiTest):
version1 = response['default_version']
self.assertEqual('v1', version1['id'])
self.assertEqual('CURRENT', version1['status'])
- self.assertEqual(versions.MIN_VERSION_STRING, version1['min_version'])
- self.assertEqual(versions.MAX_VERSION_STRING, version1['version'])
+ self.assertEqual(versions.min_version_string(),
+ version1['min_version'])
+ self.assertEqual(versions.max_version_string(), version1['version'])
class TestV1Root(base.BaseApiTest):
diff --git a/ironic/tests/unit/common/test_release_mappings.py b/ironic/tests/unit/common/test_release_mappings.py
index ec9f280f3..6ce78cf97 100644
--- a/ironic/tests/unit/common/test_release_mappings.py
+++ b/ironic/tests/unit/common/test_release_mappings.py
@@ -16,6 +16,7 @@ import mock
from oslo_utils import versionutils
import six
+from ironic.api.controllers.v1 import versions as api_versions
from ironic.common import release_mappings
from ironic.conductor import rpcapi
from ironic.db.sqlalchemy import models
@@ -49,7 +50,11 @@ class ReleaseMappingsTestCase(base.TestCase):
def test_structure(self):
for value in release_mappings.RELEASE_MAPPING.values():
self.assertIsInstance(value, dict)
- self.assertEqual({'rpc', 'objects'}, set(value))
+ self.assertEqual({'api', 'rpc', 'objects'}, set(value))
+ self.assertIsInstance(value['api'], six.string_types)
+ (major, minor) = value['api'].split('.')
+ self.assertEqual(1, int(major))
+ self.assertLessEqual(int(minor), api_versions.MINOR_MAX_VERSION)
self.assertIsInstance(value['rpc'], six.string_types)
self.assertIsInstance(value['objects'], dict)
for obj_value in value['objects'].values():
@@ -110,6 +115,7 @@ class GetObjectVersionsTestCase(base.TestCase):
TEST_MAPPING = {
'7.0': {
+ 'api': '1.30',
'rpc': '1.40',
'objects': {
'Node': ['1.21'],
@@ -119,6 +125,7 @@ class GetObjectVersionsTestCase(base.TestCase):
}
},
'8.0': {
+ 'api': '1.30',
'rpc': '1.40',
'objects': {
'Node': ['1.22'],
@@ -129,6 +136,7 @@ class GetObjectVersionsTestCase(base.TestCase):
}
},
'master': {
+ 'api': '1.34',
'rpc': '1.40',
'objects': {
'Node': ['1.23'],
diff --git a/releasenotes/notes/pin-api-version-029748f7d3be68d1.yaml b/releasenotes/notes/pin-api-version-029748f7d3be68d1.yaml
new file mode 100644
index 000000000..eb1bac0a0
--- /dev/null
+++ b/releasenotes/notes/pin-api-version-029748f7d3be68d1.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ During a `rolling upgrade
+ <https://docs.openstack.org/ironic/latest/admin/upgrade-guide.html#during-maintenance-window>`_
+ when the new services are pinned to the old release,
+ the Bare Metal API version will also be pinned to the old release. This will
+ prevent new features from being accessed until after the upgrade is done.