The keystoneauth1.session.Session
class was introduced into
keystoneauth1 as an attempt to bring a unified interface to the various
OpenStack clients that share common authentication and request parameters
between a variety of services.
The model for using a Session and auth plugin as well as the general terms used have been heavily inspired by the requests library. However neither the Session class nor any of the authentication plugins rely directly on those concepts from the requests library so you should not expect a direct translation.
Common client authentication
Authentication is handled by one of a variety of authentication plugins and then this authentication information is shared between all the services that use the same Session object.
Security maintenance
Security code is maintained in a single place and reused between all clients such that in the event of problems it can be fixed in a single location.
Standard service and version discovery
Clients are not expected to have any knowledge of an identity token or any other form of identification credential. Service, endpoint, major version discovery, and microversion support discovery are handled by the Session and plugins. Discovery information is automatically cached in memory, so the user need not worry about excessive use of discovery metadata.
The Session object is the contact point to your OpenStack cloud services. It stores the authentication credentials and connection information required to communicate with OpenStack such that it can be reused to communicate with many services. When creating services this Session object is passed to the client so that it may use this information.
A Session will authenticate on demand. When a request that requires authentication passes through the Session the authentication plugin will be asked for a valid token. If a valid token is available it will be used otherwise the authentication plugin may attempt to contact the authentication service and fetch a new one.
An example using keystoneclient to wrap a Session:
>>> from keystoneauth1.identity import v3
>>> from keystoneauth1 import session
>>> from keystoneclient.v3 import client
>>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3',
... username='myuser',
... password='mypassword',
... project_name='proj',
... user_domain_id='default',
... project_domain_id='default')
>>> sess = session.Session(auth=auth,
... verify='/path/to/ca.cert')
>>> ks = client.Client(session=sess)
>>> users = ks.users.list()
As other OpenStack client libraries adopt this means of operating they will be created in a similar fashion by passing the Session object to the client’s constructor.
A Session can only contain one authentication plugin. However, there is nothing that specifically binds the authentication plugin to that Session - a new Session can be created that reuses the existing authentication plugin:
>>> new_sess = session.Session(auth=sess.auth,
verify='/path/to/different-cas.cert')
In this case we cannot know which Session object will be used when the plugin performs the authentication call so the command must be able to succeed with either.
Authentication plugins can also be provided on a per-request basis. This will be beneficial in a situation where a single Session is juggling multiple authentication credentials:
>>> sess.get('https://my.keystone.com:5000/v3',
auth=my_auth_plugin)
If an auth plugin is provided via parameter then it will override any auth plugin on the Session.
Sessions are intended to take away much of the hassle of dealing with authentication data and token formats. Clients should be able to specify filter parameters for selecting the endpoint and have the parsing of the catalog managed for them.
In OpenStack, the root URLs of available services are distributed to the user in an object called the Service Catalog, which is part of the token they receive. Clients are expected to use the URLs from the Service Catalog rather than have them provided. The root URL of a given service is referred to as the endpoint of the service. The URL of a specific version of a service is referred to as a versioned endpoint. REST requests for a service are made against a given versioned endpoint.
The topic of Major API versions and microversions can be confusing. As keystoneauth provides facilities for discovery of versioned endpoints associated with a Major API Version and for fetching information about the microversions that versioned endpoint supports, it is important to be aware of the distinction between the two.
Conceptually the most important thing to understand is that a Major API Version describes the URL of a discrete versioned endpoint, while a given versioned endpoint might have properties that express that it supports a range of microversions.
When a user wants to make a REST request against a service, the user expresses
the Major API version and the type of service so that the appropriate versioned
endpoint can be found and used. For example, a user might request
version 2 of the compute service from cloud.example.com and end up with a
versioned endpoint of https://compute.example.com/v2
.
Each service provides a discovery document at the root of each versioned endpoint that contains information about that versioned endpoint. Each service also provides a document at the root of the unversioned endpoint that contains a list of the discovery documents for all of the available versioned endpoints. By examining these documents, it is possible to find the versioned endpoint that corresponds with the user’s desired Major API version.
Each of those documents may also indicate that the given versioned endpoint supports microversions by listing a minimum and maximum microversion that it understands. As a result of having found the versioned endpoint for the requested Major API version, the user will also know which microversions, if any, may be used in requests to that versioned endpoint.
When a client makes REST requests to the Major API version’s endpoint, the client can, optionally, on a request-by-request basis, include a header specifying that the individual request use the behavior defined by the given microversion. If a client does not request a microversion, the service will behave as if the minimum supported microversion was specified.
The overall transaction then has three parts:
keystoneauth provides facilities for discovering the endpoint for a given Major API of a given service, as well as reporting the available microversion ranges that endpoint supports, if any.
More information is available in the API-WG Specs on the topics of Microversions and Consuming the Catalog.
When making a request with a Session object you can simply pass the keyword
parameter authenticated
to indicate whether the argument should contain a
token, by default a token is included if an authentication plugin is available:
>>> # In keystone this route is unprotected by default
>>> resp = sess.get('https://my.keystone.com:5000/v3',
authenticated=False)
In general a client does not need to know the full URL for the server that they are communicating with, simply that it should send a request to a path belonging to the correct service.
This is controlled by the endpoint_filter
parameter to a request which
contains all the information an authentication plugin requires to determine the
correct URL to which to send a request. When using this mode only the path for
the request needs to be specified:
>>> resp = session.get('/users',
endpoint_filter={'service_type': 'identity',
'interface': 'admin',
'region_name': 'myregion',
'min_version': '2.0',
'max_version': '3.4',
'discover_versions': False})
Note
The min_version and max_version arguments in this example indicate
acceptable range for finding the endpoint for the given Major API
versions. They are in the endpoint_filter, they are not requesting
the call to /users
be made at a specific microversion.
endpoint_filter accepts a number of arguments with which it can determine an endpoint url:
identity
, compute
, volume
or
many other predefined identifiers.the network exposure the interface has. Can also be a list, in which case the first matching interface will be used. Valid values are:
public
: An endpoint that is available to the wider internet or network.internal
: An endpoint that is only accessible within the private
network.admin
: An endpoint to be used for administrative tasks.2.2
will match 2.2
and 2.3
but not 2.1
or
3.0
. Mutually exclusive with min_version and max_version.the maximum version of a given API, intended to be used as the upper bound of a range with min_version. For example:
'min_version': '2.2',
'max_version': '3.3'
will match 2.2
, 2.10
, 3.0
, and 3.3
, but not 1.42
,
2.1
, or 3.20
. Mutually exclusive with version.
Note
version, min_version and max_version are all used to help determine the endpoint for a given Major API version of a service.
True
.All version arguments (version, min_version and max_version) can be given as:
'2.0'
2
2.0
(2, 0)
version and max_version can also be given the string latest
, which
indicates that the highest available version should be used.
The endpoint filter is a simple key-value filter and can be provided with any number of arguments. It is then up to the auth plugin to correctly use the parameters it understands.
If you want to further limit your service discovery by allowing experimental
APIs or disallowing deprecated APIs, you can use the allow
parameter:
>>> resp = session.get('/<project-id>/volumes',
endpoint_filter={'service_type': 'volume',
'interface': 'public',
'version': 1},
allow={'allow_deprecated': False})
The discoverable types of endpoints that allow can recognize are:
The Session object creates a valid request by determining the URL matching the filters and appending it to the provided path. If multiple URL matches are found then any one may be chosen.
While authentication plugins will endeavour to maintain a consistent set of
arguments for an endpoint_filter
the concept of an authentication plugin is
purposefully generic. A specific mechanism may not know how to interpret
certain arguments in which case it may ignore them. For example the
keystoneauth1.token_endpoint.Token
plugin (which is used when you want
to always use a specific endpoint and token combination) will always return the
same endpoint regardless of the parameters to endpoint_filter
or a custom
OpenStack authentication mechanism may not have the concept of multiple
interface
options and choose to ignore that parameter.
There is some expectation on the user that they understand the limitations of the authentication system they are using.
If the developer would prefer not to provide endpoint_filter with every API
call, a keystoneauth1.adapter.Adapter
can be created. The Adapter
constructor takes the same arguments as endpoint_filter, as well as a
Session. An Adapter behaves much like a Session, with the same REST
methods, but is “mounted” on the endpoint that would be found by
endpoint_filter.
adapter = keystoneauth1.adapter.Adapter(
session=session,
service_type='volume',
interface='public',
version=1)
response = adapter.get('/volumes')
As with endpoint_filter
on a Session, the version
, min_version
and max_version
parameters exist to help determine the appropriate
endpoint for a Major API of a service.
Both keystoneauth1.adapter.Adapter
and
keystoneauth1.session.Session
have a method for getting metadata about
the endpoint found for a given service: get_endpoint_data
.
On the keystoneauth1.session.Session
it takes the same arguments as
endpoint_filter.
On the keystoneauth1.adapter.Adapter
it does not take arguments, as
it returns the information for the Endpoint the Adapter is mounted on.
get_endpoint_data
returns an keystoneauth1.discovery.EndpointData
object. This object can be used to find information about the Endpoint,
including which major api_version was found, or which interface in case
of ranges, lists of input values or latest
version.
It can also be used to determine the min_microversion and max_microversion
supported by the API. If an API does not support microversions, the values for
both will be None
. It will also contain values for next_min_version and
not_before if they exist for the endpoint, or None
if they do not. The
keystoneauth1.discovery.EndpointData
object will always contain
microversion related attributes regardless of whether the REST document does
or not.
get_endpoint_data
makes use of the same cache as the rest of the discovery
process, so calling it should incur no undue expense. By default it will make
at least one version discovery call so that it can fetch microversion metadata.
If the user knows a service does not support microversions and is merely
curious as to which major version was discovered, discover_versions
can be
set to False
to prevent fetching microversion metadata.
A user who wants to specify a microversion for a given request can pass it to
the microversion
parameter of the request method on the
keystoneauth1.session.Session
object, or the
keystoneauth1.adapter.Adapter
object. This will cause keystoneauth
to pass the appropriate header to the service informing the service of the
microversion the user wants.
resp = session.get('/volumes',
microversion='3.15',
endpoint_filter={'service_type': 'volume',
'interface': 'public',
'min_version': '3',
'max_version': 'latest'})
If the user is using a keystoneauth1.adapter.Adapter
, the
service_type, which is a part of the data sent in the microversion header,
will be taken from the Adapter’s service_type.
adapter = keystoneauth1.adapter.Adapter(
session=session,
service_type='compute',
interface='public',
min_version='2.1')
response = adapter.get('/servers', microversion='2.38')
The user can also provide a default_microversion
parameter to the Adapter
constructor which will be used on all requests where an explicit microversion
is not requested.
adapter = keystoneauth1.adapter.Adapter(
session=session,
service_type='compute',
interface='public',
min_version='2.1',
default_microversion='2.38')
response = adapter.get('/servers')
If the user is using a keystoneauth1.session.Session
, the
service_type will be taken from the service_type in endpoint_filter.
If the service_type is the incorrect value to use for the microversion header
for the service in question, the parameter microversion_service_type can be
given. For instance, although keystoneauth already knows about Cinder, the
service_type for Cinder is block-storage
but the microversion header
expects volume
.
# Interactions with cinder do not need to explicitly override the
# microversion_service_type - it is only being used as an example for the
# use of the parameter.
resp = session.get('/volumes',
microversion='3.15',
microversion_service_type='volume',
endpoint_filter={'service_type': 'block-storage',
'interface': 'public',
'min_version': '3',
'max_version': 'latest'})
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.