Ejemplo n.º 1
0
    def list(self, project=None, domain=None, group=None, default_project=None,
             **kwargs):
        """List users.

        If project, domain or group are provided, then filter
        users with those attributes.

        If ``**kwargs`` are provided, then filter users with
        attributes matching ``**kwargs``.

        .. warning::

          The project argument is deprecated, use default_project instead.

        If both default_project and project is provided, the default_project
        will be used.
        """
        if project:
            LOG.warning(_LW("The project argument is deprecated, "
                            "use default_project instead."))
        default_project_id = base.getid(default_project) or base.getid(project)
        if group:
            base_url = '/groups/%s' % base.getid(group)
        else:
            base_url = None

        return super(UserManager, self).list(
            base_url=base_url,
            domain_id=base.getid(domain),
            default_project_id=default_project_id,
            **kwargs)
Ejemplo n.º 2
0
    def get_auth_ref_from_keyring(self, **kwargs):
        """Retrieve auth_ref from keyring.

        If auth_ref is found in keyring, (keyring_key, auth_ref) is returned.
        Otherwise, (keyring_key, None) is returned.

        :returns: (keyring_key, auth_ref) or (keyring_key, None)
        :returns: or (None, None) if use_keyring is not set in the object

        """
        keyring_key = None
        auth_ref = None
        if self.use_keyring:
            keyring_key = self._build_keyring_key(**kwargs)
            try:
                auth_ref = keyring.get_password("keystoneclient_auth",
                                                keyring_key)
                if auth_ref:
                    auth_ref = pickle.loads(auth_ref)  # nosec
                    if auth_ref.will_expire_soon(self.stale_duration):
                        # token has expired, don't use it
                        auth_ref = None
            except Exception as e:
                auth_ref = None
                _logger.warning(
                    _LW('Unable to retrieve token from keyring %s'), e)
        return (keyring_key, auth_ref)
Ejemplo n.º 3
0
    def update(self, user, name=None, domain=None, project=None, password=None,
               email=None, description=None, enabled=None,
               default_project=None, **kwargs):
        """Update a user.

        .. warning::

          The project argument is deprecated, use default_project instead.

        If both default_project and project is provided, the default_project
        will be used.
        """
        if project:
            LOG.warning(_LW("The project argument is deprecated, "
                            "use default_project instead."))
        default_project_id = base.getid(default_project) or base.getid(project)
        user_data = base.filter_none(name=name,
                                     domain_id=base.getid(domain),
                                     default_project_id=default_project_id,
                                     password=password,
                                     email=email,
                                     description=description,
                                     enabled=enabled,
                                     **kwargs)

        return self._update('/users/%s' % base.getid(user),
                            {'user': user_data},
                            'user',
                            method='PATCH',
                            log=False)
Ejemplo n.º 4
0
    def create(self, name, domain=None, project=None, password=None,
               email=None, description=None, enabled=True,
               default_project=None, **kwargs):
        """Create a user.

        .. warning::

          The project argument is deprecated, use default_project instead.

        If both default_project and project is provided, the default_project
        will be used.
        """
        if project:
            LOG.warning(_LW("The project argument is deprecated, "
                            "use default_project instead."))
        default_project_id = base.getid(default_project) or base.getid(project)
        user_data = base.filter_none(name=name,
                                     domain_id=base.getid(domain),
                                     default_project_id=default_project_id,
                                     password=password,
                                     email=email,
                                     description=description,
                                     enabled=enabled,
                                     **kwargs)

        return self._create('/users', {'user': user_data}, 'user',
                            log=not bool(password))
Ejemplo n.º 5
0
    def store_auth_ref_into_keyring(self, keyring_key):
        """Store auth_ref into keyring.

        """
        if self.use_keyring:
            try:
                keyring.set_password("keystoneclient_auth", keyring_key, pickle.dumps(self.auth_ref))
            except Exception as e:
                _logger.warning(_LW("Failed to store token into keyring %s"), e)
