summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/index.rst7
-rw-r--r--doc/source/man/keystone.rst18
-rw-r--r--doc/source/middlewarearchitecture.rst428
-rw-r--r--doc/source/using-api-v2.rst8
-rw-r--r--doc/source/using-api-v3.rst29
-rw-r--r--doc/source/using-sessions.rst10
-rw-r--r--keystoneclient/_discover.py22
-rw-r--r--keystoneclient/access.py6
-rw-r--r--keystoneclient/adapter.py9
-rw-r--r--keystoneclient/auth/base.py7
-rw-r--r--keystoneclient/auth/cli.py6
-rw-r--r--keystoneclient/auth/conf.py3
-rw-r--r--keystoneclient/auth/identity/base.py34
-rw-r--r--keystoneclient/auth/identity/generic/base.py10
-rw-r--r--keystoneclient/auth/identity/v3.py14
-rw-r--r--keystoneclient/base.py9
-rw-r--r--keystoneclient/client.py6
-rw-r--r--keystoneclient/common/cms.py64
-rw-r--r--keystoneclient/contrib/auth/v3/saml2.py57
-rw-r--r--keystoneclient/contrib/ec2/utils.py8
-rw-r--r--keystoneclient/contrib/revoke/model.py2
-rw-r--r--keystoneclient/discover.py20
-rw-r--r--keystoneclient/exceptions.py24
-rw-r--r--keystoneclient/fixture/discovery.py3
-rw-r--r--keystoneclient/fixture/v2.py3
-rw-r--r--keystoneclient/fixture/v3.py3
-rw-r--r--keystoneclient/generic/client.py19
-rw-r--r--keystoneclient/generic/shell.py12
-rw-r--r--keystoneclient/httpclient.py35
-rw-r--r--keystoneclient/i18n.py37
-rw-r--r--keystoneclient/middleware/auth_token.py13
-rw-r--r--keystoneclient/middleware/s3_token.py3
-rw-r--r--keystoneclient/openstack/common/__init__.py17
-rw-r--r--keystoneclient/openstack/common/_i18n.py40
-rw-r--r--keystoneclient/openstack/common/apiclient/base.py6
-rw-r--r--keystoneclient/openstack/common/apiclient/client.py35
-rw-r--r--keystoneclient/openstack/common/apiclient/exceptions.py10
-rw-r--r--keystoneclient/openstack/common/apiclient/fake_client.py2
-rw-r--r--keystoneclient/openstack/common/apiclient/utils.py87
-rw-r--r--keystoneclient/openstack/common/gettextutils.py479
-rw-r--r--keystoneclient/openstack/common/importutils.py73
-rw-r--r--keystoneclient/openstack/common/jsonutils.py202
-rw-r--r--keystoneclient/openstack/common/memorycache.py3
-rw-r--r--keystoneclient/openstack/common/strutils.py311
-rw-r--r--keystoneclient/openstack/common/timeutils.py210
-rw-r--r--keystoneclient/openstack/common/uuidutils.py37
-rw-r--r--keystoneclient/service_catalog.py35
-rw-r--r--keystoneclient/session.py50
-rw-r--r--keystoneclient/shell.py4
-rw-r--r--keystoneclient/tests/auth/test_identity_common.py2
-rw-r--r--keystoneclient/tests/client_fixtures.py4
-rw-r--r--keystoneclient/tests/generic/test_client.py3
-rw-r--r--keystoneclient/tests/test_auth_token_middleware.py14
-rw-r--r--keystoneclient/tests/test_cms.py12
-rw-r--r--keystoneclient/tests/test_discovery.py2
-rw-r--r--keystoneclient/tests/test_keyring.py2
-rw-r--r--keystoneclient/tests/test_s3_token_middleware.py2
-rw-r--r--keystoneclient/tests/test_session.py2
-rw-r--r--keystoneclient/tests/utils.py3
-rw-r--r--keystoneclient/tests/v2_0/test_access.py2
-rw-r--r--keystoneclient/tests/v2_0/test_auth.py5
-rw-r--r--keystoneclient/tests/v2_0/test_shell.py2
-rw-r--r--keystoneclient/tests/v3/test_access.py3
-rw-r--r--keystoneclient/tests/v3/test_auth.py3
-rw-r--r--keystoneclient/tests/v3/test_client.py1
-rw-r--r--keystoneclient/tests/v3/test_oauth1.py2
-rw-r--r--keystoneclient/tests/v3/test_trusts.py3
-rw-r--r--keystoneclient/utils.py6
-rw-r--r--keystoneclient/v2_0/__init__.py3
-rw-r--r--keystoneclient/v2_0/client.py16
-rwxr-xr-xkeystoneclient/v2_0/shell.py43
-rw-r--r--keystoneclient/v2_0/tokens.py4
-rw-r--r--keystoneclient/v3/__init__.py4
-rw-r--r--keystoneclient/v3/client.py24
-rw-r--r--keystoneclient/v3/contrib/endpoint_filter.py13
-rw-r--r--keystoneclient/v3/contrib/endpoint_policy.py11
-rw-r--r--keystoneclient/v3/contrib/federation/__init__.py2
-rw-r--r--keystoneclient/v3/contrib/federation/mappings.py107
-rw-r--r--keystoneclient/v3/contrib/oauth1/core.py3
-rw-r--r--keystoneclient/v3/contrib/trusts.py8
-rw-r--r--keystoneclient/v3/credentials.py3
-rw-r--r--keystoneclient/v3/endpoints.py3
-rw-r--r--keystoneclient/v3/role_assignments.py29
-rw-r--r--keystoneclient/v3/roles.py9
-rw-r--r--keystoneclient/v3/users.py19
-rw-r--r--openstack-common.conf3
-rw-r--r--requirements.txt7
-rw-r--r--test-requirements.txt7
-rwxr-xr-xtools/debug_helper.sh18
-rw-r--r--tox.ini7
90 files changed, 776 insertions, 2170 deletions
diff --git a/doc/source/index.rst b/doc/source/index.rst
index d75547c..08f8145 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -12,13 +12,10 @@ Contents:
:maxdepth: 1
man/keystone
- using-sessions
- using-api-v2
using-api-v3
-
+ using-sessions
authentication-plugins
- middlewarearchitecture
-
+ using-api-v2
api/modules
Contributing
diff --git a/doc/source/man/keystone.rst b/doc/source/man/keystone.rst
index d96d89f..10d9d93 100644
--- a/doc/source/man/keystone.rst
+++ b/doc/source/man/keystone.rst
@@ -1,6 +1,6 @@
-========================================
-:program:`keystone` command line utility
-========================================
+==============================================================
+:program:`keystone` command line utility (pending deprecation)
+==============================================================
.. program:: keystone
.. highlight:: bash
@@ -18,13 +18,21 @@ SYNOPSIS
DESCRIPTION
===========
+.. WARNING::
+
+ The :program:`keystone` command line utility is pending deprecation. The
+ `OpenStackClient unified command line utility
+ <http://docs.openstack.org/developer/python-openstackclient/>`_ should be
+ used instead. The :program:`keystone` command line utility only supports V2
+ of the Identity API whereas the OSC program supports both V2 and V3.
+
The :program:`keystone` command line utility interacts with services providing
OpenStack Identity API (e.g. Keystone).
To communicate with the API, you will need to be authenticated - and the
:program:`keystone` provides multiple options for this.
-While bootstrapping keystone the authentication is accomplished with a
+While bootstrapping Keystone the authentication is accomplished with a
shared secret token and the location of the Identity API endpoint. The
shared secret token is configured in keystone.conf as "admin_token".
@@ -33,7 +41,7 @@ and :option:`--os-endpoint`, or set them in environment variables:
.. envvar:: OS_SERVICE_TOKEN
- Your keystone administrative token
+ Your Keystone administrative token
.. envvar:: OS_SERVICE_ENDPOINT
diff --git a/doc/source/middlewarearchitecture.rst b/doc/source/middlewarearchitecture.rst
deleted file mode 100644
index 47ae531..0000000
--- a/doc/source/middlewarearchitecture.rst
+++ /dev/null
@@ -1,428 +0,0 @@
-..
- Copyright 2011-2013 OpenStack Foundation
- 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.
-
-=======================
-Middleware Architecture
-=======================
-
-Abstract
-========
-
-The Keystone middleware architecture supports a common authentication protocol
-in use between the OpenStack projects. By using keystone as a common
-authentication and authorization mechanism, the OpenStack project can plug in
-to existing authentication and authorization systems in use by existing
-environments.
-
-In this document, we describe the architecture and responsibilities of the
-authentication middleware which acts as the internal API mechanism for
-OpenStack projects based on the WSGI standard.
-
-This documentation describes the implementation in
-:class:`keystoneclient.middleware.auth_token`
-
-Specification Overview
-======================
-
-'Authentication' is the process of determining that users are who they say they
-are. Typically, 'authentication protocols' such as HTTP Basic Auth, Digest
-Access, public key, token, etc, are used to verify a user's identity. In this
-document, we define an ''authentication component'' as a software module that
-implements an authentication protocol for an OpenStack service. OpenStack is
-using a token based mechanism to represent authentication and authorization.
-
-At a high level, an authentication middleware component is a proxy that
-intercepts HTTP calls from clients and populates HTTP headers in the request
-context for other WSGI middleware or applications to use. The general flow
-of the middleware processing is:
-
-* clear any existing authorization headers to prevent forgery
-* collect the token from the existing HTTP request headers
-* validate the token
-
- * if valid, populate additional headers representing the identity that has
- been authenticated and authorized
- * if invalid, or no token present, reject the request (HTTPUnauthorized)
- or pass along a header indicating the request is unauthorized (configurable
- in the middleware)
- * if the keystone service is unavailable to validate the token, reject
- the request with HTTPServiceUnavailable.
-
-.. _authComponent:
-
-Authentication Component
-------------------------
-
-Figure 1. Authentication Component
-
-.. image:: images/graphs_authComp.svg
- :width: 100%
- :height: 180
- :alt: An Authentication Component
-
-The middleware may also be configured to operate in a 'delegated mode'.
-In this mode, the decision to reject an unauthenticated client is delegated to
-the OpenStack service, as illustrated in :ref:`authComponentDelegated`.
-
-Here, requests are forwarded to the OpenStack service with an identity status
-message that indicates whether the client's identity has been confirmed or is
-indeterminate. It is the OpenStack service that decides whether or not a reject
-message should be sent to the client.
-
-.. _authComponentDelegated:
-
-Authentication Component (Delegated Mode)
------------------------------------------
-
-Figure 2. Authentication Component (Delegated Mode)
-
-.. image:: images/graphs_authCompDelegate.svg
- :width: 100%
- :height: 180
- :alt: An Authentication Component (Delegated Mode)
-
-.. _deployStrategies:
-
-Deployment Strategy
-===================
-
-The middleware is intended to be used inline with OpenStack wsgi components,
-based on the Oslo WSGI middleware class. It is typically deployed
-as a configuration element in a paste configuration pipeline of other
-middleware components, with the pipeline terminating in the service
-application. The middleware conforms to the python WSGI standard [PEP-333]_.
-In initializing the middleware, a configuration item (which acts like a python
-dictionary) is passed to the middleware with relevant configuration options.
-
-Configuration
--------------
-
-The middleware is configured within the config file of the main application as
-a WSGI component. Example for the auth_token middleware::
-
- [app:myService]
- paste.app_factory = myService:app_factory
-
- [pipeline:main]
- pipeline = authtoken myService
-
- [filter:authtoken]
- paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
-
- # Prefix to prepend at the beginning of the path (string
- # value)
- #auth_admin_prefix=
-
- # Host providing the admin Identity API endpoint (string
- # value)
- auth_host=127.0.0.1
-
- # Port of the admin Identity API endpoint (integer value)
- auth_port=35357
-
- # Protocol of the admin Identity API endpoint(http or https)
- # (string value)
- auth_protocol=https
-
- # Complete public Identity API endpoint (string value)
- #auth_uri=<None>
-
- # API version of the admin Identity API endpoint (string
- # value)
- #auth_version=<None>
-
- # Do not handle authorization requests within the middleware,
- # but delegate the authorization decision to downstream WSGI
- # components (boolean value)
- #delay_auth_decision=false
-
- # Request timeout value for communicating with Identity API
- # server. (boolean value)
- #http_connect_timeout=<None>
-
- # How many times are we trying to reconnect when communicating
- # with Identity API Server. (integer value)
- #http_request_max_retries=3
-
- # Single shared secret with the Keystone configuration used
- # for bootstrapping a Keystone installation, or otherwise
- # bypassing the normal authentication process. (string value)
- #admin_token=<None>
-
- # Keystone account username (string value)
- #admin_user=<None>
-
- # Keystone account password (string value)
- admin_password=SuperSekretPassword
-
- # Keystone service account tenant name to validate user tokens
- # (string value)
- #admin_tenant_name=admin
-
- # Env key for the swift cache (string value)
- #cache=<None>
-
- # Required if Keystone server requires client certificate
- # (string value)
- #certfile=<None>
-
- # Required if Keystone server requires client certificate
- # (string value)
- #keyfile=<None>
-
- # A PEM encoded Certificate Authority to use when verifying
- # HTTPs connections. Defaults to system CAs. (string value)
- #cafile=<None>
-
- # Verify HTTPS connections. (boolean value)
- #insecure=false
-
- # Directory used to cache files related to PKI tokens (string
- # value)
- #signing_dir=<None>
-
- # If defined, the memcache server(s) to use for caching (list
- # value)
- # Deprecated group/name - [DEFAULT]/memcache_servers
- #memcached_servers=<None>
-
- # In order to prevent excessive requests and validations, the
- # middleware uses an in-memory cache for the tokens the
- # Keystone API returns. This is only valid if memcache_servers
- # is defined. Set to -1 to disable caching completely.
- # (integer value)
- #token_cache_time=300
-
- # Value only used for unit testing (integer value)
- #revocation_cache_time=1
-
- # (optional) if defined, indicate whether token data should be
- # authenticated or authenticated and encrypted. Acceptable
- # values are MAC or ENCRYPT. If MAC, token data is
- # authenticated (with HMAC) in the cache. If ENCRYPT, token
- # data is encrypted and authenticated in the cache. If the
- # value is not one of these options or empty, auth_token will
- # raise an exception on initialization. (string value)
- #memcache_security_strategy=<None>
-
- # (optional, mandatory if memcache_security_strategy is
- # defined) this string is used for key derivation. (string
- # value)
- #memcache_secret_key=<None>
-
- # (optional) indicate whether to set the X-Service-Catalog
- # header. If False, middleware will not ask for service
- # catalog on token validation and will not set the X-Service-
- # Catalog header. (boolean value)
- #include_service_catalog=true
-
- # Used to control the use and type of token binding. Can be
- # set to: "disabled" to not check token binding. "permissive"
- # (default) to validate binding information if the bind type
- # is of a form known to the server and ignore it if not.
- # "strict" like "permissive" but if the bind type is unknown
- # the token will be rejected. "required" any form of token
- # binding is needed to be allowed. Finally the name of a
- # binding method that must be present in tokens. (string
- # value)
- #enforce_token_bind=permissive
-
-For services which have a separate paste-deploy ini file, auth_token middleware
-can be alternatively configured in [keystone_authtoken] section in the main
-config file. For example in Nova, all middleware parameters can be removed
-from api-paste.ini::
-
- [filter:authtoken]
- paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
-
-and set in nova.conf::
-
- [DEFAULT]
- ...
- auth_strategy=keystone
-
- [keystone_authtoken]
- auth_host = 127.0.0.1
- auth_port = 35357
- auth_protocol = http
- admin_user = admin
- admin_password = SuperSekretPassword
- admin_tenant_name = service
- # Any of the options that could be set in api-paste.ini can be set here.
-
-Note that middleware parameters in paste config take priority, they must be
-removed to use values in [keystone_authtoken] section.
-
-Configuration Options
----------------------
-
-* ``auth_admin_prefix``: Prefix to prepend at the beginning of the path
-* ``auth_host``: (required) the host providing the keystone service API endpoint
- for validating and requesting tokens
-* ``auth_port``: (optional, default `35357`) the port used to validate tokens
-* ``auth_protocol``: (optional, default `https`)
-* ``auth_uri``: (optional, defaults to
- `auth_protocol`://`auth_host`:`auth_port`)
-* ``auth_version``: API version of the admin Identity API endpoint
-* ``delay_auth_decision``: (optional, default `0`) (off). If on, the middleware
- will not reject invalid auth requests, but will delegate that decision to
- downstream WSGI components.
-* ``http_connect_timeout``: (optional) Request timeout value for communicating
- with Identity API server.
-* ``http_request_max_retries``: (default 3) How many times are we trying to
- reconnect when communicating with Identity API Server.
-* ``http_handler``: (optional) Allows to pass in the name of a fake
- http_handler callback function used instead of `httplib.HTTPConnection` or
- `httplib.HTTPSConnection`. Useful for unit testing where network is not
- available.
-
-* ``admin_token``: either this or the following three options are required. If
- set, this is a single shared secret with the keystone configuration used to
- validate tokens.
-* ``admin_user``, ``admin_password``, ``admin_tenant_name``: if ``admin_token``
- is not set, or invalid, then admin_user, admin_password, and
- admin_tenant_name are defined as a service account which is expected to have
- been previously configured in Keystone to validate user tokens.
-
-* ``cache``: (optional) Env key for the swift cache
-
-* ``certfile``: (required, if Keystone server requires client cert)
-* ``keyfile``: (required, if Keystone server requires client cert) This can be
- the same as the certfile if the certfile includes the private key.
-* ``cafile``: (optional, defaults to use system CA bundle) the path to a PEM
- encoded CA file/bundle that will be used to verify HTTPS connections.
-* ``insecure``: (optional, default `False`) Don't verify HTTPS connections
- (overrides `cafile`).
-
-* ``signing_dir``: (optional) Directory used to cache files related to PKI
- tokens
-
-* ``memcached_servers``: (optional) If defined, the memcache server(s) to use
- for caching
-* ``token_cache_time``: (default 300) In order to prevent excessive requests
- and validations, the middleware uses an in-memory cache for the tokens the
- Keystone API returns. This is only valid if memcache_servers s defined. Set
- to -1 to disable caching completely.
-* ``memcache_security_strategy``: (optional) if defined, indicate whether token
- data should be authenticated or authenticated and encrypted. Acceptable
- values are MAC or ENCRYPT. If MAC, token data is authenticated (with HMAC)
- in the cache. If ENCRYPT, token data is encrypted and authenticated in the
- cache. If the value is not one of these options or empty, auth_token will
- raise an exception on initialization.
-* ``memcache_secret_key``: (mandatory if memcache_security_strategy is defined)
- this string is used for key derivation.
-* ``include_service_catalog``: (optional, default `True`) Indicate whether to
- set the X-Service-Catalog header. If False, middleware will not ask for
- service catalog on token validation and will not set the X-Service-Catalog
- header.
-* ``enforce_token_bind``: (default ``permissive``) Used to control the use and
- type of token binding. Can be set to: "disabled" to not check token binding.
- "permissive" (default) to validate binding information if the bind type is of
- a form known to the server and ignore it if not. "strict" like "permissive"
- but if the bind type is unknown the token will be rejected. "required" any
- form of token binding is needed to be allowed. Finally the name of a binding
- method that must be present in tokens.
-
-Caching for improved response
------------------------------
-
-In order to prevent excessive requests and validations, the middleware uses an
-in-memory cache for the tokens the keystone API returns. Keep in mind that
-invalidated tokens may continue to work if they are still in the token cache,
-so token_cache_time is configurable. For larger deployments, the middleware
-also supports memcache based caching.
-
-* ``memcached_servers``: (optonal) if defined, the memcache server(s) to use for
- cacheing. It will be ignored if Swift MemcacheRing is used instead.
-* ``token_cache_time``: (optional, default 300 seconds) Set to -1 to disable
- caching completely.
-
-When deploying auth_token middleware with Swift, user may elect
-to use Swift MemcacheRing instead of the local Keystone memcache.
-The Swift MemcacheRing object is passed in from the request environment
-and it defaults to 'swift.cache'. However it could be
-different, depending on deployment. To use Swift MemcacheRing, you must
-provide the ``cache`` option.
-
-* ``cache``: (optional) if defined, the environment key where the Swift
- MemcacheRing object is stored.
-
-Memcached and System Time
-=========================
-
-When using `memcached`_ with ``auth_token`` middleware, ensure that the system
-time of memcached hosts is set to UTC. Memcached uses the host's system
-time in determining whether a key has expired, whereas Keystone sets
-key expiry in UTC. The timezone used by Keystone and memcached must
-match if key expiry is to behave as expected.
-
-.. _`memcached`: http://memcached.org/
-
-Memcache Protection
-===================
-
-When using memcached, we are storing user tokens and token validation
-information into the cache as raw data. Which means that anyone who
-has access to the memcache servers can read and modify data stored
-there. To mitigate this risk, ``auth_token`` middleware provides an
-option to authenticate and optionally encrypt the token data stored in
-the cache.
-
-* ``memcache_security_strategy``: (optional) if defined, indicate
- whether token data should be authenticated or authenticated and
- encrypted. Acceptable values are ``MAC`` or ``ENCRYPT``. If ``MAC``,
- token data is authenticated (with HMAC) in the cache. If
- ``ENCRYPT``, token data is encrypted and authenticated in the
- cache. If the value is not one of these options or empty,
- ``auth_token`` will raise an exception on initialization.
-* ``memcache_secret_key``: (optional, mandatory if
- ``memcache_security_strategy`` is defined) this string is used for
- key derivation. If ``memcache_security_strategy`` is defined and
- ``memcache_secret_key`` is absent, ``auth_token`` will raise an
- exception on initialization.
-
-Exchanging User Information
-===========================
-
-The middleware expects to find a token representing the user with the header
-``X-Auth-Token`` or ``X-Storage-Token``. `X-Storage-Token` is supported for
-swift/cloud files and for legacy Rackspace use. If the token isn't present and
-the middleware is configured to not delegate auth responsibility, it will
-respond to the HTTP request with HTTPUnauthorized, returning the header
-``WWW-Authenticate`` with the value `Keystone uri='...'` to indicate where to
-request a token. The auth_uri returned is configured with the middleware.
-
-The authentication middleware extends the HTTP request with the header
-``X-Identity-Status``. If a request is successfully authenticated, the value
-is set to `Confirmed`. If the middleware is delegating the auth decision to the
-service, then the status is set to `Invalid` if the auth request was
-unsuccessful.
-
-Extended the request with additional User Information
------------------------------------------------------
-
-:py:class:`keystoneclient.middleware.auth_token.AuthProtocol` extends the
-request with additional information if the user has been authenticated. See the
-"What we add to the request for use by the OpenStack service" section in
-:py:mod:`keystoneclient.middleware.auth_token` for the list of fields set by
-the auth_token middleware.
-
-
-References
-==========
-
-.. [PEP-333] pep0333 Phillip J Eby. 'Python Web Server Gateway Interface
- v1.0.'' http://www.python.org/dev/peps/pep-0333/.
diff --git a/doc/source/using-api-v2.rst b/doc/source/using-api-v2.rst
index 192e683..03e76e1 100644
--- a/doc/source/using-api-v2.rst
+++ b/doc/source/using-api-v2.rst
@@ -1,6 +1,6 @@
-=================
-The Client v2 API
-=================
+=======================
+Using the V2 Client API
+=======================
Introduction
============
@@ -13,7 +13,7 @@ The main concepts in the Identity v2 API are:
* services
* endpoints
-The client v2 API lets you query and make changes through
+The V2 client API lets you query and make changes through
managers. For example, to manipulate tenants, you interact with a
``keystoneclient.v2_0.tenants.TenantManager`` object.
diff --git a/doc/source/using-api-v3.rst b/doc/source/using-api-v3.rst
index 1327446..d339636 100644
--- a/doc/source/using-api-v3.rst
+++ b/doc/source/using-api-v3.rst
@@ -1,23 +1,24 @@
-=================
-The Client v3 API
-=================
+=======================
+Using the V3 Client API
+=======================
Introduction
============
The main concepts in the Identity v3 API are:
- * credentials
- * domains
- * endpoints
- * groups
- * policies
- * projects
- * role assignments
- * roles
- * services
- * trusts
- * users
+ * :py:mod:`~keystoneclient.v3.credentials`
+ * :py:mod:`~keystoneclient.v3.domains`
+ * :py:mod:`~keystoneclient.v3.endpoints`
+ * :py:mod:`~keystoneclient.v3.groups`
+ * :py:mod:`~keystoneclient.v3.policies`
+ * :py:mod:`~keystoneclient.v3.projects`
+ * :py:mod:`~keystoneclient.v3.regions`
+ * :py:mod:`~keystoneclient.v3.role_assignments`
+ * :py:mod:`~keystoneclient.v3.roles`
+ * :py:mod:`~keystoneclient.v3.services`
+ * :py:mod:`~keystoneclient.v3.tokens`
+ * :py:mod:`~keystoneclient.v3.users`
The :py:mod:`keystoneclient.v3.client` API lets you query and make changes
through ``managers``. For example, to manipulate a project (formerly
diff --git a/doc/source/using-sessions.rst b/doc/source/using-sessions.rst
index 099dc70..7d31a2d 100644
--- a/doc/source/using-sessions.rst
+++ b/doc/source/using-sessions.rst
@@ -75,13 +75,13 @@ fashion by passing the Session object to the client's constructor.
Migrating keystoneclient to use a Session
-----------------------------------------
-By using a session with a keystonclient Client we define that you have opted in
-to new behaviour defined by the session. For example authentication is now
-on-demand rather than on creation. To allow this change in behaviour there are
-a number of functions that have changed behaviour or are no longer available.
+By using a session with a keystoneclient Client we presume that you have opted
+in to new behavior defined by the session. For example authentication is now
+on-demand rather than on creation. To allow this change in behavior there are
+a number of functions that have changed behavior or are no longer available.
For example the
-:py:meth:`keystoneclient.httpclient.HTTPClient.authenticate` command used
+:py:meth:`keystoneclient.httpclient.HTTPClient.authenticate` method used
to be able to always re-authenticate the current client and fetch a new token.
As this is now controlled by the Session and not the client this has changed,
however the function will still exist to provide compatibility with older
diff --git a/keystoneclient/_discover.py b/keystoneclient/_discover.py
index 05e74d9..07d0ae6 100644
--- a/keystoneclient/_discover.py
+++ b/keystoneclient/_discover.py
@@ -25,6 +25,7 @@ import logging
import re
from keystoneclient import exceptions
+from keystoneclient.i18n import _, _LI, _LW
from keystoneclient import utils
@@ -65,7 +66,7 @@ def get_version_data(session, url, authenticated=None):
pass
err_text = resp.text[:50] + '...' if len(resp.text) > 50 else resp.text
- msg = 'Invalid Response - Bad version data returned: %s' % err_text
+ msg = _('Invalid Response - Bad version data returned: %s') % err_text
raise exceptions.DiscoveryFailure(msg)
@@ -99,7 +100,7 @@ def normalize_version_number(version):
except Exception:
pass
- raise TypeError('Invalid version specified: %s' % version)
+ raise TypeError(_('Invalid version specified: %s') % version)
def version_match(required, candidate):
@@ -161,8 +162,8 @@ class Discover(object):
try:
status = v['status']
except KeyError:
- _LOGGER.warning('Skipping over invalid version data. '
- 'No stability status in version.')
+ _LOGGER.warning(_LW('Skipping over invalid version data. '
+ 'No stability status in version.'))
continue
status = status.lower()
@@ -201,13 +202,14 @@ class Discover(object):
try:
version_str = v['id']
except KeyError:
- _LOGGER.info('Skipping invalid version data. Missing ID.')
+ _LOGGER.info(_LI('Skipping invalid version data. Missing ID.'))
continue
try:
links = v['links']
except KeyError:
- _LOGGER.info('Skipping invalid version data. Missing links')
+ _LOGGER.info(
+ _LI('Skipping invalid version data. Missing links'))
continue
version_number = normalize_version_number(version_str)
@@ -217,15 +219,15 @@ class Discover(object):
rel = link['rel']
url = link['href']
except (KeyError, TypeError):
- _LOGGER.info('Skipping invalid version link. '
- 'Missing link URL or relationship.')
+ _LOGGER.info(_LI('Skipping invalid version link. '
+ 'Missing link URL or relationship.'))
continue
if rel.lower() == 'self':
break
else:
- _LOGGER.info('Skipping invalid version data. '
- 'Missing link to endpoint.')
+ _LOGGER.info(_LI('Skipping invalid version data. '
+ 'Missing link to endpoint.'))
continue
versions.append({'version': version_number,
diff --git a/keystoneclient/access.py b/keystoneclient/access.py
index 95a041e..8d85c68 100644
--- a/keystoneclient/access.py
+++ b/keystoneclient/access.py
@@ -17,7 +17,9 @@
import datetime
-from keystoneclient.openstack.common import timeutils
+from oslo.utils import timeutils
+
+from keystoneclient.i18n import _
from keystoneclient import service_catalog
@@ -62,7 +64,7 @@ class AccessInfo(dict):
else:
auth_ref = AccessInfoV2(**kwargs)
else:
- raise NotImplementedError('Unrecognized auth response')
+ raise NotImplementedError(_('Unrecognized auth response'))
else:
auth_ref = AccessInfoV2(**kwargs)
diff --git a/keystoneclient/adapter.py b/keystoneclient/adapter.py
index b75b713..49dd198 100644
--- a/keystoneclient/adapter.py
+++ b/keystoneclient/adapter.py
@@ -10,7 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystoneclient.openstack.common import jsonutils
+from oslo.serialization import jsonutils
+
from keystoneclient import utils
@@ -93,7 +94,8 @@ class Adapter(object):
on the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
- :raises AuthorizationFailure: if a new token fetch fails.
+ :raises keystoneclient.exceptions.AuthorizationFailure: if a new token
+ fetch fails.
:returns: A valid token.
:rtype: string
@@ -107,7 +109,8 @@ class Adapter(object):
the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
- :raises MissingAuthPlugin: if a plugin is not available.
+ :raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not
+ available.
:returns: An endpoint if available or None.
:rtype: string
diff --git a/keystoneclient/auth/base.py b/keystoneclient/auth/base.py
index a766a0b..1f4ce29 100644
--- a/keystoneclient/auth/base.py
+++ b/keystoneclient/auth/base.py
@@ -17,6 +17,8 @@ import six
import stevedore
from keystoneclient import exceptions
+from keystoneclient.i18n import _
+
# NOTE(jamielennox): The AUTH_INTERFACE is a special value that can be
# requested from get_endpoint. If a plugin receives this as the value of
@@ -33,14 +35,15 @@ def get_plugin_class(name):
:returns: An auth plugin class.
- :raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
+ :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
+ created.
"""
try:
mgr = stevedore.DriverManager(namespace=PLUGIN_NAMESPACE,
name=name,
invoke_on_load=False)
except RuntimeError:
- msg = 'The plugin %s could not be found' % name
+ msg = _('The plugin %s could not be found') % name
raise exceptions.NoMatchingPlugin(msg)
return mgr.driver
diff --git a/keystoneclient/auth/cli.py b/keystoneclient/auth/cli.py
index f1f2de3..ce4f11f 100644
--- a/keystoneclient/auth/cli.py
+++ b/keystoneclient/auth/cli.py
@@ -31,7 +31,8 @@ def register_argparse_arguments(parser, argv, default=None):
:returns: The plugin class that will be loaded or None if not provided.
- :raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
+ :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
+ created.
"""
in_parser = argparse.ArgumentParser(add_help=False)
env_plugin = os.environ.get('OS_AUTH_PLUGIN', default)
@@ -68,7 +69,8 @@ def load_from_argparse_arguments(namespace, **kwargs):
:returns: An auth plugin, or None if a name is not provided.
- :raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
+ :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
+ created.
"""
if not namespace.os_auth_plugin:
return None
diff --git a/keystoneclient/auth/conf.py b/keystoneclient/auth/conf.py
index ad90281..0b68184 100644
--- a/keystoneclient/auth/conf.py
+++ b/keystoneclient/auth/conf.py
@@ -91,7 +91,8 @@ def load_from_conf_options(conf, group, **kwargs):
:returns: An authentication Plugin or None if a name is not provided
:rtype: plugin
- :raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
+ :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
+ created.
"""
# NOTE(jamielennox): plugins are allowed to specify a 'section' which is
# the group that auth options should be taken from. If not present they
diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py
index 1169cf6..94b0712 100644
--- a/keystoneclient/auth/identity/base.py
+++ b/keystoneclient/auth/identity/base.py
@@ -19,6 +19,7 @@ import six
from keystoneclient import _discover
from keystoneclient.auth import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _LW
from keystoneclient import utils
LOG = logging.getLogger(__name__)
@@ -73,8 +74,11 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
when invoked. If you are looking to just retrieve the current auth
data then you should use get_access.
- :raises InvalidResponse: The response returned wasn't appropriate.
- :raises HttpError: An error from an invalid HTTP response.
+ :raises keystoneclient.exceptions.InvalidResponse: The response
+ returned wasn't
+ appropriate.
+ :raises keystoneclient.exceptions.HttpError: An error from an invalid
+ HTTP response.
:returns: Token access information.
:rtype: :py:class:`keystoneclient.access.AccessInfo`
@@ -85,7 +89,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
If a valid token is not present then a new one will be fetched.
- :raises HttpError: An error from an invalid HTTP response.
+ :raises keystoneclient.exceptions.HttpError: An error from an invalid
+ HTTP response.
:return: A valid token.
:rtype: string
@@ -120,7 +125,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
If a valid AccessInfo is present then it is returned otherwise a new
one will be fetched.
- :raises HttpError: An error from an invalid HTTP response.
+ :raises keystoneclient.exceptions.HttpError: An error from an invalid
+ HTTP response.
:returns: Valid AccessInfo
:rtype: :py:class:`keystoneclient.access.AccessInfo`
@@ -173,7 +179,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
:param tuple version: The minimum version number required for this
endpoint. (optional)
- :raises HttpError: An error from an invalid HTTP response.
+ :raises keystoneclient.exceptions.HttpError: An error from an invalid
+ HTTP response.
:return: A valid endpoint URL or None if not available.
:rtype: string or None
@@ -186,9 +193,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
return self.auth_url
if not service_type:
- LOG.warn('Plugin cannot return an endpoint without knowing the '
- 'service type that is required. Add service_type to '
- 'endpoint filtering data.')
+ LOG.warn(_LW('Plugin cannot return an endpoint without knowing '
+ 'the service type that is required. Add service_type '
+ 'to endpoint filtering data.'))
return None
if not interface:
@@ -221,8 +228,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
# NOTE(jamielennox): Again if we can't contact the server we fall
# back to just returning the URL from the catalog. This may not be
# the best default but we need it for now.
- LOG.warn('Failed to contact the endpoint at %s for discovery. '
- 'Fallback to using that endpoint as the base url.', url)
+ LOG.warn(_LW('Failed to contact the endpoint at %s for discovery. '
+ 'Fallback to using that endpoint as the base url.'),
+ url)
else:
url = disc.url_for(version)
@@ -245,8 +253,10 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
(optional) Defaults to None (use a token
if a plugin is installed).
- :raises: DiscoveryFailure if for some reason the lookup fails.
- :raises: HttpError An error from an invalid HTTP response.
+ :raises keystoneclient.exceptions.DiscoveryFailure: if for some reason
+ the lookup fails.
+ :raises keystoneclient.exceptions.HttpError: An error from an invalid
+ HTTP response.
:returns: A discovery object with the results of looking up that URL.
"""
diff --git a/keystoneclient/auth/identity/generic/base.py b/keystoneclient/auth/identity/generic/base.py
index 94d48ec..631eebd 100644
--- a/keystoneclient/auth/identity/generic/base.py
+++ b/keystoneclient/auth/identity/generic/base.py
@@ -20,6 +20,8 @@ import six.moves.urllib.parse as urlparse
from keystoneclient import _discover
from keystoneclient.auth.identity import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _, _LW
+
LOG = logging.getLogger(__name__)
@@ -127,9 +129,9 @@ class BaseGenericPlugin(base.BaseIdentityPlugin):
except (exceptions.DiscoveryFailure,
exceptions.HTTPError,
exceptions.ConnectionError):
- LOG.warn('Discovering versions from the identity service failed '
- 'when creating the password plugin. Attempting to '
- 'determine version from URL.')
+ LOG.warn(_LW('Discovering versions from the identity service '
+ 'failed when creating the password plugin. '
+ 'Attempting to determine version from URL.'))
url_parts = urlparse.urlparse(self.auth_url)
path = url_parts.path.lower()
@@ -163,7 +165,7 @@ class BaseGenericPlugin(base.BaseIdentityPlugin):
return plugin
# so there were no URLs that i could use for auth of any version.
- msg = 'Could not determine a suitable URL for the plugin'
+ msg = _('Could not determine a suitable URL for the plugin')
raise exceptions.DiscoveryFailure(msg)
def get_auth_ref(self, session, **kwargs):
diff --git a/keystoneclient/auth/identity/v3.py b/keystoneclient/auth/identity/v3.py
index 6755bef..0749924 100644
--- a/keystoneclient/auth/identity/v3.py
+++ b/keystoneclient/auth/identity/v3.py
@@ -19,6 +19,7 @@ import six
from keystoneclient import access
from keystoneclient.auth.identity import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
_logger = logging.getLogger(__name__)
@@ -84,18 +85,17 @@ class Auth(base.BaseIdentityPlugin):
ident[name] = auth_data
if not ident:
- raise exceptions.AuthorizationFailure('Authentication method '
- 'required (e.g. password)')
+ raise exceptions.AuthorizationFailure(
+ _('Authentication method required (e.g. password)'))
mutual_exclusion = [bool(self.domain_id or self.domain_name),
bool(self.project_id or self.project_name),
bool(self.trust_id)]
if sum(mutual_exclusion) > 1:
- raise exceptions.AuthorizationFailure('Authentication cannot be '
- 'scoped to multiple '
- 'targets. Pick one of: '
- 'project, domain or trust')
+ raise exceptions.AuthorizationFailure(
+ _('Authentication cannot be scoped to multiple targets. Pick '
+ 'one of: project, domain or trust'))
if self.domain_id:
body['auth']['scope'] = {'domain': {'id': self.domain_id}}
@@ -165,7 +165,7 @@ class AuthMethod(object):
setattr(self, param, kwargs.pop(param, None))
if kwargs:
- msg = "Unexpected Attributes: %s" % ", ".join(kwargs.keys())
+ msg = _("Unexpected Attributes: %s") % ", ".join(kwargs.keys())
raise AttributeError(msg)
@classmethod
diff --git a/keystoneclient/base.py b/keystoneclient/base.py
index 2571a37..030afef 100644
--- a/keystoneclient/base.py
+++ b/keystoneclient/base.py
@@ -27,6 +27,7 @@ from six.moves import urllib
from keystoneclient import auth
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient.openstack.common.apiclient import base
@@ -219,7 +220,7 @@ class Manager(object):
management=management,
**kwargs)
except KeyError:
- raise exceptions.ClientException("Invalid update method: %s"
+ raise exceptions.ClientException(_("Invalid update method: %s")
% method)
# PUT requests may not return a body
if body:
@@ -244,7 +245,8 @@ class ManagerWithFind(Manager):
num = len(rl)
if num == 0:
- msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
+ msg = _("No %(name)s matching %(kwargs)s.") % {
+ 'name': self.resource_class.__name__, 'kwargs': kwargs}
raise exceptions.NotFound(404, msg)
elif num > 1:
raise exceptions.NoUniqueMatch
@@ -395,7 +397,8 @@ class CrudManager(Manager):
num = len(rl)
if num == 0:
- msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
+ msg = _("No %(name)s matching %(kwargs)s.") % {
+ 'name': self.resource_class.__name__, 'kwargs': kwargs}
raise exceptions.NotFound(404, msg)
elif num > 1:
raise exceptions.NoUniqueMatch
diff --git a/keystoneclient/client.py b/keystoneclient/client.py
index c58009b..8b6a6b0 100644
--- a/keystoneclient/client.py
+++ b/keystoneclient/client.py
@@ -36,8 +36,10 @@ def Client(version=None, unstable=False, session=None, **kwargs):
:returns: New keystone client object
(keystoneclient.v2_0.Client or keystoneclient.v3.Client).
- :raises: DiscoveryFailure if the server's response is invalid
- :raises: VersionNotAvailable if a suitable client cannot be found.
+ :raises keystoneclient.exceptions.DiscoveryFailure: if the server's
+ response is invalid
+ :raises keystoneclient.exceptions.VersionNotAvailable: if a suitable client
+ cannot be found.
"""
if not session:
session = client_session.Session.construct(kwargs)
diff --git a/keystoneclient/common/cms.py b/keystoneclient/common/cms.py
index 1c343f6..d49a0c5 100644
--- a/keystoneclient/common/cms.py
+++ b/keystoneclient/common/cms.py
@@ -28,6 +28,7 @@ import zlib
import six
from keystoneclient import exceptions
+from keystoneclient.i18n import _, _LE, _LW
subprocess = None
@@ -38,6 +39,14 @@ PKIZ_CMS_FORM = 'DER'
PKI_ASN1_FORM = 'PEM'
+# The openssl cms command exits with these status codes.
+# See https://www.openssl.org/docs/apps/cms.html#EXIT_CODES
+class OpensslCmsExitStatus:
+ SUCCESS = 0
+ INPUT_FILE_READ_ERROR = 2
+ CREATE_CMS_READ_MIME_ERROR = 3
+
+
def _ensure_subprocess():
# NOTE(vish): late loading subprocess so we can
# use the green version if we are in
@@ -73,19 +82,12 @@ def _check_files_accessible(files):
except IOError as e:
# Catching IOError means there is an issue with
# the given file.
- err = ('Hit OSError in _process_communicate_handle_oserror()\n'
- 'Likely due to %s: %s') % (try_file, e.strerror)
+ err = _('Hit OSError in _process_communicate_handle_oserror()\n'
+ 'Likely due to %(file)s: %(error)s') % {'file': try_file,
+ 'error': e.strerror}
# Emulate openssl behavior, which returns with code 2 when
- # access to a file failed:
-
- # You can get more from
- # http://www.openssl.org/docs/apps/cms.html#EXIT_CODES
- #
- # $ openssl cms -verify -certfile not_exist_file -CAfile \
- # not_exist_file -inform PEM -nosmimecap -nodetach \
- # -nocerts -noattr
- # Error opening certificate file not_exist_file
- retcode = 2
+ # access to a file failed.
+ retcode = OpensslCmsExitStatus.INPUT_FILE_READ_ERROR
return retcode, err
@@ -122,8 +124,9 @@ def _encoding_for_form(inform):
elif inform == PKIZ_CMS_FORM:
encoding = 'hex'
else:
- raise ValueError('"inform" must be either %s or %s' %
- (PKI_ASN1_FORM, PKIZ_CMS_FORM))
+ raise ValueError(
+ _('"inform" must be one of: %s') % ','.join((PKI_ASN1_FORM,
+ PKIZ_CMS_FORM)))
return encoding
@@ -132,8 +135,10 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name,
inform=PKI_ASN1_FORM):
"""Verifies the signature of the contents IAW CMS syntax.
- :raises: subprocess.CalledProcessError
- :raises: CertificateConfigError if certificate is not configured properly.
+ :raises subprocess.CalledProcessError:
+ :raises keystoneclient.exceptions.CertificateConfigError: if certificate
+ is not configured
+ properly.
"""
_ensure_subprocess()
if isinstance(formatted, six.string_types):
@@ -148,7 +153,8 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name,
'-nocerts', '-noattr'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stderr=subprocess.PIPE,
+ close_fds=True)
output, err, retcode = _process_communicate_handle_oserror(
process, data, (signing_cert_file_name, ca_file_name))
@@ -165,12 +171,12 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name,
# -nocerts -noattr
# Error opening certificate file not_exist_file
#
- if retcode == 2:
+ if retcode == OpensslCmsExitStatus.INPUT_FILE_READ_ERROR:
if err.startswith('Error reading S/MIME message'):
raise exceptions.CMSError(err)
else:
raise exceptions.CertificateConfigError(err)
- elif retcode:
+ elif retcode != OpensslCmsExitStatus.SUCCESS:
# NOTE(dmllr): Python 2.6 compatibility:
# CalledProcessError did not have output keyword argument
e = subprocess.CalledProcessError(retcode, 'openssl')
@@ -295,8 +301,8 @@ def is_asn1_token(token):
def is_ans1_token(token):
"""Deprecated. Use is_asn1_token() instead."""
- LOG.warning('The function is_ans1_token() is deprecated, '
- 'use is_asn1_token() instead.')
+ LOG.warning(_LW('The function is_ans1_token() is deprecated, '
+ 'use is_asn1_token() instead.'))
return is_asn1_token(token)
@@ -336,19 +342,19 @@ def cms_sign_data(data_to_sign, signing_cert_file_name, signing_key_file_name,
'-md', 'sha256', ],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stderr=subprocess.PIPE,
+ close_fds=True)
output, err, retcode = _process_communicate_handle_oserror(
process, data, (signing_cert_file_name, signing_key_file_name))
- if retcode or ('Error' in err):
- LOG.error('Signing error: %s', err)
- if retcode == 3:
- LOG.error('Signing error: Unable to load certificate - '
- 'ensure you have configured PKI with '
- '"keystone-manage pki_setup"')
+ if retcode != OpensslCmsExitStatus.SUCCESS or ('Error' in err):
+ if retcode == OpensslCmsExitStatus.CREATE_CMS_READ_MIME_ERROR:
+ LOG.error(_LE('Signing error: Unable to load certificate - '
+ 'ensure you have configured PKI with '
+ '"keystone-manage pki_setup"'))
else:
- LOG.error('Signing error: %s', err)
+ LOG.error(_LE('Signing error: %s'), err)
raise subprocess.CalledProcessError(retcode, 'openssl')
if outform == PKI_ASN1_FORM:
return output.decode('utf-8')
diff --git a/keystoneclient/contrib/auth/v3/saml2.py b/keystoneclient/contrib/auth/v3/saml2.py
index bb32c15..5458ac1 100644
--- a/keystoneclient/contrib/auth/v3/saml2.py
+++ b/keystoneclient/contrib/auth/v3/saml2.py
@@ -20,6 +20,7 @@ from six.moves import urllib
from keystoneclient import access
from keystoneclient.auth.identity import v3
from keystoneclient import exceptions
+from keystoneclient.i18n import _
class _BaseSAMLPlugin(v3.AuthConstructor):
@@ -30,7 +31,7 @@ class _BaseSAMLPlugin(v3.AuthConstructor):
@staticmethod
def _first(_list):
if len(_list) != 1:
- raise IndexError("Only single element list is acceptable")
+ raise IndexError(_("Only single element list is acceptable"))
return _list[0]
@staticmethod
@@ -80,8 +81,8 @@ class Saml2UnscopedTokenAuthMethod(v3.AuthMethod):
_method_parameters = []
def get_auth_data(self, session, auth, headers, **kwargs):
- raise exceptions.MethodNotImplemented(('This method should never '
- 'be called'))
+ raise exceptions.MethodNotImplemented(_('This method should never '
+ 'be called'))
class Saml2UnscopedToken(_BaseSAMLPlugin):
@@ -211,9 +212,9 @@ class Saml2UnscopedToken(_BaseSAMLPlugin):
authenticated=False)
# prepare error message and raise an exception.
- msg = ("Consumer URLs from Service Provider %(service_provider)s "
- "%(sp_consumer_url)s and Identity Provider "
- "%(identity_provider)s %(idp_consumer_url)s are not equal")
+ msg = _("Consumer URLs from Service Provider %(service_provider)s "
+ "%(sp_consumer_url)s and Identity Provider "
+ "%(identity_provider)s %(idp_consumer_url)s are not equal")
msg = msg % {
'service_provider': self.token_url,
'sp_consumer_url': sp_response_consumer_url,
@@ -257,8 +258,8 @@ class Saml2UnscopedToken(_BaseSAMLPlugin):
try:
self.saml2_authn_request = etree.XML(sp_response.content)
except etree.XMLSyntaxError as e:
- msg = ("SAML2: Error parsing XML returned "
- "from Service Provider, reason: %s" % e)
+ msg = _("SAML2: Error parsing XML returned "
+ "from Service Provider, reason: %s") % e
raise exceptions.AuthorizationFailure(msg)
relay_state = self.saml2_authn_request.xpath(
@@ -288,8 +289,8 @@ class Saml2UnscopedToken(_BaseSAMLPlugin):
try:
self.saml2_idp_authn_response = etree.XML(idp_response.content)
except etree.XMLSyntaxError as e:
- msg = ("SAML2: Error parsing XML returned "
- "from Identity Provider, reason: %s" % e)
+ msg = _("SAML2: Error parsing XML returned "
+ "from Identity Provider, reason: %s") % e
raise exceptions.AuthorizationFailure(msg)
idp_response_consumer_url = self.saml2_idp_authn_response.xpath(
@@ -506,10 +507,6 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
])
return options
- @property
- def _uuid4(self):
- return str(uuid.uuid4())
-
def _cookies(self, session):
"""Check if cookie jar is not empty.
@@ -520,7 +517,7 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
:param session
:returns: True if cookie jar is nonempty, False otherwise
- :raises: AttributeError in case cookies are not find anywhere
+ :raises AttributeError: in case cookies are not find anywhere
"""
try:
@@ -591,7 +588,7 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
messageID = etree.SubElement(
header, '{http://www.w3.org/2005/08/addressing}MessageID')
- messageID.text = 'urn:uuid:' + self._uuid4
+ messageID.text = 'urn:uuid:' + uuid.uuid4().hex
replyID = etree.SubElement(
header, '{http://www.w3.org/2005/08/addressing}ReplyTo')
address = etree.SubElement(
@@ -632,7 +629,7 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
'wss-wssecurity-secext-1.0.xsd}UsernameToken')
usernametoken.set(
('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-'
- 'wssecurity-utility-1.0.xsd}u'), "uuid-%s-1" % self._uuid4)
+ 'wssecurity-utility-1.0.xsd}u'), "uuid-%s-1" % uuid.uuid4().hex)
username = etree.SubElement(
usernametoken, ('{http://docs.oasis-open.org/wss/2004/01/oasis-'
@@ -705,11 +702,12 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
- :raises: exceptions.AuthorizationFailure when HTTP response from the
- ADFS server is not a valid XML ADFS security token.
- :raises: exceptions.InternalServerError: If response status code is
- HTTP 500 and the response XML cannot be recognized.
-
+ :raises keystoneclient.exceptions.AuthorizationFailure: when HTTP
+ response from the ADFS server is not a valid XML ADFS security
+ token.
+ :raises keystoneclient.exceptions.InternalServerError: If response
+ status code is HTTP 500 and the response XML cannot be
+ recognized.
"""
def _get_failure(e):
@@ -737,8 +735,8 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
except exceptions.InternalServerError as e:
reason = _get_failure(e)
raise exceptions.AuthorizationFailure(reason)
- msg = ("Error parsing XML returned from "
- "the ADFS Identity Provider, reason: %s")
+ msg = _("Error parsing XML returned from "
+ "the ADFS Identity Provider, reason: %s")
self.adfs_token = self.str_to_xml(response.content, msg)
def _prepare_sp_request(self):
@@ -798,14 +796,15 @@ class ADFSUnscopedToken(_BaseSAMLPlugin):
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
- :raises: exceptions.AuthorizationFailure: in case session object
- has empty cookie jar.
+ :raises keystoneclient.exceptions.AuthorizationFailure: in case session
+ object has empty cookie jar.
"""
if self._cookies(session) is False:
raise exceptions.AuthorizationFailure(
- "Session object doesn't contain a cookie, therefore you are "
- "not allowed to enter the Identity Provider's protected area.")
+ _("Session object doesn't contain a cookie, therefore you are "
+ "not allowed to enter the Identity Provider's protected "
+ "area."))
self.authenticated_response = session.get(self.token_url,
authenticated=False)
@@ -884,4 +883,4 @@ class Saml2ScopedToken(v3.Token):
super(Saml2ScopedToken, self).__init__(auth_url, token, **kwargs)
if not (self.project_id or self.domain_id):
raise exceptions.ValidationError(
- 'Neither project nor domain specified')
+ _('Neither project nor domain specified'))
diff --git a/keystoneclient/contrib/ec2/utils.py b/keystoneclient/contrib/ec2/utils.py
index 899b95a..d093b6e 100644
--- a/keystoneclient/contrib/ec2/utils.py
+++ b/keystoneclient/contrib/ec2/utils.py
@@ -24,6 +24,8 @@ import re
import six
from six.moves import urllib
+from keystoneclient.i18n import _
+
class Ec2Signer(object):
"""Utility class which adds allows a request to be signed with an AWS style
@@ -91,10 +93,10 @@ class Ec2Signer(object):
credentials['body_hash'])
if signature_version is not None:
- raise Exception('Unknown signature version: %s' %
+ raise Exception(_('Unknown signature version: %s') %
signature_version)
else:
- raise Exception('Unexpected signature format')
+ raise Exception(_('Unexpected signature format'))
@staticmethod
def _get_utf8_value(value):
@@ -257,7 +259,7 @@ class Ec2Signer(object):
credential_date = credential_split[1]
param_date = date_param()
if not param_date.startswith(credential_date):
- raise Exception('Request date mismatch error')
+ raise Exception(_('Request date mismatch error'))
# Create the string to sign
# http://docs.aws.amazon.com/general/latest/gr/
diff --git a/keystoneclient/contrib/revoke/model.py b/keystoneclient/contrib/revoke/model.py
index 18d5034..c3f3864 100644
--- a/keystoneclient/contrib/revoke/model.py
+++ b/keystoneclient/contrib/revoke/model.py
@@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystoneclient.openstack.common import timeutils
+from oslo.utils import timeutils
# The set of attributes common between the RevokeEvent
# and the dictionaries created from the token Data.
diff --git a/keystoneclient/discover.py b/keystoneclient/discover.py
index ee73ddc..695d345 100644
--- a/keystoneclient/discover.py
+++ b/keystoneclient/discover.py
@@ -16,6 +16,7 @@ import six
from keystoneclient import _discover
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import session as client_session
from keystoneclient import utils
from keystoneclient.v2_0 import client as v2_client
@@ -122,9 +123,9 @@ class Discover(_discover.Discover):
url = auth_url
if not url:
- raise exceptions.DiscoveryFailure('Not enough information to '
- 'determine URL. Provide either '
- 'auth_url or endpoint')
+ raise exceptions.DiscoveryFailure(
+ _('Not enough information to determine URL. Provide either '
+ 'auth_url or endpoint'))
self._client_kwargs = kwargs
super(Discover, self).__init__(session, url,
@@ -214,10 +215,11 @@ class Discover(_discover.Discover):
version_data = all_versions[-1]
if not version_data:
- msg = 'Could not find a suitable endpoint'
+ msg = _('Could not find a suitable endpoint')
if version:
- msg += ' for client version: %s' % str(version)
+ msg = _('Could not find a suitable endpoint for client '
+ 'version: %s') % str(version)
raise exceptions.VersionNotAvailable(msg)
@@ -229,7 +231,7 @@ class Discover(_discover.Discover):
client_class = _CLIENT_VERSIONS[version_data['version'][0]]
except KeyError:
version = '.'.join(str(v) for v in version_data['version'])
- msg = 'No client available for version: %s' % version
+ msg = _('No client available for version: %s') % version
raise exceptions.DiscoveryFailure(msg)
# kwargs should take priority over stored kwargs.
@@ -262,8 +264,10 @@ class Discover(_discover.Discover):
:returns: An instantiated identity client object.
- :raises: DiscoveryFailure if the server response is invalid
- :raises: VersionNotAvailable if a suitable client cannot be found.
+ :raises keystoneclient.exceptions.DiscoveryFailure: if the server
+ response is invalid
+ :raises keystoneclient.exceptions.VersionNotAvailable: if a suitable
+ client cannot be found.
"""
version_data = self._calculate_version(version, unstable)
return self._create_client(version_data, **kwargs)
diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py
index 5ad84c0..241d27b 100644
--- a/keystoneclient/exceptions.py
+++ b/keystoneclient/exceptions.py
@@ -14,10 +14,19 @@
# under the License.
"""
Exception definitions.
+
+.. py:exception:: AuthorizationFailure
+
+.. py:exception:: ClientException
+
+.. py:exception:: HttpError
+
+.. py:exception:: Unauthorized
+
"""
-#flake8: noqa
-from keystoneclient.openstack.common.apiclient.exceptions import *
+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
@@ -29,18 +38,18 @@ HTTPError = HttpError
class CertificateConfigError(Exception):
- """Error reading the certificate"""
+ """Error reading the certificate."""
def __init__(self, output):
self.output = output
- msg = 'Unable to load certificate.'
+ msg = _('Unable to load certificate.')
super(CertificateConfigError, self).__init__(msg)
class CMSError(Exception):
- """Error reading the certificate"""
+ """Error reading the certificate."""
def __init__(self, output):
self.output = output
- msg = 'Unable to sign or verify data.'
+ msg = _('Unable to sign or verify data.')
super(CMSError, self).__init__(msg)
@@ -71,7 +80,8 @@ class MissingAuthPlugin(ClientException):
class NoMatchingPlugin(ClientException):
"""There were no auth plugins that could be created from the parameters
- provided."""
+ provided.
+ """
class InvalidResponse(ClientException):
diff --git a/keystoneclient/fixture/discovery.py b/keystoneclient/fixture/discovery.py
index 94cfe11..c7edf15 100644
--- a/keystoneclient/fixture/discovery.py
+++ b/keystoneclient/fixture/discovery.py
@@ -12,7 +12,8 @@
import datetime
-from keystoneclient.openstack.common import timeutils
+from oslo.utils import timeutils
+
from keystoneclient import utils
__all__ = ['DiscoveryList',
diff --git a/keystoneclient/fixture/v2.py b/keystoneclient/fixture/v2.py
index 467ad4c..3a107f4 100644
--- a/keystoneclient/fixture/v2.py
+++ b/keystoneclient/fixture/v2.py
@@ -13,8 +13,9 @@
import datetime
import uuid
+from oslo.utils import timeutils
+
from keystoneclient.fixture import exception
-from keystoneclient.openstack.common import timeutils
class _Service(dict):
diff --git a/keystoneclient/fixture/v3.py b/keystoneclient/fixture/v3.py
index e40b314..4f0d581 100644
--- a/keystoneclient/fixture/v3.py
+++ b/keystoneclient/fixture/v3.py
@@ -13,8 +13,9 @@
import datetime
import uuid
+from oslo.utils import timeutils
+
from keystoneclient.fixture import exception
-from keystoneclient.openstack.common import timeutils
class _Service(dict):
diff --git a/keystoneclient/generic/client.py b/keystoneclient/generic/client.py
index 3c81268..7ca39fb 100644
--- a/keystoneclient/generic/client.py
+++ b/keystoneclient/generic/client.py
@@ -19,6 +19,7 @@ from six.moves.urllib import parse as urlparse
from keystoneclient import exceptions
from keystoneclient import httpclient
+from keystoneclient.i18n import _, _LE
_logger = logging.getLogger(__name__)
@@ -94,7 +95,7 @@ class Client(httpclient.HTTPClient):
try:
results = {}
if 'version' in body:
- results['message'] = "Keystone found at %s" % url
+ results['message'] = _("Keystone found at %s") % url
version = body['version']
# Stable/diablo incorrect format
id, status, version_url = (
@@ -105,7 +106,7 @@ class Client(httpclient.HTTPClient):
return results
elif 'versions' in body:
# Correct format
- results['message'] = "Keystone found at %s" % url
+ results['message'] = _("Keystone found at %s") % url
for version in body['versions']['values']:
id, status, version_url = (
self._get_version_info(version, url))
@@ -114,8 +115,8 @@ class Client(httpclient.HTTPClient):
"url": version_url}
return results
else:
- results['message'] = ("Unrecognized response from %s"
- % url)
+ results['message'] = (
+ _("Unrecognized response from %s") % url)
return results
except KeyError:
raise exceptions.AuthorizationFailure()
@@ -123,8 +124,8 @@ class Client(httpclient.HTTPClient):
return self._check_keystone_versions(resp['location'])
else:
raise exceptions.from_response(resp, "GET", url)
- except Exception as e:
- _logger.exception(e)
+ except Exception:
+ _logger.exception(_LE('Failed to detect available versions.'))
def discover_extensions(self, url=None):
"""Discover Keystone extensions supported.
@@ -159,7 +160,7 @@ class Client(httpclient.HTTPClient):
extensions = body['extensions']
else:
return dict(message=(
- 'Unrecognized extensions response from %s' % url))
+ _('Unrecognized extensions response from %s') % url))
return dict(self._get_extension_info(e) for e in extensions)
elif resp.status_code == 305:
@@ -167,8 +168,8 @@ class Client(httpclient.HTTPClient):
else:
raise exceptions.from_response(
resp, "GET", "%sextensions" % url)
- except Exception as e:
- _logger.exception(e)
+ except Exception:
+ _logger.exception(_LE('Failed to check keystone extensions.'))
@staticmethod
def _get_version_info(version, root_url):
diff --git a/keystoneclient/generic/shell.py b/keystoneclient/generic/shell.py
index 4f9bd33..e5330a5 100644
--- a/keystoneclient/generic/shell.py
+++ b/keystoneclient/generic/shell.py
@@ -16,6 +16,7 @@
import six
from keystoneclient.generic import client
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -37,13 +38,14 @@ def do_discover(cs, args):
print(versions['message'])
for key, version in six.iteritems(versions):
if key != 'message':
- print(" - supports version %s (%s) here %s" %
- (version['id'], version['status'], version['url']))
+ print(_(" - supports version %(id)s (%(status)s) here "
+ "%(url)s") %
+ version)
extensions = cs.discover_extensions(version['url'])
if extensions:
for key, extension in six.iteritems(extensions):
if key != 'message':
- print(" - and %s: %s" %
- (key, extension))
+ print(_(" - and %(key)s: %(extension)s") %
+ {'key': key, 'extension': extension})
else:
- print("No Keystone-compatible endpoint found")
+ print(_("No Keystone-compatible endpoint found"))
diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py
index ce49dc4..79b5e88 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 oslo.serialization import jsonutils
import pkg_resources
import requests
from six.moves.urllib import parse as urlparse
@@ -54,7 +55,7 @@ from keystoneclient import access
from keystoneclient.auth import base
from keystoneclient import baseclient
from keystoneclient import exceptions
-from keystoneclient.openstack.common import jsonutils
+from keystoneclient.i18n import _, _LI, _LW
from keystoneclient import session as client_session
from keystoneclient import utils
@@ -265,7 +266,7 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
# keyring setup
if use_keyring and keyring is None:
- _logger.warning('Failed to load keyring modules.')
+ _logger.warning(_LW('Failed to load keyring modules.'))
self.use_keyring = use_keyring and keyring is not None
self.force_new_token = force_new_token
self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
@@ -312,7 +313,7 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
def has_service_catalog(self):
"""Returns True if this client provides a service catalog."""
- return self.auth_ref.has_service_catalog()
+ return self.auth_ref and self.auth_ref.has_service_catalog()
@property
def tenant_id(self):
@@ -364,9 +365,10 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
self.management_url from the details provided in the token.
:returns: ``True`` if authentication was successful.
- :raises: AuthorizationFailure if unable to authenticate or validate
- the existing authorization token
- :raises: ValueError if insufficient parameters are used.
+ :raises keystoneclient.exceptions.AuthorizationFailure: if unable to
+ authenticate or validate the existing authorization token
+ :raises keystoneclient.exceptions.ValueError: if insufficient
+ parameters are used.
If keyring is used, token is retrieved from keyring instead.
Authentication will only be necessary if any of the following
@@ -476,7 +478,8 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
auth_ref = None
except Exception as e:
auth_ref = None
- _logger.warning('Unable to retrieve token from keyring %s', e)
+ _logger.warning(
+ _LW('Unable to retrieve token from keyring %s'), e)
return (keyring_key, auth_ref)
def store_auth_ref_into_keyring(self, keyring_key):
@@ -489,7 +492,8 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
keyring_key,
pickle.dumps(self.auth_ref))
except Exception as e:
- _logger.warning("Failed to store token into keyring %s", e)
+ _logger.warning(
+ _LW("Failed to store token into keyring %s"), e)
def _process_management_url(self, region_name):
try:
@@ -498,7 +502,7 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
endpoint_type='admin',
region_name=region_name)
except exceptions.EndpointNotFound:
- _logger.warning("Failed to retrieve management_url from token")
+ pass
def process_token(self, region_name=None):
"""Extract and process information from the new auth_ref.
@@ -511,14 +515,14 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
if self.auth_ref.project_scoped:
if not self.auth_ref.tenant_id:
raise exceptions.AuthorizationFailure(
- "Token didn't provide tenant_id")
+ _("Token didn't provide tenant_id"))
self._process_management_url(region_name)
self.project_name = self.auth_ref.tenant_name
self.project_id = self.auth_ref.tenant_id
if not self.auth_ref.user_id:
raise exceptions.AuthorizationFailure(
- "Token didn't provide user_id")
+ _("Token didn't provide user_id"))
self.user_id = self.auth_ref.user_id
@@ -620,10 +624,11 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
try:
return self.request(url, method, **kwargs)
except exceptions.MissingAuthPlugin:
- _logger.info('Cannot get authenticated endpoint without an '
- 'auth plugin')
+ _logger.info(_LI('Cannot get authenticated endpoint without an '
+ 'auth plugin'))
raise exceptions.AuthorizationFailure(
- 'Current authorization does not have a known management url')
+ _('Current authorization does not have a known management '
+ 'url'))
def get(self, url, **kwargs):
return self._cs_request(url, 'GET', **kwargs)
@@ -656,7 +661,7 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
try:
var_name = self.deprecated_session_variables[name]
except KeyError:
- raise AttributeError("Unknown Attribute: %s" % name)
+ raise AttributeError(_("Unknown Attribute: %s") % name)
return getattr(self.session, var_name or name)
diff --git a/keystoneclient/i18n.py b/keystoneclient/i18n.py
new file mode 100644
index 0000000..fd1c03a
--- /dev/null
+++ b/keystoneclient/i18n.py
@@ -0,0 +1,37 @@
+# Copyright 2014 IBM Corp.
+#
+# 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.
+
+"""oslo.i18n integration module.
+
+See http://docs.openstack.org/developer/oslo.i18n/usage.html .
+
+"""
+
+from oslo import i18n
+
+
+_translators = i18n.TranslatorFactory(domain='keystoneclient')
+
+# The primary translation function using the well-known name "_"
+_ = _translators.primary
+
+# Translators for log levels.
+#
+# The abbreviated names are meant to reflect the usual use of a short
+# name like '_'. The "L" is for "log" and the other letter comes from
+# the level.
+_LI = _translators.log_info
+_LW = _translators.log_warning
+_LE = _translators.log_error
+_LC = _translators.log_critical
diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py
index bb1e041..43a7ea5 100644
--- a/keystoneclient/middleware/auth_token.py
+++ b/keystoneclient/middleware/auth_token.py
@@ -16,6 +16,12 @@
"""
TOKEN-BASED AUTH MIDDLEWARE
+.. warning::
+
+ This module is DEPRECATED. The auth_token middleware has been moved to the
+ `keystonemiddleware repository
+ <http://docs.openstack.org/developer/keystonemiddleware/>`_.
+
This WSGI component:
* Verifies that incoming client requests have valid tokens by validating
@@ -26,9 +32,6 @@ This WSGI component:
* Collects and forwards identity information based on a valid token
such as user name, tenant, etc
-Refer to: http://docs.openstack.org/developer/python-keystoneclient/
-middlewarearchitecture.html
-
HEADERS
-------
@@ -155,6 +158,8 @@ import time
import netaddr
from oslo.config import cfg
+from oslo.serialization import jsonutils
+from oslo.utils import timeutils
import requests
import six
from six.moves import urllib
@@ -163,9 +168,7 @@ from keystoneclient import access
from keystoneclient.common import cms
from keystoneclient import exceptions
from keystoneclient.middleware import memcache_crypt
-from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache
-from keystoneclient.openstack.common import timeutils
# alternative middleware configuration in the main application's
diff --git a/keystoneclient/middleware/s3_token.py b/keystoneclient/middleware/s3_token.py
index 50d0f1c..b27b9ce 100644
--- a/keystoneclient/middleware/s3_token.py
+++ b/keystoneclient/middleware/s3_token.py
@@ -33,13 +33,12 @@ This WSGI component:
import logging
+from oslo.serialization import jsonutils
import requests
import six
from six.moves import urllib
import webob
-from keystoneclient.openstack.common import jsonutils
-
PROTOCOL_NAME = 'S3 Token Authentication'
diff --git a/keystoneclient/openstack/common/__init__.py b/keystoneclient/openstack/common/__init__.py
index d1223ea..e69de29 100644
--- a/keystoneclient/openstack/common/__init__.py
+++ b/keystoneclient/openstack/common/__init__.py
@@ -1,17 +0,0 @@
-#
-# 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 six
-
-
-six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
diff --git a/keystoneclient/openstack/common/_i18n.py b/keystoneclient/openstack/common/_i18n.py
new file mode 100644
index 0000000..52a5e84
--- /dev/null
+++ b/keystoneclient/openstack/common/_i18n.py
@@ -0,0 +1,40 @@
+# 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.
+
+"""oslo.i18n integration module.
+
+See http://docs.openstack.org/developer/oslo.i18n/usage.html
+
+"""
+
+import oslo.i18n
+
+
+# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
+# application name when this module is synced into the separate
+# repository. It is OK to have more than one translation function
+# using the same domain, since there will still only be one message
+# catalog.
+_translators = oslo.i18n.TranslatorFactory(domain='keystoneclient')
+
+# The primary translation function using the well-known name "_"
+_ = _translators.primary
+
+# Translators for log levels.
+#
+# The abbreviated names are meant to reflect the usual use of a short
+# name like '_'. The "L" is for "log" and the other letter comes from
+# the level.
+_LI = _translators.log_info
+_LW = _translators.log_warning
+_LE = _translators.log_error
+_LC = _translators.log_critical
diff --git a/keystoneclient/openstack/common/apiclient/base.py b/keystoneclient/openstack/common/apiclient/base.py
index 9d7119d..72d7999 100644
--- a/keystoneclient/openstack/common/apiclient/base.py
+++ b/keystoneclient/openstack/common/apiclient/base.py
@@ -26,12 +26,12 @@ Base utilities to build API operation managers and objects on top of.
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.openstack.common.apiclient import exceptions
-from keystoneclient.openstack.common.gettextutils import _
-from keystoneclient.openstack.common import strutils
def getid(obj):
@@ -495,6 +495,8 @@ class Resource(object):
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):
diff --git a/keystoneclient/openstack/common/apiclient/client.py b/keystoneclient/openstack/common/apiclient/client.py
index 4e435b7..dd560ab 100644
--- a/keystoneclient/openstack/common/apiclient/client.py
+++ b/keystoneclient/openstack/common/apiclient/client.py
@@ -25,6 +25,7 @@ OpenStack Client interface. Handles the REST calls and responses.
# E0202: An attribute inherited from %s hide this method
# pylint: disable=E0202
+import hashlib
import logging
import time
@@ -33,14 +34,15 @@ try:
except ImportError:
import json
+from oslo.utils import encodeutils
+from oslo.utils import importutils
import requests
+from keystoneclient.openstack.common._i18n import _
from keystoneclient.openstack.common.apiclient import exceptions
-from keystoneclient.openstack.common.gettextutils import _
-from keystoneclient.openstack.common import importutils
-
_logger = logging.getLogger(__name__)
+SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',)
class HTTPClient(object):
@@ -98,6 +100,18 @@ class HTTPClient(object):
self.http = http or requests.Session()
self.cached_token = None
+ self.last_request_id = None
+
+ def _safe_header(self, name, value):
+ if name in SENSITIVE_HEADERS:
+ # because in python3 byte string handling is ... ug
+ v = value.encode('utf-8')
+ h = hashlib.sha1(v)
+ d = h.hexdigest()
+ return encodeutils.safe_decode(name), "{SHA1}%s" % d
+ else:
+ return (encodeutils.safe_decode(name),
+ encodeutils.safe_decode(value))
def _http_log_req(self, method, url, kwargs):
if not self.debug:
@@ -110,7 +124,8 @@ class HTTPClient(object):
]
for element in kwargs['headers']:
- header = "-H '%s: %s'" % (element, kwargs['headers'][element])
+ header = ("-H '%s: %s'" %
+ self._safe_header(element, kwargs['headers'][element]))
string_parts.append(header)
_logger.debug("REQ: %s" % " ".join(string_parts))
@@ -156,7 +171,7 @@ class HTTPClient(object):
requests.Session.request (such as `headers`) or `json`
that will be encoded as JSON and used as `data` argument
"""
- kwargs.setdefault("headers", kwargs.get("headers", {}))
+ kwargs.setdefault("headers", {})
kwargs["headers"]["User-Agent"] = self.user_agent
if self.original_ip:
kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
@@ -177,6 +192,8 @@ class HTTPClient(object):
start_time, time.time()))
self._http_log_resp(resp)
+ self.last_request_id = resp.headers.get('x-openstack-request-id')
+
if resp.status_code >= 400:
_logger.debug(
"Request returned failure status: %s",
@@ -247,6 +264,10 @@ class HTTPClient(object):
raise
self.cached_token = None
client.cached_endpoint = None
+ if self.auth_plugin.opts.get('token'):
+ self.auth_plugin.opts['token'] = None
+ if self.auth_plugin.opts.get('endpoint'):
+ self.auth_plugin.opts['endpoint'] = None
self.authenticate()
try:
token, endpoint = self.auth_plugin.token_and_endpoint(
@@ -323,6 +344,10 @@ class BaseClient(object):
return self.http_client.client_request(
self, method, url, **kwargs)
+ @property
+ def last_request_id(self):
+ return self.http_client.last_request_id
+
def head(self, url, **kwargs):
return self.client_request("HEAD", url, **kwargs)
diff --git a/keystoneclient/openstack/common/apiclient/exceptions.py b/keystoneclient/openstack/common/apiclient/exceptions.py
index 7e5c2ea..a4ff25a 100644
--- a/keystoneclient/openstack/common/apiclient/exceptions.py
+++ b/keystoneclient/openstack/common/apiclient/exceptions.py
@@ -25,7 +25,7 @@ import sys
import six
-from keystoneclient.openstack.common.gettextutils import _
+from keystoneclient.openstack.common._i18n import _
class ClientException(Exception):
@@ -34,14 +34,6 @@ class ClientException(Exception):
pass
-class MissingArgs(ClientException):
- """Supplied arguments are not sufficient for calling a function."""
- def __init__(self, missing):
- self.missing = missing
- msg = _("Missing arguments: %s") % ", ".join(missing)
- super(MissingArgs, self).__init__(msg)
-
-
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
diff --git a/keystoneclient/openstack/common/apiclient/fake_client.py b/keystoneclient/openstack/common/apiclient/fake_client.py
index ce7311c..46fc536 100644
--- a/keystoneclient/openstack/common/apiclient/fake_client.py
+++ b/keystoneclient/openstack/common/apiclient/fake_client.py
@@ -168,6 +168,8 @@ class FakeHTTPClient(client.HTTPClient):
else:
status, body = resp
headers = {}
+ self.last_request_id = headers.get('x-openstack-request-id',
+ 'req-test')
return TestResponse({
"status_code": status,
"text": body,
diff --git a/keystoneclient/openstack/common/apiclient/utils.py b/keystoneclient/openstack/common/apiclient/utils.py
new file mode 100644
index 0000000..6aa2975
--- /dev/null
+++ b/keystoneclient/openstack/common/apiclient/utils.py
@@ -0,0 +1,87 @@
+#
+# 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 oslo.utils import encodeutils
+import six
+
+from keystoneclient.openstack.common._i18n import _
+from keystoneclient.openstack.common.apiclient import exceptions
+from keystoneclient.openstack.common import uuidutils
+
+
+def find_resource(manager, name_or_id, **find_args):
+ """Look for resource in a given manager.
+
+ Used as a helper for the _find_* methods.
+ Example:
+
+ .. code-block:: python
+
+ def _find_hypervisor(cs, hypervisor):
+ #Get a hypervisor by name or ID.
+ return cliutils.find_resource(cs.hypervisors, hypervisor)
+ """
+ # first try to get entity as integer id
+ try:
+ return manager.get(int(name_or_id))
+ except (TypeError, ValueError, exceptions.NotFound):
+ pass
+
+ # now try to get entity as uuid
+ try:
+ if six.PY2:
+ tmp_id = encodeutils.safe_encode(name_or_id)
+ else:
+ tmp_id = encodeutils.safe_decode(name_or_id)
+
+ if uuidutils.is_uuid_like(tmp_id):
+ return manager.get(tmp_id)
+ except (TypeError, ValueError, exceptions.NotFound):
+ pass
+
+ # for str id which is not uuid
+ if getattr(manager, 'is_alphanum_id_allowed', False):
+ try:
+ return manager.get(name_or_id)
+ except exceptions.NotFound:
+ pass
+
+ try:
+ try:
+ return manager.find(human_id=name_or_id, **find_args)
+ except exceptions.NotFound:
+ pass
+
+ # finally try to find entity by name
+ try:
+ resource = getattr(manager, 'resource_class', None)
+ name_attr = resource.NAME_ATTR if resource else 'name'
+ kwargs = {name_attr: name_or_id}
+ kwargs.update(find_args)
+ return manager.find(**kwargs)
+ except exceptions.NotFound:
+ msg = _("No %(name)s with a name or "
+ "ID of '%(name_or_id)s' exists.") % \
+ {
+ "name": manager.resource_class.__name__.lower(),
+ "name_or_id": name_or_id
+ }
+ raise exceptions.CommandError(msg)
+ except exceptions.NoUniqueMatch:
+ msg = _("Multiple %(name)s matches found for "
+ "'%(name_or_id)s', use an ID to be more specific.") % \
+ {
+ "name": manager.resource_class.__name__.lower(),
+ "name_or_id": name_or_id
+ }
+ raise exceptions.CommandError(msg)
diff --git a/keystoneclient/openstack/common/gettextutils.py b/keystoneclient/openstack/common/gettextutils.py
deleted file mode 100644
index 55a60df..0000000
--- a/keystoneclient/openstack/common/gettextutils.py
+++ /dev/null
@@ -1,479 +0,0 @@
-# Copyright 2012 Red Hat, Inc.
-# Copyright 2013 IBM Corp.
-# 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.
-
-"""
-gettext for openstack-common modules.
-
-Usual usage in an openstack.common module:
-
- from keystoneclient.openstack.common.gettextutils import _
-"""
-
-import copy
-import gettext
-import locale
-from logging import handlers
-import os
-
-from babel import localedata
-import six
-
-_AVAILABLE_LANGUAGES = {}
-
-# FIXME(dhellmann): Remove this when moving to oslo.i18n.
-USE_LAZY = False
-
-
-class TranslatorFactory(object):
- """Create translator functions
- """
-
- def __init__(self, domain, localedir=None):
- """Establish a set of translation functions for the domain.
-
- :param domain: Name of translation domain,
- specifying a message catalog.
- :type domain: str
- :param lazy: Delays translation until a message is emitted.
- Defaults to False.
- :type lazy: Boolean
- :param localedir: Directory with translation catalogs.
- :type localedir: str
- """
- self.domain = domain
- if localedir is None:
- localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
- self.localedir = localedir
-
- def _make_translation_func(self, domain=None):
- """Return a new translation function ready for use.
-
- Takes into account whether or not lazy translation is being
- done.
-
- The domain can be specified to override the default from the
- factory, but the localedir from the factory is always used
- because we assume the log-level translation catalogs are
- installed in the same directory as the main application
- catalog.
-
- """
- if domain is None:
- domain = self.domain
- t = gettext.translation(domain,
- localedir=self.localedir,
- fallback=True)
- # Use the appropriate method of the translation object based
- # on the python version.
- m = t.gettext if six.PY3 else t.ugettext
-
- def f(msg):
- """oslo.i18n.gettextutils translation function."""
- if USE_LAZY:
- return Message(msg, domain=domain)
- return m(msg)
- return f
-
- @property
- def primary(self):
- "The default translation function."
- return self._make_translation_func()
-
- def _make_log_translation_func(self, level):
- return self._make_translation_func(self.domain + '-log-' + level)
-
- @property
- def log_info(self):
- "Translate info-level log messages."
- return self._make_log_translation_func('info')
-
- @property
- def log_warning(self):
- "Translate warning-level log messages."
- return self._make_log_translation_func('warning')
-
- @property
- def log_error(self):
- "Translate error-level log messages."
- return self._make_log_translation_func('error')
-
- @property
- def log_critical(self):
- "Translate critical-level log messages."
- return self._make_log_translation_func('critical')
-
-
-# NOTE(dhellmann): When this module moves out of the incubator into
-# oslo.i18n, these global variables can be moved to an integration
-# module within each application.
-
-# Create the global translation functions.
-_translators = TranslatorFactory('keystoneclient')
-
-# The primary translation function using the well-known name "_"
-_ = _translators.primary
-
-# Translators for log levels.
-#
-# The abbreviated names are meant to reflect the usual use of a short
-# name like '_'. The "L" is for "log" and the other letter comes from
-# the level.
-_LI = _translators.log_info
-_LW = _translators.log_warning
-_LE = _translators.log_error
-_LC = _translators.log_critical
-
-# NOTE(dhellmann): End of globals that will move to the application's
-# integration module.
-
-
-def enable_lazy():
- """Convenience function for configuring _() to use lazy gettext
-
- Call this at the start of execution to enable the gettextutils._
- function to use lazy gettext functionality. This is useful if
- your project is importing _ directly instead of using the
- gettextutils.install() way of importing the _ function.
- """
- global USE_LAZY
- USE_LAZY = True
-
-
-def install(domain):
- """Install a _() function using the given translation domain.
-
- Given a translation domain, install a _() function using gettext's
- install() function.
-
- The main difference from gettext.install() is that we allow
- overriding the default localedir (e.g. /usr/share/locale) using
- a translation-domain-specific environment variable (e.g.
- NOVA_LOCALEDIR).
-
- Note that to enable lazy translation, enable_lazy must be
- called.
-
- :param domain: the translation domain
- """
- from six import moves
- tf = TranslatorFactory(domain)
- moves.builtins.__dict__['_'] = tf.primary
-
-
-class Message(six.text_type):
- """A Message object is a unicode object that can be translated.
-
- Translation of Message is done explicitly using the translate() method.
- For all non-translation intents and purposes, a Message is simply unicode,
- and can be treated as such.
- """
-
- def __new__(cls, msgid, msgtext=None, params=None,
- domain='keystoneclient', *args):
- """Create a new Message object.
-
- In order for translation to work gettext requires a message ID, this
- msgid will be used as the base unicode text. It is also possible
- for the msgid and the base unicode text to be different by passing
- the msgtext parameter.
- """
- # If the base msgtext is not given, we use the default translation
- # of the msgid (which is in English) just in case the system locale is
- # not English, so that the base text will be in that locale by default.
- if not msgtext:
- msgtext = Message._translate_msgid(msgid, domain)
- # We want to initialize the parent unicode with the actual object that
- # would have been plain unicode if 'Message' was not enabled.
- msg = super(Message, cls).__new__(cls, msgtext)
- msg.msgid = msgid
- msg.domain = domain
- msg.params = params
- return msg
-
- def translate(self, desired_locale=None):
- """Translate this message to the desired locale.
-
- :param desired_locale: The desired locale to translate the message to,
- if no locale is provided the message will be
- translated to the system's default locale.
-
- :returns: the translated message in unicode
- """
-
- translated_message = Message._translate_msgid(self.msgid,
- self.domain,
- desired_locale)
- if self.params is None:
- # No need for more translation
- return translated_message
-
- # This Message object may have been formatted with one or more
- # Message objects as substitution arguments, given either as a single
- # argument, part of a tuple, or as one or more values in a dictionary.
- # When translating this Message we need to translate those Messages too
- translated_params = _translate_args(self.params, desired_locale)
-
- translated_message = translated_message % translated_params
-
- return translated_message
-
- @staticmethod
- def _translate_msgid(msgid, domain, desired_locale=None):
- if not desired_locale:
- system_locale = locale.getdefaultlocale()
- # If the system locale is not available to the runtime use English
- if not system_locale[0]:
- desired_locale = 'en_US'
- else:
- desired_locale = system_locale[0]
-
- locale_dir = os.environ.get(domain.upper() + '_LOCALEDIR')
- lang = gettext.translation(domain,
- localedir=locale_dir,
- languages=[desired_locale],
- fallback=True)
- if six.PY3:
- translator = lang.gettext
- else:
- translator = lang.ugettext
-
- translated_message = translator(msgid)
- return translated_message
-
- def __mod__(self, other):
- # When we mod a Message we want the actual operation to be performed
- # by the parent class (i.e. unicode()), the only thing we do here is
- # save the original msgid and the parameters in case of a translation
- params = self._sanitize_mod_params(other)
- unicode_mod = super(Message, self).__mod__(params)
- modded = Message(self.msgid,
- msgtext=unicode_mod,
- params=params,
- domain=self.domain)
- return modded
-
- def _sanitize_mod_params(self, other):
- """Sanitize the object being modded with this Message.
-
- - Add support for modding 'None' so translation supports it
- - Trim the modded object, which can be a large dictionary, to only
- those keys that would actually be used in a translation
- - Snapshot the object being modded, in case the message is
- translated, it will be used as it was when the Message was created
- """
- if other is None:
- params = (other,)
- elif isinstance(other, dict):
- # Merge the dictionaries
- # Copy each item in case one does not support deep copy.
- params = {}
- if isinstance(self.params, dict):
- for key, val in self.params.items():
- params[key] = self._copy_param(val)
- for key, val in other.items():
- params[key] = self._copy_param(val)
- else:
- params = self._copy_param(other)
- return params
-
- def _copy_param(self, param):
- try:
- return copy.deepcopy(param)
- except Exception:
- # Fallback to casting to unicode this will handle the
- # python code-like objects that can't be deep-copied
- return six.text_type(param)
-
- def __add__(self, other):
- msg = _('Message objects do not support addition.')
- raise TypeError(msg)
-
- def __radd__(self, other):
- return self.__add__(other)
-
- if six.PY2:
- def __str__(self):
- # NOTE(luisg): Logging in python 2.6 tries to str() log records,
- # and it expects specifically a UnicodeError in order to proceed.
- msg = _('Message objects do not support str() because they may '
- 'contain non-ascii characters. '
- 'Please use unicode() or translate() instead.')
- raise UnicodeError(msg)
-
-
-def get_available_languages(domain):
- """Lists the available languages for the given translation domain.
-
- :param domain: the domain to get languages for
- """
- if domain in _AVAILABLE_LANGUAGES:
- return copy.copy(_AVAILABLE_LANGUAGES[domain])
-
- localedir = '%s_LOCALEDIR' % domain.upper()
- find = lambda x: gettext.find(domain,
- localedir=os.environ.get(localedir),
- languages=[x])
-
- # NOTE(mrodden): en_US should always be available (and first in case
- # order matters) since our in-line message strings are en_US
- language_list = ['en_US']
- # NOTE(luisg): Babel <1.0 used a function called list(), which was
- # renamed to locale_identifiers() in >=1.0, the requirements master list
- # requires >=0.9.6, uncapped, so defensively work with both. We can remove
- # this check when the master list updates to >=1.0, and update all projects
- list_identifiers = (getattr(localedata, 'list', None) or
- getattr(localedata, 'locale_identifiers'))
- locale_identifiers = list_identifiers()
-
- for i in locale_identifiers:
- if find(i) is not None:
- language_list.append(i)
-
- # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported
- # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they
- # are perfectly legitimate locales:
- # https://github.com/mitsuhiko/babel/issues/37
- # In Babel 1.3 they fixed the bug and they support these locales, but
- # they are still not explicitly "listed" by locale_identifiers().
- # That is why we add the locales here explicitly if necessary so that
- # they are listed as supported.
- aliases = {'zh': 'zh_CN',
- 'zh_Hant_HK': 'zh_HK',
- 'zh_Hant': 'zh_TW',
- 'fil': 'tl_PH'}
- for (locale_, alias) in six.iteritems(aliases):
- if locale_ in language_list and alias not in language_list:
- language_list.append(alias)
-
- _AVAILABLE_LANGUAGES[domain] = language_list
- return copy.copy(language_list)
-
-
-def translate(obj, desired_locale=None):
- """Gets the translated unicode representation of the given object.
-
- If the object is not translatable it is returned as-is.
- If the locale is None the object is translated to the system locale.
-
- :param obj: the object to translate
- :param desired_locale: the locale to translate the message to, if None the
- default system locale will be used
- :returns: the translated object in unicode, or the original object if
- it could not be translated
- """
- message = obj
- if not isinstance(message, Message):
- # If the object to translate is not already translatable,
- # let's first get its unicode representation
- message = six.text_type(obj)
- if isinstance(message, Message):
- # Even after unicoding() we still need to check if we are
- # running with translatable unicode before translating
- return message.translate(desired_locale)
- return obj
-
-
-def _translate_args(args, desired_locale=None):
- """Translates all the translatable elements of the given arguments object.
-
- This method is used for translating the translatable values in method
- arguments which include values of tuples or dictionaries.
- If the object is not a tuple or a dictionary the object itself is
- translated if it is translatable.
-
- If the locale is None the object is translated to the system locale.
-
- :param args: the args to translate
- :param desired_locale: the locale to translate the args to, if None the
- default system locale will be used
- :returns: a new args object with the translated contents of the original
- """
- if isinstance(args, tuple):
- return tuple(translate(v, desired_locale) for v in args)
- if isinstance(args, dict):
- translated_dict = {}
- for (k, v) in six.iteritems(args):
- translated_v = translate(v, desired_locale)
- translated_dict[k] = translated_v
- return translated_dict
- return translate(args, desired_locale)
-
-
-class TranslationHandler(handlers.MemoryHandler):
- """Handler that translates records before logging them.
-
- The TranslationHandler takes a locale and a target logging.Handler object
- to forward LogRecord objects to after translating them. This handler
- depends on Message objects being logged, instead of regular strings.
-
- The handler can be configured declaratively in the logging.conf as follows:
-
- [handlers]
- keys = translatedlog, translator
-
- [handler_translatedlog]
- class = handlers.WatchedFileHandler
- args = ('/var/log/api-localized.log',)
- formatter = context
-
- [handler_translator]
- class = openstack.common.log.TranslationHandler
- target = translatedlog
- args = ('zh_CN',)
-
- If the specified locale is not available in the system, the handler will
- log in the default locale.
- """
-
- def __init__(self, locale=None, target=None):
- """Initialize a TranslationHandler
-
- :param locale: locale to use for translating messages
- :param target: logging.Handler object to forward
- LogRecord objects to after translation
- """
- # NOTE(luisg): In order to allow this handler to be a wrapper for
- # other handlers, such as a FileHandler, and still be able to
- # configure it using logging.conf, this handler has to extend
- # MemoryHandler because only the MemoryHandlers' logging.conf
- # parsing is implemented such that it accepts a target handler.
- handlers.MemoryHandler.__init__(self, capacity=0, target=target)
- self.locale = locale
-
- def setFormatter(self, fmt):
- self.target.setFormatter(fmt)
-
- def emit(self, record):
- # We save the message from the original record to restore it
- # after translation, so other handlers are not affected by this
- original_msg = record.msg
- original_args = record.args
-
- try:
- self._translate_and_log_record(record)
- finally:
- record.msg = original_msg
- record.args = original_args
-
- def _translate_and_log_record(self, record):
- record.msg = translate(record.msg, self.locale)
-
- # In addition to translating the message, we also need to translate
- # arguments that were passed to the log method that were not part
- # of the main message e.g., log.info(_('Some message %s'), this_one))
- record.args = _translate_args(record.args, self.locale)
-
- self.target.emit(record)
diff --git a/keystoneclient/openstack/common/importutils.py b/keystoneclient/openstack/common/importutils.py
deleted file mode 100644
index 1261278..0000000
--- a/keystoneclient/openstack/common/importutils.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# 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.
-
-"""
-Import related utilities and helper functions.
-"""
-
-import sys
-import traceback
-
-
-def import_class(import_str):
- """Returns a class from a string including module and class."""
- mod_str, _sep, class_str = import_str.rpartition('.')
- __import__(mod_str)
- try:
- return getattr(sys.modules[mod_str], class_str)
- except AttributeError:
- raise ImportError('Class %s cannot be found (%s)' %
- (class_str,
- traceback.format_exception(*sys.exc_info())))
-
-
-def import_object(import_str, *args, **kwargs):
- """Import a class and return an instance of it."""
- return import_class(import_str)(*args, **kwargs)
-
-
-def import_object_ns(name_space, import_str, *args, **kwargs):
- """Tries to import object from default namespace.
-
- Imports a class and return an instance of it, first by trying
- to find the class in a default namespace, then failing back to
- a full path if not found in the default namespace.
- """
- import_value = "%s.%s" % (name_space, import_str)
- try:
- return import_class(import_value)(*args, **kwargs)
- except ImportError:
- return import_class(import_str)(*args, **kwargs)
-
-
-def import_module(import_str):
- """Import a module."""
- __import__(import_str)
- return sys.modules[import_str]
-
-
-def import_versioned_module(version, submodule=None):
- module = 'keystoneclient.v%s' % version
- if submodule:
- module = '.'.join((module, submodule))
- return import_module(module)
-
-
-def try_import(import_str, default=None):
- """Try to import a module and if it fails return default."""
- try:
- return import_module(import_str)
- except ImportError:
- return default
diff --git a/keystoneclient/openstack/common/jsonutils.py b/keystoneclient/openstack/common/jsonutils.py
deleted file mode 100644
index 3252588..0000000
--- a/keystoneclient/openstack/common/jsonutils.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# Copyright 2011 Justin Santa Barbara
-# 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.
-
-'''
-JSON related utilities.
-
-This module provides a few things:
-
- 1) A handy function for getting an object down to something that can be
- JSON serialized. See to_primitive().
-
- 2) Wrappers around loads() and dumps(). The dumps() wrapper will
- automatically use to_primitive() for you if needed.
-
- 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
- is available.
-'''
-
-
-import codecs
-import datetime
-import functools
-import inspect
-import itertools
-import sys
-
-is_simplejson = False
-if sys.version_info < (2, 7):
- # On Python <= 2.6, json module is not C boosted, so try to use
- # simplejson module if available
- try:
- import simplejson as json
- # NOTE(mriedem): Make sure we have a new enough version of simplejson
- # to support the namedobject_as_tuple argument. This can be removed
- # in the Kilo release when python 2.6 support is dropped.
- if 'namedtuple_as_object' in inspect.getargspec(json.dumps).args:
- is_simplejson = True
- else:
- import json
- except ImportError:
- import json
-else:
- import json
-
-import six
-import six.moves.xmlrpc_client as xmlrpclib
-
-from keystoneclient.openstack.common import gettextutils
-from keystoneclient.openstack.common import importutils
-from keystoneclient.openstack.common import strutils
-from keystoneclient.openstack.common import timeutils
-
-netaddr = importutils.try_import("netaddr")
-
-_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
- inspect.isfunction, inspect.isgeneratorfunction,
- inspect.isgenerator, inspect.istraceback, inspect.isframe,
- inspect.iscode, inspect.isbuiltin, inspect.isroutine,
- inspect.isabstract]
-
-_simple_types = (six.string_types + six.integer_types
- + (type(None), bool, float))
-
-
-def to_primitive(value, convert_instances=False, convert_datetime=True,
- level=0, max_depth=3):
- """Convert a complex object into primitives.
-
- Handy for JSON serialization. We can optionally handle instances,
- but since this is a recursive function, we could have cyclical
- data structures.
-
- To handle cyclical data structures we could track the actual objects
- visited in a set, but not all objects are hashable. Instead we just
- track the depth of the object inspections and don't go too deep.
-
- Therefore, convert_instances=True is lossy ... be aware.
-
- """
- # handle obvious types first - order of basic types determined by running
- # full tests on nova project, resulting in the following counts:
- # 572754 <type 'NoneType'>
- # 460353 <type 'int'>
- # 379632 <type 'unicode'>
- # 274610 <type 'str'>
- # 199918 <type 'dict'>
- # 114200 <type 'datetime.datetime'>
- # 51817 <type 'bool'>
- # 26164 <type 'list'>
- # 6491 <type 'float'>
- # 283 <type 'tuple'>
- # 19 <type 'long'>
- if isinstance(value, _simple_types):
- return value
-
- if isinstance(value, datetime.datetime):
- if convert_datetime:
- return timeutils.strtime(value)
- else:
- return value
-
- # value of itertools.count doesn't get caught by nasty_type_tests
- # and results in infinite loop when list(value) is called.
- if type(value) == itertools.count:
- return six.text_type(value)
-
- # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
- # tests that raise an exception in a mocked method that
- # has a @wrap_exception with a notifier will fail. If
- # we up the dependency to 0.5.4 (when it is released) we
- # can remove this workaround.
- if getattr(value, '__module__', None) == 'mox':
- return 'mock'
-
- if level > max_depth:
- return '?'
-
- # The try block may not be necessary after the class check above,
- # but just in case ...
- try:
- recursive = functools.partial(to_primitive,
- convert_instances=convert_instances,
- convert_datetime=convert_datetime,
- level=level,
- max_depth=max_depth)
- if isinstance(value, dict):
- return dict((k, recursive(v)) for k, v in six.iteritems(value))
- elif isinstance(value, (list, tuple)):
- return [recursive(lv) for lv in value]
-
- # It's not clear why xmlrpclib created their own DateTime type, but
- # for our purposes, make it a datetime type which is explicitly
- # handled
- if isinstance(value, xmlrpclib.DateTime):
- value = datetime.datetime(*tuple(value.timetuple())[:6])
-
- if convert_datetime and isinstance(value, datetime.datetime):
- return timeutils.strtime(value)
- elif isinstance(value, gettextutils.Message):
- return value.data
- elif hasattr(value, 'iteritems'):
- return recursive(dict(value.iteritems()), level=level + 1)
- elif hasattr(value, '__iter__'):
- return recursive(list(value))
- elif convert_instances and hasattr(value, '__dict__'):
- # Likely an instance of something. Watch for cycles.
- # Ignore class member vars.
- return recursive(value.__dict__, level=level + 1)
- elif netaddr and isinstance(value, netaddr.IPAddress):
- return six.text_type(value)
- else:
- if any(test(value) for test in _nasty_type_tests):
- return six.text_type(value)
- return value
- except TypeError:
- # Class objects are tricky since they may define something like
- # __iter__ defined but it isn't callable as list().
- return six.text_type(value)
-
-
-def dumps(value, default=to_primitive, **kwargs):
- if is_simplejson:
- kwargs['namedtuple_as_object'] = False
- return json.dumps(value, default=default, **kwargs)
-
-
-def dump(obj, fp, *args, **kwargs):
- if is_simplejson:
- kwargs['namedtuple_as_object'] = False
- return json.dump(obj, fp, *args, **kwargs)
-
-
-def loads(s, encoding='utf-8', **kwargs):
- return json.loads(strutils.safe_decode(s, encoding), **kwargs)
-
-
-def load(fp, encoding='utf-8', **kwargs):
- return json.load(codecs.getreader(encoding)(fp), **kwargs)
-
-
-try:
- import anyjson
-except ImportError:
- pass
-else:
- anyjson._modules.append((__name__, 'dumps', TypeError,
- 'loads', ValueError, 'load'))
- anyjson.force_implementation(__name__)
diff --git a/keystoneclient/openstack/common/memorycache.py b/keystoneclient/openstack/common/memorycache.py
index a833634..4826865 100644
--- a/keystoneclient/openstack/common/memorycache.py
+++ b/keystoneclient/openstack/common/memorycache.py
@@ -17,8 +17,7 @@
"""Super simple fake memcache client."""
from oslo.config import cfg
-
-from keystoneclient.openstack.common import timeutils
+from oslo.utils import timeutils
memcache_opts = [
cfg.ListOpt('memcached_servers',
diff --git a/keystoneclient/openstack/common/strutils.py b/keystoneclient/openstack/common/strutils.py
deleted file mode 100644
index fc3ef3f..0000000
--- a/keystoneclient/openstack/common/strutils.py
+++ /dev/null
@@ -1,311 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# 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.
-
-"""
-System-level utilities and helper functions.
-"""
-
-import math
-import re
-import sys
-import unicodedata
-
-import six
-
-from keystoneclient.openstack.common.gettextutils import _
-
-
-UNIT_PREFIX_EXPONENT = {
- 'k': 1,
- 'K': 1,
- 'Ki': 1,
- 'M': 2,
- 'Mi': 2,
- 'G': 3,
- 'Gi': 3,
- 'T': 4,
- 'Ti': 4,
-}
-UNIT_SYSTEM_INFO = {
- 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')),
- 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')),
-}
-
-TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
-FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
-
-SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
-SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
-
-
-# NOTE(flaper87): The following globals are used by `mask_password`
-_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password']
-
-# NOTE(ldbragst): Let's build a list of regex objects using the list of
-# _SANITIZE_KEYS we already have. This way, we only have to add the new key
-# to the list of _SANITIZE_KEYS and we can generate regular expressions
-# for XML and JSON automatically.
-_SANITIZE_PATTERNS_2 = []
-_SANITIZE_PATTERNS_1 = []
-
-# NOTE(amrith): Some regular expressions have only one parameter, some
-# have two parameters. Use different lists of patterns here.
-_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+']
-_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])',
- r'(%(key)s\s+[\"\']).*?([\"\'])',
- r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)',
- r'(<%(key)s>).*?(</%(key)s>)',
- r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])',
- r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])',
- r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?'
- '[\'"]).*?([\'"])',
- r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)']
-
-for key in _SANITIZE_KEYS:
- for pattern in _FORMAT_PATTERNS_2:
- reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
- _SANITIZE_PATTERNS_2.append(reg_ex)
-
- for pattern in _FORMAT_PATTERNS_1:
- reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
- _SANITIZE_PATTERNS_1.append(reg_ex)
-
-
-def int_from_bool_as_string(subject):
- """Interpret a string as a boolean and return either 1 or 0.
-
- Any string value in:
-
- ('True', 'true', 'On', 'on', '1')
-
- is interpreted as a boolean True.
-
- Useful for JSON-decoded stuff and config file parsing
- """
- return bool_from_string(subject) and 1 or 0
-
-
-def bool_from_string(subject, strict=False, default=False):
- """Interpret a string as a boolean.
-
- A case-insensitive match is performed such that strings matching 't',
- 'true', 'on', 'y', 'yes', or '1' are considered True and, when
- `strict=False`, anything else returns the value specified by 'default'.
-
- Useful for JSON-decoded stuff and config file parsing.
-
- If `strict=True`, unrecognized values, including None, will raise a
- ValueError which is useful when parsing values passed in from an API call.
- Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
- """
- if not isinstance(subject, six.string_types):
- subject = six.text_type(subject)
-
- lowered = subject.strip().lower()
-
- if lowered in TRUE_STRINGS:
- return True
- elif lowered in FALSE_STRINGS:
- return False
- elif strict:
- acceptable = ', '.join(
- "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
- msg = _("Unrecognized value '%(val)s', acceptable values are:"
- " %(acceptable)s") % {'val': subject,
- 'acceptable': acceptable}
- raise ValueError(msg)
- else:
- return default
-
-
-def safe_decode(text, incoming=None, errors='strict'):
- """Decodes incoming text/bytes string using `incoming` if they're not
- already unicode.
-
- :param incoming: Text's current encoding
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: text or a unicode `incoming` encoded
- representation of it.
- :raises TypeError: If text is not an instance of str
- """
- if not isinstance(text, (six.string_types, six.binary_type)):
- raise TypeError("%s can't be decoded" % type(text))
-
- if isinstance(text, six.text_type):
- return text
-
- if not incoming:
- incoming = (sys.stdin.encoding or
- sys.getdefaultencoding())
-
- try:
- return text.decode(incoming, errors)
- except UnicodeDecodeError:
- # Note(flaper87) If we get here, it means that
- # sys.stdin.encoding / sys.getdefaultencoding
- # didn't return a suitable encoding to decode
- # text. This happens mostly when global LANG
- # var is not set correctly and there's no
- # default encoding. In this case, most likely
- # python will use ASCII or ANSI encoders as
- # default encodings but they won't be capable
- # of decoding non-ASCII characters.
- #
- # Also, UTF-8 is being used since it's an ASCII
- # extension.
- return text.decode('utf-8', errors)
-
-
-def safe_encode(text, incoming=None,
- encoding='utf-8', errors='strict'):
- """Encodes incoming text/bytes string using `encoding`.
-
- If incoming is not specified, text is expected to be encoded with
- current python's default encoding. (`sys.getdefaultencoding`)
-
- :param incoming: Text's current encoding
- :param encoding: Expected encoding for text (Default UTF-8)
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: text or a bytestring `encoding` encoded
- representation of it.
- :raises TypeError: If text is not an instance of str
- """
- if not isinstance(text, (six.string_types, six.binary_type)):
- raise TypeError("%s can't be encoded" % type(text))
-
- if not incoming:
- incoming = (sys.stdin.encoding or
- sys.getdefaultencoding())
-
- if isinstance(text, six.text_type):
- return text.encode(encoding, errors)
- elif text and encoding != incoming:
- # Decode text before encoding it with `encoding`
- text = safe_decode(text, incoming, errors)
- return text.encode(encoding, errors)
- else:
- return text
-
-
-def string_to_bytes(text, unit_system='IEC', return_int=False):
- """Converts a string into an float representation of bytes.
-
- The units supported for IEC ::
-
- Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it)
- KB, KiB, MB, MiB, GB, GiB, TB, TiB
-
- The units supported for SI ::
-
- kb(it), Mb(it), Gb(it), Tb(it)
- kB, MB, GB, TB
-
- Note that the SI unit system does not support capital letter 'K'
-
- :param text: String input for bytes size conversion.
- :param unit_system: Unit system for byte size conversion.
- :param return_int: If True, returns integer representation of text
- in bytes. (default: decimal)
- :returns: Numerical representation of text in bytes.
- :raises ValueError: If text has an invalid value.
-
- """
- try:
- base, reg_ex = UNIT_SYSTEM_INFO[unit_system]
- except KeyError:
- msg = _('Invalid unit system: "%s"') % unit_system
- raise ValueError(msg)
- match = reg_ex.match(text)
- if match:
- magnitude = float(match.group(1))
- unit_prefix = match.group(2)
- if match.group(3) in ['b', 'bit']:
- magnitude /= 8
- else:
- msg = _('Invalid string format: %s') % text
- raise ValueError(msg)
- if not unit_prefix:
- res = magnitude
- else:
- res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix])
- if return_int:
- return int(math.ceil(res))
- return res
-
-
-def to_slug(value, incoming=None, errors="strict"):
- """Normalize string.
-
- Convert to lowercase, remove non-word characters, and convert spaces
- to hyphens.
-
- Inspired by Django's `slugify` filter.
-
- :param value: Text to slugify
- :param incoming: Text's current encoding
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: slugified unicode representation of `value`
- :raises TypeError: If text is not an instance of str
- """
- value = safe_decode(value, incoming, errors)
- # NOTE(aababilov): no need to use safe_(encode|decode) here:
- # encodings are always "ascii", error handling is always "ignore"
- # and types are always known (first: unicode; second: str)
- value = unicodedata.normalize("NFKD", value).encode(
- "ascii", "ignore").decode("ascii")
- value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
- return SLUGIFY_HYPHENATE_RE.sub("-", value)
-
-
-def mask_password(message, secret="***"):
- """Replace password with 'secret' in message.
-
- :param message: The string which includes security information.
- :param secret: value with which to replace passwords.
- :returns: The unicode value of message with the password fields masked.
-
- For example:
-
- >>> mask_password("'adminPass' : 'aaaaa'")
- "'adminPass' : '***'"
- >>> mask_password("'admin_pass' : 'aaaaa'")
- "'admin_pass' : '***'"
- >>> mask_password('"password" : "aaaaa"')
- '"password" : "***"'
- >>> mask_password("'original_password' : 'aaaaa'")
- "'original_password' : '***'"
- >>> mask_password("u'original_password' : u'aaaaa'")
- "u'original_password' : u'***'"
- """
- message = six.text_type(message)
-
- # NOTE(ldbragst): Check to see if anything in message contains any key
- # specified in _SANITIZE_KEYS, if not then just return the message since
- # we don't have to mask any passwords.
- if not any(key in message for key in _SANITIZE_KEYS):
- return message
-
- substitute = r'\g<1>' + secret + r'\g<2>'
- for pattern in _SANITIZE_PATTERNS_2:
- message = re.sub(pattern, substitute, message)
-
- substitute = r'\g<1>' + secret
- for pattern in _SANITIZE_PATTERNS_1:
- message = re.sub(pattern, substitute, message)
-
- return message
diff --git a/keystoneclient/openstack/common/timeutils.py b/keystoneclient/openstack/common/timeutils.py
deleted file mode 100644
index 49d8287..0000000
--- a/keystoneclient/openstack/common/timeutils.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# 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.
-
-"""
-Time related utilities and helper functions.
-"""
-
-import calendar
-import datetime
-import time
-
-import iso8601
-import six
-
-
-# ISO 8601 extended time format with microseconds
-_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
-_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
-PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
-
-
-def isotime(at=None, subsecond=False):
- """Stringify time in ISO 8601 format."""
- if not at:
- at = utcnow()
- st = at.strftime(_ISO8601_TIME_FORMAT
- if not subsecond
- else _ISO8601_TIME_FORMAT_SUBSECOND)
- tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
- st += ('Z' if tz == 'UTC' else tz)
- return st
-
-
-def parse_isotime(timestr):
- """Parse time from ISO 8601 format."""
- try:
- return iso8601.parse_date(timestr)
- except iso8601.ParseError as e:
- raise ValueError(six.text_type(e))
- except TypeError as e:
- raise ValueError(six.text_type(e))
-
-
-def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
- """Returns formatted utcnow."""
- if not at:
- at = utcnow()
- return at.strftime(fmt)
-
-
-def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
- """Turn a formatted time back into a datetime."""
- return datetime.datetime.strptime(timestr, fmt)
-
-
-def normalize_time(timestamp):
- """Normalize time in arbitrary timezone to UTC naive object."""
- offset = timestamp.utcoffset()
- if offset is None:
- return timestamp
- return timestamp.replace(tzinfo=None) - offset
-
-
-def is_older_than(before, seconds):
- """Return True if before is older than seconds."""
- if isinstance(before, six.string_types):
- before = parse_strtime(before).replace(tzinfo=None)
- else:
- before = before.replace(tzinfo=None)
-
- return utcnow() - before > datetime.timedelta(seconds=seconds)
-
-
-def is_newer_than(after, seconds):
- """Return True if after is newer than seconds."""
- if isinstance(after, six.string_types):
- after = parse_strtime(after).replace(tzinfo=None)
- else:
- after = after.replace(tzinfo=None)
-
- return after - utcnow() > datetime.timedelta(seconds=seconds)
-
-
-def utcnow_ts():
- """Timestamp version of our utcnow function."""
- if utcnow.override_time is None:
- # NOTE(kgriffs): This is several times faster
- # than going through calendar.timegm(...)
- return int(time.time())
-
- return calendar.timegm(utcnow().timetuple())
-
-
-def utcnow():
- """Overridable version of utils.utcnow."""
- if utcnow.override_time:
- try:
- return utcnow.override_time.pop(0)
- except AttributeError:
- return utcnow.override_time
- return datetime.datetime.utcnow()
-
-
-def iso8601_from_timestamp(timestamp):
- """Returns an iso8601 formatted date from timestamp."""
- return isotime(datetime.datetime.utcfromtimestamp(timestamp))
-
-
-utcnow.override_time = None
-
-
-def set_time_override(override_time=None):
- """Overrides utils.utcnow.
-
- Make it return a constant time or a list thereof, one at a time.
-
- :param override_time: datetime instance or list thereof. If not
- given, defaults to the current UTC time.
- """
- utcnow.override_time = override_time or datetime.datetime.utcnow()
-
-
-def advance_time_delta(timedelta):
- """Advance overridden time using a datetime.timedelta."""
- assert utcnow.override_time is not None
- try:
- for dt in utcnow.override_time:
- dt += timedelta
- except TypeError:
- utcnow.override_time += timedelta
-
-
-def advance_time_seconds(seconds):
- """Advance overridden time by seconds."""
- advance_time_delta(datetime.timedelta(0, seconds))
-
-
-def clear_time_override():
- """Remove the overridden time."""
- utcnow.override_time = None
-
-
-def marshall_now(now=None):
- """Make an rpc-safe datetime with microseconds.
-
- Note: tzinfo is stripped, but not required for relative times.
- """
- if not now:
- now = utcnow()
- return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
- minute=now.minute, second=now.second,
- microsecond=now.microsecond)
-
-
-def unmarshall_time(tyme):
- """Unmarshall a datetime dict."""
- return datetime.datetime(day=tyme['day'],
- month=tyme['month'],
- year=tyme['year'],
- hour=tyme['hour'],
- minute=tyme['minute'],
- second=tyme['second'],
- microsecond=tyme['microsecond'])
-
-
-def delta_seconds(before, after):
- """Return the difference between two timing objects.
-
- Compute the difference in seconds between two date, time, or
- datetime objects (as a float, to microsecond resolution).
- """
- delta = after - before
- return total_seconds(delta)
-
-
-def total_seconds(delta):
- """Return the total seconds of datetime.timedelta object.
-
- Compute total seconds of datetime.timedelta, datetime.timedelta
- doesn't have method total_seconds in Python2.6, calculate it manually.
- """
- try:
- return delta.total_seconds()
- except AttributeError:
- return ((delta.days * 24 * 3600) + delta.seconds +
- float(delta.microseconds) / (10 ** 6))
-
-
-def is_soon(dt, window):
- """Determines if time is going to happen in the next window seconds.
-
- :param dt: the time
- :param window: minimum seconds to remain to consider the time not soon
-
- :returns: True if expiration is within the given duration
- """
- soon = (utcnow() + datetime.timedelta(seconds=window))
- return normalize_time(dt) <= soon
diff --git a/keystoneclient/openstack/common/uuidutils.py b/keystoneclient/openstack/common/uuidutils.py
new file mode 100644
index 0000000..234b880
--- /dev/null
+++ b/keystoneclient/openstack/common/uuidutils.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2012 Intel Corporation.
+# 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.
+
+"""
+UUID related utilities and helper functions.
+"""
+
+import uuid
+
+
+def generate_uuid():
+ return str(uuid.uuid4())
+
+
+def is_uuid_like(val):
+ """Returns validation of a value as a UUID.
+
+ For our purposes, a UUID is a canonical form string:
+ aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
+
+ """
+ try:
+ return str(uuid.UUID(val)) == val
+ except (TypeError, ValueError, AttributeError):
+ return False
diff --git a/keystoneclient/service_catalog.py b/keystoneclient/service_catalog.py
index cf85ed7..7c9085b 100644
--- a/keystoneclient/service_catalog.py
+++ b/keystoneclient/service_catalog.py
@@ -21,6 +21,7 @@ import abc
import six
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -36,7 +37,7 @@ class ServiceCatalog(object):
elif ServiceCatalogV2.is_valid(resource_dict):
return ServiceCatalogV2(resource_dict, region_name)
else:
- raise NotImplementedError('Unrecognized auth response')
+ raise NotImplementedError(_('Unrecognized auth response'))
def __init__(self, region_name=None):
self._region_name = region_name
@@ -208,7 +209,7 @@ class ServiceCatalog(object):
"""
if not self.get_data():
- raise exceptions.EmptyCatalog('The service catalog is empty.')
+ raise exceptions.EmptyCatalog(_('The service catalog is empty.'))
urls = self.get_urls(attr=attr,
filter_value=filter_value,
@@ -222,12 +223,30 @@ class ServiceCatalog(object):
except Exception:
pass
- msg = '%s endpoint for %s service' % (endpoint_type, service_type)
- if service_name:
- msg += ' named %s' % service_name
- if region_name:
- msg += ' in %s region' % region_name
- msg += ' not found'
+ if service_name and region_name:
+ msg = (_('%(endpoint_type)s endpoint for %(service_type)s service '
+ 'named %(service_name)s in %(region_name)s region not '
+ 'found') %
+ {'endpoint_type': endpoint_type,
+ 'service_type': service_type, 'service_name': service_name,
+ 'region_name': region_name})
+ elif service_name:
+ msg = (_('%(endpoint_type)s endpoint for %(service_type)s service '
+ 'named %(service_name)s not found') %
+ {'endpoint_type': endpoint_type,
+ 'service_type': service_type,
+ 'service_name': service_name})
+ elif region_name:
+ msg = (_('%(endpoint_type)s endpoint for %(service_type)s service '
+ 'in %(region_name)s region not found') %
+ {'endpoint_type': endpoint_type,
+ 'service_type': service_type, 'region_name': region_name})
+ else:
+ msg = (_('%(endpoint_type)s endpoint for %(service_type)s service '
+ 'not found') %
+ {'endpoint_type': endpoint_type,
+ 'service_type': service_type})
+
raise exceptions.EndpointNotFound(msg)
@abc.abstractmethod
diff --git a/keystoneclient/session.py b/keystoneclient/session.py
index 111d369..084613f 100644
--- a/keystoneclient/session.py
+++ b/keystoneclient/session.py
@@ -18,13 +18,14 @@ import os
import time
from oslo.config import cfg
+from oslo.serialization import jsonutils
+from oslo.utils import importutils
import requests
import six
from six.moves import urllib
from keystoneclient import exceptions
-from keystoneclient.openstack.common import importutils
-from keystoneclient.openstack.common import jsonutils
+from keystoneclient.i18n import _, _LI, _LW
from keystoneclient import utils
osprofiler_web = importutils.try_import("osprofiler.web")
@@ -40,10 +41,10 @@ def _positive_non_zero_float(argument_value):
try:
value = float(argument_value)
except ValueError:
- msg = "%s must be a float" % argument_value
+ msg = _("%s must be a float") % argument_value
raise argparse.ArgumentTypeError(msg)
if value <= 0:
- msg = "%s must be greater than 0" % argument_value
+ msg = _("%s must be greater than 0") % argument_value
raise argparse.ArgumentTypeError(msg)
return value
@@ -138,7 +139,7 @@ class Session(object):
# debug log.
return
- string_parts = ['REQ: curl -i']
+ string_parts = ['REQ: curl -g -i']
# NOTE(jamielennox): None means let requests do its default validation
# so we need to actually check that this is False.
@@ -259,8 +260,8 @@ class Session(object):
'allow_redirects' is ignored as redirects are handled
by the session.
- :raises exceptions.ClientException: For connection failure, or to
- indicate an error response code.
+ :raises keystoneclient.exceptions.ClientException: For connection
+ failure, or to indicate an error response code.
:returns: The response to the request.
"""
@@ -274,7 +275,7 @@ class Session(object):
token = self.get_token(auth)
if not token:
- raise exceptions.AuthorizationFailure("No token Available")
+ raise exceptions.AuthorizationFailure(_("No token Available"))
headers['X-Auth-Token'] = token
@@ -372,20 +373,20 @@ class Session(object):
try:
resp = self.session.request(method, url, **kwargs)
except requests.exceptions.SSLError:
- msg = 'SSL exception connecting to %s' % url
+ msg = _('SSL exception connecting to %s') % url
raise exceptions.SSLError(msg)
except requests.exceptions.Timeout:
- msg = 'Request to %s timed out' % url
+ msg = _('Request to %s timed out') % url
raise exceptions.RequestTimeout(msg)
except requests.exceptions.ConnectionError:
- msg = 'Unable to establish connection to %s' % url
+ msg = _('Unable to establish connection to %s') % url
raise exceptions.ConnectionRefused(msg)
except (exceptions.RequestTimeout, exceptions.ConnectionRefused) as e:
if connect_retries <= 0:
raise
- _logger.info('Failure: %s. Retrying in %.1fs.',
- e, connect_retry_delay)
+ _logger.info(_LI('Failure: %(e)s. Retrying in %(delay).1fs.'),
+ {'e': e, 'delay': connect_retry_delay})
time.sleep(connect_retry_delay)
return self._send_request(
@@ -411,8 +412,8 @@ class Session(object):
try:
location = resp.headers['location']
except KeyError:
- _logger.warn("Failed to redirect request to %s as new "
- "location was not provided.", resp.url)
+ _logger.warn(_LW("Failed to redirect request to %s as new "
+ "location was not provided."), resp.url)
else:
# NOTE(jamielennox): We don't pass through connect_retry_delay.
# This request actually worked so we can reset the delay count.
@@ -500,7 +501,8 @@ class Session(object):
on the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
- :raises AuthorizationFailure: if a new token fetch fails.
+ :raises keystoneclient.exceptions.AuthorizationFailure: if a new token
+ fetch fails.
:returns: A valid token.
:rtype: string
@@ -509,13 +511,13 @@ class Session(object):
auth = self.auth
if not auth:
- raise exceptions.MissingAuthPlugin("Token Required")
+ raise exceptions.MissingAuthPlugin(_("Token Required"))
try:
return auth.get_token(self)
except exceptions.HttpError as exc:
- raise exceptions.AuthorizationFailure("Authentication failure: "
- "%s" % exc)
+ raise exceptions.AuthorizationFailure(
+ _("Authentication failure: %s") % exc)
def get_endpoint(self, auth=None, **kwargs):
"""Get an endpoint as provided by the auth plugin.
@@ -524,7 +526,8 @@ class Session(object):
the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
- :raises MissingAuthPlugin: if a plugin is not available.
+ :raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not
+ available.
:returns: An endpoint if available or None.
:rtype: string
@@ -533,8 +536,9 @@ class Session(object):
auth = self.auth
if not auth:
- raise exceptions.MissingAuthPlugin('An auth plugin is required to '
- 'determine the endpoint URL.')
+ raise exceptions.MissingAuthPlugin(
+ _('An auth plugin is required to determine the endpoint '
+ 'URL.'))
return auth.get_endpoint(self, **kwargs)
@@ -545,7 +549,7 @@ class Session(object):
auth = self.auth
if not auth:
- msg = 'Auth plugin not available to invalidate'
+ msg = _('Auth plugin not available to invalidate')
raise exceptions.MissingAuthPlugin(msg)
return auth.invalidate()
diff --git a/keystoneclient/shell.py b/keystoneclient/shell.py
index ad8d127..be7330c 100644
--- a/keystoneclient/shell.py
+++ b/keystoneclient/shell.py
@@ -30,6 +30,7 @@ import logging
import os
import sys
+from oslo.utils import encodeutils
import six
import keystoneclient
@@ -37,7 +38,6 @@ from keystoneclient import access
from keystoneclient.contrib.bootstrap import shell as shell_bootstrap
from keystoneclient import exceptions as exc
from keystoneclient.generic import shell as shell_generic
-from keystoneclient.openstack.common import strutils
from keystoneclient import session
from keystoneclient import utils
from keystoneclient.v2_0 import shell as shell_v2_0
@@ -463,7 +463,7 @@ def main():
OpenStackIdentityShell().main(sys.argv[1:])
except Exception as e:
- print(strutils.safe_encode(six.text_type(e)), file=sys.stderr)
+ print(encodeutils.safe_encode(six.text_type(e)), file=sys.stderr)
sys.exit(1)
diff --git a/keystoneclient/tests/auth/test_identity_common.py b/keystoneclient/tests/auth/test_identity_common.py
index 9a369f7..4a0cf57 100644
--- a/keystoneclient/tests/auth/test_identity_common.py
+++ b/keystoneclient/tests/auth/test_identity_common.py
@@ -14,6 +14,7 @@ import abc
import datetime
import uuid
+from oslo.utils import timeutils
import six
from keystoneclient import access
@@ -21,7 +22,6 @@ from keystoneclient.auth import base
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
from keystoneclient import fixture
-from keystoneclient.openstack.common import timeutils
from keystoneclient import session
from keystoneclient.tests import utils
diff --git a/keystoneclient/tests/client_fixtures.py b/keystoneclient/tests/client_fixtures.py
index d58deb2..cadae11 100644
--- a/keystoneclient/tests/client_fixtures.py
+++ b/keystoneclient/tests/client_fixtures.py
@@ -15,12 +15,12 @@
import os
import fixtures
+from oslo.serialization import jsonutils
+from oslo.utils import timeutils
import six
import testresources
from keystoneclient.common import cms
-from keystoneclient.openstack.common import jsonutils
-from keystoneclient.openstack.common import timeutils
from keystoneclient import utils
diff --git a/keystoneclient/tests/generic/test_client.py b/keystoneclient/tests/generic/test_client.py
index fc5816b..0f25f41 100644
--- a/keystoneclient/tests/generic/test_client.py
+++ b/keystoneclient/tests/generic/test_client.py
@@ -13,8 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo.serialization import jsonutils
+
from keystoneclient.generic import client
-from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests import utils
BASE_HOST = 'http://keystone.example.com'
diff --git a/keystoneclient/tests/test_auth_token_middleware.py b/keystoneclient/tests/test_auth_token_middleware.py
index 3c42db3..4e70b73 100644
--- a/keystoneclient/tests/test_auth_token_middleware.py
+++ b/keystoneclient/tests/test_auth_token_middleware.py
@@ -25,6 +25,8 @@ import uuid
import fixtures
import iso8601
import mock
+from oslo.serialization import jsonutils
+from oslo.utils import timeutils
from requests_mock.contrib import fixture as mock_fixture
from six.moves.urllib import parse as urlparse
import testresources
@@ -37,9 +39,7 @@ from keystoneclient.common import cms
from keystoneclient import exceptions
from keystoneclient import fixture
from keystoneclient.middleware import auth_token
-from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests import client_fixtures
from keystoneclient.tests import utils
@@ -1007,7 +1007,7 @@ class CommonAuthTokenMiddlewareTest(object):
token = self.token_dict['signed_token_scoped']
req.headers['X-Auth-Token'] = token
req.environ.update(extra_environ)
- timeutils_utcnow = 'keystoneclient.openstack.common.timeutils.utcnow'
+ timeutils_utcnow = 'oslo.utils.timeutils.utcnow'
now = datetime.datetime.utcnow()
with mock.patch(timeutils_utcnow) as mock_utcnow:
mock_utcnow.return_value = now
@@ -1759,7 +1759,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest):
auth_token.confirm_token_not_expired,
data)
- @mock.patch('keystoneclient.openstack.common.timeutils.utcnow')
+ @mock.patch('oslo.utils.timeutils.utcnow')
def test_v2_token_with_timezone_offset_not_expired(self, mock_utcnow):
current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z')
current_time = timeutils.normalize_time(current_time)
@@ -1770,7 +1770,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest):
actual_expires = auth_token.confirm_token_not_expired(data)
self.assertEqual(actual_expires, expected_expires)
- @mock.patch('keystoneclient.openstack.common.timeutils.utcnow')
+ @mock.patch('oslo.utils.timeutils.utcnow')
def test_v2_token_with_timezone_offset_expired(self, mock_utcnow):
current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z')
current_time = timeutils.normalize_time(current_time)
@@ -1794,7 +1794,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest):
auth_token.confirm_token_not_expired,
data)
- @mock.patch('keystoneclient.openstack.common.timeutils.utcnow')
+ @mock.patch('oslo.utils.timeutils.utcnow')
def test_v3_token_with_timezone_offset_not_expired(self, mock_utcnow):
current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z')
current_time = timeutils.normalize_time(current_time)
@@ -1806,7 +1806,7 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest):
actual_expires = auth_token.confirm_token_not_expired(data)
self.assertEqual(actual_expires, expected_expires)
- @mock.patch('keystoneclient.openstack.common.timeutils.utcnow')
+ @mock.patch('oslo.utils.timeutils.utcnow')
def test_v3_token_with_timezone_offset_expired(self, mock_utcnow):
current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z')
current_time = timeutils.normalize_time(current_time)
diff --git a/keystoneclient/tests/test_cms.py b/keystoneclient/tests/test_cms.py
index 8cef987..9af3bd4 100644
--- a/keystoneclient/tests/test_cms.py
+++ b/keystoneclient/tests/test_cms.py
@@ -30,6 +30,18 @@ class CMSTest(utils.TestCase, testresources.ResourcedTestCase):
resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)]
+ def __init__(self, *args, **kwargs):
+ super(CMSTest, self).__init__(*args, **kwargs)
+ process = subprocess.Popen(['openssl', 'version'],
+ stdout=subprocess.PIPE)
+ out, err = process.communicate()
+ # Example output: 'OpenSSL 0.9.8za 5 Jun 2014'
+ openssl_version = out.split()[1]
+
+ if err or openssl_version.startswith(b'0'):
+ raise Exception('Your version of OpenSSL is not supported. '
+ 'You will need to update it to 1.0 or later.')
+
def test_cms_verify(self):
self.assertRaises(exceptions.CertificateConfigError,
cms.cms_verify,
diff --git a/keystoneclient/tests/test_discovery.py b/keystoneclient/tests/test_discovery.py
index 811e65c..a999a19 100644
--- a/keystoneclient/tests/test_discovery.py
+++ b/keystoneclient/tests/test_discovery.py
@@ -13,6 +13,7 @@
import re
import uuid
+from oslo.serialization import jsonutils
import six
from testtools import matchers
@@ -22,7 +23,6 @@ from keystoneclient import client
from keystoneclient import discover
from keystoneclient import exceptions
from keystoneclient import fixture
-from keystoneclient.openstack.common import jsonutils
from keystoneclient import session
from keystoneclient.tests import utils
from keystoneclient.v2_0 import client as v2_client
diff --git a/keystoneclient/tests/test_keyring.py b/keystoneclient/tests/test_keyring.py
index 0ad0587..4bdf731 100644
--- a/keystoneclient/tests/test_keyring.py
+++ b/keystoneclient/tests/test_keyring.py
@@ -13,10 +13,10 @@
import datetime
import mock
+from oslo.utils import timeutils
from keystoneclient import access
from keystoneclient import httpclient
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests import utils
from keystoneclient.tests.v2_0 import client_fixtures
diff --git a/keystoneclient/tests/test_s3_token_middleware.py b/keystoneclient/tests/test_s3_token_middleware.py
index 0233e8b..ab77b79 100644
--- a/keystoneclient/tests/test_s3_token_middleware.py
+++ b/keystoneclient/tests/test_s3_token_middleware.py
@@ -13,13 +13,13 @@
# under the License.
import mock
+from oslo.serialization import jsonutils
import requests
import six
import testtools
import webob
from keystoneclient.middleware import s3_token
-from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests import utils
diff --git a/keystoneclient/tests/test_session.py b/keystoneclient/tests/test_session.py
index 99c9e6e..6a9d408 100644
--- a/keystoneclient/tests/test_session.py
+++ b/keystoneclient/tests/test_session.py
@@ -17,6 +17,7 @@ import uuid
import mock
from oslo.config import cfg
from oslo.config import fixture as config
+from oslo.serialization import jsonutils
import requests
import six
from testtools import matchers
@@ -24,7 +25,6 @@ from testtools import matchers
from keystoneclient import adapter
from keystoneclient.auth import base
from keystoneclient import exceptions
-from keystoneclient.openstack.common import jsonutils
from keystoneclient import session as client_session
from keystoneclient.tests import utils
diff --git a/keystoneclient/tests/utils.py b/keystoneclient/tests/utils.py
index 4465835..a6f06c5 100644
--- a/keystoneclient/tests/utils.py
+++ b/keystoneclient/tests/utils.py
@@ -18,14 +18,13 @@ import uuid
import fixtures
import mock
from mox3 import mox
+from oslo.serialization import jsonutils
import requests
from requests_mock.contrib import fixture
import six
from six.moves.urllib import parse as urlparse
import testtools
-from keystoneclient.openstack.common import jsonutils
-
class TestCase(testtools.TestCase):
diff --git a/keystoneclient/tests/v2_0/test_access.py b/keystoneclient/tests/v2_0/test_access.py
index f384473..1cd926a 100644
--- a/keystoneclient/tests/v2_0/test_access.py
+++ b/keystoneclient/tests/v2_0/test_access.py
@@ -13,11 +13,11 @@
import datetime
import uuid
+from oslo.utils import timeutils
import testresources
from keystoneclient import access
from keystoneclient import fixture
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests import client_fixtures as token_data
from keystoneclient.tests.v2_0 import client_fixtures
from keystoneclient.tests.v2_0 import utils
diff --git a/keystoneclient/tests/v2_0/test_auth.py b/keystoneclient/tests/v2_0/test_auth.py
index cdf05f4..fd9ffc3 100644
--- a/keystoneclient/tests/v2_0/test_auth.py
+++ b/keystoneclient/tests/v2_0/test_auth.py
@@ -13,9 +13,10 @@
import copy
import datetime
+from oslo.serialization import jsonutils
+from oslo.utils import timeutils
+
from keystoneclient import exceptions
-from keystoneclient.openstack.common import jsonutils
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import client
diff --git a/keystoneclient/tests/v2_0/test_shell.py b/keystoneclient/tests/v2_0/test_shell.py
index 0fafb71..57bbe9d 100644
--- a/keystoneclient/tests/v2_0/test_shell.py
+++ b/keystoneclient/tests/v2_0/test_shell.py
@@ -104,6 +104,8 @@ class ShellTests(utils.TestCase):
self.stub_url('POST', ['users'], json={'user': {}})
with mock.patch('getpass.getpass') as mock_getpass:
+ del(os.environ['OS_PASSWORD'])
+ mock_stdin.isatty = lambda: True
mock_getpass.return_value = 'newpass'
self.run_command('user-create --name new-user --pass')
diff --git a/keystoneclient/tests/v3/test_access.py b/keystoneclient/tests/v3/test_access.py
index df2566d..0f0b0e2 100644
--- a/keystoneclient/tests/v3/test_access.py
+++ b/keystoneclient/tests/v3/test_access.py
@@ -13,9 +13,10 @@
import datetime
import uuid
+from oslo.utils import timeutils
+
from keystoneclient import access
from keystoneclient import fixture
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import utils
diff --git a/keystoneclient/tests/v3/test_auth.py b/keystoneclient/tests/v3/test_auth.py
index fb079b6..14a762f 100644
--- a/keystoneclient/tests/v3/test_auth.py
+++ b/keystoneclient/tests/v3/test_auth.py
@@ -10,8 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo.serialization import jsonutils
+
from keystoneclient import exceptions
-from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import client
diff --git a/keystoneclient/tests/v3/test_client.py b/keystoneclient/tests/v3/test_client.py
index 640ee76..ddc7850 100644
--- a/keystoneclient/tests/v3/test_client.py
+++ b/keystoneclient/tests/v3/test_client.py
@@ -33,6 +33,7 @@ class KeystoneClientTest(utils.TestCase):
self.assertFalse(c.auth_ref.project_scoped)
self.assertEqual(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
+ self.assertFalse(c.has_service_catalog())
def test_domain_scoped_init(self):
self.stub_auth(json=client_fixtures.domain_scoped_token())
diff --git a/keystoneclient/tests/v3/test_oauth1.py b/keystoneclient/tests/v3/test_oauth1.py
index 0dc9c2a..d12ffdd 100644
--- a/keystoneclient/tests/v3/test_oauth1.py
+++ b/keystoneclient/tests/v3/test_oauth1.py
@@ -14,11 +14,11 @@
import uuid
import mock
+from oslo.utils import timeutils
import six
from six.moves.urllib import parse as urlparse
from testtools import matchers
-from keystoneclient.openstack.common import timeutils
from keystoneclient import session
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import utils
diff --git a/keystoneclient/tests/v3/test_trusts.py b/keystoneclient/tests/v3/test_trusts.py
index 15e9348..0b8028f 100644
--- a/keystoneclient/tests/v3/test_trusts.py
+++ b/keystoneclient/tests/v3/test_trusts.py
@@ -13,8 +13,9 @@
import uuid
+from oslo.utils import timeutils
+
from keystoneclient import exceptions
-from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v3 import utils
from keystoneclient.v3.contrib import trusts
diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py
index d3342f4..436b2f6 100644
--- a/keystoneclient/utils.py
+++ b/keystoneclient/utils.py
@@ -17,11 +17,11 @@ import inspect
import logging
import sys
+from oslo.utils import encodeutils
import prettytable
import six
from keystoneclient import exceptions
-from keystoneclient.openstack.common import strutils
logger = logging.getLogger(__name__)
@@ -61,7 +61,7 @@ def print_list(objs, fields, formatters={}, order_by=None):
if order_by is None:
order_by = fields[0]
- encoded = strutils.safe_encode(pt.get_string(sortby=order_by))
+ encoded = encodeutils.safe_encode(pt.get_string(sortby=order_by))
if six.PY3:
encoded = encoded.decode()
print(encoded)
@@ -88,7 +88,7 @@ def print_dict(d, wrap=0):
value = ''
value = _word_wrap(value, max_length=wrap)
pt.add_row([prop, value])
- encoded = strutils.safe_encode(pt.get_string(sortby='Property'))
+ encoded = encodeutils.safe_encode(pt.get_string(sortby='Property'))
if six.PY3:
encoded = encoded.decode()
print(encoded)
diff --git a/keystoneclient/v2_0/__init__.py b/keystoneclient/v2_0/__init__.py
index f99be3bb..30407e0 100644
--- a/keystoneclient/v2_0/__init__.py
+++ b/keystoneclient/v2_0/__init__.py
@@ -1,5 +1,4 @@
-# flake8: noqa
-from keystoneclient.v2_0.client import Client
+from keystoneclient.v2_0.client import Client # noqa
__all__ = [
diff --git a/keystoneclient/v2_0/client.py b/keystoneclient/v2_0/client.py
index 062678f..dc790e8 100644
--- a/keystoneclient/v2_0/client.py
+++ b/keystoneclient/v2_0/client.py
@@ -18,6 +18,7 @@ import logging
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient import exceptions
from keystoneclient import httpclient
+from keystoneclient.i18n import _
from keystoneclient.v2_0 import ec2
from keystoneclient.v2_0 import endpoints
from keystoneclient.v2_0 import extensions
@@ -158,12 +159,12 @@ class Client(httpclient.HTTPClient):
password.
:returns: access.AccessInfo if authentication was successful.
- :raises: AuthorizationFailure if unable to authenticate or validate
- the existing authorization token
+ :raises keystoneclient.exceptions.AuthorizationFailure: if unable to
+ authenticate or validate the existing authorization token
"""
try:
if auth_url is None:
- raise ValueError("Cannot authenticate without an auth_url")
+ raise ValueError(_("Cannot authenticate without an auth_url"))
new_kwargs = {'trust_id': trust_id,
'tenant_id': project_id or tenant_id,
@@ -175,7 +176,7 @@ class Client(httpclient.HTTPClient):
plugin = v2_auth.Password(auth_url, username, password,
**new_kwargs)
else:
- msg = 'A username and password or token is required.'
+ msg = _('A username and password or token is required.')
raise exceptions.AuthorizationFailure(msg)
return plugin.get_auth_ref(self.session)
@@ -183,8 +184,9 @@ class Client(httpclient.HTTPClient):
_logger.debug("Authorization Failed.")
raise
except exceptions.EndpointNotFound:
- msg = 'There was no suitable authentication url for this request'
+ msg = (
+ _('There was no suitable authentication url for this request'))
raise exceptions.AuthorizationFailure(msg)
except Exception as e:
- raise exceptions.AuthorizationFailure("Authorization Failed: "
- "%s" % e)
+ raise exceptions.AuthorizationFailure(
+ _("Authorization Failed: %s") % e)
diff --git a/keystoneclient/v2_0/shell.py b/keystoneclient/v2_0/shell.py
index 5d8d265..a2cb1ab 100755
--- a/keystoneclient/v2_0/shell.py
+++ b/keystoneclient/v2_0/shell.py
@@ -26,9 +26,10 @@ import argparse
import getpass
import sys
+from oslo.utils import strutils
import six
-from keystoneclient.openstack.common import strutils
+from keystoneclient.i18n import _
from keystoneclient import utils
from keystoneclient.v2_0 import client
@@ -38,9 +39,9 @@ ASK_FOR_PASSWORD = object()
def require_service_catalog(f):
- msg = ('Configuration error: Client configured to run without a service '
- 'catalog. Run the client using --os-auth-url or OS_AUTH_URL, '
- 'instead of --os-endpoint or OS_SERVICE_ENDPOINT, for example.')
+ msg = _('Configuration error: Client configured to run without a service '
+ 'catalog. Run the client using --os-auth-url or OS_AUTH_URL, '
+ 'instead of --os-endpoint or OS_SERVICE_ENDPOINT, for example.')
def wrapped(kc, args):
if not kc.has_service_catalog():
@@ -121,15 +122,15 @@ def do_user_update(kc, args):
kwargs['enabled'] = strutils.bool_from_string(args.enabled)
if not len(kwargs):
- print("User not updated, no arguments present.")
+ print(_("User not updated, no arguments present."))
return
user = utils.find_resource(kc.users, args.user)
try:
kc.users.update(user, **kwargs)
- print('User has been updated.')
+ print(_('User has been updated.'))
except Exception as e:
- print('Unable to update user: %s' % e)
+ print(_('Unable to update user: %s') % e)
@utils.arg('--pass', metavar='<password>', dest='passwd', required=False,
@@ -141,8 +142,8 @@ def do_user_password_update(kc, args):
user = utils.find_resource(kc.users, args.user)
new_passwd = args.passwd or utils.prompt_for_password()
if new_passwd is None:
- msg = ("\nPlease specify password using the --pass option "
- "or using the prompt")
+ msg = (_("\nPlease specify password using the --pass option "
+ "or using the prompt"))
sys.exit(msg)
kc.users.update_password(user, new_passwd)
@@ -163,20 +164,20 @@ def do_password_update(kc, args):
if args.currentpasswd is not None:
currentpasswd = args.currentpasswd
if currentpasswd is None:
- currentpasswd = getpass.getpass('Current Password: ')
+ currentpasswd = getpass.getpass(_('Current Password: '))
newpasswd = args.newpasswd
while newpasswd is None:
- passwd1 = getpass.getpass('New Password: ')
- passwd2 = getpass.getpass('Repeat New Password: ')
+ passwd1 = getpass.getpass(_('New Password: '))
+ passwd2 = getpass.getpass(_('Repeat New Password: '))
if passwd1 == passwd2:
newpasswd = passwd1
kc.users.update_own_password(currentpasswd, newpasswd)
if args.os_password != newpasswd:
- print("You should update the password you are using to authenticate "
- "to match your new password")
+ print(_("You should update the password you are using to authenticate "
+ "to match your new password"))
@utils.arg('user', metavar='<user>', help='Name or ID of user to delete.')
@@ -234,7 +235,7 @@ def do_tenant_update(kc, args):
kwargs.update({'enabled': strutils.bool_from_string(args.enabled)})
if kwargs == {}:
- print("Tenant not updated, no arguments present.")
+ print(_("Tenant not updated, no arguments present."))
return
tenant.update(**kwargs)
@@ -450,9 +451,9 @@ def do_ec2_credentials_delete(kc, args):
args.user_id = kc.auth_user_id
try:
kc.ec2.delete(args.user_id, args.access)
- print('Credential has been deleted.')
+ print(_('Credential has been deleted.'))
except Exception as e:
- print('Unable to delete credential: %s' % e)
+ print(_('Unable to delete credential: %s') % e)
@utils.arg('--service', metavar='<service-type>', default=None,
@@ -463,7 +464,7 @@ def do_catalog(kc, args):
endpoints = kc.service_catalog.get_endpoints(service_type=args.service)
for (service, service_endpoints) in six.iteritems(endpoints):
if len(service_endpoints) > 0:
- print("Service: %s" % service)
+ print(_("Service: %s") % service)
for ep in service_endpoints:
utils.print_dict(ep)
@@ -489,7 +490,7 @@ def do_endpoint_get(kc, args):
if args.attr and args.value:
kwargs.update({'attr': args.attr, 'filter_value': args.value})
elif args.attr or args.value:
- print('Both --attr and --value required.')
+ print(_('Both --attr and --value required.'))
return
url = kc.service_catalog.url_for(**kwargs)
@@ -531,9 +532,9 @@ def do_endpoint_delete(kc, args):
"""Delete a service endpoint."""
try:
kc.endpoints.delete(args.id)
- print('Endpoint has been deleted.')
+ print(_('Endpoint has been deleted.'))
except Exception:
- print('Unable to delete endpoint.')
+ print(_('Unable to delete endpoint.'))
@utils.arg('--wrap', metavar='<integer>', default=0,
diff --git a/keystoneclient/v2_0/tokens.py b/keystoneclient/v2_0/tokens.py
index e5a21d4..fb48738 100644
--- a/keystoneclient/v2_0/tokens.py
+++ b/keystoneclient/v2_0/tokens.py
@@ -13,6 +13,7 @@
from keystoneclient import auth
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -45,7 +46,8 @@ class TokenManager(base.Manager):
params = {"auth": {"passwordCredentials": {"username": username,
"password": password}}}
else:
- raise ValueError('A username and password or token is required.')
+ raise ValueError(
+ _('A username and password or token is required.'))
if tenant_id:
params['auth']['tenantId'] = tenant_id
elif tenant_name:
diff --git a/keystoneclient/v3/__init__.py b/keystoneclient/v3/__init__.py
index 4685f9f..56b26d3 100644
--- a/keystoneclient/v3/__init__.py
+++ b/keystoneclient/v3/__init__.py
@@ -1,5 +1,5 @@
-# flake8: noqa
-from keystoneclient.v3.client import Client
+
+from keystoneclient.v3.client import Client # noqa
__all__ = [
diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py
index a271db3..7d3f0fe 100644
--- a/keystoneclient/v3/client.py
+++ b/keystoneclient/v3/client.py
@@ -15,10 +15,12 @@
import logging
+from oslo.serialization import jsonutils
+
from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient import exceptions
from keystoneclient import httpclient
-from keystoneclient.openstack.common import jsonutils
+from keystoneclient.i18n import _
from keystoneclient.v3.contrib import endpoint_filter
from keystoneclient.v3.contrib import endpoint_policy
from keystoneclient.v3.contrib import federation
@@ -202,7 +204,7 @@ EndpointPolicyManager`
if self.auth_ref.domain_scoped:
if not self.auth_ref.domain_id:
raise exceptions.AuthorizationFailure(
- "Token didn't provide domain_id")
+ _("Token didn't provide domain_id"))
self._process_management_url(kwargs.get('region_name'))
self.domain_name = self.auth_ref.domain_name
self.domain_id = self.auth_ref.domain_id
@@ -227,14 +229,15 @@ EndpointPolicyManager`
be used in the request.
:returns: access.AccessInfo if authentication was successful.
- :raises: AuthorizationFailure if unable to authenticate or validate
- the existing authorization token
- :raises: Unauthorized if authentication fails due to invalid token
+ :raises keystoneclient.exceptions.AuthorizationFailure: if unable to
+ authenticate or validate the existing authorization token.
+ :raises keystoneclient.exceptions.Unauthorized: if authentication fails
+ due to invalid token.
"""
try:
if auth_url is None:
- raise ValueError("Cannot authenticate without an auth_url")
+ raise ValueError(_("Cannot authenticate without an auth_url"))
auth_methods = []
@@ -250,7 +253,7 @@ EndpointPolicyManager`
auth_methods.append(m)
if not auth_methods:
- msg = 'A user and password or token is required.'
+ msg = _('A user and password or token is required.')
raise exceptions.AuthorizationFailure(msg)
plugin = v3_auth.Auth(auth_url, auth_methods,
@@ -267,8 +270,9 @@ EndpointPolicyManager`
_logger.debug('Authorization failed.')
raise
except exceptions.EndpointNotFound:
- msg = 'There was no suitable authentication url for this request'
+ msg = _('There was no suitable authentication url for this'
+ ' request')
raise exceptions.AuthorizationFailure(msg)
except Exception as e:
- raise exceptions.AuthorizationFailure('Authorization failed: '
- '%s' % e)
+ raise exceptions.AuthorizationFailure(
+ _('Authorization failed: %s') % e)
diff --git a/keystoneclient/v3/contrib/endpoint_filter.py b/keystoneclient/v3/contrib/endpoint_filter.py
index c0a1eef..3e3b7ef 100644
--- a/keystoneclient/v3/contrib/endpoint_filter.py
+++ b/keystoneclient/v3/contrib/endpoint_filter.py
@@ -14,6 +14,7 @@
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
class EndpointFilterManager(base.Manager):
@@ -31,7 +32,7 @@ class EndpointFilterManager(base.Manager):
elif endpoint_id:
api_path = '/endpoints/%s/projects' % (endpoint_id)
else:
- msg = 'Must specify a project, an endpoint, or both'
+ msg = _('Must specify a project, an endpoint, or both')
raise exceptions.ValidationError(msg)
return self.OS_EP_FILTER_EXT + api_path
@@ -39,7 +40,7 @@ class EndpointFilterManager(base.Manager):
def add_endpoint_to_project(self, project, endpoint):
"""Create a project-endpoint association."""
if not (project and endpoint):
- raise ValueError('project and endpoint are required')
+ raise ValueError(_('project and endpoint are required'))
base_url = self._build_base_url(project=project,
endpoint=endpoint)
@@ -48,7 +49,7 @@ class EndpointFilterManager(base.Manager):
def delete_endpoint_from_project(self, project, endpoint):
"""Remove a project-endpoint association."""
if not (project and endpoint):
- raise ValueError('project and endpoint are required')
+ raise ValueError(_('project and endpoint are required'))
base_url = self._build_base_url(project=project,
endpoint=endpoint)
@@ -57,7 +58,7 @@ class EndpointFilterManager(base.Manager):
def check_endpoint_in_project(self, project, endpoint):
"""Checks if project-endpoint association exist."""
if not (project and endpoint):
- raise ValueError('project and endpoint are required')
+ raise ValueError(_('project and endpoint are required'))
base_url = self._build_base_url(project=project,
endpoint=endpoint)
@@ -66,7 +67,7 @@ class EndpointFilterManager(base.Manager):
def list_endpoints_for_project(self, project):
"""List all endpoints for a given project."""
if not project:
- raise ValueError('project is required')
+ raise ValueError(_('project is required'))
base_url = self._build_base_url(project=project)
return super(EndpointFilterManager, self)._list(
@@ -77,7 +78,7 @@ class EndpointFilterManager(base.Manager):
def list_projects_for_endpoint(self, endpoint):
"""List all projects for a given endpoint."""
if not endpoint:
- raise ValueError('endpoint is required')
+ raise ValueError(_('endpoint is required'))
base_url = self._build_base_url(endpoint=endpoint)
return super(EndpointFilterManager, self)._list(
diff --git a/keystoneclient/v3/contrib/endpoint_policy.py b/keystoneclient/v3/contrib/endpoint_policy.py
index 9d4d997..c473ad6 100644
--- a/keystoneclient/v3/contrib/endpoint_policy.py
+++ b/keystoneclient/v3/contrib/endpoint_policy.py
@@ -13,6 +13,7 @@
# under the License.
from keystoneclient import base
+from keystoneclient.i18n import _
from keystoneclient.v3 import policies
@@ -24,7 +25,7 @@ class EndpointPolicyManager(base.Manager):
def _act_on_policy_association_for_endpoint(
self, policy, endpoint, action):
if not (policy and endpoint):
- raise ValueError('policy and endpoint are required')
+ raise ValueError(_('policy and endpoint are required'))
policy_id = base.getid(policy)
endpoint_id = base.getid(endpoint)
@@ -52,7 +53,7 @@ class EndpointPolicyManager(base.Manager):
def _act_on_policy_association_for_service(self, policy, service, action):
if not (policy and service):
- raise ValueError('policy and service are required')
+ raise ValueError(_('policy and service are required'))
policy_id = base.getid(policy)
service_id = base.getid(service)
@@ -81,7 +82,7 @@ class EndpointPolicyManager(base.Manager):
def _act_on_policy_association_for_region_and_service(
self, policy, region, service, action):
if not (policy and region and service):
- raise ValueError('policy, region and service are required')
+ raise ValueError(_('policy, region and service are required'))
policy_id = base.getid(policy)
region_id = base.getid(region)
@@ -121,7 +122,7 @@ class EndpointPolicyManager(base.Manager):
"""
if not endpoint:
- raise ValueError('endpoint is required')
+ raise ValueError(_('endpoint is required'))
endpoint_id = base.getid(endpoint)
url = ('/endpoints/%(endpoint_id)s/%(ext_name)s/policy') % {
@@ -141,7 +142,7 @@ class EndpointPolicyManager(base.Manager):
"""
if not policy:
- raise ValueError('policy is required')
+ raise ValueError(_('policy is required'))
policy_id = base.getid(policy)
url = ('/policies/%(policy_id)s/%(ext_name)s/endpoints') % {
diff --git a/keystoneclient/v3/contrib/federation/__init__.py b/keystoneclient/v3/contrib/federation/__init__.py
index 8d31b75..ee9bcef 100644
--- a/keystoneclient/v3/contrib/federation/__init__.py
+++ b/keystoneclient/v3/contrib/federation/__init__.py
@@ -10,4 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystoneclient.v3.contrib.federation.core import * # flake8: noqa
+from keystoneclient.v3.contrib.federation.core import * # noqa
diff --git a/keystoneclient/v3/contrib/federation/mappings.py b/keystoneclient/v3/contrib/federation/mappings.py
index d0c033f..1cdb879 100644
--- a/keystoneclient/v3/contrib/federation/mappings.py
+++ b/keystoneclient/v3/contrib/federation/mappings.py
@@ -48,35 +48,30 @@ class MappingManager(base.CrudManager):
:param mapping_id: user defined string identifier of the federation
mapping.
- :param rules: a JSON dictionary with list a list
- of mapping rules.
-
- Example of the ``rules``::
-
- {
- "mapping": {
- "rules": [
- {
- "local": [
- {
- "group": {
- "id": "0cd5e9"
- }
- }
- ],
- "remote": [
- {
- "type": "orgPersonType",
- "not_any_of": [
- "Contractor",
- "Guest"
- ]
- }
- ]
- }
- ]
- }
- }
+ :param rules: a list of mapping rules.
+
+ Example of the ``rules`` parameter::
+
+ [
+ {
+ "local": [
+ {
+ "group": {
+ "id": "0cd5e9"
+ }
+ }
+ ],
+ "remote": [
+ {
+ "type": "orgPersonType",
+ "not_any_of": [
+ "Contractor",
+ "Guest"
+ ]
+ }
+ ]
+ }
+ ]
"""
return self._build_url_and_put(
@@ -112,35 +107,31 @@ class MappingManager(base.CrudManager):
:param mapping: a Mapping type object with mapping id
stored inside.
- :param rules: a JSON dictionary with list a list
- of mapping rules.
-
- Example of the ``rules``::
-
- {
- "mapping": {
- "rules": [
- {
- "local": [
- {
- "group": {
- "id": "0cd5e9"
- }
- }
- ],
- "remote": [
- {
- "type": "orgPersonType",
- "not_any_of": [
- "Contractor",
- "Guest"
- ]
- }
- ]
- }
- ]
- }
- }
+ :param rules: a list of mapping rules.
+
+ Example of the ``rules`` parameter::
+
+
+ [
+ {
+ "local": [
+ {
+ "group": {
+ "id": "0cd5e9"
+ }
+ }
+ ],
+ "remote": [
+ {
+ "type": "orgPersonType",
+ "not_any_of": [
+ "Contractor",
+ "Guest"
+ ]
+ }
+ ]
+ }
+ ]
"""
return super(MappingManager, self).update(
diff --git a/keystoneclient/v3/contrib/oauth1/core.py b/keystoneclient/v3/contrib/oauth1/core.py
index 36823f0..8d10325 100644
--- a/keystoneclient/v3/contrib/oauth1/core.py
+++ b/keystoneclient/v3/contrib/oauth1/core.py
@@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from keystoneclient.i18n import _
from keystoneclient.v3.contrib.oauth1 import access_tokens
from keystoneclient.v3.contrib.oauth1 import consumers
from keystoneclient.v3.contrib.oauth1 import request_tokens
@@ -59,6 +60,6 @@ class OAuthManagerOptionalImportProxy(object):
def __getattribute__(self, name):
if name in ('access_tokens', 'consumers', 'request_tokens'):
raise NotImplementedError(
- 'To use %r oauthlib must be installed' % name)
+ _('To use %r oauthlib must be installed') % name)
return super(OAuthManagerOptionalImportProxy,
self).__getattribute__(name)
diff --git a/keystoneclient/v3/contrib/trusts.py b/keystoneclient/v3/contrib/trusts.py
index ed199e6..0fb75ca 100644
--- a/keystoneclient/v3/contrib/trusts.py
+++ b/keystoneclient/v3/contrib/trusts.py
@@ -10,9 +10,11 @@
# 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.openstack.common import timeutils
+from keystoneclient.i18n import _
class Trust(base.Resource):
@@ -74,8 +76,8 @@ class TrustManager(base.CrudManager):
**kwargs)
def update(self):
- raise exceptions.MethodNotImplemented('Update not supported'
- ' for trusts')
+ raise exceptions.MethodNotImplemented(
+ _('Update not supported for trusts'))
def list(self, trustee_user=None, trustor_user=None, **kwargs):
"""List Trusts."""
diff --git a/keystoneclient/v3/credentials.py b/keystoneclient/v3/credentials.py
index be28cc5..833a32f 100644
--- a/keystoneclient/v3/credentials.py
+++ b/keystoneclient/v3/credentials.py
@@ -15,6 +15,7 @@
# under the License.
from keystoneclient import base
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -46,7 +47,7 @@ class CredentialManager(base.CrudManager):
return data
else:
raise ValueError(
- "Credential requires blob to be specified")
+ _("Credential requires blob to be specified"))
@utils.positional(1, enforcement=utils.positional.WARN)
def create(self, user, type, blob=None, data=None, project=None, **kwargs):
diff --git a/keystoneclient/v3/endpoints.py b/keystoneclient/v3/endpoints.py
index 3f9dfbd..eeddc4c 100644
--- a/keystoneclient/v3/endpoints.py
+++ b/keystoneclient/v3/endpoints.py
@@ -16,6 +16,7 @@
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -45,7 +46,7 @@ class EndpointManager(base.CrudManager):
def _validate_interface(self, interface):
if interface is not None and interface not in VALID_INTERFACES:
- msg = '"interface" must be one of: %s'
+ msg = _('"interface" must be one of: %s')
msg = msg % ', '.join(VALID_INTERFACES)
raise exceptions.ValidationError(msg)
diff --git a/keystoneclient/v3/role_assignments.py b/keystoneclient/v3/role_assignments.py
index 5394c3d..6518e43 100644
--- a/keystoneclient/v3/role_assignments.py
+++ b/keystoneclient/v3/role_assignments.py
@@ -12,6 +12,7 @@
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
class RoleAssignment(base.Resource):
@@ -37,12 +38,12 @@ class RoleAssignmentManager(base.CrudManager):
def _check_not_user_and_group(self, user, group):
if user and group:
- msg = 'Specify either a user or group, not both'
+ msg = _('Specify either a user or group, not both')
raise exceptions.ValidationError(msg)
def _check_not_domain_and_project(self, domain, project):
if domain and project:
- msg = 'Specify either a domain or project, not both'
+ msg = _('Specify either a domain or project, not both')
raise exceptions.ValidationError(msg)
def list(self, user=None, group=None, project=None, domain=None, role=None,
@@ -87,25 +88,25 @@ class RoleAssignmentManager(base.CrudManager):
return super(RoleAssignmentManager, self).list(**query_params)
def create(self, **kwargs):
- raise exceptions.MethodNotImplemented('Create not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Create not supported for role assignments'))
def update(self, **kwargs):
- raise exceptions.MethodNotImplemented('Update not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Update not supported for role assignments'))
def get(self, **kwargs):
- raise exceptions.MethodNotImplemented('Get not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Get not supported for role assignments'))
def find(self, **kwargs):
- raise exceptions.MethodNotImplemented('Find not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Find not supported for role assignments'))
def put(self, **kwargs):
- raise exceptions.MethodNotImplemented('Put not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Put not supported for role assignments'))
def delete(self, **kwargs):
- raise exceptions.MethodNotImplemented('Delete not supported for'
- ' role assignments')
+ raise exceptions.MethodNotImplemented(
+ _('Delete not supported for role assignments'))
diff --git a/keystoneclient/v3/roles.py b/keystoneclient/v3/roles.py
index 40c624a..3eb68d1 100644
--- a/keystoneclient/v3/roles.py
+++ b/keystoneclient/v3/roles.py
@@ -16,6 +16,7 @@
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -59,18 +60,18 @@ class RoleManager(base.CrudManager):
def _require_domain_xor_project(self, domain, project):
if domain and project:
- msg = 'Specify either a domain or project, not both'
+ msg = _('Specify either a domain or project, not both')
raise exceptions.ValidationError(msg)
elif not (domain or project):
- msg = 'Must specify either a domain or project'
+ msg = _('Must specify either a domain or project')
raise exceptions.ValidationError(msg)
def _require_user_xor_group(self, user, group):
if user and group:
- msg = 'Specify either a user or group, not both'
+ msg = _('Specify either a user or group, not both')
raise exceptions.ValidationError(msg)
elif not (user or group):
- msg = 'Must specify either a user or group'
+ msg = _('Must specify either a user or group')
raise exceptions.ValidationError(msg)
@utils.positional(1, enforcement=utils.positional.WARN)
diff --git a/keystoneclient/v3/users.py b/keystoneclient/v3/users.py
index 140c785..3343d50 100644
--- a/keystoneclient/v3/users.py
+++ b/keystoneclient/v3/users.py
@@ -18,6 +18,7 @@ import logging
from keystoneclient import base
from keystoneclient import exceptions
+from keystoneclient.i18n import _, _LW
from keystoneclient import utils
LOG = logging.getLogger(__name__)
@@ -41,7 +42,7 @@ class UserManager(base.CrudManager):
def _require_user_and_group(self, user, group):
if not (user and group):
- msg = 'Specify both a user and a group'
+ msg = _('Specify both a user and a group')
raise exceptions.ValidationError(msg)
@utils.positional(1, enforcement=utils.positional.WARN)
@@ -58,8 +59,8 @@ class UserManager(base.CrudManager):
will be used.
"""
if project:
- LOG.warning("The project argument is deprecated, "
- "use default_project instead.")
+ LOG.warning(_LW("The project argument is deprecated, "
+ "use default_project instead."))
default_project_id = base.getid(default_project) or base.getid(project)
user_data = base.filter_none(name=name,
domain_id=base.getid(domain),
@@ -92,8 +93,8 @@ class UserManager(base.CrudManager):
will be used.
"""
if project:
- LOG.warning("The project argument is deprecated, "
- "use default_project instead.")
+ LOG.warning(_LW("The project argument is deprecated, "
+ "use default_project instead."))
default_project_id = base.getid(default_project) or base.getid(project)
if group:
base_url = '/groups/%s' % base.getid(group)
@@ -124,8 +125,8 @@ class UserManager(base.CrudManager):
will be used.
"""
if project:
- LOG.warning("The project argument is deprecated, "
- "use default_project instead.")
+ LOG.warning(_LW("The project argument is deprecated, "
+ "use default_project instead."))
default_project_id = base.getid(default_project) or base.getid(project)
user_data = base.filter_none(name=name,
domain_id=base.getid(domain),
@@ -145,11 +146,11 @@ class UserManager(base.CrudManager):
def update_password(self, old_password, new_password):
"""Update the password for the user the token belongs to."""
if not (old_password and new_password):
- msg = 'Specify both the current password and a new password'
+ msg = _('Specify both the current password and a new password')
raise exceptions.ValidationError(msg)
if old_password == new_password:
- msg = 'Old password and new password must be different.'
+ msg = _('Old password and new password must be different.')
raise exceptions.ValidationError(msg)
params = {'user': {'password': new_password,
diff --git a/openstack-common.conf b/openstack-common.conf
index 586aac3..10f7b9b 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -3,10 +3,7 @@
# The list of modules to copy from oslo-incubator
module=apiclient
module=install_venv_common
-module=jsonutils
module=memorycache
-module=strutils
-module=timeutils
# The base module to hold the copy of openstack.common
base=keystoneclient
diff --git a/requirements.txt b/requirements.txt
index 5a08b75..e970640 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,10 @@ Babel>=1.3
iso8601>=0.1.9
netaddr>=0.7.12
oslo.config>=1.4.0 # Apache-2.0
+oslo.i18n>=1.0.0 # Apache-2.0
+oslo.serialization>=1.0.0 # Apache-2.0
+oslo.utils>=1.0.0 # Apache-2.0
PrettyTable>=0.7,<0.8
-requests>=1.2.1,!=2.4.0
+requests>=2.2.0,!=2.4.0
six>=1.7.0
-stevedore>=1.0.0 # Apache-2.0
+stevedore>=1.1.0 # Apache-2.0
diff --git a/test-requirements.txt b/test-requirements.txt
index e0387f0..be0eba4 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -13,10 +13,11 @@ mock>=1.0
mox3>=0.7.0
oauthlib>=0.6
oslosphinx>=2.2.0 # Apache-2.0
+oslotest>=1.2.0 # Apache-2.0
pycrypto>=2.6
-requests-mock>=0.4.0 # Apache-2.0
-sphinx>=1.1.2,!=1.2.0,<1.3
+requests-mock>=0.5.1 # Apache-2.0
+sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
testrepository>=0.0.18
testresources>=0.2.4
-testtools>=0.9.34
+testtools>=0.9.36
WebOb>=1.2.3
diff --git a/tools/debug_helper.sh b/tools/debug_helper.sh
deleted file mode 100755
index b80b10c..0000000
--- a/tools/debug_helper.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-TMP_DIR=`mktemp -d` || exit 1
-trap "rm -rf $TMP_DIR" EXIT
-
-ALL_TESTS=$TMP_DIR/all_tests
-TESTS_TO_RUN=$TMP_DIR/ksc_to_run
-
-python -m testtools.run discover -t ./ ./keystoneclient/tests --list > $ALL_TESTS
-
-if [ "$1" ]
-then
- grep "$1" < $ALL_TESTS > $TESTS_TO_RUN
-else
- mv $ALL_TESTS $TESTS_TO_RUN
-fi
-
-python -m testtools.run discover --load-list $TESTS_TO_RUN
diff --git a/tox.ini b/tox.ini
index e2b8617..57db9c0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,9 +28,7 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
downloadcache = ~/cache/pip
[testenv:debug]
-
-commands =
- {toxinidir}/tools/debug_helper.sh {posargs}
+commands = oslo_debug_helper -t keystoneclient/tests {posargs}
[flake8]
# F821: undefined name
@@ -47,3 +45,6 @@ exclude = .venv,.tox,dist,doc,*egg,build,*openstack/common*
commands=
python setup.py build_sphinx
+[hacking]
+import_exceptions =
+ keystoneclient.i18n