text: # coding=utf-8
  #
  # 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.
  
  from keystoneclient import exceptions as ksexception
  from oslo_concurrency import lockutils
  from oslo_config import cfg
  from six.moves.urllib import parse
  
  from ironic.common import exception
  from ironic.common.i18n import _
  
  CONF = cfg.CONF
  
  keystone_opts = [
      cfg.StrOpt('region_name',
                 help=_('The region used for getting endpoints of OpenStack'
                        'services.')),
  ]
  
  CONF.register_opts(keystone_opts, group='keystone')
  CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
  
  _KS_CLIENT = None
  
  
  def _is_apiv3(auth_url, auth_version):
      """Checks if V3 version of API is being used or not.
  
      This method inspects auth_url and auth_version, and checks whether V3
      version of the API is being used or not.
  
      :param auth_url: a http or https url to be inspected (like
          'http://127.0.0.1:9898/').
      :param auth_version: a string containing the version (like 'v2', 'v3.0')
      :returns: True if V3 of the API is being used.
      """
      return auth_version == 'v3.0' or '/v3' in parse.urlparse(auth_url).path
  
  
  def _get_ksclient(token=None):
      auth_url = CONF.keystone_authtoken.auth_uri
      if not auth_url:
          raise exception.KeystoneFailure(_('Keystone API endpoint is missing'))
  
      auth_version = CONF.keystone_authtoken.auth_version
      api_v3 = _is_apiv3(auth_url, auth_version)
  
      if api_v3:
          from keystoneclient.v3 import client
      else:
          from keystoneclient.v2_0 import client
  
      auth_url = get_keystone_url(auth_url, auth_version)
      try:
          if token:
              return client.Client(token=token, auth_url=auth_url)
          else:
              params = {'username': CONF.keystone_authtoken.admin_user,
                        'password': CONF.keystone_authtoken.admin_password,
                        'tenant_name': CONF.keystone_authtoken.admin_tenant_name,
                        'region_name': CONF.keystone.region_name,
                        'auth_url': auth_url}
              return _get_ksclient_from_conf(client, **params)
      except ksexception.Unauthorized:
          raise exception.KeystoneUnauthorized()
      except ksexception.AuthorizationFailure as err:
          raise exception.KeystoneFailure(_('Could not authorize in Keystone:'
                                            ' %s') % err)
  
  
  @lockutils.synchronized('keystone_client', 'ironic-')
  def _get_ksclient_from_conf(client, **params):
      global _KS_CLIENT
      # NOTE(yuriyz): use Keystone client default gap, to determine whether the
      # given token is about to expire
      if _KS_CLIENT is None or _KS_CLIENT.auth_ref.will_expire_soon():
          _KS_CLIENT = client.Client(**params)
      return _KS_CLIENT
  
  
  def get_keystone_url(auth_url, auth_version):
      """Gives an http/https url to contact keystone.
  
      Given an auth_url and auth_version, this method generates the url in
      which keystone can be reached.
  
      :param auth_url: a http or https url to be inspected (like
          'http://127.0.0.1:9898/').
      :param auth_version: a string containing the version (like v2, v3.0, etc)
      :returns: a string containing the keystone url
      """
      api_v3 = _is_apiv3(auth_url, auth_version)
      api_version = 'v3' if api_v3 else 'v2.0'
      # NOTE(lucasagomes): Get rid of the trailing '/' otherwise urljoin()
      #   fails to override the version in the URL
      return parse.urljoin(auth_url.rstrip('/'), api_version)
  
  
  def get_service_url(service_type='baremetal', endpoint_type='internal'):
      """Wrapper for get service url from keystone service catalog.
  
      Given a service_type and an endpoint_type, this method queries keystone
      service catalog and provides the url for the desired endpoint.
  
      :param service_type: the keystone service for which url is required.
      :param endpoint_type: the type of endpoint for the service.
      :returns: an http/https url for the desired endpoint.
      """
      ksclient = _get_ksclient()
  
      if not ksclient.has_service_catalog():
          raise exception.KeystoneFailure(_('No Keystone service catalog '
                                            'loaded'))
  
      try:
          endpoint = ksclient.service_catalog.url_for(
              service_type=service_type,
              endpoint_type=endpoint_type,
              region_name=CONF.keystone.region_name)
  
      except ksexception.EndpointNotFound:
          raise exception.CatalogNotFound(service_type=service_type,
                                          endpoint_type=endpoint_type)
  
      return endpoint
  
  
  def get_admin_auth_token():
      """Get an admin auth_token from the Keystone."""
      ksclient = _get_ksclient()
      return ksclient.auth_token
  
  
  def token_expires_soon(token, duration=None):
      """Determines if token expiration is about to occur.
  
      :param duration: time interval in seconds
      :returns: boolean : true if expiration is within the given duration
      """
      ksclient = _get_ksclient(token=token)
      return ksclient.auth_ref.will_expire_soon(stale_duration=duration)
  