Ejemplo n.º 6
0
    def _do_create_plugin(self, session):
        plugin = None

        try:
            disc = self.get_discovery(session,
                                      self.auth_url,
                                      authenticated=False)
        except (exceptions.DiscoveryFailure,
                exceptions.HTTPError,
                exceptions.ConnectionError):
            LOG.warn(_LW('Discovering versions from the identity service '
                         'failed when creating the password plugin. '
                         'Attempting to determine version from URL.'))

            url_parts = urlparse.urlparse(self.auth_url)
            path = url_parts.path.lower()

            if path.startswith('/v2.0') and not self._has_domain_scope:
                plugin = self.create_plugin(session, (2, 0), self.auth_url)
            elif path.startswith('/v3'):
                plugin = self.create_plugin(session, (3, 0), self.auth_url)

        else:
            disc_data = disc.version_data()

            for data in disc_data:
                version = data['version']

                if (discover.version_match((2,), version) and
                        self._has_domain_scope):
                    # NOTE(jamielennox): if there are domain parameters there
                    # is no point even trying against v2 APIs.
                    continue

                plugin = self.create_plugin(session,
                                            version,
                                            data['url'],
                                            raw_status=data['raw_status'])

                if plugin:
                    break

        if plugin:
            return plugin

        # so there were no URLs that i could use for auth of any version.
        msg = _('Could not determine a suitable URL for the plugin')
        raise exceptions.DiscoveryFailure(msg)
Ejemplo n.º 7
0
    def raw_version_data(self, allow_experimental=False,
                         allow_deprecated=True, allow_unknown=False):
        """Get raw version information from URL.

        Raw data indicates that only minimal validation processing is performed
        on the data, so what is returned here will be the data in the same
        format it was received from the endpoint.

        :param bool allow_experimental: Allow experimental version endpoints.
        :param bool allow_deprecated: Allow deprecated version endpoints.
        :param bool allow_unknown: Allow endpoints with an unrecognised status.

        :returns: The endpoints returned from the server that match the
                  criteria.
        :rtype: list
        """
        versions = []
        for v in self._data:
            try:
                status = v['status']
            except KeyError:
                _LOGGER.warning(_LW('Skipping over invalid version data. '
                                    'No stability status in version.'))
                continue

            status = status.lower()

            if status in self.CURRENT_STATUSES:
                versions.append(v)
            elif status in self.DEPRECATED_STATUSES:
                if allow_deprecated:
                    versions.append(v)
            elif status in self.EXPERIMENTAL_STATUSES:
                if allow_experimental:
                    versions.append(v)
            elif allow_unknown:
                versions.append(v)

        return versions
