Restrict direct usage of novaclient.v2.client

A lot of project uses incorrect import of versioned novaclient client obj
(i.e. novaclient.v2.client.Client). It leads to unability to change
interface of such inner classes.

This patch updates docs to include warning note and add warning message to
`novaclient.v2.client.Client` object.

Change-Id: Ifeba391716d3d51d6a75a53cad405e1ec595e27b
Related-Bug: #1493576
This commit is contained in:
Andrey Kurilin 2015-09-09 14:02:42 +03:00
parent e612205ab8
commit a96e9d57c5
4 changed files with 69 additions and 47 deletions

View File

@ -1,5 +1,5 @@
Python bindings to the OpenStack Nova API
==================================================
=========================================
This is a client for the OpenStack Nova API. There's a Python API (the
``novaclient`` module), and a command-line script (``nova``). Each

View File

@ -1,5 +1,5 @@
The :mod:`novaclient` Python API
==================================
================================
.. module:: novaclient
:synopsis: A client for the OpenStack Nova API.
@ -14,7 +14,10 @@ First create a client instance with your credentials::
>>> from novaclient import client
>>> nova = client.Client(VERSION, USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)
Here ``VERSION`` can be: ``1.1``, ``2``.
Here ``VERSION`` can be a string or ``novaclient.api_versions.APIVersion`` obj.
If you prefer string value, you can use ``1.1`` (deprecated now), ``2`` or
``2.X`` (where X is a microversion).
Alternatively, you can create a client instance using the keystoneclient
session API::
@ -23,9 +26,9 @@ session API::
>>> from keystoneclient import session
>>> from novaclient import client
>>> auth = v2.Password(auth_url=AUTH_URL,
username=USERNAME,
password=PASSWORD,
tenant_name=PROJECT_ID)
... username=USERNAME,
... password=PASSWORD,
... tenant_name=PROJECT_ID)
>>> sess = session.Session(auth=auth)
>>> nova = client.Client(VERSION, session=sess)
@ -33,6 +36,23 @@ For more information on this keystoneclient API, see `Using Sessions`_.
.. _Using Sessions: http://docs.openstack.org/developer/python-keystoneclient/using-sessions.html
It is also possible to use an instance as a context manager in which case
there will be a session kept alive for the duration of the with statement::
>>> from novaclient import client
>>> with client.Client(VERSION, USERNAME, PASSWORD,
... PROJECT_ID, AUTH_URL) as nova:
... nova.servers.list()
... nova.flavors.list()
...
It is also possible to have a permanent (process-long) connection pool,
by passing a connection_pool=True::
>>> from novaclient import client
>>> nova = client.Client(VERSION, USERNAME, PASSWORD, PROJECT_ID,
... AUTH_URL, connection_pool=True)
Then call methods on its managers::
>>> nova.servers.list()
@ -51,6 +71,13 @@ Then call methods on its managers::
>>> nova.servers.create("my-server", flavor=fl)
<Server: my-server>
.. warning:: Direct initialization of ``novaclient.v2.client.Client`` object
can cause you to "shoot yourself in the foot". See launchpad bug-report
`1493576`_ for more details.
.. _1493576: https://launchpad.net/bugs/1493576
Reference
---------

View File

@ -785,6 +785,27 @@ def get_client_class(version):
def Client(version, *args, **kwargs):
"""Initialize client object based on given version."""
"""Initialize client object based on given version.
HOW-TO:
The simplest way to create a client instance is initialization with your
credentials::
>>> from novaclient import client
>>> nova = client.Client(VERSION, USERNAME, PASSWORD,
... PROJECT_ID, AUTH_URL)
Here ``VERSION`` can be a string or
``novaclient.api_versions.APIVersion`` obj. If you prefer string value,
you can use ``1.1`` (deprecated now), ``2`` or ``2.X``
(where X is a microversion).
Alternatively, you can create a client instance using the keystoneclient
session API. See "The novaclient Python API" page at
python-novaclient's doc.
"""
api_version, client_class = _get_client_class_and_version(version)
return client_class(api_version=api_version, *args, **kwargs)
kwargs.pop("direct_use", None)
return client_class(api_version=api_version, direct_use=False,
*args, **kwargs)

View File

@ -14,6 +14,7 @@
# under the License.
from novaclient import client
from novaclient.i18n import _LW
from novaclient.v2 import agents
from novaclient.v2 import aggregates
from novaclient.v2 import availability_zones
@ -53,44 +54,8 @@ class Client(object):
"""
Top-level object to access the OpenStack Compute API.
Create an instance with your creds::
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)
Or, alternatively, you can create a client instance using the
keystoneclient.session API::
>>> from keystoneclient.auth.identity import v2
>>> from keystoneclient import session
>>> from novaclient import client
>>> auth = v2.Password(auth_url=AUTH_URL,
username=USERNAME,
password=PASSWORD,
tenant_name=PROJECT_ID)
>>> sess = session.Session(auth=auth)
>>> nova = client.Client(VERSION, session=sess)
Then call methods on its managers::
>>> nova.servers.list()
...
>>> nova.flavors.list()
...
It is also possible to use an instance as a context manager in which
case there will be a session kept alive for the duration of the with
statement::
>>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client:
... client.servers.list()
... client.flavors.list()
...
It is also possible to have a permanent (process-long) connection pool,
by passing a connection_pool=True::
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID,
... AUTH_URL, connection_pool=True)
.. warning:: All scripts and projects should not initialize this class
directly. It should be done via `novaclient.client.Client` interface.
"""
def __init__(self, username=None, api_key=None, project_id=None,
@ -103,7 +68,7 @@ class Client(object):
auth_system='keystone', auth_plugin=None, auth_token=None,
cacert=None, tenant_id=None, user_id=None,
connection_pool=False, session=None, auth=None,
api_version=None, **kwargs):
api_version=None, direct_use=True, **kwargs):
"""
:param str username: Username
:param str api_key: API Key
@ -136,6 +101,15 @@ class Client(object):
:param api_version: Compute API version
:type api_version: novaclient.api_versions.APIVersion
"""
if direct_use:
import warnings
warnings.warn(
_LW("'novaclient.v2.client.Client' is not designed to be "
"initialized directly. It is inner class of novaclient. "
"Please, use 'novaclient.client.Client' instead. "
"Related lp bug-report: 1493576"))
# FIXME(comstud): Rename the api_key argument above when we
# know it's not being used as keyword argument