Merge remote-tracking branch 'origin/master' into merge-branch

Conflicts:
	keystoneclient/exceptions.py
	keystoneclient/fixture/discovery.py
	keystoneclient/fixture/v2.py
	keystoneclient/fixture/v3.py
	keystoneclient/middleware/auth_token.py
	keystoneclient/middleware/s3_token.py
	keystoneclient/tests/unit/test_auth_token_middleware.py
	keystoneclient/tests/unit/test_memcache_crypt.py
	keystoneclient/tests/unit/test_s3_token_middleware.py
	requirements.txt
	test-requirements.txt

Change-Id: Ib51acebaac7966bf37c1562fa15b9061df6a7aa5
This commit is contained in:
Brant Knudson 2015-08-06 09:37:43 -05:00
commit 4e498a54d0
68 changed files with 1321 additions and 1207 deletions

View File

@ -1,18 +1,29 @@
Python bindings to the OpenStack Identity API (Keystone)
========================================================
This is a client for the OpenStack Identity API, implemented by Keystone.
There's a Python API (the ``keystoneclient`` module), and a command-line script
(``keystone``).
This is a client for the OpenStack Identity API, implemented by the Keystone
team; it contains a Python API (the ``keystoneclient`` module) for
OpenStack's Identity Service. For command line interface support, use
`OpenStackClient`_.
Development takes place via the usual OpenStack processes as outlined in the
`developer guide <http://docs.openstack.org/infra/manual/developers.html>`_. The master
repository is in `Git <http://git.openstack.org/cgit/openstack/python-keystoneclient>`_.
* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - release management
* `Blueprints`_ - feature specifications
* `Bugs`_ - issue tracking
* `Source`_
* `Specs`_
* `How to Contribute`_
This code is a fork of Rackspace's python-novaclient which is in turn a fork of
`Jacobian's python-cloudservers
<http://github.com/jacobian/python-cloudservers>`_. ``python-keystoneclient``
is licensed under the Apache License like the rest of OpenStack.
.. _PyPi: https://pypi.python.org/pypi/python-keystoneclient
.. _Online Documentation: http://docs.openstack.org/developer/python-keystoneclient
.. _Launchpad project: https://launchpad.net/python-keystoneclient
.. _Blueprints: https://blueprints.launchpad.net/python-keystoneclient
.. _Bugs: https://bugs.launchpad.net/python-keystoneclient
.. _Source: https://git.openstack.org/cgit/openstack/python-keystoneclient
.. _OpenStackClient: https://pypi.python.org/pypi/python-openstackclient
.. _How to Contribute: http://docs.openstack.org/infra/manual/developers.html
.. _Specs: http://specs.openstack.org/openstack/keystone-specs/
.. contents:: Contents:
:local:
@ -28,175 +39,3 @@ By way of a quick-start::
>>> keystone.tenants.list()
>>> tenant = keystone.tenants.create(tenant_name="test", description="My new tenant!", enabled=True)
>>> tenant.delete()
Command-line API
----------------
Installing this package gets you a shell command, ``keystone``, that you can
use to interact with OpenStack's Identity API.
You'll need to provide your OpenStack tenant, username and password. You can do
this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password``
params, but it's easier to just set them as environment variables::
export OS_TENANT_NAME=project
export OS_USERNAME=user
export OS_PASSWORD=pass
You will also need to define the authentication url with ``--os-auth-url`` and
the version of the API with ``--os-identity-api-version``. Or set them as an
environment variables as well::
export OS_AUTH_URL=http://example.com:5000/v2.0
export OS_IDENTITY_API_VERSION=2.0
Alternatively, to bypass username/password authentication, you can provide a
pre-established token. In Keystone, this approach is necessary to bootstrap the
service with an administrative user, tenant & role (to do so, provide the
client with the value of your ``admin_token`` defined in ``keystone.conf`` in
addition to the URL of your admin API deployment, typically on port 35357)::
export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog
export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0
Since the Identity service can return multiple regions in the service catalog,
you can specify the one you want with ``--os-region-name`` (or ``export
OS_REGION_NAME``)::
export OS_REGION_NAME=north
.. WARNING::
If a region is not specified and multiple regions are returned by the
Identity service, the client may not access the same region consistently.
If you need to connect to a server that is TLS-enabled (the auth URL begins
with 'https') and it uses a certificate from a private CA or a self-signed
certificate you will need to specify the path to an appropriate CA certificate
to use to validate the server certificate with ``--os-cacert`` or an
environment variable::
export OS_CACERT=/etc/ssl/my-root-cert.pem
Certificate verification can be turned off using ``--insecure``. This should
be used with caution.
You'll find complete documentation on the shell by running ``keystone help``::
usage: keystone [--version] [--timeout <seconds>]
[--os-username <auth-user-name>]
[--os-password <auth-password>]
[--os-tenant-name <auth-tenant-name>]
[--os-tenant-id <tenant-id>] [--os-auth-url <auth-url>]
[--os-region-name <region-name>]
[--os-identity-api-version <identity-api-version>]
[--os-token <service-token>]
[--os-endpoint <service-endpoint>]
[--os-cacert <ca-certificate>] [--insecure]
[--os-cert <certificate>] [--os-key <key>] [--os-cache]
[--force-new-token] [--stale-duration <seconds>]
<subcommand> ...
Command-line interface to the OpenStack Identity API.
Positional arguments:
<subcommand>
catalog
ec2-credentials-create
Create EC2-compatible credentials for user per tenant
ec2-credentials-delete
Delete EC2-compatible credentials
ec2-credentials-get
Display EC2-compatible credentials
ec2-credentials-list
List EC2-compatible credentials for a user
endpoint-create Create a new endpoint associated with a service
endpoint-delete Delete a service endpoint
endpoint-get
endpoint-list List configured service endpoints
password-update Update own password
role-create Create new role
role-delete Delete role
role-get Display role details
role-list List all roles
service-create Add service to Service Catalog
service-delete Delete service from Service Catalog
service-get Display service from Service Catalog
service-list List all services in Service Catalog
tenant-create Create new tenant
tenant-delete Delete tenant
tenant-get Display tenant details
tenant-list List all tenants
tenant-update Update tenant name, description, enabled status
token-get
user-create Create new user
user-delete Delete user
user-get Display user details.
user-list List users
user-password-update
Update user password
user-role-add Add role to user
user-role-list List roles granted to a user
user-role-remove Remove role from user
user-update Update user's name, email, and enabled status
discover Discover Keystone servers, supported API versions and
extensions.
bootstrap Grants a new role to a new user on a new tenant, after
creating each.
bash-completion Prints all of the commands and options to stdout.
help Display help about this program or one of its
subcommands.
Optional arguments:
--version Shows the client version and exits
--timeout <seconds> Set request timeout (in seconds)
--os-username <auth-user-name>
Name used for authentication with the OpenStack
Identity service. Defaults to env[OS_USERNAME]
--os-password <auth-password>
Password used for authentication with the OpenStack
Identity service. Defaults to env[OS_PASSWORD]
--os-tenant-name <auth-tenant-name>
Tenant to request authorization on. Defaults to
env[OS_TENANT_NAME]
--os-tenant-id <tenant-id>
Tenant to request authorization on. Defaults to
env[OS_TENANT_ID]
--os-auth-url <auth-url>
Specify the Identity endpoint to use for
authentication. Defaults to env[OS_AUTH_URL]
--os-region-name <region-name>
Defaults to env[OS_REGION_NAME]
--os-identity-api-version <identity-api-version>
Defaults to env[OS_IDENTITY_API_VERSION] or 2.0
--os-token <service-token>
Specify an existing token to use instead of retrieving
one via authentication (e.g. with username &
password). Defaults to env[OS_SERVICE_TOKEN]
--os-endpoint <service-endpoint>
Specify an endpoint to use instead of retrieving one
from the service catalog (via authentication).
Defaults to env[OS_SERVICE_ENDPOINT]
--os-cacert <ca-certificate>
Specify a CA bundle file to use in verifying a TLS
(https) server certificate. Defaults to env[OS_CACERT]
--insecure Explicitly allow keystoneclient to perform "insecure"
TLS (https) requests. The server's certificate will
not be verified against any certificate authorities.
This option should be used with caution.
--os-cert <certificate>
Defaults to env[OS_CERT]
--os-key <key> Defaults to env[OS_KEY]
--os-cache Use the auth token cache. Defaults to env[OS_CACHE]
--force-new-token If the keyring is available and in use, token will
always be stored and fetched from the keyring until
the token has expired. Use this option to request a
new token and replace the existing one in the keyring.
--stale-duration <seconds>
Stale duration (in seconds) used to determine whether
a token has expired when retrieving it from keyring.
This is useful in mitigating process or network
delays. Default is 30 seconds.
See "keystone help COMMAND" for help on a specific command.

View File

