Create a versions.py file

Create constants for the versions and put them into versions.py.  Use
these constants instead of unnamed numerical constants.

In the future, versions.py, could possibly contain more than constants.
Possibly helper functions related to versioning could be placed in it.

Change-Id: I8c68133e68d77ac451506b3a5bae038b641ed31f
This commit is contained in:
John L. Villalovos 2015-08-17 13:44:04 -07:00
parent 16c5e5aecc
commit 3c5dde4ea5
5 changed files with 166 additions and 51 deletions

View File

@ -33,44 +33,18 @@ from ironic.api.controllers.v1 import chassis
from ironic.api.controllers.v1 import driver
from ironic.api.controllers.v1 import node
from ironic.api.controllers.v1 import port
from ironic.api.controllers.v1 import versions
from ironic.api import expose
from ironic.common.i18n import _
BASE_VERSION = 1
BASE_VERSION = versions.BASE_VERSION
# Here goes a short log of changes in every version.
# Refer to doc/source/webapi/v1.rst for a detailed explanation of what
# each version contains, and don't forget to update it when introducing
# a new version.
#
# v1.0: corresponds to Juno API, not supported since Kilo
# v1.1: API at the point in time when versioning support was added,
# covers the following commits from Kilo cycle:
# 827db7fe: Add Node.maintenance_reason
# 68eed82b: Add API endpoint to set/unset the node maintenance mode
# bc973889: Add sync and async support for passthru methods
# e03f443b: Vendor endpoints to support different HTTP methods
# e69e5309: Make vendor methods discoverable via the Ironic API
# edf532db: Add logic to store the config drive passed by Nova
# v1.2: Renamed NOSTATE ("None") to AVAILABLE ("available")
# v1.3: Add node.driver_internal_info
# v1.4: Add MANAGEABLE state
# v1.5: Add logical node names
# v1.6: Add INSPECT* states
# v1.7: Add node.clean_step
# v1.8: Add ability to return a subset of resource fields
# v1.9: Add ability to filter nodes by provision state
# v1.10: Logical node names support RFC 3986 unreserved characters
# v1.11: Nodes appear in ENROLL state by default
MIN_VER_STR = '1.1'
MAX_VER_STR = '1.11'
MIN_VER = base.Version({base.Version.string: MIN_VER_STR},
MIN_VER_STR, MAX_VER_STR)
MAX_VER = base.Version({base.Version.string: MAX_VER_STR},
MIN_VER_STR, MAX_VER_STR)
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)
class MediaType(base.APIBase):
@ -177,7 +151,8 @@ 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': MIN_VER_STR, 'max': MAX_VER_STR},
{'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:
@ -185,16 +160,20 @@ class Controller(rest.RestController):
"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': MIN_VER_STR, 'max': MAX_VER_STR},
{'ver': version, 'min': versions.MIN_VERSION_STRING,
'max': versions.MAX_VERSION_STRING},
headers=headers)
@pecan.expose()
def _route(self, args):
v = base.Version(pecan.request.headers, MIN_VER_STR, MAX_VER_STR)
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] = MIN_VER_STR
pecan.response.headers[base.Version.max_string] = MAX_VER_STR
pecan.response.headers[base.Version.min_string] = (
versions.MIN_VERSION_STRING)
pecan.response.headers[base.Version.max_string] = (
versions.MAX_VERSION_STRING)
# assert that requested version is supported
self._check_version(v, pecan.response.headers)

View File

