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: keystonauth.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 keystonauth.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 plugin.AUTH_INTERFACE: url = self.auth_url service_type = service_type or 'identity' else: if not service_type: LOG.warning('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, interface=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( '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
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: keystonauth.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 keystonauth.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 plugin.AUTH_INTERFACE: url = self.auth_url service_type = service_type or 'identity' else: if not service_type: LOG.warn(('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, interface=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('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
def get_endpoint(self, session, service_type=None, interface=None, region_name=None, service_name=None, version=None, allow={}, allow_version_hack=True, **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: keystoneauth1.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) :param dict allow: Extra filters to pass when discovering API versions. (optional) :param bool allow_version_hack: Allow keystoneauth to hack up catalog URLS to support older schemes. (optional, default True) :raises keystoneauth1.exceptions.http.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 plugin.AUTH_INTERFACE: url = self.auth_url service_type = service_type or 'identity' else: if not service_type: LOG.warning('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, interface=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. if allow_version_hack: vers_url = discover._get_catalog_discover_hack(service_type, url) else: vers_url = url try: disc = self.get_discovery(session, vers_url, authenticated=False) except (exceptions.DiscoveryFailure, exceptions.HttpError, exceptions.ConnectionError): # NOTE(jamielennox): The logic here is required for backwards # compatibility. By itself it is not ideal. if allow_version_hack: # NOTE(jamielennox): Again if we can't contact the server we # fall back to just returning the URL from the catalog. This # is backwards compatible behaviour and used when there is no # other choice. Realistically if you have provided a version # you should be able to rely on that version being returned or # the request failing. LOG.warning( 'Failed to contact the endpoint at %s for ' 'discovery. Fallback to using that endpoint as ' 'the base url.', url) else: # NOTE(jamielennox): If you've said no to allow_version_hack # and you can't determine the actual URL this is a failure # because we are specifying that the deployment must be up to # date enough to properly specify a version and keystoneauth # can't deliver. return None else: # NOTE(jamielennox): urljoin allows the url to be relative or even # protocol-less. The additional trailing '/' make urljoin respect # the current path as canonical even if the url doesn't include it. # for example a "v2" path from http://host/admin should resolve as # http://host/admin/v2 where it would otherwise be host/v2. # This has no effect on absolute urls returned from url_for. url = disc.url_for(version, **allow) if url: url = urllib.parse.urljoin(vers_url.rstrip('/') + '/', url) return url