@ -103,7 +103,7 @@ add_module_names = True
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
modindex_common_prefix = ['keystoneclient.']
# Grouping the document tree for man pages.
# List of tuples 'sourcefile', 'target', 'title', 'Authors name', 'manual'

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.27.20101213.0545 (20101213.0545)
-->
<!-- Title: AuthComp Pages: 1 -->
<svg width="510pt" height="118pt"
viewBox="0.00 0.00 510.00 118.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 114)">
<title>AuthComp</title>
<polygon fill="white" stroke="white" points="-4,5 -4,-114 507,-114 507,5 -4,5"/>
<!-- AuthComp -->
<g id="node2" class="node"><title>AuthComp</title>
<polygon fill="#fdefe3" stroke="#c00000" points="292,-65 194,-65 194,-25 292,-25 292,-65"/>
<text text-anchor="middle" x="243" y="-48.4" font-family="Helvetica,sans-Serif" font-size="14.00">Auth</text>
<text text-anchor="middle" x="243" y="-32.4" font-family="Helvetica,sans-Serif" font-size="14.00">Component</text>
</g>
<!-- Reject -->
<!-- AuthComp&#45;&gt;Reject -->
<g id="edge3" class="edge"><title>AuthComp&#45;&gt;Reject</title>
<path fill="none" stroke="black" d="M193.933,-51.2787C157.514,-55.939 108.38,-62.2263 73.8172,-66.649"/>
<polygon fill="black" stroke="black" points="73.0637,-63.2168 63.5888,-67.9578 73.9522,-70.1602 73.0637,-63.2168"/>
<text text-anchor="middle" x="129" y="-97.4" font-family="Times,serif" font-size="14.00">Reject</text>
<text text-anchor="middle" x="129" y="-82.4" font-family="Times,serif" font-size="14.00">Unauthenticated</text>
<text text-anchor="middle" x="129" y="-67.4" font-family="Times,serif" font-size="14.00">Requests</text>
</g>
<!-- Service -->
<g id="node6" class="node"><title>Service</title>
<polygon fill="#d1ebf1" stroke="#1f477d" points="502,-65 408,-65 408,-25 502,-25 502,-65"/>
<text text-anchor="middle" x="455" y="-48.4" font-family="Helvetica,sans-Serif" font-size="14.00">OpenStack</text>
<text text-anchor="middle" x="455" y="-32.4" font-family="Helvetica,sans-Serif" font-size="14.00">Service</text>
</g>
<!-- AuthComp&#45;&gt;Service -->
<g id="edge5" class="edge"><title>AuthComp&#45;&gt;Service</title>
<path fill="none" stroke="black" d="M292.17,-45C323.626,-45 364.563,-45 397.52,-45"/>
<polygon fill="black" stroke="black" points="397.917,-48.5001 407.917,-45 397.917,-41.5001 397.917,-48.5001"/>
<text text-anchor="middle" x="350" y="-77.4" font-family="Times,serif" font-size="14.00">Forward</text>
<text text-anchor="middle" x="350" y="-62.4" font-family="Times,serif" font-size="14.00">Authenticated</text>
<text text-anchor="middle" x="350" y="-47.4" font-family="Times,serif" font-size="14.00">Requests</text>
</g>
<!-- Start -->
<!-- Start&#45;&gt;AuthComp -->
<g id="edge7" class="edge"><title>Start&#45;&gt;AuthComp</title>
<path fill="none" stroke="black" d="M59.1526,-21.4745C90.4482,-25.4792 142.816,-32.1802 183.673,-37.4084"/>
<polygon fill="black" stroke="black" points="183.43,-40.9057 193.793,-38.7034 184.318,-33.9623 183.43,-40.9057"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.27.20101213.0545 (20101213.0545)
-->
<!-- Title: AuthCompDelegate Pages: 1 -->
<svg width="588pt" height="104pt"
viewBox="0.00 0.00 588.00 104.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 100)">
<title>AuthCompDelegate</title>
<polygon fill="white" stroke="white" points="-4,5 -4,-100 585,-100 585,5 -4,5"/>
<!-- AuthComp -->
<g id="node2" class="node"><title>AuthComp</title>
<polygon fill="#fdefe3" stroke="#c00000" points="338,-65 240,-65 240,-25 338,-25 338,-65"/>
<text text-anchor="middle" x="289" y="-48.4" font-family="Helvetica,sans-Serif" font-size="14.00">Auth</text>
<text text-anchor="middle" x="289" y="-32.4" font-family="Helvetica,sans-Serif" font-size="14.00">Component</text>
</g>
<!-- Reject -->
<!-- AuthComp&#45;&gt;Reject -->
<g id="edge3" class="edge"><title>AuthComp&#45;&gt;Reject</title>
<path fill="none" stroke="black" d="M239.6,-50.1899C191.406,-55.2531 118.917,-62.8686 73.5875,-67.6309"/>
<polygon fill="black" stroke="black" points="73.0928,-64.1635 63.5132,-68.6893 73.8242,-71.1252 73.0928,-64.1635"/>
<text text-anchor="middle" x="152" y="-83.4" font-family="Times,serif" font-size="14.00">Reject Requests</text>
<text text-anchor="middle" x="152" y="-68.4" font-family="Times,serif" font-size="14.00">Indicated by the Service</text>
</g>
<!-- Service -->
<g id="node6" class="node"><title>Service</title>
<polygon fill="#d1ebf1" stroke="#1f477d" points="580,-65 486,-65 486,-25 580,-25 580,-65"/>
<text text-anchor="middle" x="533" y="-48.4" font-family="Helvetica,sans-Serif" font-size="14.00">OpenStack</text>
<text text-anchor="middle" x="533" y="-32.4" font-family="Helvetica,sans-Serif" font-size="14.00">Service</text>
</g>
<!-- AuthComp&#45;&gt;Service -->
<g id="edge5" class="edge"><title>AuthComp&#45;&gt;Service</title>
<path fill="none" stroke="black" d="M338.009,-49.0804C344.065,-49.4598 350.172,-49.7828 356,-50 405.743,-51.8535 418.259,-51.9103 468,-50 470.523,-49.9031 473.101,-49.7851 475.704,-49.6504"/>
<polygon fill="black" stroke="black" points="476.03,-53.1374 485.807,-49.0576 475.62,-46.1494 476.03,-53.1374"/>
<text text-anchor="middle" x="412" y="-68.4" font-family="Times,serif" font-size="14.00">Forward Requests</text>
<text text-anchor="middle" x="412" y="-53.4" font-family="Times,serif" font-size="14.00">with Identiy Status</text>
</g>
<!-- Service&#45;&gt;AuthComp -->
<g id="edge7" class="edge"><title>Service&#45;&gt;AuthComp</title>
<path fill="none" stroke="black" d="M495.062,-24.9037C486.397,-21.2187 477.064,-17.9304 468,-16 419.314,-5.63183 404.743,-5.9037 356,-16 349.891,-17.2653 343.655,-19.116 337.566,-21.2803"/>
<polygon fill="black" stroke="black" points="336.234,-18.0426 328.158,-24.9003 338.748,-24.5757 336.234,-18.0426"/>
<text text-anchor="middle" x="412" y="-33.4" font-family="Times,serif" font-size="14.00">Send Response OR</text>
<text text-anchor="middle" x="412" y="-18.4" font-family="Times,serif" font-size="14.00">Reject Message</text>
</g>
<!-- Start -->
<!-- Start&#45;&gt;AuthComp -->
<g id="edge9" class="edge"><title>Start&#45;&gt;AuthComp</title>
<path fill="none" stroke="black" d="M59.0178,-20.8384C99.2135,-25.0613 175.782,-33.1055 229.492,-38.7482"/>
<polygon fill="black" stroke="black" points="229.265,-42.2435 239.576,-39.8076 229.997,-35.2818 229.265,-42.2435"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -195,6 +195,9 @@ class Discover(object):
:raw_status str: The status as provided by the server
:rtype: list(dict)
"""
if kwargs.pop('unstable', None):
kwargs.setdefault('allow_experimental', True)
kwargs.setdefault('allow_unknown', True)
data = self.raw_version_data(**kwargs)
versions = []

View File

@ -16,6 +16,7 @@
import datetime
import warnings
from oslo_utils import timeutils
@ -39,9 +40,20 @@ class AccessInfo(dict):
**kwargs):
"""Create AccessInfo object given a successful auth response & body
or a user-provided dict.
.. warning::
Use of the region_name argument is deprecated as of the 1.7.0
release and may be removed in the 2.0.0 release.
"""
# FIXME(jamielennox): Passing region_name is deprecated. Provide an
# appropriate warning.
if region_name:
warnings.warn(
'Use of the region_name argument is deprecated as of the '
'1.7.0 release and may be removed in the 2.0.0 release.',
DeprecationWarning)
auth_ref = None
if body is not None or len(kwargs):
@ -246,7 +258,10 @@ class AccessInfo(dict):
"""Returns true if the authorization token was scoped to a tenant
(project), and contains a populated service catalog.
This is deprecated, use project_scoped instead.
.. warning::
This is deprecated as of the 1.7.0 release in favor of
project_scoped and may be removed in the 2.0.0 release.
:returns: bool
"""
@ -349,7 +364,8 @@ class AccessInfo(dict):
(project), this property will return None.
DEPRECATED: this doesn't correctly handle region name. You should fetch
it from the service catalog yourself.
it from the service catalog yourself. This may be removed in the 2.0.0
release.
:returns: tuple of urls
"""
@ -362,7 +378,8 @@ class AccessInfo(dict):
authentication request wasn't scoped to a tenant (project).
DEPRECATED: this doesn't correctly handle region name. You should fetch
it from the service catalog yourself.
it from the service catalog yourself. This may be removed in the 2.0.0
release.
:returns: tuple of urls
"""
@ -525,6 +542,13 @@ class AccessInfoV2(AccessInfo):
@property
def scoped(self):
"""Deprecated as of the 1.7.0 release in favor of project_scoped and
may be removed in the 2.0.0 release.
"""
warnings.warn(
'scoped is deprecated as of the 1.7.0 release in favor of '
'project_scoped and may be removed in the 2.0.0 release.',
DeprecationWarning)
if ('serviceCatalog' in self
and self['serviceCatalog']
and 'tenant' in self['token']):
@ -589,8 +613,13 @@ class AccessInfoV2(AccessInfo):
@property
def auth_url(self):
# FIXME(jamielennox): this is deprecated in favour of retrieving it
# from the service catalog. Provide a warning.
"""Deprecated as of the 1.7.0 release in favor of
service_catalog.get_urls() and may be removed in the 2.0.0 release.
"""
warnings.warn(
'auth_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='publicURL',
@ -600,8 +629,13 @@ class AccessInfoV2(AccessInfo):
@property
def management_url(self):
# FIXME(jamielennox): this is deprecated in favour of retrieving it
# from the service catalog. Provide a warning.
"""Deprecated as of the 1.7.0 release in favor of
service_catalog.get_urls() and may be removed in the 2.0.0 release.
"""
warnings.warn(
'management_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='adminURL',
@ -747,6 +781,13 @@ class AccessInfoV3(AccessInfo):
@property
def scoped(self):
"""Deprecated as of the 1.7.0 release in favor of project_scoped and
may be removed in the 2.0.0 release.
"""
warnings.warn(
'scoped is deprecated as of the 1.7.0 release in favor of '
'project_scoped and may be removed in the 2.0.0 release.',
DeprecationWarning)
return ('catalog' in self and self['catalog'] and 'project' in self)
@property
@ -775,8 +816,13 @@ class AccessInfoV3(AccessInfo):
@property
def auth_url(self):
# FIXME(jamielennox): this is deprecated in favour of retrieving it
# from the service catalog. Provide a warning.
"""Deprecated as of the 1.7.0 release in favor of
service_catalog.get_urls() and may be removed in the 2.0.0 release.
"""
warnings.warn(
'auth_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='public',
@ -786,8 +832,13 @@ class AccessInfoV3(AccessInfo):
@property
def management_url(self):
# FIXME(jamielennox): this is deprecated in favour of retrieving it
# from the service catalog. Provide a warning.
"""Deprecated as of the 1.7.0 release in favor of
service_catalog.get_urls() and may be removed in the 2.0.0 release.
"""
warnings.warn(
'management_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='admin',

View File

@ -13,7 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
import warnings
"""Deprecated.
.. warning::
This module is deprecated as of the 1.7.0 release in favor of
:py:mod:`keystoneclient.exceptions` and may be removed in the 2.0.0 release.
"""
from debtcollector import removals
from keystoneclient import exceptions
@ -22,9 +31,10 @@ from keystoneclient import exceptions
# to report 'deprecated' status of exceptions for next kind of imports
# from keystoneclient.apiclient import exceptions
warnings.warn("The 'keystoneclient.apiclient' module is deprecated since "
"v.0.7.1. Use 'keystoneclient.exceptions' instead of this "
"module.", DeprecationWarning)
removals.removed_module('keystoneclient.apiclient',
replacement='keystoneclient.exceptions',
version='0.7.1',
removal_version='2.0')
__all__ = [
'exceptions',

View File

@ -20,14 +20,15 @@
Exception definitions.
Deprecated since v0.7.1. Use 'keystoneclient.exceptions' instead of
this module.
this module. This module may be removed in the 2.0.0 release.
"""
import warnings
from debtcollector import removals
from keystoneclient.exceptions import * # noqa
warnings.warn("The 'keystoneclient.apiclient.exceptions' module is deprecated "
"since v.0.7.1. Use 'keystoneclient.exceptions' instead of this "
"module.", DeprecationWarning)
removals.removed_module('keystoneclient.apiclient.exceptions',
replacement='keystoneclient.exceptions',
version='0.7.1',
removal_version='2.0')

View File