@ -31,6 +31,7 @@ from ironic.api.controllers.v1 import collection
from ironic.api.controllers.v1 import port
from ironic.api.controllers.v1 import types
from ironic.api.controllers.v1 import utils as api_utils
from ironic.api.controllers.v1 import versions
from ironic.api import expose
from ironic.common import exception
from ironic.common.i18n import _
@ -61,33 +62,33 @@ _DEFAULT_RETURN_FIELDS = ('instance_uuid', 'maintenance', 'power_state',
MIN_VERB_VERSIONS = {
# v1.4 added the MANAGEABLE state and two verbs to move nodes into
# and out of that state. Reject requests to do this in older versions
ir_states.VERBS['manage']: 4,
ir_states.VERBS['provide']: 4,
ir_states.VERBS['manage']: versions.MINOR_4_MANAGEABLE_STATE,
ir_states.VERBS['provide']: versions.MINOR_4_MANAGEABLE_STATE,
ir_states.VERBS['inspect']: 6,
ir_states.VERBS['inspect']: versions.MINOR_6_INSPECT_STATE,
}
def hide_fields_in_newer_versions(obj):
# if requested version is < 1.3, hide driver_internal_info
if pecan.request.version.minor < 3:
if pecan.request.version.minor < versions.MINOR_3_DRIVER_INTERNAL_INFO:
obj.driver_internal_info = wsme.Unset
if not api_utils.allow_node_logical_names():
obj.name = wsme.Unset
# if requested version is < 1.6, hide inspection_*_at fields
if pecan.request.version.minor < 6:
if pecan.request.version.minor < versions.MINOR_6_INSPECT_STATE:
obj.inspection_finished_at = wsme.Unset
obj.inspection_started_at = wsme.Unset
if pecan.request.version.minor < 7:
if pecan.request.version.minor < versions.MINOR_7_NODE_CLEAN:
obj.clean_step = wsme.Unset
def assert_juno_provision_state_name(obj):
# if requested version is < 1.2, convert AVAILABLE to the old NOSTATE
if (pecan.request.version.minor < 2 and
if (pecan.request.version.minor < versions.MINOR_2_AVAILABLE_STATE and
obj.provision_state == ir_states.AVAILABLE):
obj.provision_state = ir_states.NOSTATE

View File

@ -21,6 +21,7 @@ import six
from webob.static import FileIter
import wsme
from ironic.api.controllers.v1 import versions
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import states
@ -72,7 +73,7 @@ def get_patch_value(patch, path):
def allow_node_logical_names():
# v1.5 added logical name aliases
return pecan.request.version.minor >= 5
return pecan.request.version.minor >= versions.MINOR_5_NODE_NAME
def get_rpc_node(node_ident):
@ -112,7 +113,7 @@ def is_valid_node_name(name):
def is_valid_logical_name(name):
"""Determine if the provided name is a valid hostname."""
if pecan.request.version.minor < 10:
if pecan.request.version.minor < versions.MINOR_10_UNRESTRICTED_NODE_NAME:
return utils.is_hostname_safe(name)
else:
return utils.is_valid_logical_name(name)
@ -192,7 +193,8 @@ def check_allow_specify_fields(fields):
attributes, this method checks if the required version is being
requested.
"""
if fields is not None and pecan.request.version.minor < 8:
if (fields is not None and pecan.request.version.minor <
versions.MINOR_8_FETCHING_SUBSET_OF_FIELDS):
raise exception.NotAcceptable()
@ -202,7 +204,8 @@ def check_for_invalid_state_and_allow_filter(provision_state):
Version 1.9 of the API allows filter nodes by provision state.
"""
if provision_state is not None:
if pecan.request.version.minor < 9:
if (pecan.request.version.minor <
versions.MINOR_9_PROVISION_STATE_FILTER):
raise exception.NotAcceptable()
valid_states = states.machine.states
if provision_state not in valid_states:
@ -216,5 +219,6 @@ def initial_node_provision_state():
Previously the default state for new nodes was AVAILABLE.
Starting with API 1.11 it is ENROLL.
"""
return (states.AVAILABLE if pecan.request.version.minor < 11
return (states.AVAILABLE
if pecan.request.version.minor < versions.MINOR_11_ENROLL_STATE
else states.ENROLL)

View File

@ -0,0 +1,62 @@
# Copyright (c) 2015 Intel Corporation
# All Rights Reserved.
#
# 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.
# This is the version 1 API
BASE_VERSION = 1
# Here goes a short log of changes in every version.
# Refer to doc/source/webapi/v1.rst for a detailed explanation of what
# each version contains, and don't forget to update it when introducing
# a new version.
#
# v1.0: corresponds to Juno API, not supported since Kilo
# v1.1: API at the point in time when versioning support was added,
# covers the following commits from Kilo cycle:
# 827db7fe: Add Node.maintenance_reason
# 68eed82b: Add API endpoint to set/unset the node maintenance mode
# bc973889: Add sync and async support for passthru methods
# e03f443b: Vendor endpoints to support different HTTP methods
# e69e5309: Make vendor methods discoverable via the Ironic API
# edf532db: Add logic to store the config drive passed by Nova
# v1.2: Renamed NOSTATE ("None") to AVAILABLE ("available")
# v1.3: Add node.driver_internal_info
# v1.4: Add MANAGEABLE state
# v1.5: Add logical node names
# v1.6: Add INSPECT* states
# v1.7: Add node.clean_step
# v1.8: Add ability to return a subset of resource fields
# v1.9: Add ability to filter nodes by provision state
# v1.10: Logical node names support RFC 3986 unreserved characters
# v1.11: Nodes appear in ENROLL state by default
MINOR_0_JUNO = 0
MINOR_1_INITIAL_VERSION = 1
MINOR_2_AVAILABLE_STATE = 2
MINOR_3_DRIVER_INTERNAL_INFO = 3
MINOR_4_MANAGEABLE_STATE = 4
MINOR_5_NODE_NAME = 5
MINOR_6_INSPECT_STATE = 6
MINOR_7_NODE_CLEAN = 7
MINOR_8_FETCHING_SUBSET_OF_FIELDS = 8
MINOR_9_PROVISION_STATE_FILTER = 9
MINOR_10_UNRESTRICTED_NODE_NAME = 10
MINOR_11_ENROLL_STATE = 11
# When adding another version, update MINOR_MAX_VERSION
MINOR_MAX_VERSION = MINOR_11_ENROLL_STATE
# 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)

View File

@ -0,0 +1,69 @@
# Copyright (c) 2015 Intel Corporation
#
# 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.
"""
Tests for the versions constants and methods.
"""
import re
from ironic.api.controllers.v1 import versions
from ironic.tests import base
class TestVersionConstants(base.TestCase):
def setUp(self):
super(TestVersionConstants, self).setUp()
# Get all of our named constants. They all begin with r'MINOR_[0-9]'
self.minor_consts = [x for x in dir(versions)
if re.search(r'^MINOR_[0-9]', x)]
# Sort key needs to be an integer
def minor_key(x):
return int(x.split('_', 2)[1])
self.minor_consts.sort(key=minor_key)
def test_max_ver_str(self):
# 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)
def test_min_ver_str(self):
# 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)
def test_name_value_match(self):
# Test to make sure variable name matches the value. For example
# MINOR_99_FOO should equal 99
for var_name in self.minor_consts:
version = int(var_name.split('_', 2)[1])
self.assertEqual(
version, getattr(versions, var_name),
'Constant "{}" does not equal {}'.format(var_name, version))
def test_duplicates(self):
# Test to make sure no duplicates values
seen_values = set()
for var_name in self.minor_consts:
value = getattr(versions, var_name)
self.assertNotIn(
value, seen_values,
'The value {} has been used more than once'.format(value))
seen_values.add(value)