# Copyright 2017 Huawei Technologies Co.,LTD. # 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. """Utilities and helper functions.""" import six from keystoneauth1 import loading as ks_loading from os_service_types import service_types from oslo_concurrency import lockutils from oslo_log import log from cyborg.common import exception import cyborg.conf LOG = log.getLogger(__name__) synchronized = lockutils.synchronized_with_prefix('cyborg-') _SERVICE_TYPES = service_types.ServiceTypes() CONF = cyborg.conf.CONF def safe_rstrip(value, chars=None): """Removes trailing characters from a string if that does not make it empty :param value: A string value that will be stripped. :param chars: Characters to remove. :return: Stripped value. """ if not isinstance(value, six.string_types): LOG.warning("Failed to remove trailing character. Returning " "original object. Supplied object is not a string: " "%s,", value) return value return value.rstrip(chars) or value def get_ksa_adapter(service_type, ksa_auth=None, ksa_session=None, min_version=None, max_version=None): """Construct a keystoneauth1 Adapter for a given service type. We expect to find a conf group whose name corresponds to the service_type's project according to the service-types-authority. That conf group must provide at least ksa adapter options. Depending how the result is to be used, ksa auth and/or session options may also be required, or the relevant parameter supplied. :param service_type: String name of the service type for which the Adapter is to be constructed. :param ksa_auth: A keystoneauth1 auth plugin. If not specified, we attempt to find one in ksa_session. Failing that, we attempt to load one from the conf. :param ksa_session: A keystoneauth1 Session. If not specified, we attempt to load one from the conf. :param min_version: The minimum major version of the adapter's endpoint, intended to be used as the lower bound of a range with max_version. If min_version is given with no max_version it is as if max version is 'latest'. :param max_version: The maximum major version of the adapter's endpoint, intended to be used as the upper bound of a range with min_version. :return: A keystoneauth1 Adapter object for the specified service_type. :raise: ConfGroupForServiceTypeNotFound If no conf group name could be found for the specified service_type. """ # Get the conf group corresponding to the service type. confgrp = _SERVICE_TYPES.get_project_name(service_type) if not confgrp or not hasattr(CONF, confgrp): # Try the service type as the conf group. This is necessary for e.g. # placement, while it's still part of the nova project. # Note that this might become the first thing we try if/as we move to # using service types for conf group names in general. confgrp = service_type if not confgrp or not hasattr(CONF, confgrp): raise exception.ConfGroupForServiceTypeNotFound(stype=service_type) # Ensure we have an auth. # NOTE(efried): This could be None, and that could be okay - e.g. if the # result is being used for get_endpoint() and the conf only contains # endpoint_override. if not ksa_auth: if ksa_session and ksa_session.auth: ksa_auth = ksa_session.auth else: ksa_auth = ks_loading.load_auth_from_conf_options(CONF, confgrp) if not ksa_session: ksa_session = ks_loading.load_session_from_conf_options( CONF, confgrp, auth=ksa_auth) return ks_loading.load_adapter_from_conf_options( CONF, confgrp, session=ksa_session, auth=ksa_auth, min_version=min_version, max_version=max_version) def get_endpoint(ksa_adapter): """Get the endpoint URL represented by a keystoneauth1 Adapter. This method is equivalent to what ksa_adapter.get_endpoint() should do, if it weren't for a panoply of bugs. :param ksa_adapter: keystoneauth1.adapter.Adapter, appropriately set up with an endpoint_override; or service_type, interface (list) and auth/service_catalog. :return: String endpoint URL. :raise EndpointNotFound: If endpoint discovery fails. """ # TODO(efried): This will be unnecessary once bug #1707993 is fixed. # (At least for the non-image case, until 1707995 is fixed.) if ksa_adapter.endpoint_override: return ksa_adapter.endpoint_override # TODO(efried): Remove this once bug #1707995 is fixed. if ksa_adapter.service_type == 'image': try: # LOG.warning(ksa_adapter.__dict__) return ksa_adapter.get_endpoint_data().catalog_url except AttributeError: # ksa_adapter.auth is a _ContextAuthPlugin, which doesn't have # get_endpoint_data. Fall through to using get_endpoint(). pass # TODO(efried): The remainder of this method reduces to # TODO(efried): return ksa_adapter.get_endpoint() # TODO(efried): once bug #1709118 is fixed. # NOTE(efried): Id9bd19cca68206fc64d23b0eaa95aa3e5b01b676 may also do the # trick, once it's in a ksa release. # The EndpointNotFound exception happens when _ContextAuthPlugin is in play # because its get_endpoint() method isn't yet set up to handle interface as # a list. (It could also happen with a real auth if the endpoint isn't # there; but that's covered below.) try: return ksa_adapter.get_endpoint() except ks_exc.EndpointNotFound: pass interfaces = list(ksa_adapter.interface) for interface in interfaces: ksa_adapter.interface = interface try: return ksa_adapter.get_endpoint() except ks_exc.EndpointNotFound: pass raise ks_exc.EndpointNotFound( "Could not find requested endpoint for any of the following " "interfaces: %s" % interfaces)