@ -12,6 +12,7 @@
import abc
import logging
import warnings
from oslo_config import cfg
import six
@ -54,12 +55,106 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
self._endpoint_cache = {}
# NOTE(jamielennox): DEPRECATED. The following should not really be set
# here but handled by the individual auth plugin.
self.username = username
self.password = password
self.token = token
self.trust_id = trust_id
self._username = username
self._password = password
self._token = token
self._trust_id = trust_id
@property
def username(self):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'username is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._username
@username.setter
def username(self, value):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'username is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._username = value
@property
def password(self):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'password is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._password
@password.setter
def password(self, value):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'password is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._password = value
@property
def token(self):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'token is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._token
@token.setter
def token(self, value):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'token is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._token = value
@property
def trust_id(self):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'trust_id is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._trust_id
@trust_id.setter
def trust_id(self, value):
"""Deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
"""
warnings.warn(
'trust_id is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._trust_id = value
@abc.abstractmethod
def get_auth_ref(self, session, **kwargs):

View File

@ -72,6 +72,16 @@ class BaseGenericPlugin(base.BaseIdentityPlugin):
self._plugin = None
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
@abc.abstractmethod
def create_plugin(self, session, version, url, raw_status=None):
"""Create a plugin from the given paramters.

View File

@ -57,10 +57,20 @@ class Auth(base.BaseIdentityPlugin):
super(Auth, self).__init__(auth_url=auth_url,
reauthenticate=reauthenticate)
self.trust_id = trust_id
self._trust_id = trust_id
self.tenant_id = tenant_id
self.tenant_name = tenant_name
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
def get_auth_ref(self, session, **kwargs):
headers = {'Accept': 'application/json'}
url = self.auth_url.rstrip('/') + '/tokens'
@ -131,8 +141,28 @@ class Password(Auth):
user_id = None
self.user_id = user_id
self.username = username
self.password = password
self._username = username
self._password = password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def get_auth_data(self, headers=None):
auth = {'password': self.password}
@ -182,7 +212,17 @@ class Token(Auth):
def __init__(self, auth_url, token, **kwargs):
super(Token, self).__init__(auth_url, **kwargs)
self.token = token
self._token = token
@property
def token(self):
# Override to remove deprecation.
return self._token
@token.setter
def token(self, value):
# Override to remove deprecation.
self._token = value
def get_auth_data(self, headers=None):
if headers is not None:

View File

@ -59,7 +59,7 @@ class BaseAuth(base.BaseIdentityPlugin):
include_catalog=True):
super(BaseAuth, self).__init__(auth_url=auth_url,
reauthenticate=reauthenticate)
self.trust_id = trust_id
self._trust_id = trust_id
self.domain_id = domain_id
self.domain_name = domain_name
self.project_id = project_id
@ -68,6 +68,16 @@ class BaseAuth(base.BaseIdentityPlugin):
self.project_domain_name = project_domain_name
self.include_catalog = include_catalog
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
@property
def token_url(self):
"""The full URL where we will send authentication data."""

View File

@ -20,15 +20,17 @@ Base utilities to build API operation managers and objects on top of.
"""
import abc
import copy
import functools
import warnings
from oslo_utils import strutils
import six
from six.moves import urllib
from keystoneclient import auth
from keystoneclient import exceptions
from keystoneclient.i18n import _
from keystoneclient.openstack.common.apiclient import base
def getid(obj):
@ -91,8 +93,17 @@ class Manager(object):
@property
def api(self):
"""Deprecated. Use `client` instead.
"""The client.
.. warning::
This property is deprecated as of the 1.7.0 release in favor of
:meth:`client` and may be removed in the 2.0.0 release.
"""
warnings.warn(
'api is deprecated as of the 1.7.0 release in favor of client and '
'may be removed in the 2.0.0 release', DeprecationWarning)
return self.client
def _list(self, url, response_key, obj_class=None, body=None, **kwargs):
@ -356,6 +367,17 @@ class CrudManager(Manager):
@filter_kwargs
def list(self, fallback_to_auth=False, **kwargs):
if 'id' in kwargs.keys():
# Ensure that users are not trying to call things like
# ``domains.list(id='default')`` when they should have used
# ``[domains.get(domain_id='default')]`` instead. Keystone supports
# ``GET /v3/domains/{domain_id}``, not ``GET
# /v3/domains?id={domain_id}``.
raise TypeError(
_("list() got an unexpected keyword argument 'id'. To "
"retrieve a single object using a globally unique "
"identifier, try using get() instead."))
url = self.build_url(dict_args_in_out=kwargs)
try:
@ -418,11 +440,99 @@ class CrudManager(Manager):
return rl[0]
class Resource(base.Resource):
class Resource(object):
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
"""
HUMAN_ID = False
NAME_ATTR = 'name'
def __init__(self, manager, info, loaded=False):
"""Populate and bind to a manager.
:param manager: BaseManager object
:param info: dictionary representing resource attributes
:param loaded: prevent lazy-loading if set to True
"""
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def __repr__(self):
reprkeys = sorted(k
for k in self.__dict__.keys()
if k[0] != '_' and k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
@property
def human_id(self):
"""Human-readable ID which can be used for bash completion.
"""
if self.HUMAN_ID:
name = getattr(self, self.NAME_ATTR, None)
if name is not None:
return strutils.to_slug(name)
return None
def _add_details(self, info):
for (k, v) in six.iteritems(info):
try:
setattr(self, k, v)
self._info[k] = v
except AttributeError:
# In this case we already defined the attribute on the class
pass
def __getattr__(self, k):
if k not in self.__dict__:
# NOTE(bcwaldon): disallow lazy-loading if already loaded once
if not self.is_loaded():
self.get()
return self.__getattr__(k)
raise AttributeError(k)
else:
return self.__dict__[k]
def get(self):
"""Support for lazy loading details.
Some clients, such as novaclient have the option to lazy load the
details, details which can be loaded with this function.
"""
# set_loaded() first ... so if we have to bail, we know we tried.
self.set_loaded(True)
if not hasattr(self.manager, 'get'):
return
new = self.manager.get(self.id)
if new:
self._add_details(new._info)
self._add_details(
{'x_request_id': self.manager.client.last_request_id})
def __eq__(self, other):
if not isinstance(other, Resource):
return NotImplemented
# two resources of different types are not equal
if not isinstance(other, self.__class__):
return False
if hasattr(self, 'id') and hasattr(other, 'id'):
return self.id == other.id
return self._info == other._info
def is_loaded(self):
return self._loaded
def set_loaded(self, val):
self._loaded = val
def to_dict(self):
return copy.deepcopy(self._info)
def delete(self):
return self.manager.delete(self)

View File

@ -10,13 +10,23 @@
# License for the specific language governing permissions and limitations
# under the License.
from debtcollector import removals
from keystoneclient import discover
from keystoneclient import httpclient
from keystoneclient import session as client_session
# Using client.HTTPClient is deprecated. Use httpclient.HTTPClient instead.
HTTPClient = httpclient.HTTPClient
@removals.remove(message='Use keystoneclient.httpclient.HTTPClient instead',
version='1.7.0', removal_version='2.0.0')
class HTTPClient(httpclient.HTTPClient):
"""Deprecated alias for httpclient.HTTPClient.
This class is deprecated as of the 1.7.0 release in favor of
:class:`keystoneclient.httpclient.HTTPClient` and may be removed in the
2.0.0 release.
"""
def Client(version=None, unstable=False, session=None, **kwargs):

View File

@ -26,10 +26,11 @@ import logging
import textwrap
import zlib
from debtcollector import removals
import six
from keystoneclient import exceptions
from keystoneclient.i18n import _, _LE, _LW
from keystoneclient.i18n import _, _LE
subprocess = None
@ -297,10 +298,14 @@ def is_asn1_token(token):
return token[:3] == PKI_ASN1_PREFIX
@removals.remove(message='Use is_asn1_token() instead.', version='1.7.0',
removal_version='2.0.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.'))
"""Deprecated.
This function is deprecated as of the 1.7.0 release in favor of
:func:`is_asn1_token` and may be removed in the 2.0.0 release.
"""
return is_asn1_token(token)

View File

@ -87,14 +87,34 @@ class OidcPassword(federated.FederatedBaseAuth):
"""
super(OidcPassword, self).__init__(auth_url, identity_provider,
protocol)
self.username = username
self.password = password
self._username = username
self._password = password
self.client_id = client_id
self.client_secret = client_secret
self.access_token_endpoint = access_token_endpoint
self.scope = scope
self.grant_type = grant_type
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def get_unscoped_auth_ref(self, session):
"""Authenticate with OpenID Connect and get back claims.

View File

@ -170,7 +170,27 @@ class Saml2UnscopedToken(_BaseSAMLPlugin):
super(Saml2UnscopedToken, self).__init__(auth_url=auth_url, **kwargs)
self.identity_provider = identity_provider
self.identity_provider_url = identity_provider_url
self.username, self.password = username, password
self._username, self._password = username, password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def _handle_http_302_ecp_redirect(self, session, response, method,
**kwargs):
@ -490,7 +510,27 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
self.identity_provider = identity_provider
self.identity_provider_url = identity_provider_url
self.service_provider_endpoint = service_provider_endpoint
self.username, self.password = username, password
self._username, self._password = username, password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
@classmethod
def get_options(cls):

View File

@ -12,6 +12,9 @@
from oslo_utils import timeutils
from keystoneclient import utils
# The set of attributes common between the RevokeEvent
# and the dictionaries created from the token Data.
_NAMES = ['trust_id',
@ -75,11 +78,11 @@ class RevokeEvent(object):
if self.consumer_id is not None:
event['OS-OAUTH1:access_token_id'] = self.access_token_id
if self.expires_at is not None:
event['expires_at'] = timeutils.isotime(self.expires_at,
subsecond=True)
event['expires_at'] = utils.isotime(self.expires_at,
subsecond=True)
if self.issued_before is not None:
event['issued_before'] = timeutils.isotime(self.issued_before,
subsecond=True)
event['issued_before'] = utils.isotime(self.issued_before,
subsecond=True)
return event
def key_for_name(self, name):

View File

@ -12,6 +12,7 @@
import logging
from debtcollector import removals
import six
from keystoneclient import _discover
@ -165,14 +166,19 @@ class Discover(_discover.Discover):
super(Discover, self).__init__(session, url,
authenticated=authenticated)
@removals.remove(message='Use raw_version_data instead.', version='1.7.0',
removal_version='2.0.0')
def available_versions(self, **kwargs):
"""Return a list of identity APIs available on the server and the data
associated with them.
DEPRECATED: use raw_version_data()
.. warning::
This method is deprecated as of the 1.7.0 release in favor of
:meth:`raw_version_data` and may be removed in the 2.0.0 release.
:param bool unstable: Accept endpoints not marked 'stable'. (optional)
DEPRECTED. Equates to setting allow_experimental
Equates to setting allow_experimental
and allow_unknown to True.
:param bool allow_experimental: Allow experimental version endpoints.
:param bool allow_deprecated: Allow deprecated version endpoints.
@ -185,6 +191,10 @@ class Discover(_discover.Discover):
"""
return self.raw_version_data(**kwargs)
@removals.removed_kwarg(
'unstable',
message='Use allow_experimental and allow_unknown instead.',
version='1.7.0', removal_version='2.0.0')
def raw_version_data(self, unstable=False, **kwargs):
"""Get raw version information from URL.
@ -192,8 +202,10 @@ class Discover(_discover.Discover):
on the data, so what is returned here will be the data in the same
format it was received from the endpoint.
:param bool unstable: (deprecated) equates to setting
allow_experimental and allow_unknown.
:param bool unstable: equates to setting allow_experimental and
allow_unknown. This argument is deprecated as of
the 1.7.0 release and may be removed in the 2.0.0
release.
: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.

View File

@ -12,20 +12,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Exception definitions.
.. py:exception:: AuthorizationFailure
.. py:exception:: ClientException
.. py:exception:: HttpError
.. py:exception:: ValidationError
.. py:exception:: Unauthorized
"""
"""Exception definitions."""
from keystoneauth1 import exceptions as new_exceptions
@ -68,7 +55,6 @@ GatewayTimeout = new_exceptions.GatewayTimeout
HttpVersionNotSupported = new_exceptions.HttpVersionNotSupported
from_response = new_exceptions.from_response
# NOTE(jamielennox): Rahh! this is just wrong. In the apiclient conversion
# someone mapped the connection timeout onto the HTTP timeout exception. Assume
# people want the connection timeout as this is much more common.
@ -82,9 +68,7 @@ Timeout = new_exceptions.ConnectTimeout
# NOTE(akurilin): This alias should be left here to support backwards
# compatibility until we are sure that usage of these exceptions in
# projects is correct.
ConnectionRefused = ConnectionError
HTTPNotImplemented = HttpNotImplemented
Timeout = RequestTimeout
HTTPError = HttpError
@ -105,19 +89,27 @@ class MultipleChoices(HTTPRedirection):
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
class UnsupportedVersion(ClientException):
"""User is trying to use an unsupported version of the API."""
pass
class CommandError(ClientException):
"""Error in CLI tool."""
pass
AuthorizationFailure = new_exceptions.AuthorizationFailure
class ConnectionRefused(ConnectionError):
"""Connection refused while trying to connect to API service."""
pass
class AuthPluginOptionsMissing(AuthorizationFailure):
"""Auth plugin misses some options."""
def __init__(self, opt_names):
@ -131,7 +123,7 @@ class AuthSystemNotFound(AuthorizationFailure):
"""User has specified an AuthSystem that is not installed."""
def __init__(self, auth_system):
super(AuthSystemNotFound, self).__init__(
_("AuthSystemNotFound: %s") % repr(auth_system))
_("AuthSystemNotFound: %r") % auth_system)
self.auth_system = auth_system
@ -165,7 +157,7 @@ class AmbiguousEndpoints(EndpointException):
"""Found more than one matching endpoint in Service Catalog."""
def __init__(self, endpoints=None):
super(AmbiguousEndpoints, self).__init__(
_("AmbiguousEndpoints: %s") % repr(endpoints))
_("AmbiguousEndpoints: %r") % endpoints)
self.endpoints = endpoints

View File

@ -84,9 +84,9 @@ class Client(httpclient.HTTPClient):
def _check_keystone_versions(self, url):
"""Calls Keystone URL and detects the available API versions."""
try:
resp, body = self.request(url, "GET",
headers={'Accept':
'application/json'})
resp, body = self._request(url, "GET",
headers={'Accept':
'application/json'})
# Multiple Choices status code is returned by the root
# identity endpoint, with references to one or more
# Identity API versions -- v3 spec
@ -148,9 +148,9 @@ class Client(httpclient.HTTPClient):
try:
if not url.endswith("/"):
url += '/'
resp, body = self.request("%sextensions" % url, "GET",
headers={'Accept':
'application/json'})
resp, body = self._request("%sextensions" % url, "GET",
headers={'Accept':
'application/json'})
if resp.status_code in (200, 204): # some cases we get No Content
if 'extensions' in body and 'values' in body['extensions']:
# Parse correct format (per contract)

View File

