diff --git a/doc/source/using-sessions.rst b/doc/source/using-sessions.rst index 1690ac5c..a48e78bb 100644 --- a/doc/source/using-sessions.rst +++ b/doc/source/using-sessions.rst @@ -170,6 +170,9 @@ The overall transaction then has three parts: Major API of a given service, as well as reporting the available microversion ranges that endpoint supports, if any. +More information is available in the `API-WG Specs`_ on the topics of +`Microversions`_ and `Consuming the Catalog`_. + Authentication -------------- @@ -349,7 +352,11 @@ of ranges, lists of input values or ``latest`` version. It can also be used to determine the `min_microversion` and `max_microversion` supported by the API. If an API does not support microversions, the values for -both will be ``None``. +both will be ``None``. It will also contain values for `next_min_version` and +`not_before` if they exist for the endpoint, or ``None`` if they do not. The +:class:`keystoneauth1.discovery.EndpointData` object will always contain +microversion related attributes regardless of whether the REST document does +or not. ``get_endpoint_data`` makes use of the same cache as the rest of the discovery process, so calling it should incur no undue expense. By default it will make @@ -357,3 +364,8 @@ at least one version discovery call so that it can fetch microversion metadata. If the user knows a service does not support microversions and is merely curious as to which major version was discovered, `discover_versions` can be set to `False` to prevent fetching microversion metadata. + +.. _API-WG Specs: http://specs.openstack.org/openstack/api-wg/ +.. _Consuming the Catalog: http://specs.openstack.org/openstack/api-wg/guidelines/consuming-catalog.html +.. _Microversions: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html#version-discovery + diff --git a/keystoneauth1/discover.py b/keystoneauth1/discover.py index dc664513..77752dac 100644 --- a/keystoneauth1/discover.py +++ b/keystoneauth1/discover.py @@ -390,6 +390,10 @@ class Discover(object): max_microversion = v.get('version') or None if max_microversion: max_microversion = normalize_version_number(max_microversion) + next_min_version = v.get('next_min_version') or None + if next_min_version: + next_min_version = normalize_version_number(next_min_version) + not_before = v.get('not_before') or None self_url = None collection_url = None @@ -416,6 +420,8 @@ class Discover(object): 'collection': collection_url, 'min_microversion': min_microversion, 'max_microversion': max_microversion, + 'next_min_version': next_min_version, + 'not_before': not_before, 'raw_status': v['status']}) versions.sort(key=lambda v: v['version'], reverse=reverse) @@ -581,7 +587,9 @@ class EndpointData(object): api_version=None, major_version=None, min_microversion=None, - max_microversion=None): + max_microversion=None, + next_min_version=None, + not_before=None): self.catalog_url = catalog_url self.service_url = service_url self.service_type = service_type @@ -595,6 +603,8 @@ class EndpointData(object): self.major_version = major_version self.min_microversion = min_microversion self.max_microversion = max_microversion + self.next_min_version = next_min_version + self.not_before = not_before self._saved_project_id = None self._catalog_matches_version = False self._catalog_matches_exactly = False @@ -615,7 +625,9 @@ class EndpointData(object): api_version=self.api_version, major_version=self.major_version, min_microversion=self.min_microversion, - max_microversion=self.max_microversion) + max_microversion=self.max_microversion, + next_min_version=self.next_min_version, + not_before=self.not_before) # Save cached discovery object - but we don't want to # actually provide a constructor argument new_data._disc = self._disc @@ -752,6 +764,8 @@ class EndpointData(object): self.min_microversion = discovered_data['min_microversion'] self.max_microversion = discovered_data['max_microversion'] + self.next_min_version = discovered_data['next_min_version'] + self.not_before = discovered_data['not_before'] # TODO(mordred): these next two things should be done by Discover # in versioned_data_for. diff --git a/keystoneauth1/tests/unit/test_discovery.py b/keystoneauth1/tests/unit/test_discovery.py index 8a902254..f5482e44 100644 --- a/keystoneauth1/tests/unit/test_discovery.py +++ b/keystoneauth1/tests/unit/test_discovery.py @@ -157,6 +157,8 @@ CINDER_EXAMPLES = { "id": "v3.0", "version": "3.27", "min_version": "3.0", + "next_min_version": "3.4", + "not_before": "2019-12-31", "links": [ { "href": BASE_URL, @@ -415,21 +417,31 @@ class VersionDataTests(utils.TestCase): # no version info in input test_ok({}, - {'min_microversion': None, 'max_microversion': None}) + {'min_microversion': None, 'max_microversion': None, + 'next_min_version': None, 'not_before': None}) # version => max_microversion test_ok({'version': '2.2'}, - {'min_microversion': None, 'max_microversion': (2, 2)}) + {'min_microversion': None, 'max_microversion': (2, 2), + 'next_min_version': None, 'not_before': None}) # max_version supersedes version (even if malformed). min_version & # normalization. test_ok({'min_version': '2', 'version': 'foo', 'max_version': '2.2'}, - {'min_microversion': (2, 0), 'max_microversion': (2, 2)}) + {'min_microversion': (2, 0), 'max_microversion': (2, 2), + 'next_min_version': None, 'not_before': None}) # Edge case: min/max_version ignored if present but "empty"; version # used for max_microversion. test_ok({'min_version': '', 'version': '2.1', 'max_version': ''}, - {'min_microversion': None, 'max_microversion': (2, 1)}) + {'min_microversion': None, 'max_microversion': (2, 1), + 'next_min_version': None, 'not_before': None}) + + # next_min_version set + test_ok({'min_version': '2', 'max_version': '2.2', + 'next_min_version': '2.1', 'not_before': '2019-07-01'}, + {'min_microversion': (2, 0), 'max_microversion': (2, 2), + 'next_min_version': (2, 1), 'not_before': '2019-07-01'}) # Badly-formatted min_version test_exc({'min_version': 'foo', 'max_version': '2.1'}) @@ -440,6 +452,9 @@ class VersionDataTests(utils.TestCase): # Badly-formatted version (when max_version omitted) test_exc({'min_version': '2.1', 'version': 'foo'}) + # Badly-formatted next_min_version + test_exc({'next_min_version': 'bogus', 'not_before': '2019-07-01'}) + def test_data_for_url(self): mock = self.requests_mock.get(V3_URL, status_code=200, @@ -542,6 +557,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (1, 0), 'url': v1_url, 'raw_status': 'CURRENT', @@ -550,6 +567,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (2, 0), 'url': v2_url, 'raw_status': 'CURRENT', @@ -558,6 +577,8 @@ class VersionDataTests(utils.TestCase): 'collection': BASE_URL, 'max_microversion': (3, 27), 'min_microversion': (3, 0), + 'next_min_version': (3, 4), + 'not_before': u'2019-12-31', 'version': (3, 0), 'url': v3_url, 'raw_status': 'CURRENT', @@ -610,6 +631,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (1, 0), 'url': v1_url, 'raw_status': 'SUPPORTED', @@ -618,6 +641,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (1, 1), 'url': v1_url, 'raw_status': 'CURRENT', @@ -626,6 +651,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (2, 0), 'url': v2_url, 'raw_status': 'SUPPORTED', @@ -634,6 +661,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (2, 1), 'url': v2_url, 'raw_status': 'SUPPORTED', @@ -642,6 +671,8 @@ class VersionDataTests(utils.TestCase): 'collection': None, 'max_microversion': None, 'min_microversion': None, + 'next_min_version': None, + 'not_before': None, 'version': (2, 2), 'url': v2_url, 'raw_status': 'CURRENT',