Ejemplo n.º 8
0
    def __init__(self, username=None, tenant_id=None, tenant_name=None,
                 password=None, auth_url=None, region_name=None, endpoint=None,
                 token=None, debug=False, auth_ref=None, use_keyring=False,
                 force_new_token=False, stale_duration=None, user_id=None,
                 user_domain_id=None, user_domain_name=None, domain_id=None,
                 domain_name=None, project_id=None, project_name=None,
                 project_domain_id=None, project_domain_name=None,
                 trust_id=None, session=None, service_name=None,
                 interface='admin', endpoint_override=None, auth=None,
                 user_agent=USER_AGENT, connect_retries=None, **kwargs):
        # set baseline defaults
        self.user_id = None
        self.username = None
        self.user_domain_id = None
        self.user_domain_name = None

        self.domain_id = None
        self.domain_name = None

        self.project_id = None
        self.project_name = None
        self.project_domain_id = None
        self.project_domain_name = None

        self.auth_url = None
        self._endpoint = None
        self._management_url = None

        self.trust_id = None

        # if loading from a dictionary passed in via auth_ref,
        # load values from AccessInfo parsing that dictionary
        if auth_ref:
            self.auth_ref = access.AccessInfo.factory(**auth_ref)
            self.version = self.auth_ref.version
            self.user_id = self.auth_ref.user_id
            self.username = self.auth_ref.username
            self.user_domain_id = self.auth_ref.user_domain_id
            self.domain_id = self.auth_ref.domain_id
            self.domain_name = self.auth_ref.domain_name
            self.project_id = self.auth_ref.project_id
            self.project_name = self.auth_ref.project_name
            self.project_domain_id = self.auth_ref.project_domain_id
            auth_urls = self.auth_ref.service_catalog.get_urls(
                service_type='identity', endpoint_type='public',
                region_name=region_name)
            self.auth_url = auth_urls[0]
            management_urls = self.auth_ref.service_catalog.get_urls(
                service_type='identity', endpoint_type='admin',
                region_name=region_name)
            self._management_url = management_urls[0]
            self.auth_token_from_user = self.auth_ref.auth_token
            self.trust_id = self.auth_ref.trust_id
            if self.auth_ref.has_service_catalog() and not region_name:
                region_name = self.auth_ref.service_catalog.region_name
        else:
            self.auth_ref = None

        # allow override of the auth_ref defaults from explicit
        # values provided to the client

        # apply deprecated variables first, so modern variables override them
        if tenant_id:
            self.project_id = tenant_id
        if tenant_name:
            self.project_name = tenant_name

        # user-related attributes
        self.password = password
        if user_id:
            self.user_id = user_id
        if username:
            self.username = username
        if user_domain_id:
            self.user_domain_id = user_domain_id
        elif not (user_id or user_domain_name):
            self.user_domain_id = 'default'
        if user_domain_name:
            self.user_domain_name = user_domain_name

        # domain-related attributes
        if domain_id:
            self.domain_id = domain_id
        if domain_name:
            self.domain_name = domain_name

        # project-related attributes
        if project_id:
            self.project_id = project_id
        if project_name:
            self.project_name = project_name
        if project_domain_id:
            self.project_domain_id = project_domain_id
        elif not (project_id or project_domain_name):
            self.project_domain_id = 'default'
        if project_domain_name:
            self.project_domain_name = project_domain_name

        # trust-related attributes
        if trust_id:
            self.trust_id = trust_id

        # endpoint selection
        if auth_url:
            self.auth_url = auth_url.rstrip('/')
        if token:
            self.auth_token_from_user = token
        else:
            self.auth_token_from_user = None
        if endpoint:
            self._endpoint = endpoint.rstrip('/')
        self._auth_token = None

        if not session:
            kwargs['session'] = _FakeRequestSession()
            session = client_session.Session.construct(kwargs)
            session.auth = self

        super(HTTPClient, self).__init__(session=session)
        self.domain = ''
        self.debug_log = debug

        # NOTE(jamielennox): unfortunately we can't just use **kwargs here as
        # it would incompatibly limit the kwargs that can be passed to __init__
        # try and keep this list in sync with adapter.Adapter.__init__
        version = (
            _discover.normalize_version_number(self.version) if self.version
            else None)
        self._adapter = _KeystoneAdapter(session,
                                         service_type='identity',
                                         service_name=service_name,
                                         interface=interface,
                                         region_name=region_name,
                                         endpoint_override=endpoint_override,
                                         version=version,
                                         auth=auth,
                                         user_agent=user_agent,
                                         connect_retries=connect_retries)

        # keyring setup
        if use_keyring and keyring is None:
            _logger.warning(_LW('Failed to load keyring modules.'))
        self.use_keyring = use_keyring and keyring is not None
        self.force_new_token = force_new_token
        self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
        self.stale_duration = int(self.stale_duration)
    def _send_request(self, url, method, redirect, log, logger,
                      connect_retries, connect_retry_delay=0.5, **kwargs):
        # NOTE(jamielennox): We handle redirection manually because the
        # requests lib follows some browser patterns where it will redirect
        # POSTs as GETs for certain statuses which is not want we want for an
        # API. See: https://en.wikipedia.org/wiki/Post/Redirect/Get

        # NOTE(jamielennox): The interaction between retries and redirects are
        # handled naively. We will attempt only a maximum number of retries and
        # redirects rather than per request limits. Otherwise the extreme case
        # could be redirects * retries requests. This will be sufficient in
        # most cases and can be fixed properly if there's ever a need.

        try:
            try:
                resp = self.session.request(method, url, **kwargs)
            except requests.exceptions.SSLError as e:
                msg = _('SSL exception connecting to %(url)s: '
                        '%(error)s') % {'url': url, 'error': e}
                raise exceptions.SSLError(msg)
            except requests.exceptions.Timeout:
                msg = _('Request to %s timed out') % url
                raise exceptions.RequestTimeout(msg)
            except requests.exceptions.ConnectionError:
                msg = _('Unable to establish connection to %s') % url
                raise exceptions.ConnectionRefused(msg)
        except (exceptions.RequestTimeout, exceptions.ConnectionRefused) as e:
            if connect_retries <= 0:
                raise

            logger.info(_LI('Failure: %(e)s. Retrying in %(delay).1fs.'),
                        {'e': e, 'delay': connect_retry_delay})
            time.sleep(connect_retry_delay)

            return self._send_request(
                url, method, redirect, log, logger,
                connect_retries=connect_retries - 1,
                connect_retry_delay=connect_retry_delay * 2,
                **kwargs)

        if log:
            self._http_log_response(resp, logger)

        if resp.status_code in self._REDIRECT_STATUSES:
            # be careful here in python True == 1 and False == 0
            if isinstance(redirect, bool):
                redirect_allowed = redirect
            else:
                redirect -= 1
                redirect_allowed = redirect >= 0

            if not redirect_allowed:
                return resp

            try:
                location = resp.headers['location']
            except KeyError:
                logger.warning(_LW("Failed to redirect request to %s as new "
                                   "location was not provided."), resp.url)
            else:
                # NOTE(jamielennox): We don't pass through connect_retry_delay.
                # This request actually worked so we can reset the delay count.
                new_resp = self._send_request(
                    location, method, redirect, log, logger,
                    connect_retries=connect_retries,
                    **kwargs)

                if not isinstance(new_resp.history, list):
                    new_resp.history = list(new_resp.history)
                new_resp.history.insert(0, resp)
                resp = new_resp

        return resp