@ -20,7 +20,10 @@ OpenStack Client interface. Handles the REST calls and responses.
"""
import logging
import warnings
from debtcollector import removals
from debtcollector import renames
from oslo_serialization import jsonutils
import pkg_resources
import requests
@ -64,10 +67,21 @@ from keystoneclient import utils
_logger = logging.getLogger(__name__)
# These variables are moved and using them via httpclient is deprecated.
# This variable is moved and using it via httpclient is deprecated.
# Maintain here for compatibility.
USER_AGENT = client_session.USER_AGENT
request = client_session.request
@removals.remove(message='Use keystoneclient.session.request instead.',
version='1.7.0', removal_version='2.0.0')
def request(*args, **kwargs):
"""Make a request.
This function is deprecated as of the 1.7.0 release in favor of
:func:`keystoneclient.session.request` and may be removed in the
2.0.0 release.
"""
return client_session.request(*args, **kwargs)
class _FakeRequestSession(object):
@ -178,10 +192,13 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
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.
argument is deprecated as of the 1.7.0 release
in favor of project_name and may be removed in
the 2.0.0 release.
:param string tenant_id: Tenant id. (optional) The tenant_id keyword
argument is deprecated, use project_id instead.
argument is deprecated as of the 1.7.0 release in
favor of project_id and may be removed in the
2.0.0 release.
: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.
@ -204,6 +221,10 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
version = None
@renames.renamed_kwarg('tenant_name', 'project_name', version='1.7.0',
removal_version='2.0.0')
@renames.renamed_kwarg('tenant_id', 'project_id', version='1.7.0',
removal_version='2.0.0')
@utils.positional(enforcement=utils.positional.WARN)
def __init__(self, username=None, tenant_id=None, tenant_name=None,
password=None, auth_url=None, region_name=None, endpoint=None,
@ -248,8 +269,14 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
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]
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:
@ -398,15 +425,35 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
@property
def tenant_id(self):
"""Provide read-only backwards compatibility for tenant_id.
This is deprecated, use project_id instead.
.. warning::
This is deprecated as of the 1.7.0 release in favor of project_id
and may be removed in the 2.0.0 release.
"""
warnings.warn(
'tenant_id is deprecated as of the 1.7.0 release in favor of '
'project_id and may be removed in the 2.0.0 release.',
DeprecationWarning)
return self.project_id
@property
def tenant_name(self):
"""Provide read-only backwards compatibility for tenant_name.
This is deprecated, use project_name instead.
.. warning::
This is deprecated as of the 1.7.0 release in favor of project_name
and may be removed in the 2.0.0 release.
"""
warnings.warn(
'tenant_name is deprecated as of the 1.7.0 release in favor of '
'project_name and may be removed in the 2.0.0 release.',
DeprecationWarning)
return self.project_name
@utils.positional(enforcement=utils.positional.WARN)
@ -649,6 +696,7 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
def serialize(self, entity):
return jsonutils.dumps(entity)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def request(self, *args, **kwargs):
"""Send an http request with the specified characteristics.
@ -656,10 +704,15 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
setting headers, JSON encoding/decoding, and error handling.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used only by the managers and the managers now receive an
adapter so this function is no longer on the standard request path.
This may be removed in the 2.0.0 release.
"""
return self._request(*args, **kwargs)
def _request(self, *args, **kwargs):
kwargs.setdefault('authenticated', False)
return self._adapter.request(*args, **kwargs)
@ -668,15 +721,14 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
concatenating self.management_url and url and passing in method and
any associated kwargs.
"""
# NOTE(jamielennox): This is deprecated and is no longer a part of the
# standard client request path. It now goes via the adapter instead.
if not management:
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', 'public')
kwargs.setdefault('authenticated', None)
return self.request(url, method, **kwargs)
return self._request(url, method, **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def get(self, url, **kwargs):
"""Perform an authenticated GET request.
@ -684,12 +736,16 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'GET', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def head(self, url, **kwargs):
"""Perform an authenticated HEAD request.
@ -697,12 +753,16 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'HEAD', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def post(self, url, **kwargs):
"""Perform an authenticate POST request.
@ -710,12 +770,16 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'POST', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def put(self, url, **kwargs):
"""Perform an authenticate PUT request.
@ -723,12 +787,16 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'PUT', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def patch(self, url, **kwargs):
"""Perform an authenticate PATCH request.
@ -736,12 +804,16 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
an authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'PATCH', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def delete(self, url, **kwargs):
"""Perform an authenticate DELETE request.
@ -749,15 +821,15 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
an authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used by the managers and the managers now receive an adapter so
this function is no longer on the standard request path.
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'DELETE', **kwargs)
# DEPRECATIONS: The following methods are no longer directly supported
# but maintained for compatibility purposes.
deprecated_session_variables = {'original_ip': None,
'cert': None,
'timeout': None,
@ -766,12 +838,15 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
deprecated_adapter_variables = {'region_name': None}
def __getattr__(self, name):
# FIXME(jamielennox): provide a proper deprecated warning
try:
var_name = self.deprecated_session_variables[name]
except KeyError:
pass
else:
warnings.warn(
'The %s session variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return getattr(self.session, var_name or name)
try:
@ -779,17 +854,24 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
except KeyError:
pass
else:
warnings.warn(
'The %s adapter variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return getattr(self._adapter, var_name or name)
raise AttributeError(_("Unknown Attribute: %s") % name)
def __setattr__(self, name, val):
# FIXME(jamielennox): provide a proper deprecated warning
try:
var_name = self.deprecated_session_variables[name]
except KeyError:
pass
else:
warnings.warn(
'The %s session variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return setattr(self.session, var_name or name)
try:
@ -797,6 +879,10 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
except KeyError:
pass
else:
warnings.warn(
'The %s adapter variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return setattr(self._adapter, var_name or name)
super(HTTPClient, self).__setattr__(name, val)

View File

@ -0,0 +1,22 @@
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from debtcollector import removals
removals.removed_module('keystoneclient.openstack.common.apiclient',
version='0.7.1',
removal_version='2.0')

View File

@ -33,18 +33,22 @@ Base utilities to build API operation managers and objects on top of.
#
########################################################################
########################################################################
# NOTE(blk-u): This module is not being synced with oslo-incubator
# anymore. We need to deprecate property and get rid of it.
########################################################################
# E1102: %s is not callable
# pylint: disable=E1102
import abc
import copy
from oslo_utils import strutils
import six
from six.moves.urllib import parse
from keystoneclient.openstack.common._i18n import _
from keystoneclient import base as _base
from keystoneclient.openstack.common.apiclient import exceptions
@ -437,96 +441,4 @@ class Extension(HookableMixin):
return "<Extension '%s'>" % self.name
class Resource(object):
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
"""
HUMAN_ID = False
NAME_ATTR = 'name'
def __init__(self, manager, info, loaded=False):
"""Populate and bind to a manager.
:param manager: BaseManager object
:param info: dictionary representing resource attributes
:param loaded: prevent lazy-loading if set to True
"""
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def __repr__(self):
reprkeys = sorted(k
for k in self.__dict__.keys()
if k[0] != '_' and k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
@property
def human_id(self):
"""Human-readable ID which can be used for bash completion.
"""
if self.HUMAN_ID:
name = getattr(self, self.NAME_ATTR, None)
if name is not None:
return strutils.to_slug(name)
return None
def _add_details(self, info):
for (k, v) in six.iteritems(info):
try:
setattr(self, k, v)
self._info[k] = v
except AttributeError:
# In this case we already defined the attribute on the class
pass
def __getattr__(self, k):
if k not in self.__dict__:
# NOTE(bcwaldon): disallow lazy-loading if already loaded once
if not self.is_loaded():
self.get()
return self.__getattr__(k)
raise AttributeError(k)
else:
return self.__dict__[k]
def get(self):
"""Support for lazy loading details.
Some clients, such as novaclient have the option to lazy load the
details, details which can be loaded with this function.
"""
# set_loaded() first ... so if we have to bail, we know we tried.
self.set_loaded(True)
if not hasattr(self.manager, 'get'):
return
new = self.manager.get(self.id)
if new:
self._add_details(new._info)
self._add_details(
{'x_request_id': self.manager.client.last_request_id})
def __eq__(self, other):
if not isinstance(other, Resource):
return NotImplemented
# two resources of different types are not equal
if not isinstance(other, self.__class__):
return False
if hasattr(self, 'id') and hasattr(other, 'id'):
return self.id == other.id
return self._info == other._info
def is_loaded(self):
return self._loaded
def set_loaded(self, val):
self._loaded = val
def to_dict(self):
return copy.deepcopy(self._info)
Resource = _base.Resource

View File

@ -33,447 +33,61 @@ Exception definitions.
#
########################################################################
import inspect
import sys
import six
########################################################################
#
# THIS MODULE IS NOT SYNCED WITH OSLO-INCUBATOR.
# WE'RE JUST TRYING TO GET RID OF IT.
#
########################################################################
from keystoneclient.openstack.common._i18n import _
class ClientException(Exception):
"""The base exception class for all exceptions this library raises.
"""
pass
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
class UnsupportedVersion(ClientException):
"""User is trying to use an unsupported version of the API."""
pass
class CommandError(ClientException):
"""Error in CLI tool."""
pass
class AuthorizationFailure(ClientException):
"""Cannot authorize API client."""
pass
class ConnectionError(ClientException):
"""Cannot connect to API service."""
pass
class ConnectionRefused(ConnectionError):
"""Connection refused while trying to connect to API service."""
pass
class AuthPluginOptionsMissing(AuthorizationFailure):
"""Auth plugin misses some options."""
def __init__(self, opt_names):
super(AuthPluginOptionsMissing, self).__init__(
_("Authentication failed. Missing options: %s") %
", ".join(opt_names))
self.opt_names = opt_names
class AuthSystemNotFound(AuthorizationFailure):
"""User has specified an AuthSystem that is not installed."""
def __init__(self, auth_system):
super(AuthSystemNotFound, self).__init__(
_("AuthSystemNotFound: %r") % auth_system)
self.auth_system = auth_system
class NoUniqueMatch(ClientException):
"""Multiple entities found instead of one."""
pass
class EndpointException(ClientException):
"""Something is rotten in Service Catalog."""
pass
class EndpointNotFound(EndpointException):
"""Could not find requested endpoint in Service Catalog."""
pass
class AmbiguousEndpoints(EndpointException):
"""Found more than one matching endpoint in Service Catalog."""
def __init__(self, endpoints=None):
super(AmbiguousEndpoints, self).__init__(
_("AmbiguousEndpoints: %r") % endpoints)
self.endpoints = endpoints
class HttpError(ClientException):
"""The base exception class for all HTTP exceptions.
"""
http_status = 0
message = _("HTTP Error")
def __init__(self, message=None, details=None,
response=None, request_id=None,
url=None, method=None, http_status=None):
self.http_status = http_status or self.http_status
self.message = message or self.message
self.details = details
self.request_id = request_id
self.response = response
self.url = url
self.method = method
formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
if request_id:
formatted_string += " (Request-ID: %s)" % request_id
super(HttpError, self).__init__(formatted_string)
class HTTPRedirection(HttpError):
"""HTTP Redirection."""
message = _("HTTP Redirection")
class HTTPClientError(HttpError):
"""Client-side HTTP error.
Exception for cases in which the client seems to have erred.
"""
message = _("HTTP Client Error")
class HttpServerError(HttpError):
"""Server-side HTTP error.
Exception for cases in which the server is aware that it has
erred or is incapable of performing the request.
"""
message = _("HTTP Server Error")
class MultipleChoices(HTTPRedirection):
"""HTTP 300 - Multiple Choices.
Indicates multiple options for the resource that the client may follow.
"""
http_status = 300
message = _("Multiple Choices")
class BadRequest(HTTPClientError):
"""HTTP 400 - Bad Request.
The request cannot be fulfilled due to bad syntax.
"""
http_status = 400
message = _("Bad Request")
class Unauthorized(HTTPClientError):
"""HTTP 401 - Unauthorized.
Similar to 403 Forbidden, but specifically for use when authentication
is required and has failed or has not yet been provided.
"""
http_status = 401
message = _("Unauthorized")
class PaymentRequired(HTTPClientError):
"""HTTP 402 - Payment Required.
Reserved for future use.
"""
http_status = 402
message = _("Payment Required")
class Forbidden(HTTPClientError):
"""HTTP 403 - Forbidden.
The request was a valid request, but the server is refusing to respond
to it.
"""
http_status = 403
message = _("Forbidden")
class NotFound(HTTPClientError):
"""HTTP 404 - Not Found.
The requested resource could not be found but may be available again
in the future.
"""
http_status = 404
message = _("Not Found")
class MethodNotAllowed(HTTPClientError):
"""HTTP 405 - Method Not Allowed.
A request was made of a resource using a request method not supported
by that resource.
"""
http_status = 405
message = _("Method Not Allowed")
class NotAcceptable(HTTPClientError):
"""HTTP 406 - Not Acceptable.
The requested resource is only capable of generating content not
acceptable according to the Accept headers sent in the request.
"""
http_status = 406
message = _("Not Acceptable")
class ProxyAuthenticationRequired(HTTPClientError):
"""HTTP 407 - Proxy Authentication Required.
The client must first authenticate itself with the proxy.
"""
http_status = 407
message = _("Proxy Authentication Required")
class RequestTimeout(HTTPClientError):
"""HTTP 408 - Request Timeout.
The server timed out waiting for the request.
"""
http_status = 408
message = _("Request Timeout")
class Conflict(HTTPClientError):
"""HTTP 409 - Conflict.
Indicates that the request could not be processed because of conflict
in the request, such as an edit conflict.
"""
http_status = 409
message = _("Conflict")
class Gone(HTTPClientError):
"""HTTP 410 - Gone.
Indicates that the resource requested is no longer available and will
not be available again.
"""
http_status = 410
message = _("Gone")
class LengthRequired(HTTPClientError):
"""HTTP 411 - Length Required.
The request did not specify the length of its content, which is
required by the requested resource.
"""
http_status = 411
message = _("Length Required")
class PreconditionFailed(HTTPClientError):
"""HTTP 412 - Precondition Failed.
The server does not meet one of the preconditions that the requester
put on the request.
"""
http_status = 412
message = _("Precondition Failed")
class RequestEntityTooLarge(HTTPClientError):
"""HTTP 413 - Request Entity Too Large.
The request is larger than the server is willing or able to process.
"""
http_status = 413
message = _("Request Entity Too Large")
def __init__(self, *args, **kwargs):
try:
self.retry_after = int(kwargs.pop('retry_after'))
except (KeyError, ValueError):
self.retry_after = 0
super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
class RequestUriTooLong(HTTPClientError):
"""HTTP 414 - Request-URI Too Long.
The URI provided was too long for the server to process.
"""
http_status = 414
message = _("Request-URI Too Long")
class UnsupportedMediaType(HTTPClientError):
"""HTTP 415 - Unsupported Media Type.
The request entity has a media type which the server or resource does
not support.
"""
http_status = 415
message = _("Unsupported Media Type")
class RequestedRangeNotSatisfiable(HTTPClientError):
"""HTTP 416 - Requested Range Not Satisfiable.
The client has asked for a portion of the file, but the server cannot
supply that portion.
"""
http_status = 416
message = _("Requested Range Not Satisfiable")
class ExpectationFailed(HTTPClientError):
"""HTTP 417 - Expectation Failed.
The server cannot meet the requirements of the Expect request-header field.
"""
http_status = 417
message = _("Expectation Failed")
class UnprocessableEntity(HTTPClientError):
"""HTTP 422 - Unprocessable Entity.
The request was well-formed but was unable to be followed due to semantic
errors.
"""
http_status = 422
message = _("Unprocessable Entity")
class InternalServerError(HttpServerError):
"""HTTP 500 - Internal Server Error.
A generic error message, given when no more specific message is suitable.
"""
http_status = 500
message = _("Internal Server Error")
# NotImplemented is a python keyword.
class HttpNotImplemented(HttpServerError):
"""HTTP 501 - Not Implemented.
The server either does not recognize the request method, or it lacks
the ability to fulfill the request.
"""
http_status = 501
message = _("Not Implemented")
class BadGateway(HttpServerError):
"""HTTP 502 - Bad Gateway.
The server was acting as a gateway or proxy and received an invalid
response from the upstream server.
"""
http_status = 502
message = _("Bad Gateway")
class ServiceUnavailable(HttpServerError):
"""HTTP 503 - Service Unavailable.
The server is currently unavailable.
"""
http_status = 503
message = _("Service Unavailable")
class GatewayTimeout(HttpServerError):
"""HTTP 504 - Gateway Timeout.
The server was acting as a gateway or proxy and did not receive a timely
response from the upstream server.
"""
http_status = 504
message = _("Gateway Timeout")
class HttpVersionNotSupported(HttpServerError):
"""HTTP 505 - HttpVersion Not Supported.
The server does not support the HTTP protocol version used in the request.
"""
http_status = 505
message = _("HTTP Version Not Supported")
# _code_map contains all the classes that have http_status attribute.
_code_map = dict(
(getattr(obj, 'http_status', None), obj)
for name, obj in six.iteritems(vars(sys.modules[__name__]))
if inspect.isclass(obj) and getattr(obj, 'http_status', False)
)
def from_response(response, method, url):
"""Returns an instance of :class:`HttpError` or subclass based on response.
:param response: instance of `requests.Response` class
:param method: HTTP method used for request
:param url: URL used for request
"""
req_id = response.headers.get("x-openstack-request-id")
# NOTE(hdd) true for older versions of nova and cinder
if not req_id:
req_id = response.headers.get("x-compute-request-id")
kwargs = {
"http_status": response.status_code,
"response": response,
"method": method,
"url": url,
"request_id": req_id,
}
if "retry-after" in response.headers:
kwargs["retry_after"] = response.headers["retry-after"]
content_type = response.headers.get("Content-Type", "")
if content_type.startswith("application/json"):
try:
body = response.json()
except ValueError:
pass
else:
if isinstance(body, dict):
error = body.get(list(body)[0])
if isinstance(error, dict):
kwargs["message"] = (error.get("message") or
error.get("faultstring"))
kwargs["details"] = (error.get("details") or
six.text_type(body))
elif content_type.startswith("text/"):
kwargs["details"] = getattr(response, 'text', '')
try:
cls = _code_map[response.status_code]
except KeyError:
if 500 <= response.status_code < 600:
cls = HttpServerError
elif 400 <= response.status_code < 500:
cls = HTTPClientError
else:
cls = HttpError
return cls(**kwargs)
from keystoneclient import exceptions
"""Exception definitions."""
ClientException = exceptions.ClientException
ValidationError = exceptions.ValidationError
UnsupportedVersion = exceptions.UnsupportedVersion
CommandError = exceptions.CommandError
AuthorizationFailure = exceptions.AuthorizationFailure
ConnectionError = exceptions.ConnectionError
ConnectionRefused = exceptions.ConnectionRefused
AuthPluginOptionsMissing = exceptions.AuthPluginOptionsMissing
AuthSystemNotFound = exceptions.AuthSystemNotFound
NoUniqueMatch = exceptions.NoUniqueMatch
EndpointException = exceptions.EndpointException
EndpointNotFound = exceptions.EndpointNotFound
AmbiguousEndpoints = exceptions.AmbiguousEndpoints
HttpError = exceptions.HttpError
HTTPRedirection = exceptions.HTTPRedirection
HTTPClientError = exceptions.HTTPClientError
HttpServerError = exceptions.HttpServerError
MultipleChoices = exceptions.MultipleChoices
BadRequest = exceptions.BadRequest
Unauthorized = exceptions.Unauthorized
PaymentRequired = exceptions.PaymentRequired
Forbidden = exceptions.Forbidden
NotFound = exceptions.NotFound
MethodNotAllowed = exceptions.MethodNotAllowed
NotAcceptable = exceptions.NotAcceptable
ProxyAuthenticationRequired = exceptions.ProxyAuthenticationRequired
RequestTimeout = exceptions.RequestTimeout
Conflict = exceptions.Conflict
Gone = exceptions.Gone
LengthRequired = exceptions.LengthRequired
PreconditionFailed = exceptions.PreconditionFailed
RequestEntityTooLarge = exceptions.RequestEntityTooLarge
RequestUriTooLong = exceptions.RequestUriTooLong
UnsupportedMediaType = exceptions.UnsupportedMediaType
RequestedRangeNotSatisfiable = exceptions.RequestedRangeNotSatisfiable
ExpectationFailed = exceptions.ExpectationFailed
UnprocessableEntity = exceptions.UnprocessableEntity
InternalServerError = exceptions.InternalServerError
HttpNotImplemented = exceptions.HttpNotImplemented
BadGateway = exceptions.BadGateway
ServiceUnavailable = exceptions.ServiceUnavailable
GatewayTimeout = exceptions.GatewayTimeout
HttpVersionNotSupported = exceptions.HttpVersionNotSupported
from_response = exceptions.from_response

View File

@ -899,11 +899,37 @@ class Session(object):
class TCPKeepAliveAdapter(requests.adapters.HTTPAdapter):
"""The custom adapter used to set TCP Keep-Alive on all connections."""
"""The custom adapter used to set TCP Keep-Alive on all connections.
This Adapter also preserves the default behaviour of Requests which
disables Nagle's Algorithm. See also:
http://blogs.msdn.com/b/windowsazurestorage/archive/2010/06/25/nagle-s-algorithm-is-not-friendly-towards-small-requests.aspx
"""
def init_poolmanager(self, *args, **kwargs):
if requests.__version__ >= '2.4.1':
kwargs.setdefault('socket_options', [
if 'socket_options' not in kwargs:
socket_options = [
# Keep Nagle's algorithm off
(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
# Turn on TCP Keep-Alive
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
])
# Set the maximum number of keep-alive probes
(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4),
# Send keep-alive probes every 15 seconds
(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 15),
]
# Some operating systems (e.g., OSX) do not support setting
# keepidle
if hasattr(socket, 'TCP_KEEPIDLE'):
socket_options += [
# Wait 60 seconds before sending keep-alive probes
(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
]
# After waiting 60 seconds, and then sending a probe once every 15
# seconds 4 times, these options should ensure that a connection
# hands for no longer than 2 minutes before a ConnectionError is
# raised.
kwargs['socket_options'] = socket_options
super(TCPKeepAliveAdapter, self).init_poolmanager(*args, **kwargs)

View File

@ -512,7 +512,8 @@ class V3IdentityPlugin(utils.TestCase):
auth_ref = a.get_access(s)
self.assertFalse(auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertFalse(auth_ref.scoped)
body = self.requests_mock.last_request.json()
ident = body['auth']['identity']

View File

@ -12,7 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import os
import warnings
import fixtures
from oslo_serialization import jsonutils
@ -595,3 +597,25 @@ class HackingCode(fixtures.Fixture):
(30, 0, 'K333'),
],
}
class Deprecations(fixtures.Fixture):
def setUp(self):
super(Deprecations, self).setUp()
# If keystoneclient calls any deprecated function this will raise an
# exception.
warnings.filterwarnings('error', category=DeprecationWarning,
module='^keystoneclient\\.')
self.addCleanup(warnings.resetwarnings)
def expect_deprecations(self):
"""Call this if the test expects to call deprecated function."""
warnings.resetwarnings()
@contextlib.contextmanager
def expect_deprecations_here(self):
warnings.resetwarnings()
yield
warnings.filterwarnings('error', category=DeprecationWarning,
module='^keystoneclient\\.')

View File

@ -36,9 +36,7 @@ class BaseTest(utils.TestCase):
self.assertEqual(base.getid(TmpObject), 4)
def test_resource_lazy_getattr(self):
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
self.client = client.Client(token=self.TEST_TOKEN,
auth_url='http://127.0.0.1:5000',
endpoint='http://127.0.0.1:5000')
@ -85,16 +83,15 @@ class ManagerTest(utils.TestCase):
def setUp(self):
super(ManagerTest, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
self.client = client.Client(token=self.TEST_TOKEN,
auth_url='http://127.0.0.1:5000',
endpoint='http://127.0.0.1:5000')
self.mgr = base.Manager(self.client)
self.mgr.resource_class = base.Resource
def test_api(self):
self.assertEqual(self.mgr.api, self.client)
with self.deprecations.expect_deprecations_here():
self.assertEqual(self.mgr.api, self.client)
def test_get(self):
get_mock = self.useFixture(mockpatch.PatchObject(

View File

@ -472,7 +472,8 @@ class ClientDiscoveryTests(utils.TestCase):
cl = self.assertCreatesV2(auth_url=BASE_URL, **kwargs)
self.assertEqual(cl.original_ip, '100')
with self.deprecations.expect_deprecations_here():
self.assertEqual(cl.original_ip, '100')
self.assertEqual(cl.stale_duration, 15)
self.assertFalse(cl.use_keyring)
@ -499,7 +500,8 @@ class ClientDiscoveryTests(utils.TestCase):
text=V3_VERSION_ENTRY)
disc = discover.Discover(auth_url=BASE_URL)
versions = disc.available_versions()
with self.deprecations.expect_deprecations_here():
versions = disc.available_versions()
self.assertEqual(1, len(versions))
self.assertEqual(V3_VERSION, versions[0])

View File

@ -17,12 +17,14 @@ from __future__ import unicode_literals
import testtools
from keystoneclient.contrib.ec2 import utils
from keystoneclient.tests.unit import client_fixtures
class Ec2SignerTest(testtools.TestCase):
def setUp(self):
super(Ec2SignerTest, self).setUp()
self.useFixture(client_fixtures.Deprecations())
self.access = '966afbde20b84200ae4e62e09acf46b2'
self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83'
self.signer = utils.Ec2Signer(self.secret)

View File

@ -21,6 +21,9 @@ from keystoneclient.tests.unit import client_fixtures
class TestCheckOsloNamespaceImports(testtools.TestCase):
def setUp(self):
super(TestCheckOsloNamespaceImports, self).setUp()
self.useFixture(client_fixtures.Deprecations())
# We are patching pep8 so that only the check under test is actually
# installed.

View File

@ -28,7 +28,7 @@ RESPONSE_BODY = '{"hi": "there"}'
def get_client():
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test")
project_id="tenant", auth_url="auth_test")
return cl
@ -67,7 +67,8 @@ class ClientTest(utils.TestCase):
self.stub_url('GET', text=RESPONSE_BODY)
resp, body = cl.get("/hi")
with self.deprecations.expect_deprecations_here():
resp, body = cl.get("/hi")
self.assertEqual(self.requests_mock.last_request.method, 'GET')
self.assertEqual(self.requests_mock.last_request.url, self.TEST_URL)
@ -96,7 +97,8 @@ class ClientTest(utils.TestCase):
self.stub_url('GET', status_code=400, json=err_response)
exc_raised = False
try:
cl.get('/hi')
with self.deprecations.expect_deprecations_here():
cl.get('/hi')
except exceptions.BadRequest as exc:
exc_raised = True
self.assertEqual(exc.message, "Error message string")
@ -106,7 +108,8 @@ class ClientTest(utils.TestCase):
cl = get_authed_client()
self.stub_url('POST')
cl.post("/hi", body=[1, 2, 3])
with self.deprecations.expect_deprecations_here():
cl.post("/hi", body=[1, 2, 3])
self.assertEqual(self.requests_mock.last_request.method, 'POST')
self.assertEqual(self.requests_mock.last_request.body, '[1, 2, 3]')
@ -118,12 +121,13 @@ class ClientTest(utils.TestCase):
def test_forwarded_for(self):
ORIGINAL_IP = "10.100.100.1"
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test",
project_id="tenant", auth_url="auth_test",
original_ip=ORIGINAL_IP)
self.stub_url('GET')
cl.request(self.TEST_URL, 'GET')
with self.deprecations.expect_deprecations_here():
cl.request(self.TEST_URL, 'GET')
forwarded = "for=%s;by=%s" % (ORIGINAL_IP, httpclient.USER_AGENT)
self.assertRequestHeaderEqual('Forwarded', forwarded)
@ -167,7 +171,8 @@ class BasicRequestTests(utils.TestCase):
self.requests_mock.register_uri(method, url, text=response,
status_code=status_code)
return httpclient.request(url, method, **kwargs)
with self.deprecations.expect_deprecations_here():
return httpclient.request(url, method, **kwargs)
def test_basic_params(self):
method = 'GET'

View File

@ -28,7 +28,7 @@ RESPONSE_BODY = b'{"hi": "there"}'
def get_client():
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test",
project_id="tenant", auth_url="auth_test",
cacert="ca.pem", key="key.pem", cert="cert.pem")
return cl
@ -47,7 +47,8 @@ class ClientTest(utils.TestCase):
MOCK_REQUEST.return_value = FAKE_RESPONSE
cl = get_authed_client()
resp, body = cl.get("/hi")
with self.deprecations.expect_deprecations_here():
resp, body = cl.get("/hi")
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args
@ -66,7 +67,8 @@ class ClientTest(utils.TestCase):
MOCK_REQUEST.return_value = FAKE_RESPONSE
cl = get_authed_client()
cl.post("/hi", body=[1, 2, 3])
with self.deprecations.expect_deprecations_here():
cl.post("/hi", body=[1, 2, 3])
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args
@ -82,12 +84,13 @@ class ClientTest(utils.TestCase):
def test_post_auth(self, MOCK_REQUEST):
MOCK_REQUEST.return_value = FAKE_RESPONSE
cl = httpclient.HTTPClient(
username="username", password="password", tenant_id="tenant",
username="username", password="password", project_id="tenant",
auth_url="auth_test", cacert="ca.pem", key="key.pem",
cert="cert.pem")
cl.management_url = "https://127.0.0.1:5000"
cl.auth_token = "token"
cl.post("/hi", body=[1, 2, 3])
with self.deprecations.expect_deprecations_here():
cl.post("/hi", body=[1, 2, 3])
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args

View File

@ -19,6 +19,7 @@ from keystoneclient import access
from keystoneclient import httpclient
from keystoneclient.tests.unit import utils
from keystoneclient.tests.unit.v2_0 import client_fixtures
from keystoneclient import utils as client_utils
try:
import keyring # noqa
@ -87,7 +88,7 @@ class KeyringTest(utils.TestCase):
the keyring is never accessed.
"""
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL)
project_id=TENANT_ID, auth_url=AUTH_URL)
# stub and check that a new token is received
method = 'get_raw_token_from_identity_service'
@ -104,7 +105,7 @@ class KeyringTest(utils.TestCase):
def test_build_keyring_key(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL)
project_id=TENANT_ID, auth_url=AUTH_URL)
keyring_key = cl._build_keyring_key(auth_url=AUTH_URL,
username=USERNAME,
@ -118,13 +119,13 @@ class KeyringTest(utils.TestCase):
def test_set_and_get_keyring_expired(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
project_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# set an expired token into the keyring
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
expired = timeutils.utcnow() - datetime.timedelta(minutes=30)
auth_ref['token']['expires'] = timeutils.isotime(expired)
auth_ref['token']['expires'] = client_utils.isotime(expired)
self.memory_keyring.password = pickle.dumps(auth_ref)
# stub and check that a new token is received, so not using expired
@ -146,13 +147,13 @@ class KeyringTest(utils.TestCase):
def test_get_keyring(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
project_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# set an token into the keyring
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
future = timeutils.utcnow() + datetime.timedelta(minutes=30)
auth_ref['token']['expires'] = timeutils.isotime(future)
auth_ref['token']['expires'] = client_utils.isotime(future)
self.memory_keyring.password = pickle.dumps(auth_ref)
# don't stub get_raw_token so will fail if authenticate happens
@ -162,7 +163,7 @@ class KeyringTest(utils.TestCase):
def test_set_keyring(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
project_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# stub and check that a new token is received

View File

@ -12,18 +12,18 @@
import logging
import sys
import time
import uuid
import fixtures
from oslo_serialization import jsonutils
from oslotest import mockpatch
import requests
from requests_mock.contrib import fixture
import six
from six.moves.urllib import parse as urlparse
import testtools
from keystoneclient.tests.unit import client_fixtures
class TestCase(testtools.TestCase):
@ -42,10 +42,9 @@ class TestCase(testtools.TestCase):
def setUp(self):
super(TestCase, self).setUp()
self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
self.time_patcher = self.useFixture(
mockpatch.PatchObject(time, 'time', lambda: 1234)).mock
self.deprecations = self.useFixture(client_fixtures.Deprecations())
self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
self.requests_mock = self.useFixture(fixture.Fixture())
def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs):

View File

@ -45,10 +45,13 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase):
self.assertIsNone(auth_ref.tenant_name)
self.assertIsNone(auth_ref.tenant_id)
self.assertIsNone(auth_ref.auth_url)
self.assertIsNone(auth_ref.management_url)
with self.deprecations.expect_deprecations_here():
self.assertIsNone(auth_ref.auth_url)
with self.deprecations.expect_deprecations_here():
self.assertIsNone(auth_ref.management_url)
self.assertFalse(auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertFalse(auth_ref.scoped)
self.assertFalse(auth_ref.domain_scoped)
self.assertFalse(auth_ref.project_scoped)
self.assertFalse(auth_ref.trust_scoped)
@ -98,15 +101,20 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase):
self.assertEqual(auth_ref.tenant_name, auth_ref.project_name)
self.assertEqual(auth_ref.tenant_id, auth_ref.project_id)
self.assertEqual(auth_ref.auth_url, ('http://public.com:5000/v2.0',))
self.assertEqual(auth_ref.management_url, ('http://admin:35357/v2.0',))
with self.deprecations.expect_deprecations_here():
self.assertEqual(auth_ref.auth_url,
('http://public.com:5000/v2.0',))
with self.deprecations.expect_deprecations_here():
self.assertEqual(auth_ref.management_url,
('http://admin:35357/v2.0',))
self.assertEqual(auth_ref.project_domain_id, 'default')
self.assertEqual(auth_ref.project_domain_name, 'Default')
self.assertEqual(auth_ref.user_domain_id, 'default')
self.assertEqual(auth_ref.user_domain_name, 'Default')
self.assertTrue(auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertTrue(auth_ref.scoped)
self.assertTrue(auth_ref.project_scoped)
self.assertFalse(auth_ref.domain_scoped)
@ -127,7 +135,8 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase):
self.assertEqual(auth_ref.user_domain_id, 'default')
self.assertEqual(auth_ref.user_domain_name, 'Default')
self.assertEqual(auth_ref.role_names, ['role1', 'role2'])
self.assertFalse(auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertFalse(auth_ref.scoped)
def test_grizzly_token(self):
grizzly_token = self.examples.TOKEN_RESPONSES[

View File

@ -67,7 +67,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.stub_auth(response_list=[{'json': resp_a, 'headers': headers},
{'json': resp_b, 'headers': headers}])
cs = client.Client(tenant_id=self.TEST_TENANT_ID,
cs = client.Client(project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL,
username=self.TEST_USER,
password=self.TEST_TOKEN)
@ -95,7 +95,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def client_create_wrapper():
client.Client(username=self.TEST_USER,
password="bad_key",
tenant_id=self.TEST_TENANT_ID,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertRaises(exceptions.Unauthorized, client_create_wrapper)
@ -110,7 +110,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
@ -125,7 +125,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
@ -162,7 +162,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
json_body = jsonutils.loads(self.requests_mock.last_request.body)
self.assertEqual(json_body['auth']['token']['id'], fake_token)
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -174,7 +175,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
@ -193,7 +194,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
self.stub_auth(json=response)
cs = client.Client(token=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
project_id=self.TEST_TENANT_ID,
trust_id=self.TEST_TRUST_ID,
auth_url=self.TEST_URL)
self.assertTrue(cs.auth_ref.trust_scoped)
@ -227,13 +228,14 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
self.assertEqual(cl.auth_token, self.TEST_TOKEN)
# the token returned from the authentication will be used
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -242,7 +244,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
# then override that token and the new token shall be used
cl.auth_token = fake_token
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -251,7 +254,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
# if we clear that overridden token then we fall back to the original
del cl.auth_token
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')

View File

@ -34,7 +34,8 @@ class KeystoneClientTest(utils.TestCase):
password='password',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertFalse(c.auth_ref.scoped)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertIsNone(c.auth_ref.trust_id)
@ -48,10 +49,11 @@ class KeystoneClientTest(utils.TestCase):
c = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertTrue(c.auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertTrue(c.auth_ref.scoped)
self.assertTrue(c.auth_ref.project_scoped)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertIsNone(c.auth_ref.trust_id)
@ -65,12 +67,13 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
cache = json.dumps(cl.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache))
self.assertIsNotNone(new_client.auth_ref)
self.assertTrue(new_client.auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertIsNone(new_client.auth_ref.trust_id)
@ -85,15 +88,15 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
cache = json.dumps(cl.auth_ref)
new_auth_url = "http://new-public:5000/v2.0"
new_client = client.Client(auth_ref=json.loads(cache),
auth_url=new_auth_url)
self.assertIsNotNone(new_client.auth_ref)
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.scoped)
with self.deprecations.expect_deprecations_here():
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertIsNone(new_client.auth_ref.trust_id)
@ -130,7 +133,7 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
self.assertEqual(cl.management_url, admin_url)
@ -144,7 +147,7 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL,
region_name='North')
self.assertEqual(cl.service_catalog.url_for(service_type='image'),
@ -152,7 +155,7 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL,
region_name='South')
self.assertEqual(cl.service_catalog.url_for(service_type='image'),
@ -161,7 +164,7 @@ class KeystoneClientTest(utils.TestCase):
def test_client_without_auth_params(self):
self.assertRaises(exceptions.AuthorizationFailure,
client.Client,
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
def test_client_params(self):

View File

@ -168,6 +168,9 @@ class TokenTests(utils.TestCase):
token_fixture = fixture.V2Token(token_id=id_)
self.stub_url('GET', ['tokens', id_], json=token_fixture)
token_data = self.client.tokens.get_token_data(id_)
self.assertEqual(token_fixture, token_data)
token_ref = self.client.tokens.validate(id_)
self.assertIsInstance(token_ref, tokens.Token)
self.assertEqual(id_, token_ref.id)
@ -178,6 +181,9 @@ class TokenTests(utils.TestCase):
id_ = uuid.uuid4().hex
# The server is expected to return 404 if the token is invalid.
self.stub_url('GET', ['tokens', id_], status_code=404)
self.assertRaises(exceptions.NotFound,
self.client.tokens.get_token_data, id_)
self.assertRaises(exceptions.NotFound,
self.client.tokens.validate, id_)

View File

@ -78,9 +78,7 @@ class TestCase(UnauthenticatedTestCase):
def setUp(self):
super(TestCase, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
self.client = client.Client(token=self.TEST_TOKEN,
auth_url=self.TEST_URL,
endpoint=self.TEST_URL)

View File

@ -16,26 +16,18 @@ import uuid
from keystoneclient import fixture
def unscoped_token():
return fixture.V3Token(user_id='c4da488862bd435c9e6c0275a0d0e49a',
user_name='exampleuser',
user_domain_id='4e6893b7ba0b4006840c3845660b86ed',
user_domain_name='exampledomain',
expires='2010-11-01T03:32:15-05:00')
def unscoped_token(**kwargs):
return fixture.V3Token(**kwargs)
def domain_scoped_token():
f = fixture.V3Token(user_id='c4da488862bd435c9e6c0275a0d0e49a',
user_name='exampleuser',
user_domain_id='4e6893b7ba0b4006840c3845660b86ed',
user_domain_name='exampledomain',
expires='2010-11-01T03:32:15-05:00',
domain_id='8e9283b7ba0b1038840c3842058b86ab',
domain_name='anotherdomain',
audit_chain_id=uuid.uuid4().hex)
def domain_scoped_token(**kwargs):
kwargs.setdefault('audit_chain_id', uuid.uuid4().hex)
f = fixture.V3Token(**kwargs)
if not f.domain_id:
f.set_domain_scope()
f.add_role(id='76e72a', name='admin')
f.add_role(id='f4f392', name='member')
f.add_role(name='admin')
f.add_role(name='member')
region = 'RegionOne'
s = f.add_service('volume')
@ -71,20 +63,15 @@ def domain_scoped_token():
return f
def project_scoped_token():
f = fixture.V3Token(user_id='c4da488862bd435c9e6c0275a0d0e49a',
user_name='exampleuser',
user_domain_id='4e6893b7ba0b4006840c3845660b86ed',
user_domain_name='exampledomain',
expires='2010-11-01T03:32:15-05:00',
project_id='225da22d3ce34b15877ea70b2a575f58',
project_name='exampleproject',
project_domain_id='4e6893b7ba0b4006840c3845660b86ed',
project_domain_name='exampledomain',
audit_chain_id=uuid.uuid4().hex)
def project_scoped_token(**kwargs):
kwargs.setdefault('audit_chain_id', uuid.uuid4().hex)
f = fixture.V3Token(**kwargs)
f.add_role(id='76e72a', name='admin')
f.add_role(id='f4f392', name='member')
if not f.project_id:
f.set_project_scope()
f.add_role(name='admin')
f.add_role(name='member')
region = 'RegionOne'
tenant = '225da22d3ce34b15877ea70b2a575f58'
@ -122,7 +109,7 @@ def project_scoped_token():
return f
AUTH_SUBJECT_TOKEN = '3e2813b7ba0b4006840c3825860b86ed'
AUTH_SUBJECT_TOKEN = uuid.uuid4().hex
AUTH_RESPONSE_HEADERS = {
'X-Subject-Token': AUTH_SUBJECT_TOKEN,
@ -130,19 +117,11 @@ AUTH_RESPONSE_HEADERS = {
def auth_response_body():
f = fixture.V3Token(user_id='567',
user_name='test',
user_domain_id='1',
user_domain_name='aDomain',
expires='2010-11-01T03:32:15-05:00',
project_domain_id='123',
project_domain_name='aDomain',
project_id='345',
project_name='aTenant',
audit_chain_id=uuid.uuid4().hex)
f = fixture.V3Token(audit_chain_id=uuid.uuid4().hex)
f.set_project_scope()
f.add_role(id='76e72a', name='admin')
f.add_role(id='f4f392', name='member')
f.add_role(name='admin')
f.add_role(name='member')
s = f.add_service('compute', name='nova')
s.add_standard_endpoints(
@ -175,13 +154,6 @@ def auth_response_body():
def trust_token():
return fixture.V3Token(user_id='0ca8f6',
user_name='exampleuser',
user_domain_id='4e6893b7ba0b4006840c3845660b86ed',
user_domain_name='exampledomain',
expires='2010-11-01T03:32:15-05:00',
trust_id='fe0aef',
trust_impersonation=False,
trustee_user_id='0ca8f6',
trustor_user_id='bd263c',
audit_chain_id=uuid.uuid4().hex)
f = fixture.V3Token(audit_chain_id=uuid.uuid4().hex)
f.set_trust_scope()
return f

View File

@ -38,10 +38,10 @@ class AccessInfoTest(utils.TestCase):
self.assertIn('methods', auth_ref)
self.assertNotIn('catalog', auth_ref)
self.assertEqual(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEqual(auth_ref.username, 'exampleuser')
self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(client_fixtures.AUTH_SUBJECT_TOKEN,
auth_ref.auth_token)
self.assertEqual(UNSCOPED_TOKEN.user_name, auth_ref.username)
self.assertEqual(UNSCOPED_TOKEN.user_id, auth_ref.user_id)
self.assertEqual(auth_ref.role_ids, [])
self.assertEqual(auth_ref.role_names, [])
@ -49,15 +49,18 @@ class AccessInfoTest(utils.TestCase):
self.assertIsNone(auth_ref.project_name)
self.assertIsNone(auth_ref.project_id)
self.assertIsNone(auth_ref.auth_url)
self.assertIsNone(auth_ref.management_url)
with self.deprecations.expect_deprecations_here():
self.assertIsNone(auth_ref.auth_url)
with self.deprecations.expect_deprecations_here():
self.assertIsNone(auth_ref.management_url)
self.assertFalse(auth_ref.domain_scoped)
self.assertFalse(auth_ref.project_scoped)
self.assertEqual(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEqual(auth_ref.user_domain_name, 'exampledomain')
self.assertEqual(UNSCOPED_TOKEN.user_domain_id,
auth_ref.user_domain_id)
self.assertEqual(UNSCOPED_TOKEN.user_domain_name,
auth_ref.user_domain_name)
self.assertIsNone(auth_ref.project_domain_id)
self.assertIsNone(auth_ref.project_domain_name)
@ -92,24 +95,24 @@ class AccessInfoTest(utils.TestCase):
self.assertIn('catalog', auth_ref)
self.assertTrue(auth_ref['catalog'])
self.assertEqual(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEqual(auth_ref.username, 'exampleuser')
self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(client_fixtures.AUTH_SUBJECT_TOKEN,
auth_ref.auth_token)
self.assertEqual(DOMAIN_SCOPED_TOKEN.user_name, auth_ref.username)
self.assertEqual(DOMAIN_SCOPED_TOKEN.user_id, auth_ref.user_id)
self.assertEqual(auth_ref.role_ids, ['76e72a', 'f4f392'])
self.assertEqual(auth_ref.role_names, ['admin', 'member'])
self.assertEqual(DOMAIN_SCOPED_TOKEN.role_ids, auth_ref.role_ids)
self.assertEqual(DOMAIN_SCOPED_TOKEN.role_names, auth_ref.role_names)
self.assertEqual(auth_ref.domain_name, 'anotherdomain')
self.assertEqual(auth_ref.domain_id,
'8e9283b7ba0b1038840c3842058b86ab')
self.assertEqual(DOMAIN_SCOPED_TOKEN.domain_name, auth_ref.domain_name)
self.assertEqual(DOMAIN_SCOPED_TOKEN.domain_id, auth_ref.domain_id)
self.assertIsNone(auth_ref.project_name)
self.assertIsNone(auth_ref.project_id)
self.assertEqual(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEqual(auth_ref.user_domain_name, 'exampledomain')
self.assertEqual(DOMAIN_SCOPED_TOKEN.user_domain_id,
auth_ref.user_domain_id)
self.assertEqual(DOMAIN_SCOPED_TOKEN.user_domain_name,
auth_ref.user_domain_name)
self.assertIsNone(auth_ref.project_domain_id)
self.assertIsNone(auth_ref.project_domain_name)
@ -130,36 +133,40 @@ class AccessInfoTest(utils.TestCase):
self.assertIn('catalog', auth_ref)
self.assertTrue(auth_ref['catalog'])
self.assertEqual(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEqual(auth_ref.username, 'exampleuser')
self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(client_fixtures.AUTH_SUBJECT_TOKEN,
auth_ref.auth_token)
self.assertEqual(PROJECT_SCOPED_TOKEN.user_name, auth_ref.username)
self.assertEqual(PROJECT_SCOPED_TOKEN.user_id, auth_ref.user_id)
self.assertEqual(auth_ref.role_ids, ['76e72a', 'f4f392'])
self.assertEqual(auth_ref.role_names, ['admin', 'member'])
self.assertEqual(PROJECT_SCOPED_TOKEN.role_ids, auth_ref.role_ids)
self.assertEqual(PROJECT_SCOPED_TOKEN.role_names, auth_ref.role_names)
self.assertIsNone(auth_ref.domain_name)
self.assertIsNone(auth_ref.domain_id)
self.assertEqual(auth_ref.project_name, 'exampleproject')
self.assertEqual(auth_ref.project_id,
'225da22d3ce34b15877ea70b2a575f58')
self.assertEqual(PROJECT_SCOPED_TOKEN.project_name,
auth_ref.project_name)
self.assertEqual(PROJECT_SCOPED_TOKEN.project_id, auth_ref.project_id)
self.assertEqual(auth_ref.tenant_name, auth_ref.project_name)
self.assertEqual(auth_ref.tenant_id, auth_ref.project_id)
self.assertEqual(auth_ref.auth_url,
('http://public.com:5000/v3',))
self.assertEqual(auth_ref.management_url,
('http://admin:35357/v3',))
with self.deprecations.expect_deprecations_here():
self.assertEqual(auth_ref.auth_url,
('http://public.com:5000/v3',))
with self.deprecations.expect_deprecations_here():
self.assertEqual(auth_ref.management_url,
('http://admin:35357/v3',))
self.assertEqual(auth_ref.project_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEqual(auth_ref.project_domain_name, 'exampledomain')
self.assertEqual(PROJECT_SCOPED_TOKEN.project_domain_id,
auth_ref.project_domain_id)
self.assertEqual(PROJECT_SCOPED_TOKEN.project_domain_name,
auth_ref.project_domain_name)
self.assertEqual(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEqual(auth_ref.user_domain_name, 'exampledomain')
self.assertEqual(PROJECT_SCOPED_TOKEN.user_domain_id,
auth_ref.user_domain_id)
self.assertEqual(PROJECT_SCOPED_TOKEN.user_domain_name,
auth_ref.user_domain_name)
self.assertFalse(auth_ref.domain_scoped)
self.assertTrue(auth_ref.project_scoped)

View File

@ -229,7 +229,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
body = jsonutils.loads(self.requests_mock.last_request.body)
self.assertEqual(body['auth']['identity']['token']['id'], fake_token)
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -321,13 +322,14 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL)
self.assertEqual(cl.auth_token, self.TEST_TOKEN)
# the token returned from the authentication will be used
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -336,7 +338,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
# then override that token and the new token shall be used
cl.auth_token = fake_token
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')
@ -345,7 +348,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
# if we clear that overridden token then we fall back to the original
del cl.auth_token
resp, body = cl.get(fake_url)
with self.deprecations.expect_deprecations_here():
resp, body = cl.get(fake_url)
self.assertEqual(fake_resp, body)
token = self.requests_mock.last_request.headers.get('X-Auth-Token')

View File

@ -0,0 +1,72 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.auth.identity import v3
from keystoneclient import fixture
from keystoneclient import session
from keystoneclient.tests.unit.v3 import utils
from keystoneclient.v3 import auth
from keystoneclient.v3 import client
class AuthProjectsTest(utils.TestCase):
def setUp(self):
super(AuthProjectsTest, self).setUp()
self.v3token = fixture.V3Token()
self.stub_auth(json=self.v3token)
self.stub_url('GET',
[],
json={'version': fixture.V3Discovery(self.TEST_URL)})
self.auth = v3.Password(auth_url=self.TEST_URL,
user_id=self.v3token.user_id,
password=uuid.uuid4().hex)
self.session = session.Session(auth=self.auth)
self.client = client.Client(session=self.session)
def create_resource(self, id=None, name=None, **kwargs):
kwargs['id'] = id or uuid.uuid4().hex
kwargs['name'] = name or uuid.uuid4().hex
return kwargs
def test_get_projects(self):
body = {'projects': [self.create_resource(),
self.create_resource(),
self.create_resource()]}
self.stub_url('GET', ['auth', 'projects'], json=body)
projects = self.client.auth.projects()
self.assertEqual(3, len(projects))
for p in projects:
self.assertIsInstance(p, auth.Project)
def test_get_domains(self):
body = {'domains': [self.create_resource(),
self.create_resource(),
self.create_resource()]}
self.stub_url('GET', ['auth', 'domains'], json=body)
domains = self.client.auth.domains()
self.assertEqual(3, len(domains))
for d in domains:
self.assertIsInstance(d, auth.Domain)

View File

@ -27,71 +27,67 @@ from keystoneclient.v3 import client
class KeystoneClientTest(utils.TestCase):
def test_unscoped_init(self):
self.stub_auth(json=client_fixtures.unscoped_token())
token = client_fixtures.unscoped_token()
self.stub_auth(json=token)
c = client.Client(user_domain_name='exampledomain',
username='exampleuser',
c = client.Client(user_domain_name=token.user_domain_name,
username=token.user_name,
password='password',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEqual(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(token.user_id, c.auth_user_id)
self.assertFalse(c.has_service_catalog())
self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a',
c.get_user_id(session=None))
self.assertEqual(token.user_id, c.get_user_id(session=None))
self.assertIsNone(c.get_project_id(session=None))
def test_domain_scoped_init(self):
self.stub_auth(json=client_fixtures.domain_scoped_token())
token = client_fixtures.domain_scoped_token()
self.stub_auth(json=token)
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
c = client.Client(user_id=token.user_id,
password='password',
domain_name='exampledomain',
domain_name=token.domain_name,
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertTrue(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEqual(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(c.auth_domain_id,
'8e9283b7ba0b1038840c3842058b86ab')
self.assertEqual(token.user_id, c.auth_user_id)
self.assertEqual(token.domain_id, c.auth_domain_id)
def test_project_scoped_init(self):
self.stub_auth(json=client_fixtures.project_scoped_token()),
token = client_fixtures.project_scoped_token()
self.stub_auth(json=token),
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
c = client.Client(user_id=token.user_id,
password='password',
user_domain_name='exampledomain',
project_name='exampleproject',
user_domain_name=token.user_domain_name,
project_name=token.project_name,
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertTrue(c.auth_ref.project_scoped)
self.assertEqual(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEqual(c.auth_tenant_id,
'225da22d3ce34b15877ea70b2a575f58')
self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a',
c.get_user_id(session=None))
self.assertEqual('225da22d3ce34b15877ea70b2a575f58',
c.get_project_id(session=None))
self.assertEqual(token.user_id, c.auth_user_id)
self.assertEqual(token.project_id, c.auth_tenant_id)
self.assertEqual(token.user_id, c.get_user_id(session=None))
self.assertEqual(token.project_id, c.get_project_id(session=None))
def test_auth_ref_load(self):
self.stub_auth(json=client_fixtures.project_scoped_token())
token = client_fixtures.project_scoped_token()
self.stub_auth(json=token)
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
c = client.Client(user_id=token.user_id,
password='password',
project_id='225da22d3ce34b15877ea70b2a575f58',
project_id=token.project_id,
auth_url=self.TEST_URL)
cache = json.dumps(c.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache))
self.assertIsNotNone(new_client.auth_ref)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertEqual(new_client.username, 'exampleuser')
self.assertEqual(token.user_name, new_client.username)
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v3')
@ -99,13 +95,22 @@ class KeystoneClientTest(utils.TestCase):
def test_auth_ref_load_with_overridden_arguments(self):
new_auth_url = 'https://newkeystone.com/v3'
self.stub_auth(json=client_fixtures.project_scoped_token())
self.stub_auth(json=client_fixtures.project_scoped_token(),
base_url=new_auth_url)
user_id = uuid.uuid4().hex
user_name = uuid.uuid4().hex
project_id = uuid.uuid4().hex
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
first = client_fixtures.project_scoped_token(user_id=user_id,
user_name=user_name,
project_id=project_id)
second = client_fixtures.project_scoped_token(user_id=user_id,
user_name=user_name,
project_id=project_id)
self.stub_auth(json=first)
self.stub_auth(json=second, base_url=new_auth_url)
c = client.Client(user_id=user_id,
password='password',
project_id='225da22d3ce34b15877ea70b2a575f58',
project_id=project_id,
auth_url=self.TEST_URL)
cache = json.dumps(c.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache),
@ -113,28 +118,29 @@ class KeystoneClientTest(utils.TestCase):
self.assertIsNotNone(new_client.auth_ref)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertEqual(new_client.auth_url, new_auth_url)
self.assertEqual(new_client.username, 'exampleuser')
self.assertEqual(new_auth_url, new_client.auth_url)
self.assertEqual(user_name, new_client.username)
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v3')
def test_trust_init(self):
self.stub_auth(json=client_fixtures.trust_token())
token = client_fixtures.trust_token()
self.stub_auth(json=token)
c = client.Client(user_domain_name='exampledomain',
username='exampleuser',
c = client.Client(user_domain_name=token.user_domain_name,
username=token.user_name,
password='password',
auth_url=self.TEST_URL,
trust_id='fe0aef')
trust_id=token.trust_id)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEqual(c.auth_ref.trust_id, 'fe0aef')
self.assertEqual(c.auth_ref.trustee_user_id, '0ca8f6')
self.assertEqual(c.auth_ref.trustor_user_id, 'bd263c')
self.assertEqual(token.trust_id, c.auth_ref.trust_id)
self.assertEqual(token.trustee_user_id, c.auth_ref.trustee_user_id)
self.assertEqual(token.trustor_user_id, c.auth_ref.trustor_user_id)
self.assertTrue(c.auth_ref.trust_scoped)
self.assertEqual(c.auth_user_id, '0ca8f6')
self.assertEqual(token.user_id, c.auth_user_id)
def test_init_err_no_auth_url(self):
self.assertRaises(exceptions.AuthorizationFailure,
@ -190,7 +196,7 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL,
region_name='North')
self.assertEqual(cl.service_catalog.url_for(service_type='image'),
@ -198,7 +204,7 @@ class KeystoneClientTest(utils.TestCase):
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
project_name='exampleproject',
auth_url=self.TEST_URL,
region_name='South')
self.assertEqual(cl.service_catalog.url_for(service_type='image'),

View File

@ -30,6 +30,12 @@ class DomainTests(utils.TestCase, utils.CrudTests):
kwargs.setdefault('name', uuid.uuid4().hex)
return kwargs
def test_filter_for_default_domain_by_id(self):
ref = self.new_ref(id='default')
super(DomainTests, self).test_list_by_id(
ref=ref,
id=ref['id'])
def test_list_filter_name(self):
super(DomainTests, self).test_list(name='adomain123')

View File

@ -278,6 +278,16 @@ class ProtocolTests(utils.TestCase, utils.CrudTests):
for obj, ref_obj in zip(returned, expected):
self.assertEqual(obj.to_dict(), ref_obj)
def test_list_by_id(self):
# The test in the parent class needs to be overridden because it
# assumes globally unique IDs, which is not the case with protocol IDs
# (which are contextualized per identity provider).
ref = self.new_ref()
super(ProtocolTests, self).test_list_by_id(
ref=ref,
identity_provider=ref['identity_provider'],
id=ref['id'])
def test_list_params(self):
request_args = self.new_ref()
filter_kwargs = {uuid.uuid4().hex: uuid.uuid4().hex}

View File

@ -14,7 +14,6 @@
import uuid
import mock
from oslo_utils import timeutils
import six
from six.moves.urllib import parse as urlparse
from testtools import matchers
@ -22,13 +21,13 @@ from testtools import matchers
from keystoneclient import session
from keystoneclient.tests.unit.v3 import client_fixtures
from keystoneclient.tests.unit.v3 import utils
from keystoneclient import utils as client_utils
from keystoneclient.v3.contrib.oauth1 import access_tokens
from keystoneclient.v3.contrib.oauth1 import auth
from keystoneclient.v3.contrib.oauth1 import consumers
from keystoneclient.v3.contrib.oauth1 import request_tokens
try:
import oauthlib
from oauthlib import oauth1
except ImportError:
oauth1 = None
@ -90,7 +89,7 @@ class TokenTests(BaseTest):
def _new_oauth_token_with_expires_at(self):
key, secret, token = self._new_oauth_token()
expires_at = timeutils.strtime()
expires_at = client_utils.strtime()
params = {'oauth_token': key,
'oauth_token_secret': secret,
'oauth_expires_at': expires_at}
@ -103,16 +102,8 @@ class TokenTests(BaseTest):
"""
self.assertThat(auth_header, matchers.StartsWith('OAuth '))
auth_header = auth_header[len('OAuth '):]
# NOTE(stevemar): In newer versions of oauthlib there is
# an additional argument for getting oauth parameters.
# Adding a conditional here to revert back to no arguments
# if an earlier version is detected.
if tuple(oauthlib.__version__.split('.')) > ('0', '6', '1'):
header_params = oauth_client.get_oauth_params(None)
else:
header_params = oauth_client.get_oauth_params()
parameters = dict(header_params)
parameters = dict(
oauth1.rfc5849.utils.parse_authorization_header(auth_header))
self.assertEqual('HMAC-SHA1', parameters['oauth_signature_method'])
self.assertEqual('1.0', parameters['oauth_version'])
@ -128,9 +119,6 @@ class TokenTests(BaseTest):
if oauth_client.callback_uri:
self.assertEqual(oauth_client.callback_uri,
parameters['oauth_callback'])
if oauth_client.timestamp:
self.assertEqual(oauth_client.timestamp,
parameters['oauth_timestamp'])
return parameters
@ -229,8 +217,8 @@ class AccessTokenTests(TokenTests):
resource_owner_key=request_key,
resource_owner_secret=request_secret,
signature_method=oauth1.SIGNATURE_HMAC,
verifier=verifier,
timestamp=expires_at)
verifier=verifier)
self._validate_oauth_headers(req_headers['Authorization'],
oauth_client)

View File

@ -71,6 +71,15 @@ class RoleAssignmentsTests(utils.TestCase, utils.CrudTests):
self.assertEqual(len(ref_list), len(returned_list))
[self.assertIsInstance(r, self.model) for r in returned_list]
def test_list_by_id(self):
# It doesn't make sense to "list role assignments by ID" at all, given
# that they don't have globally unique IDs in the first place. But
# calling RoleAssignmentsManager.list(id=...) should still raise a
# TypeError when given an unexpected keyword argument 'id', so we don't
# actually have to modify the test in the superclass... I just wanted
# to make a note here in case the superclass changes.
super(RoleAssignmentsTests, self).test_list_by_id()
def test_list_params(self):
ref_list = self.TEST_USER_PROJECT_LIST
self.stub_entity('GET',

View File

@ -53,6 +53,10 @@ class TokenTests(utils.TestCase, testresources.ResourcedTestCase):
self.examples.v3_UUID_TOKEN_DEFAULT]
self.stub_url('GET', ['auth', 'tokens'],
headers={'X-Subject-Token': token_id, }, json=token_ref)
token_data = self.client.tokens.get_token_data(token_id)
self.assertEqual(token_data, token_ref)
access_info = self.client.tokens.validate(token_id)
self.assertRequestHeaderEqual('X-Subject-Token', token_id)
@ -77,6 +81,9 @@ class TokenTests(utils.TestCase, testresources.ResourcedTestCase):
# When the token is invalid the server typically returns a 404.
token_id = uuid.uuid4().hex
self.stub_url('GET', ['auth', 'tokens'], status_code=404)
self.assertRaises(exceptions.NotFound,
self.client.tokens.get_token_data, token_id)
self.assertRaises(exceptions.NotFound,
self.client.tokens.validate, token_id)
@ -87,6 +94,11 @@ class TokenTests(utils.TestCase, testresources.ResourcedTestCase):
self.examples.v3_UUID_TOKEN_DEFAULT]
self.stub_url('GET', ['auth', 'tokens'],
headers={'X-Subject-Token': token_id, }, json=token_ref)
token_data = self.client.tokens.get_token_data(token_id)
self.assertQueryStringIs()
self.assertIn('catalog', token_data['token'])
access_info = self.client.tokens.validate(token_id)
self.assertQueryStringIs()
@ -99,6 +111,11 @@ class TokenTests(utils.TestCase, testresources.ResourcedTestCase):
self.examples.v3_UUID_TOKEN_UNSCOPED]
self.stub_url('GET', ['auth', 'tokens'],
headers={'X-Subject-Token': token_id, }, json=token_ref)
token_data = self.client.tokens.get_token_data(token_id)
self.assertQueryStringIs()
self.assertNotIn('catalog', token_data['token'])
access_info = self.client.tokens.validate(token_id,
include_catalog=False)

View File

@ -129,9 +129,7 @@ class TestCase(UnauthenticatedTestCase):
def setUp(self):
super(TestCase, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
self.client = client.Client(token=self.TEST_TOKEN,
auth_url=self.TEST_URL,
endpoint=self.TEST_URL)
@ -245,6 +243,20 @@ class CrudTests(object):
return expected_path
def test_list_by_id(self, ref=None, **filter_kwargs):
"""Test ``entities.list(id=x)`` being rewritten as ``GET /v3/entities/x``.
This tests an edge case of each manager's list() implementation, to
ensure that it "does the right thing" when users call ``.list()``
when they should have used ``.get()``.
"""
if 'id' not in filter_kwargs:
ref = ref or self.new_ref()
filter_kwargs['id'] = ref['id']
self.assertRaises(TypeError, self.manager.list, **filter_kwargs)
def test_list(self, ref_list=None, expected_path=None,
expected_query=None, **filter_kwargs):
ref_list = ref_list or [self.new_ref(), self.new_ref()]

View File

@ -18,6 +18,7 @@ import logging
import sys
from oslo_utils import encodeutils
from oslo_utils import timeutils
import prettytable
import six
@ -336,3 +337,37 @@ class positional(object):
return func(*args, **kwargs)
return inner
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
def isotime(at=None, subsecond=False):
"""Stringify time in ISO 8601 format."""
# Python provides a similar instance method for datetime.datetime objects
# called isoformat(). The format of the strings generated by isoformat()
# have a couple of problems:
# 1) The strings generated by isotime are used in tokens and other public
# APIs that we can't change without a deprecation period. The strings
# generated by isoformat are not the same format, so we can't just
# change to it.
# 2) The strings generated by isoformat do not include the microseconds if
# the value happens to be 0. This will likely show up as random failures
# as parsers may be written to always expect microseconds, and it will
# parse correctly most of the time.
if not at:
at = timeutils.utcnow()
st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
st += ('Z' if tz == 'UTC' else tz)
return st
def strtime(at=None):
at = at or timeutils.utcnow()
return at.strftime(timeutils.PERFECT_TIME_FORMAT)

View File

@ -15,7 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
This module is pending deprecation in favor of python-openstackclient.
This module is deprecated as of the 1.7.0 release in favor of
python-openstackclient and may be removed in the 2.0.0 release.
Bug fixes are welcome, but new features should be exposed to the CLI by
python-openstackclient after being added to the python-keystoneclient library.

View File

@ -84,6 +84,17 @@ class TokenManager(base.Manager):
"""
return self._get('/tokens/%s' % base.getid(token), 'access')
def get_token_data(self, token):
"""Fetch the data about a token from the identity server.
:param str token: The token id.
:rtype: dict
"""
url = '/tokens/%s' % token
resp, body = self.client.get(url)
return body
def validate_access_info(self, token):
"""Validate a token.
@ -100,10 +111,9 @@ class TokenManager(base.Manager):
return token.auth_token
return base.getid(token)
url = '/tokens/%s' % calc_id(token)
resp, body = self.client.get(url)
access_info = access.AccessInfo.factory(resp=resp, body=body)
return access_info
token_id = calc_id(token)
body = self.get_token_data(token_id)
return access.AccessInfo.factory(auth_token=token_id, body=body)
def get_revoked(self):
"""Returns the revoked tokens response.

View File

@ -75,7 +75,8 @@ class UserManager(base.ManagerWithFind):
params = {"user": {"password": passwd,
"original_password": origpasswd}}
return self._update("/OS-KSCRUD/users/%s" % self.api.user_id, params,
return self._update("/OS-KSCRUD/users/%s" % self.client.user_id,
params,
response_key="access",
method="PATCH",
endpoint_filter={'interface': 'public'},

81
keystoneclient/v3/auth.py Normal file
View File

@ -0,0 +1,81 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient import auth
from keystoneclient import base
from keystoneclient import exceptions
class Project(base.Resource):
"""Represents an Identity project.
Attributes:
* id: a uuid that identifies the project
* name: project name
* description: project description
* enabled: boolean to indicate if project is enabled
* parent_id: a uuid representing this project's parent in hierarchy
* parents: a list or a structured dict containing the parents of this
project in the hierarchy
* subtree: a list or a structured dict containing the subtree of this
project in the hierarchy
"""
class Domain(base.Resource):
"""Represents an Identity domain.
Attributes:
* id: a uuid that identifies the domain
"""
pass
class AuthManager(base.Manager):
"""Retrieve auth context specific information.
The information returned by the /auth routes are entirely dependant on the
authentication information provided by the user.
"""
_PROJECTS_URL = '/auth/projects'
_DOMAINS_URL = '/auth/domains'
def projects(self):
"""List projects that this token can be rescoped to.
"""
try:
return self._list(self._PROJECTS_URL,
'projects',
obj_class=Project)
except exceptions.EndpointNotFound:
endpoint_filter = {'interface': auth.AUTH_INTERFACE}
return self._list(self._PROJECTS_URL,
'projects',
obj_class=Project,
endpoint_filter=endpoint_filter)
def domains(self):
"""List Domains that this token can be rescoped to.
"""
try:
return self._list(self._DOMAINS_URL,
'domains',
obj_class=Domain)
except exceptions.EndpointNotFound:
endpoint_filter = {'interface': auth.AUTH_INTERFACE}
return self._list(self._DOMAINS_URL,
'domains',
obj_class=Domain,
endpoint_filter=endpoint_filter)

View File

@ -21,6 +21,7 @@ from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient import exceptions
from keystoneclient import httpclient
from keystoneclient.i18n import _
from keystoneclient.v3 import auth
from keystoneclient.v3.contrib import endpoint_filter
from keystoneclient.v3.contrib import endpoint_policy
from keystoneclient.v3.contrib import federation
@ -65,11 +66,13 @@ class Client(httpclient.HTTPClient):
:param string project_domain_name: Project's domain name for project
scoping. (optional)
:param string tenant_name: Tenant name. (optional)
The tenant_name keyword argument is deprecated,
use project_name instead.
The tenant_name keyword argument is deprecated
as of the 1.7.0 release in favor of project_name
and may be removed in the 2.0.0 release.
:param string tenant_id: Tenant id. (optional)
The tenant_id keyword argument is deprecated,
use project_id instead.
The tenant_id keyword argument is deprecated as of
the 1.7.0 release in favor of project_id and may
be removed in the 2.0.0 release.
: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.
@ -179,6 +182,7 @@ EndpointPolicyManager`
"""Initialize a new client for the Keystone v3 API."""
super(Client, self).__init__(**kwargs)
self.auth = auth.AuthManager(self._adapter)
self.credentials = credentials.CredentialManager(self._adapter)
self.ec2 = ec2.EC2Manager(self._adapter)
self.endpoint_filter = endpoint_filter.EndpointFilterManager(

View File

@ -40,7 +40,8 @@ class AccessTokenManager(base.CrudManager):
resource_owner_secret=request_secret,
signature_method=oauth1.SIGNATURE_HMAC,
verifier=verifier)
url = self.api.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip('/')
url = self.client.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip(
'/')
url, headers, body = oauth_client.sign(url + endpoint,
http_method='POST')
resp, body = self.client.post(endpoint, headers=headers)

View File

@ -63,7 +63,8 @@ class RequestTokenManager(base.CrudManager):
client_secret=consumer_secret,
signature_method=oauth1.SIGNATURE_HMAC,
callback_uri="oob")
url = self.api.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip("/")
url = self.client.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip(
"/")
url, headers, body = oauth_client.sign(url + endpoint,
http_method='POST',
headers=headers)

View File

@ -10,11 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import timeutils
from keystoneclient import base
from keystoneclient import exceptions
from keystoneclient.i18n import _
from keystoneclient import utils
class Trust(base.Resource):
@ -61,7 +60,7 @@ class TrustManager(base.CrudManager):
# Convert datetime.datetime expires_at to iso format string
if expires_at:
expires_str = timeutils.isotime(at=expires_at, subsecond=True)
expires_str = utils.isotime(at=expires_at, subsecond=True)
else:
expires_str = None

View File

@ -51,6 +51,25 @@ class TokenManager(object):
resp, body = self._client.get('/auth/tokens/OS-PKI/revoked')
return body
@utils.positional.method(1)
def get_token_data(self, token, include_catalog=True):
"""Fetch the data about a token from the identity server.
:param str token: The token id.
:param bool include_catalog: If False, the response is requested to not
include the catalog.
:rtype: dict
"""
headers = {'X-Subject-Token': token}
url = '/auth/tokens'
if not include_catalog:
url += '?nocatalog'
resp, body = self._client.get(url, headers=headers)
return body
@utils.positional.method(1)
def validate(self, token, include_catalog=True):
"""Validate a token.
@ -66,13 +85,5 @@ class TokenManager(object):
"""
token_id = _calc_id(token)
headers = {'X-Subject-Token': token_id}
url = '/auth/tokens'
if not include_catalog:
url += '?nocatalog'
resp, body = self._client.get(url, headers=headers)
access_info = access.AccessInfo.factory(resp=resp, body=body)
return access_info
body = self.get_token_data(token_id, include_catalog=include_catalog)
return access.AccessInfo.factory(auth_token=token_id, body=body)

View File

@ -156,7 +156,7 @@ class UserManager(base.CrudManager):
params = {'user': {'password': new_password,
'original_password': old_password}}
base_url = '/users/%s/password' % self.api.user_id
base_url = '/users/%s/password' % self.client.user_id
return self._update(base_url, params, method='POST', log=False,
endpoint_filter={'interface': 'public'})

View File

@ -2,16 +2,17 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr<2.0,>=0.11
pbr<2.0,>=1.3
argparse
Babel>=1.3
keystoneauth1 >= 0.3.0
iso8601>=0.1.9
debtcollector>=0.3.0 # Apache-2.0
oslo.config>=1.11.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0
oslo.utils>=1.6.0 # Apache-2.0
oslo.utils>=1.9.0 # Apache-2.0
PrettyTable<0.8,>=0.7
requests>=2.5.2
six>=1.9.0

View File

@ -17,7 +17,7 @@ classifier =
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
[files]
packages =

View File

@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
setup_requires=['pbr>=1.3'],
pbr=True)

View File

@ -6,17 +6,16 @@ hacking<0.11,>=0.10.0
coverage>=3.6
discover
fixtures>=0.3.14
fixtures>=1.3.1
keyring!=3.3,>=2.1
lxml>=2.3
mock>=1.1;python_version!='2.6'
mock==1.0.1;python_version=='2.6'
mock>=1.2
oauthlib>=0.6
oslosphinx>=2.5.0 # Apache-2.0
oslotest>=1.5.1 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
requests-mock>=0.6.0 # Apache-2.0
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
tempest-lib>=0.5.0
tempest-lib>=0.6.1
testrepository>=0.0.18
testresources>=0.2.4
testtools>=1.4.0

View File

@ -1,7 +1,7 @@
[tox]
minversion = 1.6
skipsdist = True
envlist = py26,py27,py33,py34,pep8,bandit
envlist = py26,py27,py34,pep8,bandit
[testenv]
usedevelop = True