Ensure Connection can be made from keyword arguments
Although driving configuration from clouds.yaml and environment variables is super handy for many use cases, there are also plenty where avoiding config files or environment variables is important. Add a flag, "load_envvars" which defaults to True but can be disabled. Update the Connection constructor to set load_yaml_config and load_envvars to False if a named cloud is not given. Update the docs to make it clear which form should be used for which use case. Also, move the profile backwards compat documentation to its own document so that it doesn't present needless complexity to users. Cherry-picked from I6e05da5e73aff4143550e1d18fb0f743d51f2b70 Change-Id: If8a8746a55b7ef5a3ff36bebd99fb1b600049a5c
This commit is contained in:
parent
14328ef14d
commit
740dbc12c9
|
@ -1,17 +1,19 @@
|
|||
===========================================
|
||||
Configuring os-client-config Applications
|
||||
===========================================
|
||||
.. _openstack-config:
|
||||
|
||||
========================================
|
||||
Configuring OpenStack SDK Applications
|
||||
========================================
|
||||
|
||||
.. _config-environment-variables:
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
`os-client-config` honors all of the normal `OS_*` variables. It does not
|
||||
`openstacksdk` honors all of the normal `OS_*` variables. It does not
|
||||
provide backwards compatibility to service-specific variables such as
|
||||
`NOVA_USERNAME`.
|
||||
|
||||
If you have OpenStack environment variables set, `os-client-config` will
|
||||
If you have OpenStack environment variables set, `openstacksdk` will
|
||||
produce a cloud config object named `envvars` containing your values from the
|
||||
environment. If you don't like the name `envvars`, that's ok, you can override
|
||||
it by setting `OS_CLOUD_NAME`.
|
||||
|
@ -29,7 +31,7 @@ for trove set
|
|||
Config Files
|
||||
------------
|
||||
|
||||
`os-client-config` will look for a file called `clouds.yaml` in the following
|
||||
`openstacksdk` will look for a file called `clouds.yaml` in the following
|
||||
locations:
|
||||
|
||||
* Current Directory
|
||||
|
@ -58,7 +60,7 @@ Site Specific File Locations
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In addition to `~/.config/openstack` and `/etc/openstack` - some platforms
|
||||
have other locations they like to put things. `os-client-config` will also
|
||||
have other locations they like to put things. `openstacksdk` will also
|
||||
look in an OS specific config dir
|
||||
|
||||
* `USER_CONFIG_DIR`
|
||||
|
@ -111,7 +113,7 @@ You may note a few things. First, since `auth_url` settings are silly
|
|||
and embarrassingly ugly, known cloud vendor profile information is included and
|
||||
may be referenced by name. One of the benefits of that is that `auth_url`
|
||||
isn't the only thing the vendor defaults contain. For instance, since
|
||||
Rackspace lists `rax:database` as the service type for trove, `os-client-config`
|
||||
Rackspace lists `rax:database` as the service type for trove, `openstacksdk`
|
||||
knows that so that you don't have to. In case the cloud vendor profile is not
|
||||
available, you can provide one called `clouds-public.yaml`, following the same
|
||||
location rules previously mentioned for the config files.
|
||||
|
@ -129,7 +131,7 @@ Auth Settings
|
|||
-------------
|
||||
|
||||
Keystone has auth plugins - which means it's not possible to know ahead of time
|
||||
which auth settings are needed. `os-client-config` sets the default plugin type
|
||||
which auth settings are needed. `openstacksdk` sets the default plugin type
|
||||
to `password`, which is what things all were before plugins came about. In
|
||||
order to facilitate validation of values, all of the parameters that exist
|
||||
as a result of a chosen plugin need to go into the auth dict. For password
|
||||
|
@ -167,7 +169,7 @@ file.
|
|||
SSL Settings
|
||||
------------
|
||||
|
||||
When the access to a cloud is done via a secure connection, `os-client-config`
|
||||
When the access to a cloud is done via a secure connection, `openstacksdk`
|
||||
will always verify the SSL cert by default. This can be disabled by setting
|
||||
`verify` to `False`. In case the cert is signed by an unknown CA, a specific
|
||||
cacert can be provided via `cacert`. **WARNING:** `verify` will always have
|
||||
|
@ -195,7 +197,7 @@ Cache Settings
|
|||
--------------
|
||||
|
||||
Accessing a cloud is often expensive, so it's quite common to want to do some
|
||||
client-side caching of those operations. To facilitate that, `os-client-config`
|
||||
client-side caching of those operations. To facilitate that, `openstacksdk`
|
||||
understands passing through cache settings to dogpile.cache, with the following
|
||||
behaviors:
|
||||
|
||||
|
@ -209,7 +211,7 @@ times on a per-resource basis by passing values, in seconds to an expiration
|
|||
mapping keyed on the singular name of the resource. A value of `-1` indicates
|
||||
that the resource should never expire.
|
||||
|
||||
`os-client-config` does not actually cache anything itself, but it collects
|
||||
`openstacksdk` does not actually cache anything itself, but it collects
|
||||
and presents the cache information so that your various applications that
|
||||
are connecting to OpenStack can share a cache should you desire.
|
||||
|
||||
|
|
|
@ -13,184 +13,13 @@ Connection Object
|
|||
:members:
|
||||
|
||||
|
||||
Transition from Profile
|
||||
=======================
|
||||
Transitioning from Profile
|
||||
--------------------------
|
||||
|
||||
.. note:: This section describes migrating code from a previous interface of
|
||||
python-openstacksdk and can be ignored by people writing new code.
|
||||
Support exists for users coming from older releases of OpenStack SDK who have
|
||||
been using the :class:`~openstack.profile.Profile` interface.
|
||||
|
||||
If you have code that currently uses the ``openstack.profile.Profile`` object
|
||||
and/or an ``authenticator`` instance from an object based on
|
||||
``openstack.auth.base.BaseAuthPlugin``, that code should be updated to use the
|
||||
`openstack.config.cloud_region.CloudRegion` object instead.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Writing Code that Works with Both
|
||||
---------------------------------
|
||||
|
||||
These examples should all work with both the old and new interface, with one
|
||||
caveat. With the old interface, the ``CloudConfig`` object comes from the
|
||||
``os-client-config`` library, and in the new interface that has been moved
|
||||
into the SDK. In order to write code that works with both the old and new
|
||||
interfaces, use the following code to import the config namespace:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
|
||||
The examples will assume that the config module has been imported in that
|
||||
manner.
|
||||
|
||||
.. note:: Yes, there is an easier and less verbose way to do all of these.
|
||||
These are verbose to handle both the old and new interfaces in the
|
||||
same codebase.
|
||||
|
||||
Replacing authenticator
|
||||
-----------------------
|
||||
|
||||
There is no direct replacement for ``openstack.auth.base.BaseAuthPlugin``.
|
||||
``python-openstacksdk`` uses the `keystoneauth`_ library for authentication
|
||||
and HTTP interactions. `keystoneauth`_ has `auth plugins`_ that can be used
|
||||
to control how authentication is done. The ``auth_type`` config parameter
|
||||
can be set to choose the correct authentication method to be used.
|
||||
|
||||
Replacing Profile
|
||||
-----------------
|
||||
|
||||
The right way to replace the use of ``openstack.profile.Profile`` depends
|
||||
a bit on what you're trying to accomplish. Common patterns are listed below,
|
||||
but in general the approach is either to pass a cloud name to the
|
||||
`openstack.connection.Connection` constructor, or to construct a
|
||||
`openstack.config.cloud_region.CloudRegion` object and pass it to the
|
||||
constructor.
|
||||
|
||||
All of the examples on this page assume that you want to support old and
|
||||
new interfaces simultaneously. There are easier and less verbose versions
|
||||
of each that are available if you can just make a clean transition.
|
||||
|
||||
Getting a Connection to a named cloud from clouds.yaml
|
||||
------------------------------------------------------
|
||||
|
||||
If you want is to construct a `openstack.connection.Connection` based on
|
||||
parameters configured in a ``clouds.yaml`` file, or from environment variables:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import openstack.connection
|
||||
|
||||
conn = connection.from_config(cloud_name='name-of-cloud-you-want')
|
||||
|
||||
Getting a Connection from python arguments avoiding clouds.yaml
|
||||
---------------------------------------------------------------
|
||||
|
||||
If, on the other hand, you want to construct a
|
||||
`openstack.connection.Connection`, but are in a context where reading config
|
||||
from a clouds.yaml file is undesirable, such as inside of a Service:
|
||||
|
||||
* create a `openstack.config.loader.OpenStackConfig` object, telling
|
||||
it to not load yaml files. Optionally pass an ``app_name`` and
|
||||
``app_version`` which will be added to user-agent strings.
|
||||
* get a `openstack.config.cloud_region.CloudRegion` object from it
|
||||
* get a `openstack.connection.Connection`
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
from openstack import connection
|
||||
|
||||
loader = occ.OpenStackConfig(
|
||||
load_yaml_files=False,
|
||||
app_name='spectacular-app',
|
||||
app_version='1.0')
|
||||
cloud_region = loader.get_one_cloud(
|
||||
region_name='my-awesome-region',
|
||||
auth_type='password',
|
||||
auth=dict(
|
||||
auth_url='https://auth.example.com',
|
||||
username='amazing-user',
|
||||
user_domain_name='example-domain',
|
||||
project_name='astounding-project',
|
||||
user_project_name='example-domain',
|
||||
password='super-secret-password',
|
||||
))
|
||||
conn = connection.from_config(cloud_config=cloud_region)
|
||||
|
||||
.. note:: app_name and app_version are completely optional, and auth_type
|
||||
defaults to 'password'. They are shown here for clarity as to
|
||||
where they should go if they want to be set.
|
||||
|
||||
Getting a Connection from python arguments and optionally clouds.yaml
|
||||
---------------------------------------------------------------------
|
||||
|
||||
If you want to make a connection from python arguments and want to allow
|
||||
one of them to optionally be ``cloud`` to allow selection of a named cloud,
|
||||
it's essentially the same as the previous example, except without
|
||||
``load_yaml_files=False``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
from openstack import connection
|
||||
|
||||
loader = occ.OpenStackConfig(
|
||||
app_name='spectacular-app',
|
||||
app_version='1.0')
|
||||
cloud_region = loader.get_one_cloud(
|
||||
region_name='my-awesome-region',
|
||||
auth_type='password',
|
||||
auth=dict(
|
||||
auth_url='https://auth.example.com',
|
||||
username='amazing-user',
|
||||
user_domain_name='example-domain',
|
||||
project_name='astounding-project',
|
||||
user_project_name='example-domain',
|
||||
password='super-secret-password',
|
||||
))
|
||||
conn = connection.from_config(cloud_config=cloud_region)
|
||||
|
||||
Parameters to get_one_cloud
|
||||
---------------------------
|
||||
|
||||
The most important things to note are:
|
||||
|
||||
* ``auth_type`` specifies which kind of authentication plugin to use. It
|
||||
controls how authentication is done, as well as what parameters are required.
|
||||
* ``auth`` is a dictionary containing the parameters needed by the auth plugin.
|
||||
The most common information it needs are user, project, domain, auth_url
|
||||
and password.
|
||||
* The rest of the keyword arguments to
|
||||
``openstack.config.loader.OpenStackConfig.get_one_cloud`` are either
|
||||
parameters needed by the `keystoneauth Session`_ object, which control how
|
||||
HTTP connections are made, or parameters needed by the
|
||||
`keystoneauth Adapter`_ object, which control how services are found in the
|
||||
Keystone Catalog.
|
||||
|
||||
For `keystoneauth Adapter`_ parameters, since there is one
|
||||
`openstack.connection.Connection` object but many services, per-service
|
||||
parameters are formed by using the official ``service_type`` of the service
|
||||
in question. For instance, to override the endpoint for the ``compute``
|
||||
service, the parameter ``compute_endpoint_override`` would be used.
|
||||
|
||||
``region_name`` in ``openstack.profile.Profile`` was a per-service parameter.
|
||||
This is no longer a valid concept. An `openstack.connection.Connection` is a
|
||||
connection to a region of a cloud. If you are in an extreme situation where
|
||||
you have one service in one region and a different service in a different
|
||||
region, you must use two different `openstack.connection.Connection` objects.
|
||||
|
||||
.. note:: service_type, although a parameter for keystoneauth1.adapter.Adapter,
|
||||
is not a valid parameter for get_one_cloud. service_type is the key
|
||||
by which services are referred, so saying
|
||||
'compute_service_type="henry"' doesn't have any meaning.
|
||||
|
||||
.. _keystoneauth: https://docs.openstack.org/keystoneauth/latest/
|
||||
.. _auth plugins: https://docs.openstack.org/keystoneauth/latest/authentication-plugins.html
|
||||
.. _keystoneauth Adapter: https://docs.openstack.org/keystoneauth/latest/api/keystoneauth1.html#keystoneauth1.adapter.Adapter
|
||||
.. _keystoneauth Session: https://docs.openstack.org/keystoneauth/latest/api/keystoneauth1.html#keystoneauth1.session.Session
|
||||
transition_from_profile
|
||||
|
|
|
@ -48,10 +48,11 @@ API Documentation
|
|||
-----------------
|
||||
|
||||
Service APIs are exposed through a two-layered approach. The classes
|
||||
exposed through our *Connection* interface are the place to start if you're
|
||||
an application developer consuming an OpenStack cloud. The *Resource*
|
||||
interface is the layer upon which the *Connection* is built, with
|
||||
*Connection* methods accepting and returning *Resource* objects.
|
||||
exposed through our `Connection Interface`_ are
|
||||
the place to start if you're an application developer consuming an OpenStack
|
||||
cloud. The `Resource Interface`_ is the layer upon which the
|
||||
`Connection Interface`_ is built, with methods on `Service Proxies`_ accepting
|
||||
and returning :class:`~openstack.resource.Resource` objects.
|
||||
|
||||
The Cloud Abstraction layer has a data model.
|
||||
|
||||
|
@ -61,26 +62,35 @@ The Cloud Abstraction layer has a data model.
|
|||
model
|
||||
|
||||
Connection Interface
|
||||
********************
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A *Connection* instance maintains your cloud config, session and authentication
|
||||
information providing you with a set of higher-level interfaces to work with
|
||||
OpenStack services.
|
||||
A :class:`~openstack.connection.Connection` instance maintains your cloud
|
||||
config, session and authentication information providing you with a set of
|
||||
higher-level interfaces to work with OpenStack services.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
connection
|
||||
|
||||
Once you have a *Connection* instance, the following services may be exposed
|
||||
to you via the :class:`~openstack.proxy.BaseProxy` interface.
|
||||
Once you have a :class:`~openstack.connection.Connection` instance, services
|
||||
are accessed through instances of :class:`~openstack.proxy.BaseProxy` or
|
||||
subclasses of it that exist as attributes on the
|
||||
:class:`~openstack.connection.Connection`.
|
||||
|
||||
.. autoclass:: openstack.proxy.BaseProxy
|
||||
:members:
|
||||
|
||||
The combination of your ``CloudRegion`` and the catalog of the cloud
|
||||
in question control which services are exposed, but listed below are the ones
|
||||
provided by the SDK.
|
||||
.. _service-proxies:
|
||||
|
||||
Service Proxies
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The following service proxies exist on the
|
||||
:class:`~openstack.connection.Connection`. The service proxies are all always
|
||||
present on the :class:`~openstack.connection.Connection` object, but the
|
||||
combination of your ``CloudRegion`` and the catalog of the cloud in question
|
||||
control which services can be used.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
@ -103,16 +113,19 @@ provided by the SDK.
|
|||
Workflow <proxies/workflow>
|
||||
|
||||
Resource Interface
|
||||
******************
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The *Resource* layer is a lower-level interface to communicate with OpenStack
|
||||
services. While the classes exposed by the *Connection* build a convenience
|
||||
layer on top of this, *Resources* can be used directly. However, the most
|
||||
common usage of this layer is in receiving an object from a class in the
|
||||
*Connection* layer, modifying it, and sending it back into the *Connection*
|
||||
layer, such as to update a resource on the server.
|
||||
The *Resource* layer is a lower-level interface to
|
||||
communicate with OpenStack services. While the classes exposed by the
|
||||
`Service Proxies`_ build a convenience layer on top of
|
||||
this, :class:`~openstack.resource.Resource` objects can be
|
||||
used directly. However, the most common usage of this layer is in receiving
|
||||
an object from a class in the `Connection Interface_`, modifying it, and
|
||||
sending it back to the `Service Proxies`_ layer, such as to update a resource
|
||||
on the server.
|
||||
|
||||
The following services have exposed *Resource* classes.
|
||||
The following services have exposed :class:`~openstack.resource.Resource`
|
||||
classes.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
@ -132,7 +145,7 @@ The following services have exposed *Resource* classes.
|
|||
Workflow <resources/workflow/index>
|
||||
|
||||
Low-Level Classes
|
||||
*****************
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following classes are not commonly used by application developers,
|
||||
but are used to construct applications to talk to OpenStack APIs. Typically
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
Transition from Profile
|
||||
=======================
|
||||
|
||||
.. note:: This section describes migrating code from a previous interface of
|
||||
python-openstacksdk and can be ignored by people writing new code.
|
||||
|
||||
If you have code that currently uses the :class:`~openstack.profile.Profile`
|
||||
object and/or an ``authenticator`` instance from an object based on
|
||||
``openstack.auth.base.BaseAuthPlugin``, that code should be updated to use the
|
||||
:class:`~openstack.config.cloud_region.CloudRegion` object instead.
|
||||
|
||||
.. important::
|
||||
|
||||
:class:`~openstack.profile.Profile` is going away. Existing code using it
|
||||
should be migrated as soon as possible.
|
||||
|
||||
Writing Code that Works with Both
|
||||
---------------------------------
|
||||
|
||||
These examples should all work with both the old and new interface, with one
|
||||
caveat. With the old interface, the ``CloudConfig`` object comes from the
|
||||
``os-client-config`` library, and in the new interface that has been moved
|
||||
into the SDK. In order to write code that works with both the old and new
|
||||
interfaces, use the following code to import the config namespace:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
|
||||
The examples will assume that the config module has been imported in that
|
||||
manner.
|
||||
|
||||
.. note:: Yes, there is an easier and less verbose way to do all of these.
|
||||
These are verbose to handle both the old and new interfaces in the
|
||||
same codebase.
|
||||
|
||||
Replacing authenticator
|
||||
-----------------------
|
||||
|
||||
There is no direct replacement for ``openstack.auth.base.BaseAuthPlugin``.
|
||||
``python-openstacksdk`` uses the `keystoneauth`_ library for authentication
|
||||
and HTTP interactions. `keystoneauth`_ has `auth plugins`_ that can be used
|
||||
to control how authentication is done. The ``auth_type`` config parameter
|
||||
can be set to choose the correct authentication method to be used.
|
||||
|
||||
Replacing Profile
|
||||
-----------------
|
||||
|
||||
The right way to replace the use of ``openstack.profile.Profile`` depends
|
||||
a bit on what you're trying to accomplish. Common patterns are listed below,
|
||||
but in general the approach is either to pass a cloud name to the
|
||||
`openstack.connection.Connection` constructor, or to construct a
|
||||
`openstack.config.cloud_region.CloudRegion` object and pass it to the
|
||||
constructor.
|
||||
|
||||
All of the examples on this page assume that you want to support old and
|
||||
new interfaces simultaneously. There are easier and less verbose versions
|
||||
of each that are available if you can just make a clean transition.
|
||||
|
||||
Getting a Connection to a named cloud from clouds.yaml
|
||||
------------------------------------------------------
|
||||
|
||||
If you want is to construct a `openstack.connection.Connection` based on
|
||||
parameters configured in a ``clouds.yaml`` file, or from environment variables:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import openstack.connection
|
||||
|
||||
conn = connection.from_config(cloud_name='name-of-cloud-you-want')
|
||||
|
||||
Getting a Connection from python arguments avoiding clouds.yaml
|
||||
---------------------------------------------------------------
|
||||
|
||||
If, on the other hand, you want to construct a
|
||||
`openstack.connection.Connection`, but are in a context where reading config
|
||||
from a clouds.yaml file is undesirable, such as inside of a Service:
|
||||
|
||||
* create a `openstack.config.loader.OpenStackConfig` object, telling
|
||||
it to not load yaml files. Optionally pass an ``app_name`` and
|
||||
``app_version`` which will be added to user-agent strings.
|
||||
* get a `openstack.config.cloud_region.CloudRegion` object from it
|
||||
* get a `openstack.connection.Connection`
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
from openstack import connection
|
||||
|
||||
loader = occ.OpenStackConfig(
|
||||
load_yaml_files=False,
|
||||
app_name='spectacular-app',
|
||||
app_version='1.0')
|
||||
cloud_region = loader.get_one_cloud(
|
||||
region_name='my-awesome-region',
|
||||
auth_type='password',
|
||||
auth=dict(
|
||||
auth_url='https://auth.example.com',
|
||||
username='amazing-user',
|
||||
user_domain_name='example-domain',
|
||||
project_name='astounding-project',
|
||||
user_project_name='example-domain',
|
||||
password='super-secret-password',
|
||||
))
|
||||
conn = connection.from_config(cloud_config=cloud_region)
|
||||
|
||||
.. note:: app_name and app_version are completely optional, and auth_type
|
||||
defaults to 'password'. They are shown here for clarity as to
|
||||
where they should go if they want to be set.
|
||||
|
||||
Getting a Connection from python arguments and optionally clouds.yaml
|
||||
---------------------------------------------------------------------
|
||||
|
||||
If you want to make a connection from python arguments and want to allow
|
||||
one of them to optionally be ``cloud`` to allow selection of a named cloud,
|
||||
it's essentially the same as the previous example, except without
|
||||
``load_yaml_files=False``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from openstack import config as occ
|
||||
except ImportError:
|
||||
from os_client_config import config as occ
|
||||
from openstack import connection
|
||||
|
||||
loader = occ.OpenStackConfig(
|
||||
app_name='spectacular-app',
|
||||
app_version='1.0')
|
||||
cloud_region = loader.get_one_cloud(
|
||||
region_name='my-awesome-region',
|
||||
auth_type='password',
|
||||
auth=dict(
|
||||
auth_url='https://auth.example.com',
|
||||
username='amazing-user',
|
||||
user_domain_name='example-domain',
|
||||
project_name='astounding-project',
|
||||
user_project_name='example-domain',
|
||||
password='super-secret-password',
|
||||
))
|
||||
conn = connection.from_config(cloud_config=cloud_region)
|
||||
|
||||
Parameters to get_one_cloud
|
||||
---------------------------
|
||||
|
||||
The most important things to note are:
|
||||
|
||||
* ``auth_type`` specifies which kind of authentication plugin to use. It
|
||||
controls how authentication is done, as well as what parameters are required.
|
||||
* ``auth`` is a dictionary containing the parameters needed by the auth plugin.
|
||||
The most common information it needs are user, project, domain, auth_url
|
||||
and password.
|
||||
* The rest of the keyword arguments to
|
||||
``openstack.config.loader.OpenStackConfig.get_one_cloud`` are either
|
||||
parameters needed by the `keystoneauth Session`_ object, which control how
|
||||
HTTP connections are made, or parameters needed by the
|
||||
`keystoneauth Adapter`_ object, which control how services are found in the
|
||||
Keystone Catalog.
|
||||
|
||||
For `keystoneauth Adapter`_ parameters, since there is one
|
||||
`openstack.connection.Connection` object but many services, per-service
|
||||
parameters are formed by using the official ``service_type`` of the service
|
||||
in question. For instance, to override the endpoint for the ``compute``
|
||||
service, the parameter ``compute_endpoint_override`` would be used.
|
||||
|
||||
``region_name`` in ``openstack.profile.Profile`` was a per-service parameter.
|
||||
This is no longer a valid concept. An `openstack.connection.Connection` is a
|
||||
connection to a region of a cloud. If you are in an extreme situation where
|
||||
you have one service in one region and a different service in a different
|
||||
region, you must use two different `openstack.connection.Connection` objects.
|
||||
|
||||
.. note:: service_type, although a parameter for keystoneauth1.adapter.Adapter,
|
||||
is not a valid parameter for get_one_cloud. service_type is the key
|
||||
by which services are referred, so saying
|
||||
'compute_service_type="henry"' doesn't have any meaning.
|
||||
|
||||
.. _keystoneauth: https://docs.openstack.org/keystoneauth/latest/
|
||||
.. _auth plugins: https://docs.openstack.org/keystoneauth/latest/authentication-plugins.html
|
||||
.. _keystoneauth Adapter: https://docs.openstack.org/keystoneauth/latest/api/keystoneauth1.html#keystoneauth1.adapter.Adapter
|
||||
.. _keystoneauth Session: https://docs.openstack.org/keystoneauth/latest/api/keystoneauth1.html#keystoneauth1.session.Session
|
|
@ -18,6 +18,42 @@ __all__ = [
|
|||
]
|
||||
|
||||
from openstack._log import enable_logging # noqa
|
||||
import openstack.config
|
||||
import openstack.connection
|
||||
|
||||
connect = openstack.connection.Connection
|
||||
|
||||
def connect(
|
||||
cloud=None,
|
||||
app_name=None, app_version=None,
|
||||
options=None,
|
||||
load_yaml_config=True, load_envvars=True,
|
||||
**kwargs):
|
||||
"""Create a :class:`~openstack.connection.Connection`
|
||||
|
||||
:param string cloud:
|
||||
The name of the configuration to load from clouds.yaml. Defaults
|
||||
to 'envvars' which will load
|
||||
:param argparse.Namespace options:
|
||||
An argparse Namespace object. allows direct passing in of
|
||||
argparse options to be added to the cloud config. Values
|
||||
of None and '' will be removed.
|
||||
:param bool load_yaml_config:
|
||||
Whether or not to load config settings from clouds.yaml files.
|
||||
Defaults to True.
|
||||
:param bool load_envvars:
|
||||
Whether or not to load config settings from environment variables.
|
||||
Defaults to True.
|
||||
:param kwargs:
|
||||
Additional configuration options.
|
||||
|
||||
:returns: openstack.connnection.Connection
|
||||
:raises: keystoneauth1.exceptions.MissingRequiredOptions
|
||||
on missing required auth parameters
|
||||
"""
|
||||
cloud_region = openstack.config.get_cloud_region(
|
||||
cloud=cloud,
|
||||
app_name=app_name, app_version=app_version,
|
||||
load_yaml_config=load_yaml_config,
|
||||
load_envvars=load_envvars,
|
||||
options=options, **kwargs)
|
||||
return openstack.connection.Connection(config=cloud_region)
|
||||
|
|
|
@ -16,23 +16,20 @@ import sys
|
|||
|
||||
from openstack.config.loader import OpenStackConfig # noqa
|
||||
|
||||
_config = None
|
||||
|
||||
|
||||
def get_cloud_region(
|
||||
service_key=None, options=None,
|
||||
app_name=None, app_version=None,
|
||||
load_yaml_config=True,
|
||||
load_envvars=True,
|
||||
**kwargs):
|
||||
load_yaml_config = kwargs.pop('load_yaml_config', True)
|
||||
global _config
|
||||
if not _config:
|
||||
_config = OpenStackConfig(
|
||||
load_yaml_config=load_yaml_config,
|
||||
app_name=app_name, app_version=app_version)
|
||||
config = OpenStackConfig(
|
||||
load_yaml_config=load_yaml_config,
|
||||
app_name=app_name, app_version=app_version)
|
||||
if options:
|
||||
_config.register_argparse_arguments(options, sys.argv, service_key)
|
||||
config.register_argparse_arguments(options, sys.argv, service_key)
|
||||
parsed_options = options.parse_known_args(sys.argv)
|
||||
else:
|
||||
parsed_options = None
|
||||
|
||||
return _config.get_one(options=parsed_options, **kwargs)
|
||||
return config.get_one(options=parsed_options, **kwargs)
|
||||
|
|
|
@ -35,7 +35,9 @@ def _make_key(key, service_type):
|
|||
return "_".join([service_type, key])
|
||||
|
||||
|
||||
def from_session(session, name=None, config=None, **kwargs):
|
||||
def from_session(session, name=None, region_name=None,
|
||||
force_ipv4=False,
|
||||
app_name=None, app_version=None, **kwargs):
|
||||
"""Construct a CloudRegion from an existing `keystoneauth1.session.Session`
|
||||
|
||||
When a Session already exists, we don't actually even need to go through
|
||||
|
@ -43,20 +45,30 @@ def from_session(session, name=None, config=None, **kwargs):
|
|||
The only parameters that are really needed are adapter/catalog related.
|
||||
|
||||
:param keystoneauth1.session.session session:
|
||||
An existing Session to use.
|
||||
An existing authenticated Session to use.
|
||||
:param str name:
|
||||
A name to use for this cloud region in logging. If left empty, the
|
||||
hostname of the auth_url found in the Session will be used.
|
||||
:param dict config:
|
||||
:param str region_name:
|
||||
The region name to connect to.
|
||||
:param bool force_ipv4:
|
||||
Whether or not to disable IPv6 support. Defaults to False.
|
||||
:param str app_name:
|
||||
Name of the application to be added to User Agent.
|
||||
:param str app_version:
|
||||
Version of the application to be added to User Agent.
|
||||
:param kwargs:
|
||||
Config settings for this cloud region.
|
||||
"""
|
||||
# If someone is constructing one of these from a Session, then they are
|
||||
# not using a named config. Use the hostname of their auth_url instead.
|
||||
name = name or urllib.parse.urlparse(session.auth.auth_url).hostname
|
||||
config_dict = config_defaults.get_defaults()
|
||||
config_dict.update(config or {})
|
||||
config_dict.update(**kwargs)
|
||||
return CloudRegion(
|
||||
name=name, session=session, config=config_dict, **kwargs)
|
||||
name=name, session=session, config=config_dict,
|
||||
region_name=region_name, force_ipv4=force_ipv4,
|
||||
app_name=app_name, app_version=app_version)
|
||||
|
||||
|
||||
class CloudRegion(object):
|
||||
|
|
|
@ -183,11 +183,12 @@ class OpenStackConfig(object):
|
|||
envvar_prefix=None, secure_files=None,
|
||||
pw_func=None, session_constructor=None,
|
||||
app_name=None, app_version=None,
|
||||
load_yaml_config=True):
|
||||
load_yaml_config=True, load_envvars=True):
|
||||
self.log = _log.setup_logging('openstack.config')
|
||||
self._session_constructor = session_constructor
|
||||
self._app_name = app_name
|
||||
self._app_version = app_version
|
||||
self._load_envvars = load_envvars
|
||||
|
||||
if load_yaml_config:
|
||||
self._config_files = config_files or CONFIG_FILES
|
||||
|
@ -198,11 +199,11 @@ class OpenStackConfig(object):
|
|||
self._secure_files = []
|
||||
self._vendor_files = []
|
||||
|
||||
config_file_override = os.environ.get('OS_CLIENT_CONFIG_FILE')
|
||||
config_file_override = self._get_envvar('OS_CLIENT_CONFIG_FILE')
|
||||
if config_file_override:
|
||||
self._config_files.insert(0, config_file_override)
|
||||
|
||||
secure_file_override = os.environ.get('OS_CLIENT_SECURE_FILE')
|
||||
secure_file_override = self._get_envvar('OS_CLIENT_SECURE_FILE')
|
||||
if secure_file_override:
|
||||
self._secure_files.insert(0, secure_file_override)
|
||||
|
||||
|
@ -231,12 +232,12 @@ class OpenStackConfig(object):
|
|||
else:
|
||||
# Get the backwards compat value
|
||||
prefer_ipv6 = get_boolean(
|
||||
os.environ.get(
|
||||
self._get_envvar(
|
||||
'OS_PREFER_IPV6', client_config.get(
|
||||
'prefer_ipv6', client_config.get(
|
||||
'prefer-ipv6', True))))
|
||||
force_ipv4 = get_boolean(
|
||||
os.environ.get(
|
||||
self._get_envvar(
|
||||
'OS_FORCE_IPV4', client_config.get(
|
||||
'force_ipv4', client_config.get(
|
||||
'broken-ipv6', False))))
|
||||
|
@ -248,7 +249,7 @@ class OpenStackConfig(object):
|
|||
self.force_ipv4 = True
|
||||
|
||||
# Next, process environment variables and add them to the mix
|
||||
self.envvar_key = os.environ.get('OS_CLOUD_NAME', 'envvars')
|
||||
self.envvar_key = self._get_envvar('OS_CLOUD_NAME', 'envvars')
|
||||
if self.envvar_key in self.cloud_config['clouds']:
|
||||
raise exceptions.OpenStackConfigException(
|
||||
'"{0}" defines a cloud named "{1}", but'
|
||||
|
@ -257,13 +258,14 @@ class OpenStackConfig(object):
|
|||
' file-based clouds.'.format(self.config_filename,
|
||||
self.envvar_key))
|
||||
|
||||
self.default_cloud = os.environ.get('OS_CLOUD')
|
||||
self.default_cloud = self._get_envvar('OS_CLOUD')
|
||||
|
||||
envvars = _get_os_environ(envvar_prefix=envvar_prefix)
|
||||
if envvars:
|
||||
self.cloud_config['clouds'][self.envvar_key] = envvars
|
||||
if not self.default_cloud:
|
||||
self.default_cloud = self.envvar_key
|
||||
if load_envvars:
|
||||
envvars = _get_os_environ(envvar_prefix=envvar_prefix)
|
||||
if envvars:
|
||||
self.cloud_config['clouds'][self.envvar_key] = envvars
|
||||
if not self.default_cloud:
|
||||
self.default_cloud = self.envvar_key
|
||||
|
||||
if not self.default_cloud and self.cloud_config['clouds']:
|
||||
if len(self.cloud_config['clouds'].keys()) == 1:
|
||||
|
@ -320,6 +322,11 @@ class OpenStackConfig(object):
|
|||
# password = self._pw_callback(prompt="Password: ")
|
||||
self._pw_callback = pw_func
|
||||
|
||||
def _get_envvar(self, key, default=None):
|
||||
if not self._load_envvars:
|
||||
return default
|
||||
return os.environ.get(key, default)
|
||||
|
||||
def get_extra_config(self, key, defaults=None):
|
||||
"""Fetch an arbitrary extra chunk of config, laying in defaults.
|
||||
|
||||
|
@ -700,7 +707,7 @@ class OpenStackConfig(object):
|
|||
p.add_argument(
|
||||
'--os-cloud',
|
||||
metavar='<name>',
|
||||
default=os.environ.get('OS_CLOUD', None),
|
||||
default=self._get_envvar('OS_CLOUD', None),
|
||||
help='Named cloud to connect to')
|
||||
|
||||
# we need to peek to see if timeout was actually passed, since
|
||||
|
|
|
@ -12,67 +12,148 @@
|
|||
|
||||
"""
|
||||
The :class:`~openstack.connection.Connection` class is the primary interface
|
||||
to the Python SDK it maintains a context for a connection to a cloud provider.
|
||||
The connection has an attribute to access each supported service.
|
||||
|
||||
Examples
|
||||
--------
|
||||
to the Python SDK. It maintains a context for a connection to a region of
|
||||
a cloud provider. The :class:`~openstack.connection.Connection` has an
|
||||
attribute to access each OpenStack service.
|
||||
|
||||
At a minimum, the :class:`~openstack.connection.Connection` class needs to be
|
||||
created with a config or the parameters to build one.
|
||||
|
||||
Create a connection
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
While the overall system is very flexible, there are four main use cases
|
||||
for different ways to create a :class:`~openstack.connection.Connection`.
|
||||
|
||||
The preferred way to create a connection is to manage named configuration
|
||||
settings in your clouds.yaml file and refer to them by name.::
|
||||
* Using config settings and keyword arguments as described in
|
||||
:ref:`openstack-config`
|
||||
* Using only keyword arguments passed to the constructor ignoring config files
|
||||
and environment variables.
|
||||
* Using an existing authenticated `keystoneauth1.session.Session`, such as
|
||||
might exist inside of an OpenStack service operational context.
|
||||
* Using an existing :class:`~openstack.config.cloud_region.CloudRegion`.
|
||||
|
||||
Using config settings
|
||||
---------------------
|
||||
|
||||
For users who want to create a :class:`~openstack.connection.Connection` making
|
||||
use of named clouds in ``clouds.yaml`` files, ``OS_`` environment variables
|
||||
and python keyword arguments, the :func:`openstack.connect` factory function
|
||||
is the recommended way to go:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import openstack
|
||||
|
||||
conn = openstack.connect(cloud='example', region_name='earth1')
|
||||
|
||||
If the application in question is a command line application that should also
|
||||
accept command line arguments, an `argparse.Namespace` can be passed to
|
||||
:func:`openstack.connect` that will have relevant arguments added to it and
|
||||
then subsequently consumed by the construtor:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import argparse
|
||||
import openstack
|
||||
|
||||
options = argparse.ArgumentParser(description='Awesome OpenStack App')
|
||||
conn = openstack.connect(options=options)
|
||||
|
||||
Using Only Keyword Arguments
|
||||
----------------------------
|
||||
|
||||
If the application wants to avoid loading any settings from ``clouds.yaml`` or
|
||||
environment variables, use the :class:`~openstack.connection.Connection`
|
||||
constructor directly. As long as the ``cloud`` argument is omitted or ``None``,
|
||||
the :class:`~openstack.connection.Connection` constructor will not load
|
||||
settings from files or the environment.
|
||||
|
||||
.. note::
|
||||
|
||||
This is a different default behavior than the :func:`~openstack.connect`
|
||||
factory function. In :func:`~openstack.connect` if ``cloud`` is omitted
|
||||
or ``None``, a default cloud will be loaded, defaulting to the ``envvars``
|
||||
cloud if it exists.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from openstack import connection
|
||||
|
||||
conn = connection.Connection(cloud='example', region_name='earth1')
|
||||
conn = connection.Connection(
|
||||
region_name='example-region',
|
||||
auth=dict(
|
||||
auth_url='https://auth.example.com',
|
||||
username='amazing-user',
|
||||
password='super-secret-password',
|
||||
project_id='33aa1afc-03fe-43b8-8201-4e0d3b4b8ab5',
|
||||
user_domain_id='054abd68-9ad9-418b-96d3-3437bb376703'),
|
||||
compute_api_version='2',
|
||||
identity_interface='internal')
|
||||
|
||||
Per-service settings as needed by `keystoneauth1.adapter.Adapter` such as
|
||||
``api_version``, ``service_name``, and ``interface`` can be set, as seen
|
||||
above, by prefixing them with the official ``service-type`` name of the
|
||||
service. ``region_name`` is a setting for the entire
|
||||
:class:`~openstack.config.cloud_region.CloudRegion` and cannot be set per
|
||||
service.
|
||||
|
||||
From existing authenticated Session
|
||||
-----------------------------------
|
||||
|
||||
For applications that already have an authenticated Session, simply passing
|
||||
it to the :class:`~openstack.connection.Connection` constructor is all that
|
||||
is needed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from openstack import connection
|
||||
|
||||
conn = connection.Connection(
|
||||
session=session,
|
||||
region_name='example-region',
|
||||
compute_api_version='2',
|
||||
identity_interface='internal')
|
||||
|
||||
From existing CloudRegion
|
||||
-------------------------
|
||||
|
||||
If you already have an :class:`~openstack.config.cloud_region.CloudRegion`
|
||||
you can pass it in instead.::
|
||||
you can pass it in instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from openstack import connection
|
||||
import openstack.config
|
||||
|
||||
config = openstack.config.OpenStackConfig.get_one(
|
||||
config = openstack.config.get_cloud_region(
|
||||
cloud='example', region_name='earth')
|
||||
conn = connection.Connection(config=config)
|
||||
|
||||
It's also possible to pass in parameters directly if needed. The following
|
||||
example constructor uses the default identity password auth
|
||||
plugin and provides a username and password.::
|
||||
Using the Connection
|
||||
--------------------
|
||||
|
||||
from openstack import connection
|
||||
auth_args = {
|
||||
'auth_url': 'http://172.20.1.108:5000/v3',
|
||||
'project_name': 'admin',
|
||||
'user_domain_name': 'default',
|
||||
'project_domain_name': 'default',
|
||||
'username': 'admin',
|
||||
'password': 'admin',
|
||||
}
|
||||
conn = connection.Connection(**auth_args)
|
||||
Services are accessed through an attribute named after the service's official
|
||||
service-type.
|
||||
|
||||
List
|
||||
~~~~
|
||||
|
||||
Services are accessed through an attribute named after the service's official
|
||||
service-type. A list of all the projects is retrieved in this manner::
|
||||
An iterator containing a list of all the projects is retrieved in this manner:
|
||||
|
||||
projects = [project for project in conn.identity.projects()]
|
||||
.. code-block:: python
|
||||
|
||||
projects = conn.identity.projects()
|
||||
|
||||
Find or create
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
If you wanted to make sure you had a network named 'zuul', you would first
|
||||
try to find it and if that fails, you would create it::
|
||||
|
||||
network = conn.network.find_network("zuul")
|
||||
if network is None:
|
||||
network = conn.network.create_network({"name": "zuul"})
|
||||
network = conn.network.create_network(name="zuul")
|
||||
|
||||
Additional information about the services can be found in the
|
||||
:ref:`service-proxies` documentation.
|
||||
"""
|
||||
__all__ = [
|
||||
'from_config',
|
||||
|
@ -88,6 +169,7 @@ import six
|
|||
from openstack import _log
|
||||
from openstack import _meta
|
||||
from openstack import config as _config
|
||||
from openstack.config import cloud_region
|
||||
from openstack import exceptions
|
||||
from openstack import service_description
|
||||
from openstack import task_manager
|
||||
|
@ -119,11 +201,11 @@ def from_config(cloud=None, config=None, options=None, **kwargs):
|
|||
:rtype: :class:`~openstack.connection.Connection`
|
||||
"""
|
||||
# TODO(mordred) Backwards compat while we transition
|
||||
cloud = cloud or kwargs.get('cloud_name')
|
||||
config = config or kwargs.get('cloud_config')
|
||||
cloud = kwargs.pop('cloud_name', cloud)
|
||||
config = kwargs.pop('cloud_config', config)
|
||||
if config is None:
|
||||
config = _config.OpenStackConfig().get_one(
|
||||
cloud=cloud, argparse=options)
|
||||
cloud=cloud, argparse=options, **kwargs)
|
||||
|
||||
return Connection(config=config)
|
||||
|
||||
|
@ -167,11 +249,12 @@ class Connection(six.with_metaclass(_meta.ConnectionMeta)):
|
|||
User Agent.
|
||||
:param authenticator: DEPRECATED. Only exists for short-term backwards
|
||||
compatibility for python-openstackclient while we
|
||||
transition. See `Transition from Profile`_ for
|
||||
details.
|
||||
transition. See :doc:`transition_from_profile`
|
||||
for details.
|
||||
:param profile: DEPRECATED. Only exists for short-term backwards
|
||||
compatibility for python-openstackclient while we
|
||||
transition. See `Transition from Profile`_ for details.
|
||||
transition. See :doc:`transition_from_profile`
|
||||
for details.
|
||||
:param extra_services: List of
|
||||
:class:`~openstack.service_description.ServiceDescription`
|
||||
objects describing services that openstacksdk otherwise does not
|
||||
|
@ -193,12 +276,20 @@ class Connection(six.with_metaclass(_meta.ConnectionMeta)):
|
|||
# python-openstackclient to not use the profile interface.
|
||||
self.config = openstack.profile._get_config_from_profile(
|
||||
profile, authenticator, **kwargs)
|
||||
else:
|
||||
openstack_config = _config.OpenStackConfig(
|
||||
elif session:
|
||||
self.config = cloud_region.from_session(
|
||||
session=session,
|
||||
app_name=app_name, app_version=app_version,
|
||||
load_yaml_config=profile is None)
|
||||
self.config = openstack_config.get_one(
|
||||
cloud=cloud, validate=session is None, **kwargs)
|
||||
load_yaml_config=False,
|
||||
load_envvars=False,
|
||||
**kwargs)
|
||||
else:
|
||||
self.config = _config.get_cloud_region(
|
||||
cloud=cloud,
|
||||
app_name=app_name, app_version=app_version,
|
||||
load_yaml_config=cloud is not None,
|
||||
load_envvars=cloud is not None,
|
||||
**kwargs)
|
||||
|
||||
if self.config.name:
|
||||
tm_name = ':'.join([
|
||||
|
|
|
@ -78,8 +78,11 @@ class TestConnection(base.RequestsMockTestCase):
|
|||
|
||||
def test_session_provided(self):
|
||||
mock_session = mock.Mock(spec=session.Session)
|
||||
mock_session.auth = mock.Mock()
|
||||
mock_session.auth.auth_url = 'https://auth.example.com'
|
||||
conn = connection.Connection(session=mock_session, cert='cert')
|
||||
self.assertEqual(mock_session, conn.session)
|
||||
self.assertEqual('auth.example.com', conn.config.name)
|
||||
|
||||
def test_create_session(self):
|
||||
conn = connection.Connection(cloud='sample')
|
||||
|
|
Loading…
Reference in New Issue