Merge "Add support for bailing on invalid service versions"

This commit is contained in:
Jenkins 2017-03-22 01:22:47 +00:00 committed by Gerrit Code Review
commit c9c1a8d8d3
3 changed files with 93 additions and 17 deletions

View File

@ -13,6 +13,7 @@
# under the License.
import importlib
import math
import warnings
from keystoneauth1 import adapter
@ -238,7 +239,19 @@ class CloudConfig(object):
interface=self.get_interface(service_key),
region_name=self.region)
def get_session_endpoint(self, service_key):
def _get_highest_endpoint(self, service_types, kwargs):
session = self.get_session()
for service_type in service_types:
kwargs['service_type'] = service_type
try:
# Return the highest version we find that matches
# the request
return session.get_endpoint(**kwargs)
except keystoneauth1.exceptions.catalog.EndpointNotFound:
pass
def get_session_endpoint(
self, service_key, min_version=None, max_version=None):
"""Return the endpoint from config or the catalog.
If a configuration lists an explicit endpoint for a service,
@ -254,27 +267,51 @@ class CloudConfig(object):
if override_endpoint:
return override_endpoint
# keystone is a special case in keystone, because what?
session = self.get_session()
endpoint = None
kwargs = {
'service_name': self.get_service_name(service_key),
'region_name': self.region
}
if service_key == 'identity':
endpoint = session.get_endpoint(interface=plugin.AUTH_INTERFACE)
# setting interface in kwargs dict even though we don't use kwargs
# dict here just for ease of warning text later
kwargs['interface'] = plugin.AUTH_INTERFACE
session = self.get_session()
endpoint = session.get_endpoint(interface=kwargs['interface'])
else:
args = {
'service_type': self.get_service_type(service_key),
'service_name': self.get_service_name(service_key),
'interface': self.get_interface(service_key),
'region_name': self.region
}
try:
endpoint = session.get_endpoint(**args)
except keystoneauth1.exceptions.catalog.EndpointNotFound:
self.log.warning("Keystone catalog entry not found (%s)",
args)
endpoint = None
kwargs['interface'] = self.get_interface(service_key)
if service_key == 'volume' and not self.get_api_version('volume'):
# If we don't have a configured cinder version, we can't know
# to request a different service_type
min_version = float(min_version or 1)
max_version = float(max_version or 3)
min_major = math.trunc(float(min_version))
max_major = math.trunc(float(max_version))
versions = range(int(max_major) + 1, int(min_major), -1)
service_types = []
for version in versions:
if version == 1:
service_types.append('volume')
else:
service_types.append('volumev{v}'.format(v=version))
else:
service_types = [self.get_service_type(service_key)]
endpoint = self._get_highest_endpoint(service_types, kwargs)
if not endpoint:
self.log.warning(
"Keystone catalog entry not found ("
"service_type=%s,service_name=%s"
"interface=%s,region_name=%s)",
service_key,
kwargs['service_name'],
kwargs['interface'],
kwargs['region_name'])
return endpoint
def get_legacy_client(
self, service_key, client_class=None, interface_key=None,
pass_version_arg=True, version=None, **kwargs):
pass_version_arg=True, version=None, min_version=None,
max_version=None, **kwargs):
"""Return a legacy OpenStack client object for the given config.
Most of the OpenStack python-*client libraries have the same
@ -308,6 +345,8 @@ class CloudConfig(object):
that case.
:param version: (optional) Version string to override the configured
version string.
:param min_version: (options) Minimum version acceptable.
:param max_version: (options) Maximum version acceptable.
:param kwargs: (optional) keyword args are passed through to the
Client constructor, so this is in case anything
additional needs to be passed in.
@ -317,7 +356,8 @@ class CloudConfig(object):
interface = self.get_interface(service_key)
# trigger exception on lack of service
endpoint = self.get_session_endpoint(service_key)
endpoint = self.get_session_endpoint(
service_key, min_version=min_version, max_version=max_version)
endpoint_override = self.get_endpoint(service_key)
if not interface_key:
@ -365,6 +405,9 @@ class CloudConfig(object):
if pass_version_arg and service_key != 'object-store':
if not version:
version = self.get_api_version(service_key)
if not version and service_key == 'volume':
from cinderclient import client as cinder_client
version = cinder_client.get_volume_api_from_url(endpoint)
# Temporary workaround while we wait for python-openstackclient
# to be able to handle 2.0 which is what neutronclient expects
if service_key == 'network' and version == '2':
@ -384,6 +427,16 @@ class CloudConfig(object):
constructor_kwargs['version'] = version[0]
else:
constructor_kwargs['version'] = version
if min_version and min_version > float(version):
raise exceptions.OpenStackConfigVersionException(
"Minimum version {min_version} requested but {version}"
" found".format(min_version=min_version, version=version),
version=version)
if max_version and max_version < float(version):
raise exceptions.OpenStackConfigVersionException(
"Maximum version {max_version} requested but {version}"
" found".format(max_version=max_version, version=version),
version=version)
if service_key == 'database':
# TODO(mordred) Remove when https://review.openstack.org/314032
# has landed and released. We're passing in a Session, but the

View File

@ -15,3 +15,11 @@
class OpenStackConfigException(Exception):
"""Something went wrong with parsing your OpenStack Config."""
class OpenStackConfigVersionException(OpenStackConfigException):
"""A version was requested that is different than what was found."""
def __init__(self, version):
super(OpenStackConfigVersionException, self).__init__()
self.version = version

View File

@ -0,0 +1,15 @@
---
features:
- Add min_version and max_version to get_legacy_client
and to get_session_endpoint. At the moment this is only
really fully plumbed through for cinder, which has extra
special fun around volume, volumev2 and volumev3. Min and max
versions to both methods will look through the options available
in the service catalog and try to return the latest one available
from the span of requested versions. This means a user can say
volume_api_version=None, min_version=2, max_version=3 will get
an endpoint from get_session_endpoint or a Client from cinderclient
that will be either v2 or v3 but not v1. In the future, min and max
version for get_session_endpoint should be able to sort out
appropriate endpoints via version discovery, but that does not
currently exist.