5c2eae8f82
The code has been moved into openstacksdk. Make os-client-config a thin wrapper around that code. The legacy client code needs to stay, as it was removed in openstacksdk. We also have to plumb os_client_config.defaults and os_client_config.cloud_region.CloudRegion in so that users get the os-client-config versions from the openstacksdk methods that return the relevant objects. Support for per-service region_name has been dropped. This was never used anywhere and was added for compatibility with openstacksdk Profile objects. Since Profile objects are no more, we can remove it. It never made any sense. Change-Id: I7180e20aa675c0ccbe3718493a6eff2be7e1b8b6
231 lines
11 KiB
Python
231 lines
11 KiB
Python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# 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.
|
|
|
|
import importlib
|
|
|
|
from openstack import _log
|
|
from openstack.config import cloud_region
|
|
|
|
import os_client_config
|
|
from os_client_config import constructors
|
|
from os_client_config import exceptions
|
|
|
|
|
|
def _get_client(service_key):
|
|
class_mapping = constructors.get_constructor_mapping()
|
|
if service_key not in class_mapping:
|
|
raise exceptions.OpenStackConfigException(
|
|
"Service {service_key} is unkown. Please pass in a client"
|
|
" constructor or submit a patch to os-client-config".format(
|
|
service_key=service_key))
|
|
mod_name, ctr_name = class_mapping[service_key].rsplit('.', 1)
|
|
lib_name = mod_name.split('.')[0]
|
|
try:
|
|
mod = importlib.import_module(mod_name)
|
|
except ImportError:
|
|
raise exceptions.OpenStackConfigException(
|
|
"Client for '{service_key}' was requested, but"
|
|
" {mod_name} was unable to be imported. Either import"
|
|
" the module yourself and pass the constructor in as an argument,"
|
|
" or perhaps you do not have python-{lib_name} installed.".format(
|
|
service_key=service_key,
|
|
mod_name=mod_name,
|
|
lib_name=lib_name))
|
|
try:
|
|
ctr = getattr(mod, ctr_name)
|
|
except AttributeError:
|
|
raise exceptions.OpenStackConfigException(
|
|
"Client for '{service_key}' was requested, but although"
|
|
" {mod_name} imported fine, the constructor at {fullname}"
|
|
" as not found. Please check your installation, we have no"
|
|
" clue what is wrong with your computer.".format(
|
|
service_key=service_key,
|
|
mod_name=mod_name,
|
|
fullname=class_mapping[service_key]))
|
|
return ctr
|
|
|
|
|
|
class CloudConfig(cloud_region.CloudRegion):
|
|
def __init__(self, *args, **kwargs):
|
|
super(CloudConfig, self).__init__(*args, **kwargs)
|
|
self.log = _log.setup_logging(__name__)
|
|
|
|
def __getattr__(self, key):
|
|
"""Return arbitrary attributes."""
|
|
|
|
if key.startswith('os_'):
|
|
key = key[3:]
|
|
|
|
if key in [attr.replace('-', '_') for attr in self.config]:
|
|
return self.config[key]
|
|
else:
|
|
return None
|
|
|
|
def insert_user_agent(self):
|
|
self._keystone_session.additional_user_agent.append(
|
|
('os-client-config', os_client_config.__version__))
|
|
super(CloudConfig, self).insert_user_agent()
|
|
|
|
@property
|
|
def region(self):
|
|
return self.region_name
|
|
|
|
def get_region_name(self, *args):
|
|
return self.region_name
|
|
|
|
def get_cache_expiration(self):
|
|
return self.get_cache_expirations()
|
|
|
|
def get_legacy_client(
|
|
self, service_key, client_class=None, interface_key=None,
|
|
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
|
|
interface for their client constructors, but there are several
|
|
parameters one wants to pass given a :class:`CloudConfig` object.
|
|
|
|
In the future, OpenStack API consumption should be done through
|
|
the OpenStack SDK, but that's not ready yet. This is for getting
|
|
Client objects from python-*client only.
|
|
|
|
:param service_key: Generic key for service, such as 'compute' or
|
|
'network'
|
|
:param client_class: Class of the client to be instantiated. This
|
|
should be the unversioned version if there
|
|
is one, such as novaclient.client.Client, or
|
|
the versioned one, such as
|
|
neutronclient.v2_0.client.Client if there isn't
|
|
:param interface_key: (optional) Some clients, such as glanceclient
|
|
only accept the parameter 'interface' instead
|
|
of 'endpoint_type' - this is a get-out-of-jail
|
|
parameter for those until they can be aligned.
|
|
os-client-config understands this to be the
|
|
case if service_key is image, so this is really
|
|
only for use with other unknown broken clients.
|
|
:param pass_version_arg: (optional) If a versioned Client constructor
|
|
was passed to client_class, set this to
|
|
False, which will tell get_client to not
|
|
pass a version parameter. os-client-config
|
|
already understand that this is the
|
|
case for network, so it can be omitted in
|
|
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.
|
|
"""
|
|
if not client_class:
|
|
client_class = _get_client(service_key)
|
|
|
|
interface = self.get_interface(service_key)
|
|
# trigger exception on lack of service
|
|
endpoint = self.get_session_endpoint(
|
|
service_key, min_version=min_version, max_version=max_version)
|
|
endpoint_override = self.get_endpoint(service_key)
|
|
|
|
if service_key == 'object-store':
|
|
constructor_kwargs = dict(
|
|
session=self.get_session(),
|
|
os_options=dict(
|
|
service_type=self.get_service_type(service_key),
|
|
object_storage_url=endpoint_override,
|
|
region_name=self.region))
|
|
else:
|
|
constructor_kwargs = dict(
|
|
session=self.get_session(),
|
|
service_name=self.get_service_name(service_key),
|
|
service_type=self.get_service_type(service_key),
|
|
endpoint_override=endpoint_override,
|
|
region_name=self.region)
|
|
|
|
if service_key == 'image':
|
|
# os-client-config does not depend on glanceclient, but if
|
|
# the user passed in glanceclient.client.Client, which they
|
|
# would need to do if they were requesting 'image' - then
|
|
# they necessarily have glanceclient installed
|
|
from glanceclient.common import utils as glance_utils
|
|
endpoint, detected_version = glance_utils.strip_version(endpoint)
|
|
# If the user has passed in a version, that's explicit, use it
|
|
if not version:
|
|
version = detected_version
|
|
# If the user has passed in or configured an override, use it.
|
|
# Otherwise, ALWAYS pass in an endpoint_override becuase
|
|
# we've already done version stripping, so we don't want version
|
|
# reconstruction to happen twice
|
|
if not endpoint_override:
|
|
constructor_kwargs['endpoint_override'] = endpoint
|
|
constructor_kwargs.update(kwargs)
|
|
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':
|
|
version = '2.0'
|
|
if service_key == 'identity':
|
|
# Workaround for bug#1513839
|
|
if 'endpoint' not in constructor_kwargs:
|
|
endpoint = self.get_session_endpoint('identity')
|
|
constructor_kwargs['endpoint'] = endpoint
|
|
if service_key == 'network':
|
|
constructor_kwargs['api_version'] = version
|
|
elif service_key == 'baremetal':
|
|
if version != '1':
|
|
# Set Ironic Microversion
|
|
constructor_kwargs['os_ironic_api_version'] = version
|
|
# Version arg is the major version, not the full microstring
|
|
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
|
|
# trove Client object has username and password as required
|
|
# args
|
|
constructor_kwargs['username'] = None
|
|
constructor_kwargs['password'] = None
|
|
|
|
if not interface_key:
|
|
if service_key in ('image', 'key-manager'):
|
|
interface_key = 'interface'
|
|
elif (service_key == 'identity'
|
|
and version and version.startswith('3')):
|
|
interface_key = 'interface'
|
|
else:
|
|
interface_key = 'endpoint_type'
|
|
if service_key == 'object-store':
|
|
constructor_kwargs['os_options'][interface_key] = interface
|
|
else:
|
|
constructor_kwargs[interface_key] = interface
|
|
|
|
return client_class(**constructor_kwargs)
|