Ejemplo n.º 10
0
def is_ans1_token(token):
    """Deprecated. Use is_asn1_token() instead."""
    LOG.warning(_LW('The function is_ans1_token() is deprecated, '
                    'use is_asn1_token() instead.'))
    return is_asn1_token(token)
Ejemplo n.º 11
0
    def get_endpoint(self, session, service_type=None, interface=None,
                     region_name=None, service_name=None, version=None,
                     **kwargs):
        """Return a valid endpoint for a service.

        If a valid token is not present then a new one will be fetched using
        the session and kwargs.

        :param session: A session object that can be used for communication.
        :type session: keystoneclient.session.Session
        :param string service_type: The type of service to lookup the endpoint
                                    for. This plugin will return None (failure)
                                    if service_type is not provided.
        :param string interface: The exposure of the endpoint. Should be
                                 `public`, `internal`, `admin`, or `auth`.
                                 `auth` is special here to use the `auth_url`
                                 rather than a URL extracted from the service
                                 catalog. Defaults to `public`.
        :param string region_name: The region the endpoint should exist in.
                                   (optional)
        :param string service_name: The name of the service in the catalog.
                                   (optional)
        :param tuple version: The minimum version number required for this
                              endpoint. (optional)

        :raises keystoneclient.exceptions.HttpError: An error from an invalid
                                                     HTTP response.

        :return: A valid endpoint URL or None if not available.
        :rtype: string or None
        """
        # NOTE(jamielennox): if you specifically ask for requests to be sent to
        # the auth url then we can ignore many of the checks. Typically if you
        # are asking for the auth endpoint it means that there is no catalog to
        # query however we still need to support asking for a specific version
        # of the auth_url for generic plugins.
        if interface is base.AUTH_INTERFACE:
            url = self.auth_url
            service_type = service_type or 'identity'

        else:
            if not service_type:
                LOG.warning(_LW(
                    'Plugin cannot return an endpoint without knowing the '
                    'service type that is required. Add service_type to '
                    'endpoint filtering data.'))
                return None

            if not interface:
                interface = 'public'

            service_catalog = self.get_access(session).service_catalog
            url = service_catalog.url_for(service_type=service_type,
                                          endpoint_type=interface,
                                          region_name=region_name,
                                          service_name=service_name)

        if not version:
            # NOTE(jamielennox): This may not be the best thing to default to
            # but is here for backwards compatibility. It may be worth
            # defaulting to the most recent version.
            return url

        # NOTE(jamielennox): For backwards compatibility people might have a
        # versioned endpoint in their catalog even though they want to use
        # other endpoint versions. So we support a list of client defined
        # situations where we can strip the version component from a URL before
        # doing discovery.
        hacked_url = _discover.get_catalog_discover_hack(service_type, url)

        try:
            disc = self.get_discovery(session, hacked_url, authenticated=False)
        except (exceptions.DiscoveryFailure,
                exceptions.HTTPError,
                exceptions.ConnectionError):
            # NOTE(jamielennox): Again if we can't contact the server we fall
            # back to just returning the URL from the catalog. This may not be
            # the best default but we need it for now.
            LOG.warning(_LW(
                'Failed to contact the endpoint at %s for discovery. Fallback '
                'to using that endpoint as the base url.'),
                url)
        else:
            url = disc.url_for(version)

        return url
