summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrant Knudson <bknudson@us.ibm.com>2015-08-06 09:37:43 -0500
committerBrant Knudson <bknudson@us.ibm.com>2015-08-06 09:37:43 -0500
commit4e498a54d0034b2ce5c87130f080ff580d241600 (patch)
treed21d11091373e80bf1a4f70ecf1ee54ff82e0ff8
parentd5c5423d6de3710e3480e47062333b33e8de0713 (diff)
parenteae8e83f5a7a170b98ef2d74a4ffd9eac7cc47ba (diff)
downloadpython-keystoneclient-feature/keystoneauth_integration.tar.gz
Merge remote-tracking branch 'origin/master' into merge-branchfeature/keystoneauth_integration
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
-rw-r--r--README.rst207
-rw-r--r--doc/source/conf.py2
-rw-r--r--doc/source/images/graphs_authComp.svg48
-rw-r--r--doc/source/images/graphs_authCompDelegate.svg53
-rw-r--r--keystoneclient/_discover.py3
-rw-r--r--keystoneclient/access.py77
-rw-r--r--keystoneclient/apiclient/__init__.py18
-rw-r--r--keystoneclient/apiclient/exceptions.py11
-rw-r--r--keystoneclient/auth/identity/base.py107
-rw-r--r--keystoneclient/auth/identity/generic/base.py10
-rw-r--r--keystoneclient/auth/identity/v2.py48
-rw-r--r--keystoneclient/auth/identity/v3/base.py12
-rw-r--r--keystoneclient/base.py116
-rw-r--r--keystoneclient/client.py14
-rw-r--r--keystoneclient/common/cms.py13
-rw-r--r--keystoneclient/contrib/auth/v3/oidc.py24
-rw-r--r--keystoneclient/contrib/auth/v3/saml2.py44
-rw-r--r--keystoneclient/contrib/revoke/model.py11
-rw-r--r--keystoneclient/discover.py20
-rw-r--r--keystoneclient/exceptions.py30
-rw-r--r--keystoneclient/generic/client.py12
-rw-r--r--keystoneclient/httpclient.py156
-rw-r--r--keystoneclient/openstack/common/apiclient/__init__.py22
-rw-r--r--keystoneclient/openstack/common/apiclient/base.py102
-rw-r--r--keystoneclient/openstack/common/apiclient/exceptions.py496
-rw-r--r--keystoneclient/session.py34
-rw-r--r--keystoneclient/tests/unit/auth/test_identity_v3.py3
-rw-r--r--keystoneclient/tests/unit/client_fixtures.py24
-rw-r--r--keystoneclient/tests/unit/test_base.py11
-rw-r--r--keystoneclient/tests/unit/test_discovery.py6
-rw-r--r--keystoneclient/tests/unit/test_ec2utils.py2
-rw-r--r--keystoneclient/tests/unit/test_hacking_checks.py3
-rw-r--r--keystoneclient/tests/unit/test_http.py19
-rw-r--r--keystoneclient/tests/unit/test_https.py13
-rw-r--r--keystoneclient/tests/unit/test_keyring.py15
-rw-r--r--keystoneclient/tests/unit/utils.py9
-rw-r--r--keystoneclient/tests/unit/v2_0/test_access.py23
-rw-r--r--keystoneclient/tests/unit/v2_0/test_auth.py26
-rw-r--r--keystoneclient/tests/unit/v2_0/test_client.py27
-rw-r--r--keystoneclient/tests/unit/v2_0/test_tokens.py6
-rw-r--r--keystoneclient/tests/unit/v2_0/utils.py4
-rw-r--r--keystoneclient/tests/unit/v3/client_fixtures.py86
-rw-r--r--keystoneclient/tests/unit/v3/test_access.py91
-rw-r--r--keystoneclient/tests/unit/v3/test_auth.py14
-rw-r--r--keystoneclient/tests/unit/v3/test_auth_manager.py72
-rw-r--r--keystoneclient/tests/unit/v3/test_client.py100
-rw-r--r--keystoneclient/tests/unit/v3/test_domains.py6
-rw-r--r--keystoneclient/tests/unit/v3/test_federation.py10
-rw-r--r--keystoneclient/tests/unit/v3/test_oauth1.py24
-rw-r--r--keystoneclient/tests/unit/v3/test_role_assignments.py9
-rw-r--r--keystoneclient/tests/unit/v3/test_tokens.py17
-rw-r--r--keystoneclient/tests/unit/v3/utils.py18
-rw-r--r--keystoneclient/utils.py35
-rwxr-xr-xkeystoneclient/v2_0/shell.py3
-rw-r--r--keystoneclient/v2_0/tokens.py18
-rw-r--r--keystoneclient/v2_0/users.py3
-rw-r--r--keystoneclient/v3/auth.py81
-rw-r--r--keystoneclient/v3/client.py12
-rw-r--r--keystoneclient/v3/contrib/oauth1/access_tokens.py3
-rw-r--r--keystoneclient/v3/contrib/oauth1/request_tokens.py3
-rw-r--r--keystoneclient/v3/contrib/trusts.py5
-rw-r--r--keystoneclient/v3/tokens.py31
-rw-r--r--keystoneclient/v3/users.py2
-rw-r--r--requirements.txt5
-rw-r--r--setup.cfg2
-rw-r--r--setup.py2
-rw-r--r--test-requirements.txt9
-rw-r--r--tox.ini2
68 files changed, 1329 insertions, 1215 deletions
diff --git a/README.rst b/README.rst
index 72d85ee..2a86690 100644
--- a/README.rst
+++ b/README.rst
@@ -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``).
-
-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>`_.
-
-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.
+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`_.
+
+* `PyPi`_ - package installation
+* `Online Documentation`_
+* `Launchpad project`_ - release management
+* `Blueprints`_ - feature specifications
+* `Bugs`_ - issue tracking
+* `Source`_
+* `Specs`_
+* `How to Contribute`_
+
+.. _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.
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 4e238aa..593d7e2 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -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'
diff --git a/doc/source/images/graphs_authComp.svg b/doc/source/images/graphs_authComp.svg
deleted file mode 100644
index 6be629c..0000000
--- a/doc/source/images/graphs_authComp.svg
+++ /dev/null
@@ -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>
diff --git a/doc/source/images/graphs_authCompDelegate.svg b/doc/source/images/graphs_authCompDelegate.svg
deleted file mode 100644
index 4788829..0000000
--- a/doc/source/images/graphs_authCompDelegate.svg
+++ /dev/null
@@ -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>
diff --git a/keystoneclient/_discover.py b/keystoneclient/_discover.py
index 07d0ae6..8d6889d 100644
--- a/keystoneclient/_discover.py
+++ b/keystoneclient/_discover.py
@@ -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 = []
diff --git a/keystoneclient/access.py b/keystoneclient/access.py
index 009b72e..f308a58 100644
--- a/keystoneclient/access.py
+++ b/keystoneclient/access.py
@@ -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',
diff --git a/keystoneclient/apiclient/__init__.py b/keystoneclient/apiclient/__init__.py
index 344b661..29b9331 100644
--- a/keystoneclient/apiclient/__init__.py
+++ b/keystoneclient/apiclient/__init__.py
@@ -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',
diff --git a/keystoneclient/apiclient/exceptions.py b/keystoneclient/apiclient/exceptions.py
index 9828208..99d9ec2 100644
--- a/keystoneclient/apiclient/exceptions.py
+++ b/keystoneclient/apiclient/exceptions.py
@@ -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')
diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py
index f785192..57e1723 100644
--- a/keystoneclient/auth/identity/base.py
+++ b/keystoneclient/auth/identity/base.py
@@ -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):
diff --git a/keystoneclient/auth/identity/generic/base.py b/keystoneclient/auth/identity/generic/base.py
index 0b87d06..d366707 100644
--- a/keystoneclient/auth/identity/generic/base.py
+++ b/keystoneclient/auth/identity/generic/base.py
@@ -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.
diff --git a/keystoneclient/auth/identity/v2.py b/keystoneclient/auth/identity/v2.py
index 3ea74b7..fd8b422 100644
--- a/keystoneclient/auth/identity/v2.py
+++ b/keystoneclient/auth/identity/v2.py
@@ -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:
diff --git a/keystoneclient/auth/identity/v3/base.py b/keystoneclient/auth/identity/v3/base.py
index 9d1f562..784bd96 100644
--- a/keystoneclient/auth/identity/v3/base.py
+++ b/keystoneclient/auth/identity/v3/base.py
@@ -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
@@ -69,6 +69,16 @@ class BaseAuth(base.BaseIdentityPlugin):
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."""
return '%s/auth/tokens' % self.auth_url.rstrip('/')
diff --git a/keystoneclient/base.py b/keystoneclient/base.py
index 025362b..f19ed84 100644
--- a/keystoneclient/base.py
+++ b/keystoneclient/base.py
@@ -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)
diff --git a/keystoneclient/client.py b/keystoneclient/client.py
index f4b9f87..f18db53 100644
--- a/keystoneclient/client.py
+++ b/keystoneclient/client.py
@@ -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):
diff --git a/keystoneclient/common/cms.py b/keystoneclient/common/cms.py
index 68af1dd..c5b35da 100644
--- a/keystoneclient/common/cms.py
+++ b/keystoneclient/common/cms.py
@@ -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)
diff --git a/keystoneclient/contrib/auth/v3/oidc.py b/keystoneclient/contrib/auth/v3/oidc.py
index 6105e06..0c94519 100644
--- a/keystoneclient/contrib/auth/v3/oidc.py
+++ b/keystoneclient/contrib/auth/v3/oidc.py
@@ -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.
diff --git a/keystoneclient/contrib/auth/v3/saml2.py b/keystoneclient/contrib/auth/v3/saml2.py
index f3bb105..fc85f49 100644
--- a/keystoneclient/contrib/auth/v3/saml2.py
+++ b/keystoneclient/contrib/auth/v3/saml2.py
@@ -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):
diff --git a/keystoneclient/contrib/revoke/model.py b/keystoneclient/contrib/revoke/model.py
index bb62840..5c17680 100644
--- a/keystoneclient/contrib/revoke/model.py
+++ b/keystoneclient/contrib/revoke/model.py
@@ -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):
diff --git a/keystoneclient/discover.py b/keystoneclient/discover.py
index 40fd85d..ff0db6d 100644
--- a/keystoneclient/discover.py
+++ b/keystoneclient/discover.py
@@ -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.
diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py
index d01ff74..e140a5e 100644
--- a/keystoneclient/exceptions.py
+++ b/keystoneclient/exceptions.py
@@ -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
diff --git a/keystoneclient/generic/client.py b/keystoneclient/generic/client.py
index 7ca39fb..f61edec 100644
--- a/keystoneclient/generic/client.py
+++ b/keystoneclient/generic/client.py
@@ -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)
diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py
index e1ee4c6..c663535 100644
--- a/keystoneclient/httpclient.py
+++ b/keystoneclient/httpclient.py
@@ -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)
diff --git a/keystoneclient/openstack/common/apiclient/__init__.py b/keystoneclient/openstack/common/apiclient/__init__.py
index e69de29..6482e36 100644
--- a/keystoneclient/openstack/common/apiclient/__init__.py
+++ b/keystoneclient/openstack/common/apiclient/__init__.py
@@ -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')
diff --git a/keystoneclient/openstack/common/apiclient/base.py b/keystoneclient/openstack/common/apiclient/base.py
index 9300c2e..5612c22 100644
--- a/keystoneclient/openstack/common/apiclient/base.py
+++ b/keystoneclient/openstack/common/apiclient/base.py
@@ -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
diff --git a/keystoneclient/openstack/common/apiclient/exceptions.py b/keystoneclient/openstack/common/apiclient/exceptions.py
index a44492d..b1721b4 100644
--- a/keystoneclient/openstack/common/apiclient/exceptions.py
+++ b/keystoneclient/openstack/common/apiclient/exceptions.py
@@ -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
diff --git a/keystoneclient/session.py b/keystoneclient/session.py
index 14edf46..cace4f7 100644
--- a/keystoneclient/session.py
+++ b/keystoneclient/session.py
@@ -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)
diff --git a/keystoneclient/tests/unit/auth/test_identity_v3.py b/keystoneclient/tests/unit/auth/test_identity_v3.py
index 99062b3..8c23807 100644
--- a/keystoneclient/tests/unit/auth/test_identity_v3.py
+++ b/keystoneclient/tests/unit/auth/test_identity_v3.py
@@ -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']
diff --git a/keystoneclient/tests/unit/client_fixtures.py b/keystoneclient/tests/unit/client_fixtures.py
index b226e32..46266ce 100644
--- a/keystoneclient/tests/unit/client_fixtures.py
+++ b/keystoneclient/tests/unit/client_fixtures.py
@@ -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\\.')
diff --git a/keystoneclient/tests/unit/test_base.py b/keystoneclient/tests/unit/test_base.py
index dcfbb13..115a35c 100644
--- a/keystoneclient/tests/unit/test_base.py
+++ b/keystoneclient/tests/unit/test_base.py
@@ -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(
diff --git a/keystoneclient/tests/unit/test_discovery.py b/keystoneclient/tests/unit/test_discovery.py
index 76aaf03..34901ba 100644
--- a/keystoneclient/tests/unit/test_discovery.py
+++ b/keystoneclient/tests/unit/test_discovery.py
@@ -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])
diff --git a/keystoneclient/tests/unit/test_ec2utils.py b/keystoneclient/tests/unit/test_ec2utils.py
index 71fc176..f74eb2f 100644
--- a/keystoneclient/tests/unit/test_ec2utils.py
+++ b/keystoneclient/tests/unit/test_ec2utils.py
@@ -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)
diff --git a/keystoneclient/tests/unit/test_hacking_checks.py b/keystoneclient/tests/unit/test_hacking_checks.py
index 220d258..2e4cc1d 100644
--- a/keystoneclient/tests/unit/test_hacking_checks.py
+++ b/keystoneclient/tests/unit/test_hacking_checks.py
@@ -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.
diff --git a/keystoneclient/tests/unit/test_http.py b/keystoneclient/tests/unit/test_http.py
index 436c374..2b29ee7 100644
--- a/keystoneclient/tests/unit/test_http.py
+++ b/keystoneclient/tests/unit/test_http.py
@@ -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'
diff --git a/keystoneclient/tests/unit/test_https.py b/keystoneclient/tests/unit/test_https.py
index ad52de9..607ab9e 100644
--- a/keystoneclient/tests/unit/test_https.py
+++ b/keystoneclient/tests/unit/test_https.py
@@ -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
diff --git a/keystoneclient/tests/unit/test_keyring.py b/keystoneclient/tests/unit/test_keyring.py
index a54009e..defd233 100644
--- a/keystoneclient/tests/unit/test_keyring.py
+++ b/keystoneclient/tests/unit/test_keyring.py
@@ -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
diff --git a/keystoneclient/tests/unit/utils.py b/keystoneclient/tests/unit/utils.py
index 2274519..7c6de95 100644
--- a/keystoneclient/tests/unit/utils.py
+++ b/keystoneclient/tests/unit/utils.py
@@ -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):
diff --git a/keystoneclient/tests/unit/v2_0/test_access.py b/keystoneclient/tests/unit/v2_0/test_access.py
index e966874..b768150 100644
--- a/keystoneclient/tests/unit/v2_0/test_access.py
+++ b/keystoneclient/tests/unit/v2_0/test_access.py
@@ -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[
diff --git a/keystoneclient/tests/unit/v2_0/test_auth.py b/keystoneclient/tests/unit/v2_0/test_auth.py
index 2c69dc3..5f318ff 100644
--- a/keystoneclient/tests/unit/v2_0/test_auth.py
+++ b/keystoneclient/tests/unit/v2_0/test_auth.py
@@ -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')
diff --git a/keystoneclient/tests/unit/v2_0/test_client.py b/keystoneclient/tests/unit/v2_0/test_client.py
index 379bea4..447c750 100644
--- a/keystoneclient/tests/unit/v2_0/test_client.py
+++ b/keystoneclient/tests/unit/v2_0/test_client.py
@@ -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):
diff --git a/keystoneclient/tests/unit/v2_0/test_tokens.py b/keystoneclient/tests/unit/v2_0/test_tokens.py
index 8a40f82..d60f0f8 100644
--- a/keystoneclient/tests/unit/v2_0/test_tokens.py
+++ b/keystoneclient/tests/unit/v2_0/test_tokens.py
@@ -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_)
diff --git a/keystoneclient/tests/unit/v2_0/utils.py b/keystoneclient/tests/unit/v2_0/utils.py
index 475181f..191e8db 100644
--- a/keystoneclient/tests/unit/v2_0/utils.py
+++ b/keystoneclient/tests/unit/v2_0/utils.py
@@ -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)
diff --git a/keystoneclient/tests/unit/v3/client_fixtures.py b/keystoneclient/tests/unit/v3/client_fixtures.py
index 99e49f0..56eaf7d 100644
--- a/keystoneclient/tests/unit/v3/client_fixtures.py
+++ b/keystoneclient/tests/unit/v3/client_fixtures.py
@@ -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 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)
-
- f.add_role(id='76e72a', name='admin')
- f.add_role(id='f4f392', name='member')
+def unscoped_token(**kwargs):
+ return fixture.V3Token(**kwargs)
+
+
+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(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.add_role(id='76e72a', name='admin')
- f.add_role(id='f4f392', name='member')
+ f = fixture.V3Token(audit_chain_id=uuid.uuid4().hex)
+ f.set_project_scope()
+
+ 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
diff --git a/keystoneclient/tests/unit/v3/test_access.py b/keystoneclient/tests/unit/v3/test_access.py
index f069f71..82ed0fb 100644
--- a/keystoneclient/tests/unit/v3/test_access.py
+++ b/keystoneclient/tests/unit/v3/test_access.py
@@ -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',))
-
- self.assertEqual(auth_ref.project_domain_id,
- '4e6893b7ba0b4006840c3845660b86ed')
- self.assertEqual(auth_ref.project_domain_name, 'exampledomain')
-
- self.assertEqual(auth_ref.user_domain_id,
- '4e6893b7ba0b4006840c3845660b86ed')
- self.assertEqual(auth_ref.user_domain_name, 'exampledomain')
+ 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(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(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)
diff --git a/keystoneclient/tests/unit/v3/test_auth.py b/keystoneclient/tests/unit/v3/test_auth.py
index b3f29d6..8352528 100644
--- a/keystoneclient/tests/unit/v3/test_auth.py
+++ b/keystoneclient/tests/unit/v3/test_auth.py
@@ -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')
diff --git a/keystoneclient/tests/unit/v3/test_auth_manager.py b/keystoneclient/tests/unit/v3/test_auth_manager.py
new file mode 100644
index 0000000..68f00c6
--- /dev/null
+++ b/keystoneclient/tests/unit/v3/test_auth_manager.py
@@ -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)
diff --git a/keystoneclient/tests/unit/v3/test_client.py b/keystoneclient/tests/unit/v3/test_client.py
index c01cac2..6e1c06e 100644
--- a/keystoneclient/tests/unit/v3/test_client.py
+++ b/keystoneclient/tests/unit/v3/test_client.py
@@ -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'),
diff --git a/keystoneclient/tests/unit/v3/test_domains.py b/keystoneclient/tests/unit/v3/test_domains.py
index 9cc23e7..4dbfd73 100644
--- a/keystoneclient/tests/unit/v3/test_domains.py
+++ b/keystoneclient/tests/unit/v3/test_domains.py
@@ -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')
diff --git a/keystoneclient/tests/unit/v3/test_federation.py b/keystoneclient/tests/unit/v3/test_federation.py
index 2c209cd..7b7f19d 100644
--- a/keystoneclient/tests/unit/v3/test_federation.py
+++ b/keystoneclient/tests/unit/v3/test_federation.py
@@ -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}
diff --git a/keystoneclient/tests/unit/v3/test_oauth1.py b/keystoneclient/tests/unit/v3/test_oauth1.py
index b52a759..2ebfa50 100644
--- a/keystoneclient/tests/unit/v3/test_oauth1.py
+++ b/keystoneclient/tests/unit/v3/test_oauth1.py
@@ -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)
diff --git a/keystoneclient/tests/unit/v3/test_role_assignments.py b/keystoneclient/tests/unit/v3/test_role_assignments.py
index 79d2585..e77bdcc 100644
--- a/keystoneclient/tests/unit/v3/test_role_assignments.py
+++ b/keystoneclient/tests/unit/v3/test_role_assignments.py
@@ -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',
diff --git a/keystoneclient/tests/unit/v3/test_tokens.py b/keystoneclient/tests/unit/v3/test_tokens.py
index 2c27fd0..0363a61 100644
--- a/keystoneclient/tests/unit/v3/test_tokens.py
+++ b/keystoneclient/tests/unit/v3/test_tokens.py
@@ -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)
diff --git a/keystoneclient/tests/unit/v3/utils.py b/keystoneclient/tests/unit/v3/utils.py
index 7f2d633..442c3a9 100644
--- a/keystoneclient/tests/unit/v3/utils.py
+++ b/keystoneclient/tests/unit/v3/utils.py
@@ -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()]
diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py
index 300ca54..5017431 100644
--- a/keystoneclient/utils.py
+++ b/keystoneclient/utils.py
@@ -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)
diff --git a/keystoneclient/v2_0/shell.py b/keystoneclient/v2_0/shell.py
index 9d7d7ce..b2cb68c 100755
--- a/keystoneclient/v2_0/shell.py
+++ b/keystoneclient/v2_0/shell.py
@@ -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.
diff --git a/keystoneclient/v2_0/tokens.py b/keystoneclient/v2_0/tokens.py
index 670d65b..1874b48 100644
--- a/keystoneclient/v2_0/tokens.py
+++ b/keystoneclient/v2_0/tokens.py
@@ -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.
diff --git a/keystoneclient/v2_0/users.py b/keystoneclient/v2_0/users.py
index 11e06f3..ccbaf2c 100644
--- a/keystoneclient/v2_0/users.py
+++ b/keystoneclient/v2_0/users.py
@@ -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'},
diff --git a/keystoneclient/v3/auth.py b/keystoneclient/v3/auth.py
new file mode 100644
index 0000000..8f26d3a
--- /dev/null
+++ b/keystoneclient/v3/auth.py
@@ -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)
diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py
index 34bdfad..3d37e3c 100644
--- a/keystoneclient/v3/client.py
+++ b/keystoneclient/v3/client.py
@@ -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(
diff --git a/keystoneclient/v3/contrib/oauth1/access_tokens.py b/keystoneclient/v3/contrib/oauth1/access_tokens.py
index 12b0c6b..d45bf3f 100644
--- a/keystoneclient/v3/contrib/oauth1/access_tokens.py
+++ b/keystoneclient/v3/contrib/oauth1/access_tokens.py
@@ -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)
diff --git a/keystoneclient/v3/contrib/oauth1/request_tokens.py b/keystoneclient/v3/contrib/oauth1/request_tokens.py
index 33ecc3a..27f79c1 100644
--- a/keystoneclient/v3/contrib/oauth1/request_tokens.py
+++ b/keystoneclient/v3/contrib/oauth1/request_tokens.py
@@ -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)
diff --git a/keystoneclient/v3/contrib/trusts.py b/keystoneclient/v3/contrib/trusts.py
index 5fe88f8..1b3033c 100644
--- a/keystoneclient/v3/contrib/trusts.py
+++ b/keystoneclient/v3/contrib/trusts.py
@@ -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
diff --git a/keystoneclient/v3/tokens.py b/keystoneclient/v3/tokens.py
index 77edbc0..38f4e9f 100644
--- a/keystoneclient/v3/tokens.py
+++ b/keystoneclient/v3/tokens.py
@@ -52,6 +52,25 @@ class TokenManager(object):
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)
diff --git a/keystoneclient/v3/users.py b/keystoneclient/v3/users.py
index 2e20ede..35c42cc 100644
--- a/keystoneclient/v3/users.py
+++ b/keystoneclient/v3/users.py
@@ -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'})
diff --git a/requirements.txt b/requirements.txt
index 8715aeb..4672307 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
diff --git a/setup.cfg b/setup.cfg
index e7aa998..bdd7fe6 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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 =
diff --git a/setup.py b/setup.py
index 056c16c..d8080d0 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
- setup_requires=['pbr'],
+ setup_requires=['pbr>=1.3'],
pbr=True)
diff --git a/test-requirements.txt b/test-requirements.txt
index 600a35b..6ac4dcf 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -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
diff --git a/tox.ini b/tox.ini
index 515c542..74f648c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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