From 831ba037b0ba2077e168cb33976c78e565aece88 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Mon, 30 Mar 2015 17:16:25 +1100 Subject: Support /auth routes for list projects and domains The /auth routes are the preferred mechanism for listing the projects and domains that the current token can be authenticated to as they supports both federated and regular tokens. Expose these routes via the client so that they can be consumed. Change-Id: I9724a648ebd9d21edf8ffcc64f4cdb897a99101c --- keystoneclient/tests/unit/v3/test_auth_manager.py | 72 ++++++++++++++++++++ keystoneclient/v3/auth.py | 81 +++++++++++++++++++++++ keystoneclient/v3/client.py | 2 + 3 files changed, 155 insertions(+) create mode 100644 keystoneclient/tests/unit/v3/test_auth_manager.py create mode 100644 keystoneclient/v3/auth.py 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/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 f7becbb..a0072fa 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 @@ -174,6 +175,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.endpoint_filter = endpoint_filter.EndpointFilterManager( self._adapter) -- cgit v1.2.1 From a951023f7cfbebebaafd89c84d301dc7efe8fb76 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 28 Jun 2015 05:49:46 +0000 Subject: Remove unused images from docs These images were used to show how auth_token worked, these images are now shown on keystonemiddleware's docs, so lets remove them from here. Change-Id: I2e882d3580737ee091a5e05cf98b0d652f3fefcb --- doc/source/images/graphs_authComp.svg | 48 ------------------------ doc/source/images/graphs_authCompDelegate.svg | 53 --------------------------- 2 files changed, 101 deletions(-) delete mode 100644 doc/source/images/graphs_authComp.svg delete mode 100644 doc/source/images/graphs_authCompDelegate.svg 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 @@ - - - - - - -AuthComp - - -AuthComp - -Auth -Component - - - -AuthComp->Reject - - -Reject -Unauthenticated -Requests - - -Service - -OpenStack -Service - - -AuthComp->Service - - -Forward -Authenticated -Requests - - - -Start->AuthComp - - - - - 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 @@ - - - - - - -AuthCompDelegate - - -AuthComp - -Auth -Component - - - -AuthComp->Reject - - -Reject Requests -Indicated by the Service - - -Service - -OpenStack -Service - - -AuthComp->Service - - -Forward Requests -with Identiy Status - - -Service->AuthComp - - -Send Response OR -Reject Message - - - -Start->AuthComp - - - - - -- cgit v1.2.1 From 20db11f8bdf0bc9b3ce23c21bf67753ca5690372 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Wed, 29 Apr 2015 12:27:30 -0400 Subject: Update README.rst and remove ancient reference There's no need to give a shout out to a project that is going on 6 years old and has been deprecated. Also provide more valuable links, such as where we track bugs, our source code, and docs. Change-Id: I9ea5ca83366f9dc0b2732c5db017257a1250fc05 --- README.rst | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 72d85ee..70cebfa 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 `_. The master -repository is in `Git `_. - -This code is a fork of Rackspace's python-novaclient which is in turn a fork of -`Jacobian's 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: -- cgit v1.2.1 From ef0f2677c2efbba709859785415ec1cc1bf583cc Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 28 Jun 2015 05:47:32 +0000 Subject: Remove keystoneclient CLI references in README The content of this file determines what it shown in pypi, which many end users see. Highlighting our soon to be deprecated CLI as a feature for python-keystoneclient seems illogical. Change-Id: Ia756353f0c58fac245be2e2daaa63ca7831478d1 --- README.rst | 172 ------------------------------------------------------------- 1 file changed, 172 deletions(-) diff --git a/README.rst b/README.rst index 70cebfa..2a86690 100644 --- a/README.rst +++ b/README.rst @@ -39,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 ] - [--os-username ] - [--os-password ] - [--os-tenant-name ] - [--os-tenant-id ] [--os-auth-url ] - [--os-region-name ] - [--os-identity-api-version ] - [--os-token ] - [--os-endpoint ] - [--os-cacert ] [--insecure] - [--os-cert ] [--os-key ] [--os-cache] - [--force-new-token] [--stale-duration ] - ... - - Command-line interface to the OpenStack Identity API. - - Positional arguments: - - 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 Set request timeout (in seconds) - --os-username - Name used for authentication with the OpenStack - Identity service. Defaults to env[OS_USERNAME] - --os-password - Password used for authentication with the OpenStack - Identity service. Defaults to env[OS_PASSWORD] - --os-tenant-name - Tenant to request authorization on. Defaults to - env[OS_TENANT_NAME] - --os-tenant-id - Tenant to request authorization on. Defaults to - env[OS_TENANT_ID] - --os-auth-url - Specify the Identity endpoint to use for - authentication. Defaults to env[OS_AUTH_URL] - --os-region-name - Defaults to env[OS_REGION_NAME] - --os-identity-api-version - Defaults to env[OS_IDENTITY_API_VERSION] or 2.0 - --os-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 - Specify an endpoint to use instead of retrieving one - from the service catalog (via authentication). - Defaults to env[OS_SERVICE_ENDPOINT] - --os-cacert - 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 - Defaults to env[OS_CERT] - --os-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 - 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. -- cgit v1.2.1 From 97c2c690d8983fd1d929a4eae3b0d62bbcb2cf6a Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Sun, 7 Jun 2015 11:20:44 -0500 Subject: Switch from deprecated isotime oslo_utils.timeutils.isotime() is deprecated as of 1.6 so we need to stop using it. The deprecation message says to use datetime.datetime.isoformat() instead, but the format of the string generated by isoformat isn't the same as the format of the string generated by isotime. The string is used in tokens and other public APIs and we can't change it without potentially breaking clients. So the workaround is to copy the current implementation from oslo_utils.timeutils.isotime() to keystone.common.utils.isotime(). Change-Id: I34b12b96de3ea21beaf935ed8a9f6bae2fe0d0bc Closes-Bug: 1461251 --- keystoneclient/contrib/revoke/model.py | 11 +++++--- keystoneclient/fixture/discovery.py | 2 +- keystoneclient/fixture/v2.py | 5 ++-- keystoneclient/fixture/v3.py | 5 ++-- keystoneclient/middleware/auth_token.py | 3 ++- .../tests/unit/test_auth_token_middleware.py | 9 ++++--- keystoneclient/tests/unit/test_keyring.py | 5 ++-- keystoneclient/utils.py | 30 ++++++++++++++++++++++ keystoneclient/v3/contrib/trusts.py | 5 ++-- 9 files changed, 56 insertions(+), 19 deletions(-) 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/fixture/discovery.py b/keystoneclient/fixture/discovery.py index eae0bcd..50a6bce 100644 --- a/keystoneclient/fixture/discovery.py +++ b/keystoneclient/fixture/discovery.py @@ -77,7 +77,7 @@ class DiscoveryBase(dict): @updated.setter def updated(self, value): - self.updated_str = timeutils.isotime(value) + self.updated_str = utils.isotime(value) @utils.positional() def add_link(self, href, rel='self', type=None): diff --git a/keystoneclient/fixture/v2.py b/keystoneclient/fixture/v2.py index 3022f2c..da60b9b 100644 --- a/keystoneclient/fixture/v2.py +++ b/keystoneclient/fixture/v2.py @@ -16,6 +16,7 @@ import uuid from oslo_utils import timeutils from keystoneclient.fixture import exception +from keystoneclient import utils class _Service(dict): @@ -112,7 +113,7 @@ class Token(dict): @expires.setter def expires(self, value): - self.expires_str = timeutils.isotime(value) + self.expires_str = utils.isotime(value) @property def issued_str(self): @@ -128,7 +129,7 @@ class Token(dict): @issued.setter def issued(self, value): - self.issued_str = timeutils.isotime(value) + self.issued_str = utils.isotime(value) @property def _user(self): diff --git a/keystoneclient/fixture/v3.py b/keystoneclient/fixture/v3.py index a1781dd..bda5e19 100644 --- a/keystoneclient/fixture/v3.py +++ b/keystoneclient/fixture/v3.py @@ -16,6 +16,7 @@ import uuid from oslo_utils import timeutils from keystoneclient.fixture import exception +from keystoneclient import utils class _Service(dict): @@ -136,7 +137,7 @@ class Token(dict): @expires.setter def expires(self, value): - self.expires_str = timeutils.isotime(value, subsecond=True) + self.expires_str = utils.isotime(value, subsecond=True) @property def issued_str(self): @@ -152,7 +153,7 @@ class Token(dict): @issued.setter def issued(self, value): - self.issued_str = timeutils.isotime(value, subsecond=True) + self.issued_str = utils.isotime(value, subsecond=True) @property def _user(self): diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py index d0fe102..86cc11a 100644 --- a/keystoneclient/middleware/auth_token.py +++ b/keystoneclient/middleware/auth_token.py @@ -169,6 +169,7 @@ from keystoneclient.common import cms from keystoneclient import exceptions from keystoneclient.middleware import memcache_crypt from keystoneclient.openstack.common import memorycache +from keystoneclient import utils # alternative middleware configuration in the main application's @@ -382,7 +383,7 @@ def confirm_token_not_expired(data): utcnow = timeutils.utcnow() if utcnow >= expires: raise InvalidUserToken('Token authorization failed') - return timeutils.isotime(at=expires, subsecond=True) + return utils.isotime(at=expires, subsecond=True) def _v3_to_v2_catalog(catalog): diff --git a/keystoneclient/tests/unit/test_auth_token_middleware.py b/keystoneclient/tests/unit/test_auth_token_middleware.py index 091f956..b6c67fa 100644 --- a/keystoneclient/tests/unit/test_auth_token_middleware.py +++ b/keystoneclient/tests/unit/test_auth_token_middleware.py @@ -43,6 +43,7 @@ from keystoneclient.middleware import auth_token from keystoneclient.openstack.common import memorycache from keystoneclient.tests.unit import client_fixtures from keystoneclient.tests.unit import utils +from keystoneclient import utils as client_utils EXPECTED_V2_DEFAULT_ENV_RESPONSE = { @@ -1684,10 +1685,10 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): super(TokenExpirationTest, self).setUp() self.now = timeutils.utcnow() self.delta = datetime.timedelta(hours=1) - self.one_hour_ago = timeutils.isotime(self.now - self.delta, - subsecond=True) - self.one_hour_earlier = timeutils.isotime(self.now + self.delta, - subsecond=True) + self.one_hour_ago = client_utils.isotime(self.now - self.delta, + subsecond=True) + self.one_hour_earlier = client_utils.isotime(self.now + self.delta, + subsecond=True) def create_v2_token_fixture(self, expires=None): v2_fixture = { diff --git a/keystoneclient/tests/unit/test_keyring.py b/keystoneclient/tests/unit/test_keyring.py index a54009e..4fea794 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 @@ -124,7 +125,7 @@ class KeyringTest(utils.TestCase): # 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 @@ -152,7 +153,7 @@ class KeyringTest(utils.TestCase): # 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 diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py index 300ca54..3565565 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,32 @@ 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 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 -- cgit v1.2.1 From 225832f5913e12f8c86c429193e6ed4531dcc101 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Mon, 29 Jun 2015 15:46:35 -0500 Subject: Switch from deprecated oslo_utils.timeutils.strtime The oslo_utils.timeutils.strtime function is deprecated as of oslo_utils 1.7. DeprecationWarning: Using function/method 'oslo_utils.timeutils.strtime()' is deprecated in version '1.6' and will be removed in a future version: use either datetime.datetime.isoformat() or datetime.datetime.strftime() instead Closes-Bug: 1469867 Change-Id: I97897728703547414a621b6687989cff07e01b3e --- keystoneclient/tests/unit/test_auth_token_middleware.py | 14 +++++++------- keystoneclient/tests/unit/v3/test_oauth1.py | 4 ++-- keystoneclient/utils.py | 5 +++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/keystoneclient/tests/unit/test_auth_token_middleware.py b/keystoneclient/tests/unit/test_auth_token_middleware.py index b6c67fa..8320bc5 100644 --- a/keystoneclient/tests/unit/test_auth_token_middleware.py +++ b/keystoneclient/tests/unit/test_auth_token_middleware.py @@ -453,7 +453,7 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.set_middleware(conf=conf) token = b'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) - expires = timeutils.strtime(some_time_later) + expires = client_utils.strtime(some_time_later) data = ('this_data', expires) token_cache = self.middleware._token_cache token_cache.initialize({}) @@ -470,7 +470,7 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.set_middleware(conf=conf) token = b'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) - expires = timeutils.strtime(some_time_later) + expires = client_utils.strtime(some_time_later) data = ('this_data', expires) token_cache = self.middleware._token_cache token_cache.initialize({}) @@ -486,7 +486,7 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.set_middleware(conf=conf) token = 'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) - expires = timeutils.strtime(some_time_later) + expires = client_utils.strtime(some_time_later) data = ('this_data', expires) token_cache = self.middleware._token_cache token_cache.initialize({}) @@ -1821,7 +1821,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): data = 'this_data' self.set_middleware() self.middleware._token_cache.initialize({}) - some_time_later = timeutils.strtime(at=(self.now + self.delta)) + some_time_later = client_utils.strtime(at=(self.now + self.delta)) expires = some_time_later self.middleware._token_cache.store(token, data, expires) self.assertEqual(self.middleware._token_cache._cache_get(token), data) @@ -1849,7 +1849,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): data = 'this_data' self.set_middleware() self.middleware._token_cache.initialize({}) - some_time_earlier = timeutils.strtime(at=(self.now - self.delta)) + some_time_earlier = client_utils.strtime(at=(self.now - self.delta)) expires = some_time_earlier self.middleware._token_cache.store(token, data, expires) self.assertThat(lambda: self.middleware._token_cache._cache_get(token), @@ -1862,7 +1862,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): self.middleware._token_cache.initialize({}) timezone_offset = datetime.timedelta(hours=2) some_time_later = self.now - timezone_offset + self.delta - expires = timeutils.strtime(some_time_later) + '-02:00' + expires = client_utils.strtime(some_time_later) + '-02:00' self.middleware._token_cache.store(token, data, expires) self.assertEqual(self.middleware._token_cache._cache_get(token), data) @@ -1873,7 +1873,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): self.middleware._token_cache.initialize({}) timezone_offset = datetime.timedelta(hours=2) some_time_earlier = self.now - timezone_offset - self.delta - expires = timeutils.strtime(some_time_earlier) + '-02:00' + expires = client_utils.strtime(some_time_earlier) + '-02:00' self.middleware._token_cache.store(token, data, expires) self.assertThat(lambda: self.middleware._token_cache._cache_get(token), matchers.raises(auth_token.InvalidUserToken)) diff --git a/keystoneclient/tests/unit/v3/test_oauth1.py b/keystoneclient/tests/unit/v3/test_oauth1.py index b52a759..48af836 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,6 +21,7 @@ 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 @@ -90,7 +90,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} diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py index 3565565..5017431 100644 --- a/keystoneclient/utils.py +++ b/keystoneclient/utils.py @@ -366,3 +366,8 @@ def isotime(at=None, subsecond=False): 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) -- cgit v1.2.1 From 31f326dab688cab0274b3326ca3a5d3c6cf547fc Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Mon, 8 Jun 2015 20:36:02 -0500 Subject: Unit tests catch deprecated function usage Rather than continue to call deprecated functions without knowing it, have the unit tests fail when deprecated function is used. This will ensure that code isn't added that calls deprecated functions. Change-Id: If9f58e30a08a88778e4ae3fc01399ad90997e812 --- keystoneclient/tests/unit/client_fixtures.py | 12 ++++++++++++ keystoneclient/tests/unit/test_auth_token_middleware.py | 8 +++++++- keystoneclient/tests/unit/test_ec2utils.py | 2 ++ keystoneclient/tests/unit/test_hacking_checks.py | 3 +++ keystoneclient/tests/unit/test_memcache_crypt.py | 5 +++++ keystoneclient/tests/unit/test_s3_token_middleware.py | 5 +++++ keystoneclient/tests/unit/utils.py | 4 ++++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/keystoneclient/tests/unit/client_fixtures.py b/keystoneclient/tests/unit/client_fixtures.py index b226e32..dfb2b21 100644 --- a/keystoneclient/tests/unit/client_fixtures.py +++ b/keystoneclient/tests/unit/client_fixtures.py @@ -13,6 +13,7 @@ # under the License. import os +import warnings import fixtures from oslo_serialization import jsonutils @@ -595,3 +596,14 @@ 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) diff --git a/keystoneclient/tests/unit/test_auth_token_middleware.py b/keystoneclient/tests/unit/test_auth_token_middleware.py index 8320bc5..9cdaf92 100644 --- a/keystoneclient/tests/unit/test_auth_token_middleware.py +++ b/keystoneclient/tests/unit/test_auth_token_middleware.py @@ -206,7 +206,9 @@ class BaseAuthTokenMiddlewareTest(testtools.TestCase): """ def setUp(self, expected_env=None, auth_version=None, fake_app=None): - testtools.TestCase.setUp(self) + super(BaseAuthTokenMiddlewareTest, self).setUp() + + self.useFixture(client_fixtures.Deprecations()) self.expected_env = expected_env or dict() self.fake_app = fake_app or FakeApp @@ -1673,6 +1675,10 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, class TokenEncodingTest(testtools.TestCase): + def setUp(self): + super(TokenEncodingTest, self).setUp() + self.useFixture(client_fixtures.Deprecations()) + def test_unquoted_token(self): self.assertEqual('foo%20bar', auth_token.safe_quote('foo bar')) 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_memcache_crypt.py b/keystoneclient/tests/unit/test_memcache_crypt.py index be07b24..2546121 100644 --- a/keystoneclient/tests/unit/test_memcache_crypt.py +++ b/keystoneclient/tests/unit/test_memcache_crypt.py @@ -14,9 +14,14 @@ import six import testtools from keystoneclient.middleware import memcache_crypt +from keystoneclient.tests.unit import client_fixtures class MemcacheCryptPositiveTests(testtools.TestCase): + def setUp(self): + super(MemcacheCryptPositiveTests, self).setUp() + self.useFixture(client_fixtures.Deprecations()) + def _setup_keys(self, strategy): return memcache_crypt.derive_keys(b'token', b'secret', strategy) diff --git a/keystoneclient/tests/unit/test_s3_token_middleware.py b/keystoneclient/tests/unit/test_s3_token_middleware.py index dfb4406..1f8aa1c 100644 --- a/keystoneclient/tests/unit/test_s3_token_middleware.py +++ b/keystoneclient/tests/unit/test_s3_token_middleware.py @@ -20,6 +20,7 @@ import testtools import webob from keystoneclient.middleware import s3_token +from keystoneclient.tests.unit import client_fixtures from keystoneclient.tests.unit import utils @@ -221,6 +222,10 @@ class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase): class S3TokenMiddlewareTestUtil(testtools.TestCase): + def setUp(self): + super(S3TokenMiddlewareTestUtil, self).setUp() + self.useFixture(client_fixtures.Deprecations()) + def test_split_path_failed(self): self.assertRaises(ValueError, s3_token.split_path, '') self.assertRaises(ValueError, s3_token.split_path, '/') diff --git a/keystoneclient/tests/unit/utils.py b/keystoneclient/tests/unit/utils.py index 2274519..2e759af 100644 --- a/keystoneclient/tests/unit/utils.py +++ b/keystoneclient/tests/unit/utils.py @@ -24,6 +24,8 @@ import six from six.moves.urllib import parse as urlparse import testtools +from keystoneclient.tests.unit import client_fixtures + class TestCase(testtools.TestCase): @@ -42,6 +44,8 @@ class TestCase(testtools.TestCase): def setUp(self): super(TestCase, self).setUp() + self.useFixture(client_fixtures.Deprecations()) + self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) self.time_patcher = self.useFixture( mockpatch.PatchObject(time, 'time', lambda: 1234)).mock -- cgit v1.2.1 From c503c29f930638b31c99ed4827ec8a3dd0bde8fe Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 2 Jul 2015 18:57:20 +0000 Subject: Updated from global requirements Change-Id: I55424466d38369fdbd12852255fe94a0e2676c0a --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index e8882b7..79bbaf3 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ 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.0 @@ -16,7 +16,7 @@ oslotest>=1.5.1 # Apache-2.0 pycrypto>=2.6 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 -- cgit v1.2.1 From 4034366b51244547e6b589160f8db015bb72be37 Mon Sep 17 00:00:00 2001 From: David Stanek Date: Tue, 7 Jul 2015 21:53:22 +0000 Subject: Fixes modules index generated by Sphinx Sphinx was always using (k)eystoneclient for the prefix so the index wasn't very useful. Change-Id: I9f883e1005874b5f5019f9030b94174a2169ed77 --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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' -- cgit v1.2.1 From d3b97553955dfa970228649c7a25c32a26dd44b9 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sun, 12 Jul 2015 15:22:13 +0000 Subject: Updated from global requirements Change-Id: I584fe4bee5a4785d6c5862fe8ae417481cc04d36 --- test-requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 79bbaf3..dde3268 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,8 @@ discover fixtures>=1.3.1 keyring!=3.3,>=2.1 lxml>=2.3 -mock>=1.0 +mock>=1.1;python_version!='2.6' +mock==1.0.1;python_version=='2.6' oauthlib>=0.6 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.5.1 # Apache-2.0 -- cgit v1.2.1 From 3668d9cae2f620b5414c2c181cbd3adccd95e6e9 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 13 Jul 2015 04:53:17 -0700 Subject: py34 not py33 is tested and supported The setup.cfg refers to Programming Language of Python 3.3 whereas jenkins is setup only to test Python 3.4. This patch updates setup.cfg and removes py33 from tox.ini. TrivialFix Change-Id: I1bc7fae6481c4fef71746ed1c144af37445a81ac --- setup.cfg | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 -- cgit v1.2.1 From 2f90bb68ac1d98f7d14f23f1f2dde7c88346daf5 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 15 Jul 2015 01:37:25 +0000 Subject: Updated from global requirements Change-Id: I25bef4f42fc42765b493f17295add61ba3353803 --- requirements.txt | 4 ++-- setup.py | 2 +- test-requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9776c88..8e0c72f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # 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 @@ -11,7 +11,7 @@ netaddr>=0.7.12 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.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 dde3268..ab885b4 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -13,7 +13,7 @@ mock>=1.1;python_version!='2.6' mock==1.0.1;python_version=='2.6' oauthlib>=0.6 oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.5.1 # Apache-2.0 +oslotest>=1.7.0 # Apache-2.0 pycrypto>=2.6 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -- cgit v1.2.1 From a4584c4ba7fa62c923d7da883e1cf8080e1275ad Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Tue, 23 Jun 2015 11:32:11 +0800 Subject: Add get_token_data to token CRUD We already have the validate method that returns an AccessInfo object. For auth_token middleware it would be simpler if the client returned simply the token data so it presented the same way as other sources of token data. It would also help with the keystoneauth transition in auth_token as we could bypass the keystoneclient.AccessInfo objects. Closes-Bug: #1475041 Change-Id: Ifbe7a7004937d910739c325cc04ae7264a4498e0 --- keystoneclient/tests/unit/v2_0/test_tokens.py | 6 ++++++ keystoneclient/tests/unit/v3/test_tokens.py | 17 +++++++++++++++ keystoneclient/v2_0/tokens.py | 18 ++++++++++++---- keystoneclient/v3/tokens.py | 31 ++++++++++++++++++--------- 4 files changed, 58 insertions(+), 14 deletions(-) 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/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/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/v3/tokens.py b/keystoneclient/v3/tokens.py index 77edbc0..38f4e9f 100644 --- a/keystoneclient/v3/tokens.py +++ b/keystoneclient/v3/tokens.py @@ -51,6 +51,25 @@ class TokenManager(object): resp, body = self._client.get('/auth/tokens/OS-PKI/revoked') return body + @utils.positional.method(1) + def get_token_data(self, token, include_catalog=True): + """Fetch the data about a token from the identity server. + + :param str token: The token id. + :param bool include_catalog: If False, the response is requested to not + include the catalog. + + :rtype: dict + """ + headers = {'X-Subject-Token': token} + + url = '/auth/tokens' + if not include_catalog: + url += '?nocatalog' + + resp, body = self._client.get(url, headers=headers) + return body + @utils.positional.method(1) def validate(self, token, include_catalog=True): """Validate a token. @@ -66,13 +85,5 @@ class TokenManager(object): """ token_id = _calc_id(token) - headers = {'X-Subject-Token': token_id} - - url = '/auth/tokens' - if not include_catalog: - url += '?nocatalog' - - resp, body = self._client.get(url, headers=headers) - - access_info = access.AccessInfo.factory(resp=resp, body=body) - return access_info + body = self.get_token_data(token_id, include_catalog=include_catalog) + return access.AccessInfo.factory(auth_token=token_id, body=body) -- cgit v1.2.1 From 98326c72f732481d73f2941827a1dae75c61388b Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Wed, 13 May 2015 16:38:44 +0000 Subject: Prevent attempts to "filter" list() calls by globally unique IDs This use case isn't covered by our current APIs: GET /entities?id={entity_id} Because we have a dedicated API for that: GET /entities/{entity_id} But our list() methods generally support **kwargs, which are passed as query parameters to keystone. When an 'id' is passed to keystone as a query parameter, keystone rightly ignores it and returns an unfiltered collection. This change raises a client-side TypeError (as you'd expect when you try to pass a keyword argument that a function isn't expecting), and includes a helpful suggestion to try calling get() instead. Change-Id: I100b69bbf571ad6de49ccc5ad1099c20b877d13d Closes-Bug: 1452298 --- keystoneclient/base.py | 11 +++++++++++ keystoneclient/tests/unit/v3/test_domains.py | 6 ++++++ keystoneclient/tests/unit/v3/test_federation.py | 10 ++++++++++ keystoneclient/tests/unit/v3/test_role_assignments.py | 9 +++++++++ keystoneclient/tests/unit/v3/utils.py | 14 ++++++++++++++ 5 files changed, 50 insertions(+) diff --git a/keystoneclient/base.py b/keystoneclient/base.py index 025362b..eabbdc4 100644 --- a/keystoneclient/base.py +++ b/keystoneclient/base.py @@ -356,6 +356,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: 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 4cbae8d..5782aa6 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_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/utils.py b/keystoneclient/tests/unit/v3/utils.py index 7f2d633..b5fb7ce 100644 --- a/keystoneclient/tests/unit/v3/utils.py +++ b/keystoneclient/tests/unit/v3/utils.py @@ -245,6 +245,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()] -- cgit v1.2.1 From 7d5d8b343232ee5faf4de3381024095619335929 Mon Sep 17 00:00:00 2001 From: Boris Bobrov Date: Wed, 22 Jul 2015 18:52:49 +0300 Subject: Make OAuth testcase use actual request headers OAuth test verifies that access_token manager's methods make requests with certain parameters. It is supposed to use values from mocked http handler and compare them with referential values acquired from oauth client. But instead of using values from mocked handler, it used the values from oauth client and compared them with values from the client acquired using attributes, basically testing oauthlib and not access_token manager's methods. Make the test compare correct values and remove check of timestamp, which was useless because it is always mocked in tests and not provided in actual requests. As a consequence, use of get_oauth_params, which changed in oauthlib 1.0 and blocked the gate, was removed. Closes-Bug: 1477177 Closes-Bug: 1477247 Change-Id: I5e049163f84fde5827104fd4a6441222eb08468f --- keystoneclient/tests/unit/v3/test_oauth1.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/keystoneclient/tests/unit/v3/test_oauth1.py b/keystoneclient/tests/unit/v3/test_oauth1.py index 48af836..2ebfa50 100644 --- a/keystoneclient/tests/unit/v3/test_oauth1.py +++ b/keystoneclient/tests/unit/v3/test_oauth1.py @@ -28,7 +28,6 @@ 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 @@ -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) -- cgit v1.2.1 From 0d5415e9e979a5377034352662dcfe8edc086fae Mon Sep 17 00:00:00 2001 From: Boris Bobrov Date: Thu, 23 Jul 2015 00:34:43 +0300 Subject: Remove unused time_patcher There is no reason to patch time now, tests succeed without it Change-Id: I3cf3017b5f86bae35276037c60cfec8dc3be20ae --- keystoneclient/tests/unit/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/keystoneclient/tests/unit/utils.py b/keystoneclient/tests/unit/utils.py index 2274519..22dbe02 100644 --- a/keystoneclient/tests/unit/utils.py +++ b/keystoneclient/tests/unit/utils.py @@ -12,12 +12,10 @@ 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 @@ -43,9 +41,6 @@ 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.requests_mock = self.useFixture(fixture.Fixture()) def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs): -- cgit v1.2.1 From bb6463e20fce83886205f82b320db739ce6c7311 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 23 Jul 2015 07:44:44 +0000 Subject: Updated from global requirements Change-Id: I772ee10288955bae1f3f3707eb1b34573df03e60 --- test-requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index ab885b4..572c8e7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,8 +9,7 @@ discover 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.7.0 # Apache-2.0 -- cgit v1.2.1 From c6b14f94c5021452796d7bd151c2c98ae983afdd Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 22 Jul 2015 14:53:32 -0500 Subject: Set reasonable defaults for TCP Keep-Alive Previously we simply turned on TCP Keep-Alive which relied on per-distribution, per-operating system defaults for keep-alive options. Here we set reasonable defaults since long running processes can get stuck for hours on end by using system defaults. This also adds comments around the options to explain why they're being set. Closes-bug: 1477275 Related-bug: 1323862 Change-Id: Ibd53ae2d4d2455db0ebc9951e5c764befc57850f --- keystoneclient/session.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/keystoneclient/session.py b/keystoneclient/session.py index 14edf46..d353c98 100644 --- a/keystoneclient/session.py +++ b/keystoneclient/session.py @@ -899,11 +899,36 @@ 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', [ + 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.setdefault('socket_options', socket_options) super(TCPKeepAliveAdapter, self).init_poolmanager(*args, **kwargs) -- cgit v1.2.1 From 610844d06db5b2af93c0747a7690c44d3724510b Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 08:05:13 -0500 Subject: Deprecations fixture support calling deprecated function Sometimes a test is expected to call deprecated function, such as when testing that deprecated function still works. Now the test can tell the Deprecations fixture that it's calling deprecated function. Change-Id: Ic7486b74f681989eb5110dfeaf8dae0e5d7ae50e --- keystoneclient/tests/unit/client_fixtures.py | 12 ++++++++++++ keystoneclient/tests/unit/utils.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/keystoneclient/tests/unit/client_fixtures.py b/keystoneclient/tests/unit/client_fixtures.py index dfb2b21..46266ce 100644 --- a/keystoneclient/tests/unit/client_fixtures.py +++ b/keystoneclient/tests/unit/client_fixtures.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib import os import warnings @@ -607,3 +608,14 @@ class Deprecations(fixtures.Fixture): 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/utils.py b/keystoneclient/tests/unit/utils.py index d865e68..7c6de95 100644 --- a/keystoneclient/tests/unit/utils.py +++ b/keystoneclient/tests/unit/utils.py @@ -42,7 +42,7 @@ class TestCase(testtools.TestCase): def setUp(self): super(TestCase, self).setUp() - self.useFixture(client_fixtures.Deprecations()) + self.deprecations = self.useFixture(client_fixtures.Deprecations()) self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) self.requests_mock = self.useFixture(fixture.Fixture()) -- cgit v1.2.1 From 8d65259cb887c0a4f9c26d3994aef131633c5189 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 08:15:46 -0500 Subject: Proper deprecation for AccessInfo region_name parameter Properly deprecate constructing AccessInfo with region_name parameter. bp deprecations Change-Id: Ic5f48a4f5354beb8be68c2fd788bf0a974501917 --- keystoneclient/access.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/keystoneclient/access.py b/keystoneclient/access.py index 009b72e..881f4c0 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): -- cgit v1.2.1 From f782ee853c49dda7f86055192a01c75269e26aff Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 09:10:49 -0500 Subject: Proper deprecation for AccessInfo scoped property Properly deprecate constructing AccessInfo's scoped parameter. bp deprecations Change-Id: I8f81c75eb8e758feb9d4c62ce7f041957562e766 --- keystoneclient/access.py | 19 ++++++++++++++++++- keystoneclient/tests/unit/auth/test_identity_v3.py | 3 ++- keystoneclient/tests/unit/v2_0/test_access.py | 9 ++++++--- keystoneclient/tests/unit/v2_0/test_client.py | 13 ++++++++----- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/keystoneclient/access.py b/keystoneclient/access.py index 881f4c0..576312b 100644 --- a/keystoneclient/access.py +++ b/keystoneclient/access.py @@ -258,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 """ @@ -537,6 +540,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']): @@ -759,6 +769,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 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/v2_0/test_access.py b/keystoneclient/tests/unit/v2_0/test_access.py index e966874..17b8fe4 100644 --- a/keystoneclient/tests/unit/v2_0/test_access.py +++ b/keystoneclient/tests/unit/v2_0/test_access.py @@ -48,7 +48,8 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): self.assertIsNone(auth_ref.auth_url) 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) @@ -106,7 +107,8 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): 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 +129,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_client.py b/keystoneclient/tests/unit/v2_0/test_client.py index 379bea4..be48e84 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) @@ -51,7 +52,8 @@ class KeystoneClientTest(utils.TestCase): tenant_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) @@ -70,7 +72,8 @@ class KeystoneClientTest(utils.TestCase): 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) @@ -92,8 +95,8 @@ class KeystoneClientTest(utils.TestCase): 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) -- cgit v1.2.1 From 66fd1eb7484f92a7d3daa468be8c47910ba79216 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 10:11:57 -0500 Subject: Stop using deprecated AccessInfo.auth_url and management_url The comments for the auth_url and management_url properties say that they're deprecated and to use the service catalog, but internal code was using the deprecated function. The code needs to be changed to use non-deprecated function. bp deprecations Change-Id: I6ada821fe305650d22e58a55192332f0f4986537 --- keystoneclient/httpclient.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index e1ee4c6..9c51f6d 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -248,8 +248,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: -- cgit v1.2.1 From 6d82f1f17ca99b4e318b6c5bfa24b6dc507ba497 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 10:12:12 -0500 Subject: Proper deprecation for AccessInfo auth_url property Properly deprecate accessing AccessInfo's auth_url parameter. bp deprecations Change-Id: I3824904f517434b507587cf73d4389b72f73f22b --- keystoneclient/access.py | 21 ++++++++++++++++----- keystoneclient/tests/unit/v2_0/test_access.py | 7 +++++-- keystoneclient/tests/unit/v3/test_access.py | 8 +++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/keystoneclient/access.py b/keystoneclient/access.py index 576312b..ee90f0e 100644 --- a/keystoneclient/access.py +++ b/keystoneclient/access.py @@ -364,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 """ @@ -611,8 +612,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', @@ -804,8 +810,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', diff --git a/keystoneclient/tests/unit/v2_0/test_access.py b/keystoneclient/tests/unit/v2_0/test_access.py index 17b8fe4..c004fa0 100644 --- a/keystoneclient/tests/unit/v2_0/test_access.py +++ b/keystoneclient/tests/unit/v2_0/test_access.py @@ -45,7 +45,8 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): self.assertIsNone(auth_ref.tenant_name) self.assertIsNone(auth_ref.tenant_id) - self.assertIsNone(auth_ref.auth_url) + with self.deprecations.expect_deprecations_here(): + self.assertIsNone(auth_ref.auth_url) self.assertIsNone(auth_ref.management_url) with self.deprecations.expect_deprecations_here(): @@ -99,7 +100,9 @@ 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',)) + with self.deprecations.expect_deprecations_here(): + self.assertEqual(auth_ref.auth_url, + ('http://public.com:5000/v2.0',)) self.assertEqual(auth_ref.management_url, ('http://admin:35357/v2.0',)) self.assertEqual(auth_ref.project_domain_id, 'default') diff --git a/keystoneclient/tests/unit/v3/test_access.py b/keystoneclient/tests/unit/v3/test_access.py index f069f71..a3171df 100644 --- a/keystoneclient/tests/unit/v3/test_access.py +++ b/keystoneclient/tests/unit/v3/test_access.py @@ -49,7 +49,8 @@ class AccessInfoTest(utils.TestCase): self.assertIsNone(auth_ref.project_name) self.assertIsNone(auth_ref.project_id) - self.assertIsNone(auth_ref.auth_url) + with self.deprecations.expect_deprecations_here(): + self.assertIsNone(auth_ref.auth_url) self.assertIsNone(auth_ref.management_url) self.assertFalse(auth_ref.domain_scoped) @@ -148,8 +149,9 @@ class AccessInfoTest(utils.TestCase): 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',)) + with self.deprecations.expect_deprecations_here(): + self.assertEqual(auth_ref.auth_url, + ('http://public.com:5000/v3',)) self.assertEqual(auth_ref.management_url, ('http://admin:35357/v3',)) -- cgit v1.2.1 From 1a2ccb001bdbb09a3b66c6aca651ce71e62734d8 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 10:51:49 -0500 Subject: Proper deprecation for AccessInfo management_url property AccessInfo's management_url parameter wasn't properly deprecated since all it had was a comment in the code. Proper deprecation requires use of warnings and documentation. bp deprecations Change-Id: I0ee07c5adc6a7c91f8b23b291eea76f4ae7b3b89 --- keystoneclient/access.py | 21 ++++++++++++++++----- keystoneclient/tests/unit/v2_0/test_access.py | 7 +++++-- keystoneclient/tests/unit/v3/test_access.py | 8 +++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/keystoneclient/access.py b/keystoneclient/access.py index ee90f0e..f308a58 100644 --- a/keystoneclient/access.py +++ b/keystoneclient/access.py @@ -378,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 """ @@ -628,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', @@ -826,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/tests/unit/v2_0/test_access.py b/keystoneclient/tests/unit/v2_0/test_access.py index c004fa0..b768150 100644 --- a/keystoneclient/tests/unit/v2_0/test_access.py +++ b/keystoneclient/tests/unit/v2_0/test_access.py @@ -47,7 +47,8 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): with self.deprecations.expect_deprecations_here(): self.assertIsNone(auth_ref.auth_url) - self.assertIsNone(auth_ref.management_url) + with self.deprecations.expect_deprecations_here(): + self.assertIsNone(auth_ref.management_url) with self.deprecations.expect_deprecations_here(): self.assertFalse(auth_ref.scoped) @@ -103,7 +104,9 @@ class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): with self.deprecations.expect_deprecations_here(): 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.management_url, + ('http://admin:35357/v2.0',)) self.assertEqual(auth_ref.project_domain_id, 'default') self.assertEqual(auth_ref.project_domain_name, 'Default') diff --git a/keystoneclient/tests/unit/v3/test_access.py b/keystoneclient/tests/unit/v3/test_access.py index a3171df..74e4438 100644 --- a/keystoneclient/tests/unit/v3/test_access.py +++ b/keystoneclient/tests/unit/v3/test_access.py @@ -51,7 +51,8 @@ class AccessInfoTest(utils.TestCase): with self.deprecations.expect_deprecations_here(): self.assertIsNone(auth_ref.auth_url) - self.assertIsNone(auth_ref.management_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) @@ -152,8 +153,9 @@ class AccessInfoTest(utils.TestCase): with self.deprecations.expect_deprecations_here(): self.assertEqual(auth_ref.auth_url, ('http://public.com:5000/v3',)) - self.assertEqual(auth_ref.management_url, - ('http://admin:35357/v3',)) + with self.deprecations.expect_deprecations_here(): + self.assertEqual(auth_ref.management_url, + ('http://admin:35357/v3',)) self.assertEqual(auth_ref.project_domain_id, '4e6893b7ba0b4006840c3845660b86ed') -- cgit v1.2.1 From 6950527f09f759c44ef145bde71d600a4066daf7 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Sat, 28 Mar 2015 15:03:19 +1100 Subject: Use UUID values in v3 test fixtures The fixtures will automatically stub UUID values for required token fields, so we can check for those returned values rather than specify fixed string values. Change-Id: I8a6cc675c6c8ee14772a38d8fc38475885ebc605 --- keystoneclient/fixture/v3.py | 8 +++ keystoneclient/tests/unit/v3/client_fixtures.py | 86 ++++++++-------------- keystoneclient/tests/unit/v3/test_access.py | 71 +++++++++--------- keystoneclient/tests/unit/v3/test_client.py | 96 +++++++++++++------------ 4 files changed, 125 insertions(+), 136 deletions(-) diff --git a/keystoneclient/fixture/v3.py b/keystoneclient/fixture/v3.py index bda5e19..d27a93b 100644 --- a/keystoneclient/fixture/v3.py +++ b/keystoneclient/fixture/v3.py @@ -325,6 +325,14 @@ class Token(dict): def audit_chain_id(self, value): self.root['audit_ids'] = [self.audit_id, value] + @property + def role_ids(self): + return [r['id'] for r in self.root.get('roles', [])] + + @property + def role_names(self): + return [r['name'] for r in self.root.get('roles', [])] + def validate(self): project = self.root.get('project') domain = self.root.get('domain') 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..d417d6b 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, []) @@ -55,9 +55,10 @@ class AccessInfoTest(utils.TestCase): 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 +93,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,20 +131,20 @@ 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) @@ -153,13 +154,15 @@ class AccessInfoTest(utils.TestCase): self.assertEqual(auth_ref.management_url, ('http://admin:35357/v3',)) - self.assertEqual(auth_ref.project_domain_id, - '4e6893b7ba0b4006840c3845660b86ed') - self.assertEqual(auth_ref.project_domain_name, 'exampledomain') + self.assertEqual(PROJECT_SCOPED_TOKEN.project_domain_id, + auth_ref.project_domain_id) + self.assertEqual(PROJECT_SCOPED_TOKEN.project_domain_name, + auth_ref.project_domain_name) - self.assertEqual(auth_ref.user_domain_id, - '4e6893b7ba0b4006840c3845660b86ed') - self.assertEqual(auth_ref.user_domain_name, 'exampledomain') + self.assertEqual(PROJECT_SCOPED_TOKEN.user_domain_id, + auth_ref.user_domain_id) + self.assertEqual(PROJECT_SCOPED_TOKEN.user_domain_name, + auth_ref.user_domain_name) self.assertFalse(auth_ref.domain_scoped) self.assertTrue(auth_ref.project_scoped) diff --git a/keystoneclient/tests/unit/v3/test_client.py b/keystoneclient/tests/unit/v3/test_client.py index c01cac2..6f9c98f 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, -- cgit v1.2.1 From 85b32fcb9d1df1362209c2902768a4de013004cb Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 11:15:34 -0500 Subject: Proper deprecations for modules Use debtcollector and update docs for deprecating the apiclient, apiclient.exceptions, middleware.s3_token, and v2_0.shell modules. bp deprecations Change-Id: I84e8eac39a209210542f19de08d4c3de15a9dcac --- keystoneclient/apiclient/__init__.py | 18 ++++++++++++++---- keystoneclient/apiclient/exceptions.py | 11 ++++++----- keystoneclient/middleware/s3_token.py | 6 ++++++ keystoneclient/v2_0/shell.py | 3 ++- requirements.txt | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) 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/middleware/s3_token.py b/keystoneclient/middleware/s3_token.py index f8d1ce0..ea804bb 100644 --- a/keystoneclient/middleware/s3_token.py +++ b/keystoneclient/middleware/s3_token.py @@ -22,6 +22,12 @@ """ S3 TOKEN MIDDLEWARE +.. warning:: + + This module is DEPRECATED and may be removed in the 2.0.0 release. The + s3_token middleware has been moved to the `keystonemiddleware repository + `_. + This WSGI component: * Get a request from the swift3 middleware with an S3 Authorization 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/requirements.txt b/requirements.txt index 8e0c72f..b799ea2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ pbr<2.0,>=1.3 argparse Babel>=1.3 iso8601>=0.1.9 +debtcollector>=0.3.0 # Apache-2.0 netaddr>=0.7.12 oslo.config>=1.11.0 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0 -- cgit v1.2.1 From 799e1faa505ac0c62d6db76ad68cdbb2d7148888 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 13:16:00 -0500 Subject: Proper deprecation for BaseIdentityPlugin username, password, token_id properties BaseIdentityPlugin's username, password, and token_id properties weren't properly deprecated since all they had was a comment in the code. Proper deprecation requires use of warnings and documentation. Where the plugins already provide their own properties, the properties need to be un-deprecated. bp deprecations Change-Id: Ic9fce89b8544d8c01f16e8f9c2f9dd2659d03c18 --- keystoneclient/auth/identity/base.py | 79 +++++++++++++++++++++++++++++++-- keystoneclient/auth/identity/v2.py | 36 +++++++++++++-- keystoneclient/contrib/auth/v3/oidc.py | 24 +++++++++- keystoneclient/contrib/auth/v3/saml2.py | 44 +++++++++++++++++- 4 files changed, 173 insertions(+), 10 deletions(-) diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py index f785192..b6ff4ae 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,13 +55,85 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): self._endpoint_cache = {} + self._username = username + self._password = password + self._token = token # 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 + @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 + @abc.abstractmethod def get_auth_ref(self, session, **kwargs): """Obtain a token from an OpenStack Identity Service. diff --git a/keystoneclient/auth/identity/v2.py b/keystoneclient/auth/identity/v2.py index 3ea74b7..725da3b 100644 --- a/keystoneclient/auth/identity/v2.py +++ b/keystoneclient/auth/identity/v2.py @@ -131,8 +131,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 +202,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/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): -- cgit v1.2.1 From b1496abbb77360672d1d94e689513daeeb3cafd0 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 13:57:12 -0500 Subject: Proper deprecation for BaseIdentityPlugin trust_id property BaseIdentityPlugin's trust_id property wasn't properly deprecated since all it had was a comment in the code. Proper deprecation requires use of warnings and documentation. Where the plugins already provide their own trust_id, the property needs to be un-deprecated. bp deprecations Change-Id: I15d4e019bfc5542990120ba39be65ad83cf040d5 --- keystoneclient/auth/identity/base.py | 28 +++++++++++++++++++++++++--- keystoneclient/auth/identity/generic/base.py | 10 ++++++++++ keystoneclient/auth/identity/v2.py | 12 +++++++++++- keystoneclient/auth/identity/v3/base.py | 12 +++++++++++- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py index b6ff4ae..57e1723 100644 --- a/keystoneclient/auth/identity/base.py +++ b/keystoneclient/auth/identity/base.py @@ -58,9 +58,7 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): self._username = username self._password = password self._token = token - # NOTE(jamielennox): DEPRECATED. The following should not really be set - # here but handled by the individual auth plugin. - self.trust_id = trust_id + self._trust_id = trust_id @property def username(self): @@ -134,6 +132,30 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): 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): """Obtain a token from an OpenStack Identity Service. 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 725da3b..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' 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 @@ -68,6 +68,16 @@ class BaseAuth(base.BaseIdentityPlugin): self.project_domain_name = project_domain_name self.include_catalog = include_catalog + @property + def trust_id(self): + # Override to remove deprecation. + return self._trust_id + + @trust_id.setter + def trust_id(self, value): + # Override to remove deprecation. + self._trust_id = value + @property def token_url(self): """The full URL where we will send authentication data.""" -- cgit v1.2.1 From fee5ba7432ff4b282aacbb8dafac948af2006f45 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:14:00 -0500 Subject: Stop using Manager.api base.Manager's api is documented as being deprecated, but there was still code using it. Deprecated function must not be used. bp deprecations Change-Id: I58678626b55f3cd11f4fdbcddbe4cc9461692fbf --- keystoneclient/v2_0/users.py | 3 ++- keystoneclient/v3/contrib/oauth1/access_tokens.py | 3 ++- keystoneclient/v3/contrib/oauth1/request_tokens.py | 3 ++- keystoneclient/v3/users.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) 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/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/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'}) -- cgit v1.2.1 From c5b03191b6714fed15bd88769c89e897257c337d Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:14:13 -0500 Subject: Proper deprecation for Manager.api base.Manager's api property wasn't properly deprecated since all it had was documentation. Proper deprecation requires use of warnings and documentation. bp deprecations Change-Id: Ic5e218151e9b3f3b66f78729052680691d5ad582 --- keystoneclient/base.py | 12 +++++++++++- keystoneclient/tests/unit/test_base.py | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/keystoneclient/base.py b/keystoneclient/base.py index eabbdc4..d2c3ea0 100644 --- a/keystoneclient/base.py +++ b/keystoneclient/base.py @@ -21,6 +21,7 @@ Base utilities to build API operation managers and objects on top of. import abc import functools +import warnings import six from six.moves import urllib @@ -91,8 +92,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): diff --git a/keystoneclient/tests/unit/test_base.py b/keystoneclient/tests/unit/test_base.py index dcfbb13..6871702 100644 --- a/keystoneclient/tests/unit/test_base.py +++ b/keystoneclient/tests/unit/test_base.py @@ -94,7 +94,8 @@ class ManagerTest(utils.TestCase): 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( -- cgit v1.2.1 From ce58b07eea29a43ee42655627e78820add16d1f5 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:21:37 -0500 Subject: Proper deprecation for client.HTTPClient client.HTTPClient wasn't properly deprecated since all it had was a comment in the code. Proper deprecation requires use of warnings and documentation. bp deprecations Change-Id: I1c50c1441b23a79831e6e1df749084130e4b9af7 --- keystoneclient/client.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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): -- cgit v1.2.1 From 5547fe80b082035393b2bf1f59fd3a1a5c531817 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:25:05 -0500 Subject: Proper deprecation for is_ans1_token is_ans1_token wasn't properly deprecated since it used LOG.warn rather than warnings/debtcollector. Proper deprecation requires use of warnings and documentation. bp deprecations Change-Id: I81be2844014745a5951ce91a336e9e9ecf4d5328 --- keystoneclient/common/cms.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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) -- cgit v1.2.1 From a303cbc0ece4a423e58a2fc704c4062b25ede29f Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:32:29 -0500 Subject: Proper deprecation for Dicover.available_versions() Dicover.available_versions() wasn't properly deprecated since it was only mentioned in the docstring. Proper deprecation requires use of warnings/debtcollector and documentation. bp deprecations Change-Id: Ifbcedec1d464435ebb9bcec779fadac0dfb28909 --- keystoneclient/discover.py | 10 ++++++++-- keystoneclient/tests/unit/test_discovery.py | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/keystoneclient/discover.py b/keystoneclient/discover.py index 40fd85d..6ead962 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. diff --git a/keystoneclient/tests/unit/test_discovery.py b/keystoneclient/tests/unit/test_discovery.py index 76aaf03..6a76d8f 100644 --- a/keystoneclient/tests/unit/test_discovery.py +++ b/keystoneclient/tests/unit/test_discovery.py @@ -499,7 +499,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]) -- cgit v1.2.1 From fb28e1a2b80c21ff6a9728654b4d736add810ae1 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:45:36 -0500 Subject: Proper deprecation for Dicover.raw_version_data unstable parameter Dicover.raw_version_data()'s unstable parameter wasn't properly deprecated since it was only mentioned in the docstring. Prope deprecation requires use of warnings/debtcollector and documentation. Also, fixed a place where the deprecated function could be used. bp deprecations Change-Id: I42dd7c1831bcfc3c637572eb112353b8760ed8d0 --- keystoneclient/_discover.py | 3 +++ keystoneclient/discover.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) 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/discover.py b/keystoneclient/discover.py index 6ead962..ff0db6d 100644 --- a/keystoneclient/discover.py +++ b/keystoneclient/discover.py @@ -191,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. @@ -198,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. -- cgit v1.2.1 From 9f17732308c13264c3ea4c16771153f2e86369b9 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 14:52:12 -0500 Subject: Proper deprecation for httpclient.request() httpclient.request() wasn't properly deprecated since it was only mentioned in a comment. Proper deprecation requires use of warnings/debtcollector and documentation. bp deprecations Change-Id: Id35d64a8b2d536c5de90e398b2a7680fa30881d6 --- keystoneclient/httpclient.py | 16 ++++++++++++++-- keystoneclient/tests/unit/test_http.py | 3 ++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index 9c51f6d..37c6a4d 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -21,6 +21,7 @@ OpenStack Client interface. Handles the REST calls and responses. import logging +from debtcollector import removals from oslo_serialization import jsonutils import pkg_resources import requests @@ -64,10 +65,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): diff --git a/keystoneclient/tests/unit/test_http.py b/keystoneclient/tests/unit/test_http.py index 436c374..9a8bee2 100644 --- a/keystoneclient/tests/unit/test_http.py +++ b/keystoneclient/tests/unit/test_http.py @@ -167,7 +167,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' -- cgit v1.2.1 From 0b745909a66508b539c848e2d8101a56abadb69e Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 15:21:53 -0500 Subject: Fix tests passing user, project, and token The tests were creating httpclient.HTTPClient() using username, token, and project, but if you pass a token then username and project are going to be ignored since there's no need to auth. Make the tests more understandable by removing the ignored and useless parameters. bp deprecations Change-Id: Ide3f4be4dd00db89f551d014876625cff296f6a7 --- keystoneclient/tests/unit/test_base.py | 8 ++------ keystoneclient/tests/unit/v2_0/utils.py | 4 +--- keystoneclient/tests/unit/v3/utils.py | 4 +--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/keystoneclient/tests/unit/test_base.py b/keystoneclient/tests/unit/test_base.py index 6871702..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,9 +83,7 @@ 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) 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/utils.py b/keystoneclient/tests/unit/v3/utils.py index b5fb7ce..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) -- cgit v1.2.1 From 9e470a5d7757b97ef74a8c3ecdd95852221db450 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 29 Jul 2015 03:50:34 +0000 Subject: Updated from global requirements Change-Id: I08cdf12dad7fc99cddc55580ea9a99fefd79a399 --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index b799ea2..39680bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr<2.0,>=1.3 argparse Babel>=1.3 iso8601>=0.1.9 -debtcollector>=0.3.0 # Apache-2.0 +debtcollector>=0.3.0 # Apache-2.0 netaddr>=0.7.12 oslo.config>=1.11.0 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 572c8e7..36d0a2e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,7 +12,7 @@ lxml>=2.3 mock>=1.2 oauthlib>=0.6 oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.7.0 # Apache-2.0 +oslotest>=1.9.0 # Apache-2.0 pycrypto>=2.6 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -- cgit v1.2.1 From aa5738c23de6b88ff98ad649bef7b023d71a1b02 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Sun, 2 Aug 2015 11:18:45 -0500 Subject: Remove check for requests version requirements.txt has requests>=2.5.2, so requests version is always going to be >= 2.4.1 and there's no need to check it. Change-Id: I8069cfbd54ce716979bc991d137bd2e71790a1e4 --- keystoneclient/session.py | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/keystoneclient/session.py b/keystoneclient/session.py index d353c98..a3c7a6f 100644 --- a/keystoneclient/session.py +++ b/keystoneclient/session.py @@ -906,29 +906,28 @@ class TCPKeepAliveAdapter(requests.adapters.HTTPAdapter): 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': - 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), + 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) ] - # 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.setdefault('socket_options', socket_options) + # 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.setdefault('socket_options', socket_options) super(TCPKeepAliveAdapter, self).init_poolmanager(*args, **kwargs) -- cgit v1.2.1 From 22236fd763ed8d240b254ed54f892fb6fc0f454a Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Sun, 2 Aug 2015 11:22:18 -0500 Subject: Clarify setting socket_options There was a lot of code that would have no effect if kwargs already had socket_options set. To make the code clearer, only execute the code if it's going to have an effect. Change-Id: Ic42f5a0bac07113aff59d36d19293dc6d65cd58a --- keystoneclient/session.py | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/keystoneclient/session.py b/keystoneclient/session.py index a3c7a6f..cace4f7 100644 --- a/keystoneclient/session.py +++ b/keystoneclient/session.py @@ -906,28 +906,30 @@ class TCPKeepAliveAdapter(requests.adapters.HTTPAdapter): 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): - 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) + 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), ] - # 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.setdefault('socket_options', socket_options) + # 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) -- cgit v1.2.1 From a9ef92a43f7d3cecd6a92d07fe7b4857d00eb2e4 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 4 Aug 2015 00:48:54 +0000 Subject: Updated from global requirements Change-Id: Idb9238ef74d67dc2bccf5ca49ada78507dfdf373 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 36d0a2e..9ccaa3a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,7 +12,7 @@ lxml>=2.3 mock>=1.2 oauthlib>=0.6 oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.9.0 # Apache-2.0 +oslotest>=1.10.0 # Apache-2.0 pycrypto>=2.6 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -- cgit v1.2.1 From 1721e01743324fc10630131d590659f565a3684d Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 15:06:00 -0500 Subject: Proper deprecation for HTTPClient tenant_id, tenant_name parameters HTTPClient() tenant_id and tenant_name parameters weren't properly deprecated since they were only mentioned in the docstring. Proper deprecation requires use of warnings/debtcollector and documentation. Also fixed a bunch of places in the tests where tenant_id and tenant_name were still being used despite being deprecated. bp deprecations Change-Id: I9c4f596b8ff10aede6c417886638a942cb18044c --- keystoneclient/httpclient.py | 14 +++++++++++--- keystoneclient/tests/unit/test_http.py | 4 ++-- keystoneclient/tests/unit/test_https.py | 4 ++-- keystoneclient/tests/unit/test_keyring.py | 10 +++++----- keystoneclient/tests/unit/v2_0/test_auth.py | 14 +++++++------- keystoneclient/tests/unit/v2_0/test_client.py | 14 +++++++------- keystoneclient/tests/unit/v3/test_auth.py | 2 +- keystoneclient/tests/unit/v3/test_client.py | 4 ++-- keystoneclient/v3/client.py | 10 ++++++---- 9 files changed, 43 insertions(+), 33 deletions(-) diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index 37c6a4d..fb7495f 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -22,6 +22,7 @@ OpenStack Client interface. Handles the REST calls and responses. import logging from debtcollector import removals +from debtcollector import renames from oslo_serialization import jsonutils import pkg_resources import requests @@ -190,10 +191,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. @@ -216,6 +220,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, diff --git a/keystoneclient/tests/unit/test_http.py b/keystoneclient/tests/unit/test_http.py index 9a8bee2..7ef25ee 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 @@ -118,7 +118,7 @@ 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') diff --git a/keystoneclient/tests/unit/test_https.py b/keystoneclient/tests/unit/test_https.py index 541a0d6..77ddf80 100644 --- a/keystoneclient/tests/unit/test_https.py +++ b/keystoneclient/tests/unit/test_https.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", cacert="ca.pem", key="key.pem", cert="cert.pem") return cl @@ -82,7 +82,7 @@ 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" diff --git a/keystoneclient/tests/unit/test_keyring.py b/keystoneclient/tests/unit/test_keyring.py index 4fea794..defd233 100644 --- a/keystoneclient/tests/unit/test_keyring.py +++ b/keystoneclient/tests/unit/test_keyring.py @@ -88,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' @@ -105,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, @@ -119,7 +119,7 @@ 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 @@ -147,7 +147,7 @@ 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 @@ -163,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/v2_0/test_auth.py b/keystoneclient/tests/unit/v2_0/test_auth.py index 2c69dc3..b80ba35 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] @@ -174,7 +174,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 +193,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,7 +227,7 @@ 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) diff --git a/keystoneclient/tests/unit/v2_0/test_client.py b/keystoneclient/tests/unit/v2_0/test_client.py index be48e84..447c750 100644 --- a/keystoneclient/tests/unit/v2_0/test_client.py +++ b/keystoneclient/tests/unit/v2_0/test_client.py @@ -49,7 +49,7 @@ 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) with self.deprecations.expect_deprecations_here(): @@ -67,7 +67,7 @@ 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)) @@ -88,7 +88,7 @@ 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" @@ -133,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) @@ -147,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'), @@ -155,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'), @@ -164,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/v3/test_auth.py b/keystoneclient/tests/unit/v3/test_auth.py index b3f29d6..3cb5cb6 100644 --- a/keystoneclient/tests/unit/v3/test_auth.py +++ b/keystoneclient/tests/unit/v3/test_auth.py @@ -321,7 +321,7 @@ 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) diff --git a/keystoneclient/tests/unit/v3/test_client.py b/keystoneclient/tests/unit/v3/test_client.py index c01cac2..8f665ac 100644 --- a/keystoneclient/tests/unit/v3/test_client.py +++ b/keystoneclient/tests/unit/v3/test_client.py @@ -190,7 +190,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 +198,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/v3/client.py b/keystoneclient/v3/client.py index 0e9a036..3d37e3c 100644 --- a/keystoneclient/v3/client.py +++ b/keystoneclient/v3/client.py @@ -66,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. -- cgit v1.2.1 From ada04acf4d60eafc0e31e6ac079ddb2c4f12e728 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 15:34:27 -0500 Subject: Proper deprecation for HTTPClient.tenant_id|name HTTPClient tenant_id and tenant_name properties weren't properly deprecated since they were only mentioned in the docstring. Proper deprecation requires use of warnings/debtcollector and documentation. bp deprecations Change-Id: I3c2f87df14bc9f8ffd82b99919fd1048123d0669 --- keystoneclient/httpclient.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index fb7495f..933c1ff 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -20,6 +20,7 @@ OpenStack Client interface. Handles the REST calls and responses. """ import logging +import warnings from debtcollector import removals from debtcollector import renames @@ -424,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) -- cgit v1.2.1 From 0c2fef51d2b90df088d30e9b6c5079caae7c6578 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Fri, 24 Jul 2015 15:52:57 -0500 Subject: Proper deprecation for HTTPClient.request methods HTTPClient.request and related methods weren't properly deprecated since they were only mentioned in the docstrings. Proper deprecation requires use of warnings/debtcollector and documentation. Also, fixed places where the deprecated request method was called. bp deprecations Change-Id: I0a16933252937ca046793bb6eb2e5cc5da03c9ae --- keystoneclient/generic/client.py | 12 ++--- keystoneclient/httpclient.py | 70 ++++++++++++++++++++--------- keystoneclient/tests/unit/test_http.py | 12 +++-- keystoneclient/tests/unit/test_https.py | 9 ++-- keystoneclient/tests/unit/v2_0/test_auth.py | 12 +++-- keystoneclient/tests/unit/v3/test_auth.py | 12 +++-- 6 files changed, 85 insertions(+), 42 deletions(-) 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 933c1ff..9148f80 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -696,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. @@ -703,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) @@ -715,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. @@ -731,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. @@ -744,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. @@ -757,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. @@ -770,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. @@ -783,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. @@ -796,9 +821,12 @@ 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) diff --git a/keystoneclient/tests/unit/test_http.py b/keystoneclient/tests/unit/test_http.py index 7ef25ee..2b29ee7 100644 --- a/keystoneclient/tests/unit/test_http.py +++ b/keystoneclient/tests/unit/test_http.py @@ -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]') @@ -123,7 +126,8 @@ class ClientTest(utils.TestCase): 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) diff --git a/keystoneclient/tests/unit/test_https.py b/keystoneclient/tests/unit/test_https.py index 77ddf80..bf93226 100644 --- a/keystoneclient/tests/unit/test_https.py +++ b/keystoneclient/tests/unit/test_https.py @@ -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 @@ -87,7 +89,8 @@ class ClientTest(utils.TestCase): 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/v2_0/test_auth.py b/keystoneclient/tests/unit/v2_0/test_auth.py index b80ba35..5f318ff 100644 --- a/keystoneclient/tests/unit/v2_0/test_auth.py +++ b/keystoneclient/tests/unit/v2_0/test_auth.py @@ -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') @@ -233,7 +234,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase): 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/v3/test_auth.py b/keystoneclient/tests/unit/v3/test_auth.py index 3cb5cb6..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') @@ -327,7 +328,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase): 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') -- cgit v1.2.1 From eaa7ddd7443ca166f6646e808dcad959811d158b Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Sun, 26 Jul 2015 06:53:58 -0500 Subject: Proper deprecation for HTTPClient session and adapter properties HTTPClient's forwarded session and adapter properties weren't properly deprecated since the deprecations was only mentioned in the docstring. Proper deprecation requires use of warnings/ debtcollector and documentation. bp deprecations Change-Id: Iea76d7bbc3bdeb13f7fdb097f13e007b4dd85c8d --- keystoneclient/httpclient.py | 21 ++++++++++++++++----- keystoneclient/tests/unit/test_discovery.py | 3 ++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index 9148f80..c663535 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -830,9 +830,6 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin): """ 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, @@ -841,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: @@ -854,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: @@ -872,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/tests/unit/test_discovery.py b/keystoneclient/tests/unit/test_discovery.py index 6a76d8f..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) -- cgit v1.2.1 From 26534dadb1d0be00b87b632a038839ab1c18cfe4 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Tue, 4 Aug 2015 19:57:26 -0500 Subject: oslo-incubator apiclient.exceptions to keystoneclient.exceptions Applications are using exceptions out of keystoneclient.openstack.common.apiclient.exceptions so it's part of the public interface. But since it's from oslo-incubator it's not considered stable. Since keystoneclient is all stable this creates bad situation. With this change, all the symbols out of apiclient.exceptions are moved into keystoneclient.exceptions rather than the other way around (keystoneclient.exceptions used to re-export all of apiclient.exceptions). Now we're in control of the apiclient.exceptions and keystoneclient.exceptions that applications are using. Closes-Bug: 1481806 Change-Id: Ib3afa86b9d276f6a45d1ecd6f9157ee02ec8570d --- keystoneclient/exceptions.py | 451 ++++++++++++++++++- .../openstack/common/apiclient/exceptions.py | 496 +++------------------ 2 files changed, 495 insertions(+), 452 deletions(-) diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py index fb0bd41..d88bb73 100644 --- a/keystoneclient/exceptions.py +++ b/keystoneclient/exceptions.py @@ -12,28 +12,457 @@ # 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. +"""Exception definitions.""" -.. py:exception:: AuthorizationFailure +import inspect +import sys -.. py:exception:: ClientException +import six + +from keystoneclient.i18n import _ -.. py:exception:: HttpError -.. py:exception:: ValidationError +class ClientException(Exception): + """The base exception class for all exceptions this library raises. + """ + pass -.. py:exception:: Unauthorized -""" +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.i18n import _ -from keystoneclient.openstack.common.apiclient.exceptions import * # noqa # 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. -ConnectionError = ConnectionRefused HTTPNotImplemented = HttpNotImplemented Timeout = RequestTimeout HTTPError = HttpError 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 -- cgit v1.2.1 From 16e834dd4597314d79cf4fb0bb98449e6552f804 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Wed, 5 Aug 2015 11:17:34 -0500 Subject: Move apiclient.base.Resource into keystoneclient keystoneclient is using apiclient.base and in order to properly deprecate and eventually get rid of apiclient we need to move the symbols that keystoneclient uses out of apiclient. This change moves apiclient.base.Resource into keystoneclient.base by merging apiclient.base.Resource into the existing keystoneclient.base.Resource. apiclient.base.Resource is now renaming keystoneclient.base.Resource for backwards-compatibility. Change-Id: Id479711b7c9437aaf171def6976aab8b303ec56d --- keystoneclient/base.py | 93 +++++++++++++++++++- keystoneclient/openstack/common/apiclient/base.py | 102 ++-------------------- 2 files changed, 98 insertions(+), 97 deletions(-) diff --git a/keystoneclient/base.py b/keystoneclient/base.py index d2c3ea0..f19ed84 100644 --- a/keystoneclient/base.py +++ b/keystoneclient/base.py @@ -20,16 +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): @@ -439,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/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 "" % 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 -- cgit v1.2.1 From 51d9d123a8df79f6b84d196d41f1008f8d6033d4 Mon Sep 17 00:00:00 2001 From: Brant Knudson Date: Wed, 5 Aug 2015 12:28:30 -0500 Subject: Deprecate openstack.common.apiclient Deprecate the apiclient from oslo-incubator so we can get rid of it. bp deprecations Change-Id: I1c761933816da03b6c625f14d0aac43f206e88d7 --- .../openstack/common/apiclient/__init__.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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') -- cgit v1.2.1