Ejemplo n.º 12
0
    def __init__(self, username=None, tenant_id=None, tenant_name=None,
                 password=None, auth_url=None, region_name=None, endpoint=None,
                 token=None, debug=False, auth_ref=None, use_keyring=False,
                 force_new_token=False, stale_duration=None, user_id=None,
                 user_domain_id=None, user_domain_name=None, domain_id=None,
                 domain_name=None, project_id=None, project_name=None,
                 project_domain_id=None, project_domain_name=None,
                 trust_id=None, session=None, **kwargs):
        """Construct a new http client

        :param string user_id: User ID for authentication. (optional)
        :param string username: Username for authentication. (optional)
        :param string user_domain_id: User's domain ID for authentication.
                                      (optional)
        :param string user_domain_name: User's domain name for authentication.
                                        (optional)
        :param string password: Password for authentication. (optional)
        :param string domain_id: Domain ID for domain scoping. (optional)
        :param string domain_name: Domain name for domain scoping. (optional)
        :param string project_id: Project ID for project scoping. (optional)
        :param string project_name: Project name for project scoping.
                                    (optional)
        :param string project_domain_id: Project's domain ID for project
                                         scoping. (optional)
        :param string project_domain_name: Project's domain name for project
                                           scoping. (optional)
        :param string auth_url: Identity service endpoint for authorization.
        :param string region_name: Name of a region to select when choosing an
                                   endpoint from the service catalog.
        :param integer timeout: DEPRECATED: use session. (optional)
        :param string endpoint: A user-supplied endpoint URL for the identity
                                service.  Lazy-authentication is possible for
                                API service calls if endpoint is set at
                                instantiation. (optional)
        :param string token: Token for authentication. (optional)
        :param string cacert: DEPRECATED: use session. (optional)
        :param string key: DEPRECATED: use session. (optional)
        :param string cert: DEPRECATED: use session. (optional)
        :param boolean insecure: DEPRECATED: use session. (optional)
        :param string original_ip: DEPRECATED: use session. (optional)
        :param boolean debug: DEPRECATED: use logging configuration. (optional)
        :param dict auth_ref: To allow for consumers of the client to manage
                              their own caching strategy, you may initialize a
                              client with a previously captured auth_reference
                              (token). If there are keyword arguments passed
                              that also exist in auth_ref, the value from the
                              argument will take precedence.
        :param boolean use_keyring: Enables caching auth_ref into keyring.
                                    default: False (optional)
        :param boolean force_new_token: Keyring related parameter, forces
                                       request for new token.
                                       default: False (optional)
        :param integer stale_duration: Gap in seconds to determine if token
                                       from keyring is about to expire.
                                       default: 30 (optional)
        :param string tenant_name: Tenant name. (optional)
                                   The tenant_name keyword argument is
                                   deprecated, use project_name instead.
        :param string tenant_id: Tenant id. (optional)
                                 The tenant_id keyword argument is
                                 deprecated, use project_id instead.
        :param string trust_id: Trust ID for trust scoping. (optional)
        :param object session: A Session object to be used for
                               communicating with the identity service.

        """
        # set baseline defaults
        self.user_id = None
        self.username = None
        self.user_domain_id = None
        self.user_domain_name = None

        self.domain_id = None
        self.domain_name = None

        self.project_id = None
        self.project_name = None
        self.project_domain_id = None
        self.project_domain_name = None

        self.region_name = None
        self.auth_url = None
        self._endpoint = None
        self._management_url = None

        self.trust_id = None

        # if loading from a dictionary passed in via auth_ref,
        # load values from AccessInfo parsing that dictionary
        if auth_ref:
            self.auth_ref = access.AccessInfo.factory(**auth_ref)
            self.version = self.auth_ref.version
            self.user_id = self.auth_ref.user_id
            self.username = self.auth_ref.username
            self.user_domain_id = self.auth_ref.user_domain_id
            self.domain_id = self.auth_ref.domain_id
            self.domain_name = self.auth_ref.domain_name
            self.project_id = self.auth_ref.project_id
            self.project_name = self.auth_ref.project_name
            self.project_domain_id = self.auth_ref.project_domain_id
            self.auth_url = self.auth_ref.auth_url[0]
            self._management_url = self.auth_ref.management_url[0]
            self.auth_token_from_user = self.auth_ref.auth_token
            self.trust_id = self.auth_ref.trust_id
            if self.auth_ref.has_service_catalog():
                self.region_name = self.auth_ref.service_catalog.region_name
        else:
            self.auth_ref = None

        # allow override of the auth_ref defaults from explicit
        # values provided to the client

        # apply deprecated variables first, so modern variables override them
        if tenant_id:
            self.project_id = tenant_id
        if tenant_name:
            self.project_name = tenant_name

        # user-related attributes
        self.password = password
        if user_id:
            self.user_id = user_id
        if username:
            self.username = username
        if user_domain_id:
            self.user_domain_id = user_domain_id
        elif not (user_id or user_domain_name):
            self.user_domain_id = 'default'
        if user_domain_name:
            self.user_domain_name = user_domain_name

        # domain-related attributes
        if domain_id:
            self.domain_id = domain_id
        if domain_name:
            self.domain_name = domain_name

        # project-related attributes
        if project_id:
            self.project_id = project_id
        if project_name:
            self.project_name = project_name
        if project_domain_id:
            self.project_domain_id = project_domain_id
        elif not (project_id or project_domain_name):
            self.project_domain_id = 'default'
        if project_domain_name:
            self.project_domain_name = project_domain_name

        # trust-related attributes
        if trust_id:
            self.trust_id = trust_id

        # endpoint selection
        if auth_url:
            self.auth_url = auth_url.rstrip('/')
        if token:
            self.auth_token_from_user = token
        else:
            self.auth_token_from_user = None
        if endpoint:
            self._endpoint = endpoint.rstrip('/')
        if region_name:
            self.region_name = region_name
        self._auth_token = None

        if not session:
            kwargs['session'] = _FakeRequestSession()
            session = client_session.Session.construct(kwargs)
            session.auth = self

        super(HTTPClient, self).__init__(session=session)
        self.domain = ''
        self.debug_log = debug

        # keyring setup
        if use_keyring and keyring is None:
            _logger.warning(_LW('Failed to load keyring modules.'))
        self.use_keyring = use_keyring and keyring is not None
        self.force_new_token = force_new_token
        self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
        self.stale_duration = int(self.stale_duration)
Ejemplo n.º 13
0
    def get_endpoint(self, session, service_type=None, interface=None,
                     region_name=None, service_name=None, version=None,
                     **kwargs):
        """Return a valid endpoint for a service.

        If a valid token is not present then a new one will be fetched using
        the session and kwargs.

        :param session: A session object that can be used for communication.
        :type session: keystoneclient.session.Session
        :param string service_type: The type of service to lookup the endpoint
                                    for. This plugin will return None (failure)
                                    if service_type is not provided.
        :param string interface: The exposure of the endpoint. Should be
                                 `public`, `internal`, `admin`, or `auth`.
                                 `auth` is special here to use the `auth_url`
                                 rather than a URL extracted from the service
                                 catalog. Defaults to `public`.
        :param string region_name: The region the endpoint should exist in.
                                   (optional)
        :param string service_name: The name of the service in the catalog.
                                   (optional)
        :param tuple version: The minimum version number required for this
                              endpoint. (optional)

        :raises keystoneclient.exceptions.HttpError: An error from an invalid
                                                     HTTP response.

        :return: A valid endpoint URL or None if not available.
        :rtype: string or None
        """
        # NOTE(jamielennox): if you specifically ask for requests to be sent to
        # the auth url then we can ignore the rest of the checks. Typically if
        # you are asking for the auth endpoint it means that there is no
        # catalog to query anyway.
        if interface is base.AUTH_INTERFACE:
            return self.auth_url

        if not service_type:
            LOG.warn(_LW('Plugin cannot return an endpoint without knowing '
                         'the service type that is required. Add service_type '
                         'to endpoint filtering data.'))
            return None

        if not interface:
            interface = 'public'

        service_catalog = self.get_access(session).service_catalog
        url = service_catalog.url_for(service_type=service_type,
                                      endpoint_type=interface,
                                      region_name=region_name,
                                      service_name=service_name)

        if not version:
            # NOTE(jamielennox): This may not be the best thing to default to
            # but is here for backwards compatibility. It may be worth
            # defaulting to the most recent version.
            return url

        # NOTE(jamielennox): For backwards compatibility people might have a
        # versioned endpoint in their catalog even though they want to use
        # other endpoint versions. So we support a list of client defined
        # situations where we can strip the version component from a URL before
        # doing discovery.
        hacked_url = _discover.get_catalog_discover_hack(service_type, url)

        try:
            disc = self.get_discovery(session, hacked_url, authenticated=False)
        except (exceptions.DiscoveryFailure,
                exceptions.HTTPError,
                exceptions.ConnectionError):
            # NOTE(jamielennox): Again if we can't contact the server we fall
            # back to just returning the URL from the catalog. This may not be
            # the best default but we need it for now.
            LOG.warn(_LW('Failed to contact the endpoint at %s for discovery. '
                         'Fallback to using that endpoint as the base url.'),
                     url)
        else:
            url = disc.url_for(version)

        return url
Ejemplo n.º 14
0
    def _send_request(self,
                      url,
                      method,
                      redirect,
                      log,
                      logger,
                      connect_retries,
                      connect_retry_delay=0.5,
                      **kwargs):
        # NOTE(jamielennox): We handle redirection manually because the
        # requests lib follows some browser patterns where it will redirect
        # POSTs as GETs for certain statuses which is not want we want for an
        # API. See: https://en.wikipedia.org/wiki/Post/Redirect/Get

        # NOTE(jamielennox): The interaction between retries and redirects are
        # handled naively. We will attempt only a maximum number of retries and
        # redirects rather than per request limits. Otherwise the extreme case
        # could be redirects * retries requests. This will be sufficient in
        # most cases and can be fixed properly if there's ever a need.

        try:
            try:
                resp = self.session.request(method, url, **kwargs)
            except requests.exceptions.SSLError as e:
                msg = _('SSL exception connecting to %(url)s: '
                        '%(error)s') % {
                            'url': url,
                            'error': e
                        }
                raise exceptions.SSLError(msg)
            except requests.exceptions.Timeout:
                msg = _('Request to %s timed out') % url
                raise exceptions.RequestTimeout(msg)
            except requests.exceptions.ConnectionError:
                msg = _('Unable to establish connection to %s') % url
                raise exceptions.ConnectionRefused(msg)
        except (exceptions.RequestTimeout, exceptions.ConnectionRefused) as e:
            if connect_retries <= 0:
                raise

            logger.info(_LI('Failure: %(e)s. Retrying in %(delay).1fs.'), {
                'e': e,
                'delay': connect_retry_delay
            })
            time.sleep(connect_retry_delay)

            return self._send_request(url,
                                      method,
                                      redirect,
                                      log,
                                      logger,
                                      connect_retries=connect_retries - 1,
                                      connect_retry_delay=connect_retry_delay *
                                      2,
                                      **kwargs)

        if log:
            self._http_log_response(resp, logger)

        if resp.status_code in self._REDIRECT_STATUSES:
            # be careful here in python True == 1 and False == 0
            if isinstance(redirect, bool):
                redirect_allowed = redirect
            else:
                redirect -= 1
                redirect_allowed = redirect >= 0

            if not redirect_allowed:
                return resp

            try:
                location = resp.headers['location']
            except KeyError:
                logger.warning(
                    _LW("Failed to redirect request to %s as new "
                        "location was not provided."), resp.url)
            else:
                # NOTE(jamielennox): We don't pass through connect_retry_delay.
                # This request actually worked so we can reset the delay count.
                new_resp = self._send_request(location,
                                              method,
                                              redirect,
                                              log,
                                              logger,
                                              connect_retries=connect_retries,
                                              **kwargs)

                if not isinstance(new_resp.history, list):
                    new_resp.history = list(new_resp.history)
                new_resp.history.insert(0, resp)
                resp = new_resp

        return resp
Ejemplo n.º 15
0
    def __init__(self,
                 username=None,
                 tenant_id=None,
                 tenant_name=None,
                 password=None,
                 auth_url=None,
                 region_name=None,
                 endpoint=None,
                 token=None,
                 debug=False,
                 auth_ref=None,
                 use_keyring=False,
                 force_new_token=False,
                 stale_duration=None,
                 user_id=None,
                 user_domain_id=None,
                 user_domain_name=None,
                 domain_id=None,
                 domain_name=None,
                 project_id=None,
                 project_name=None,
                 project_domain_id=None,
                 project_domain_name=None,
                 trust_id=None,
                 session=None,
                 service_name=None,
                 interface='admin',
                 endpoint_override=None,
                 auth=None,
                 user_agent=USER_AGENT,
                 connect_retries=None,
                 **kwargs):
        # set baseline defaults
        self.user_id = None
        self.username = None
        self.user_domain_id = None
        self.user_domain_name = None

        self.domain_id = None
        self.domain_name = None

        self.project_id = None
        self.project_name = None
        self.project_domain_id = None
        self.project_domain_name = None

        self.auth_url = None
        self._endpoint = None
        self._management_url = None

        self.trust_id = None

        # if loading from a dictionary passed in via auth_ref,
        # load values from AccessInfo parsing that dictionary
        if auth_ref:
            self.auth_ref = access.AccessInfo.factory(**auth_ref)
            self.version = self.auth_ref.version
            self.user_id = self.auth_ref.user_id
            self.username = self.auth_ref.username
            self.user_domain_id = self.auth_ref.user_domain_id
            self.domain_id = self.auth_ref.domain_id
            self.domain_name = self.auth_ref.domain_name
            self.project_id = self.auth_ref.project_id
            self.project_name = self.auth_ref.project_name
            self.project_domain_id = self.auth_ref.project_domain_id
            self.auth_url = self.auth_ref.auth_url[0]
            self._management_url = self.auth_ref.management_url[0]
            self.auth_token_from_user = self.auth_ref.auth_token
            self.trust_id = self.auth_ref.trust_id
            if self.auth_ref.has_service_catalog() and not region_name:
                region_name = self.auth_ref.service_catalog.region_name
        else:
            self.auth_ref = None

        # allow override of the auth_ref defaults from explicit
        # values provided to the client

        # apply deprecated variables first, so modern variables override them
        if tenant_id:
            self.project_id = tenant_id
        if tenant_name:
            self.project_name = tenant_name

        # user-related attributes
        self.password = password
        if user_id:
            self.user_id = user_id
        if username:
            self.username = username
        if user_domain_id:
            self.user_domain_id = user_domain_id
        elif not (user_id or user_domain_name):
            self.user_domain_id = 'default'
        if user_domain_name:
            self.user_domain_name = user_domain_name

        # domain-related attributes
        if domain_id:
            self.domain_id = domain_id
        if domain_name:
            self.domain_name = domain_name

        # project-related attributes
        if project_id:
            self.project_id = project_id
        if project_name:
            self.project_name = project_name
        if project_domain_id:
            self.project_domain_id = project_domain_id
        elif not (project_id or project_domain_name):
            self.project_domain_id = 'default'
        if project_domain_name:
            self.project_domain_name = project_domain_name

        # trust-related attributes
        if trust_id:
            self.trust_id = trust_id

        # endpoint selection
        if auth_url:
            self.auth_url = auth_url.rstrip('/')
        if token:
            self.auth_token_from_user = token
        else:
            self.auth_token_from_user = None
        if endpoint:
            self._endpoint = endpoint.rstrip('/')
        self._auth_token = None

        if not session:
            kwargs['session'] = _FakeRequestSession()
            session = client_session.Session.construct(kwargs)
            session.auth = self

        super(HTTPClient, self).__init__(session=session)
        self.domain = ''
        self.debug_log = debug

        # NOTE(jamielennox): unfortunately we can't just use **kwargs here as
        # it would incompatibly limit the kwargs that can be passed to __init__
        # try and keep this list in sync with adapter.Adapter.__init__
        self._adapter = _KeystoneAdapter(session,
                                         service_type='identity',
                                         service_name=service_name,
                                         interface=interface,
                                         region_name=region_name,
                                         endpoint_override=endpoint_override,
                                         version=self.version,
                                         auth=auth,
                                         user_agent=user_agent,
                                         connect_retries=connect_retries)

        # keyring setup
        if use_keyring and keyring is None:
            _logger.warning(_LW('Failed to load keyring modules.'))
        self.use_keyring = use_keyring and keyring is not None
        self.force_new_token = force_new_token
        self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
        self.stale_duration = int(self.stale_duration)