diff -Nru python-keystoneclient-0.11.2/AUTHORS python-keystoneclient-1.0.0/AUTHORS --- python-keystoneclient-0.11.2/AUTHORS 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/AUTHORS 2014-12-18 17:39:19.000000000 +0000 @@ -176,6 +176,7 @@ mathrock nachiappan-veerappan-nachiappan root +sridhargaddam termie wanghong xingzhou diff -Nru python-keystoneclient-0.11.2/ChangeLog python-keystoneclient-1.0.0/ChangeLog --- python-keystoneclient-0.11.2/ChangeLog 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/ChangeLog 2014-12-18 17:39:19.000000000 +0000 @@ -1,15 +1,55 @@ CHANGES ======= +1.0.0 +----- + +* Updated from global requirements +* Expose version matching functions to the public +* Take plugin params from ENV rather than default +* Project ID in OAuth headers was missing +* get_endpoint should return the override +* Pass all adapter parameters through to adapter +* Correct documenting constructor parameters +* Correct Session docstring +* Add missing user-id option to generic.Password +* duplicate auth-url option returned by BaseGenericPlugin +* Replace magic numbers with named symbols +* Fix importing config module and classmethod params +* Removes confusing _uuid property +* Curl statements to include globoff for IPv6 URLs +* Cleanup exception logging +* Make keystoneclient use an adapter +* Remove middleware architecture doc +* Remove useless log message +* Updated from global requirements +* Updated from global requirements +* I18n +* Correct use of noqa +* Sync oslo-incubator to 1fc3cd47 +* Log the CA cert with the debug statement +* Prevent AttributeError if no authorization + 0.11.2 ------ +* Use oslo_debug_helper and remove our own version * Updated from global requirements * set close_fds=True in Popen +* Cleanup docs - raises class * Fix mappings.Mapping docstring * Actually test interactive password prompt +* Docstring cleanup for return type +* Correct typos in man page * Docstrings should have :returns: everywhere * Use oslo.utils and oslo.serialization +* Remove warning about management token +* Document session usage first +* Doc cleanup, make concepts links +* Rename the client API docs +* Correct typos in using-sessions +* Warn that keystone CLI is pending deprecation +* Reorder index links * Explicit complaint about old OpenSSL when testing * Log token with sha1 * Redact x-subject-token from response headers diff -Nru python-keystoneclient-0.11.2/debian/changelog python-keystoneclient-1.0.0/debian/changelog --- python-keystoneclient-0.11.2/debian/changelog 2014-11-10 15:57:01.000000000 +0000 +++ python-keystoneclient-1.0.0/debian/changelog 2015-02-09 19:31:13.000000000 +0000 @@ -1,4 +1,11 @@ -python-keystoneclient (1:0.11.2-0ubuntu1) vivid; urgency=medium +python-keystoneclient (1:1.0.0-0ubuntu1) vivid; urgency=medium + + * New upstream release. + * debian/control: Add dh-python to build depedency. + + -- Chuck Short Fri, 06 Feb 2015 11:45:38 -0500 + +python-keystoneclient (1:0.11.2-0ubuntu1) utopic; urgency=medium * New upstream release. * Open up for vivid. diff -Nru python-keystoneclient-0.11.2/debian/control python-keystoneclient-1.0.0/debian/control --- python-keystoneclient-0.11.2/debian/control 2014-11-10 15:57:01.000000000 +0000 +++ python-keystoneclient-1.0.0/debian/control 2015-02-09 19:31:13.000000000 +0000 @@ -4,6 +4,7 @@ Maintainer: Chuck Short Build-Depends: debhelper (>= 8.0.0), python-all (>= 2.6.6-3~), + dh-python, python-babel, python-coverage, python-crypto, diff -Nru python-keystoneclient-0.11.2/doc/source/index.rst python-keystoneclient-1.0.0/doc/source/index.rst --- python-keystoneclient-0.11.2/doc/source/index.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/index.rst 2014-12-18 17:37:35.000000000 +0000 @@ -12,13 +12,10 @@ :maxdepth: 1 man/keystone - using-sessions - using-api-v2 using-api-v3 - + using-sessions authentication-plugins - middlewarearchitecture - + using-api-v2 api/modules Contributing diff -Nru python-keystoneclient-0.11.2/doc/source/man/keystone.rst python-keystoneclient-1.0.0/doc/source/man/keystone.rst --- python-keystoneclient-0.11.2/doc/source/man/keystone.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/man/keystone.rst 2014-12-18 17:37:35.000000000 +0000 @@ -1,6 +1,6 @@ -======================================== -:program:`keystone` command line utility -======================================== +============================================================== +:program:`keystone` command line utility (pending deprecation) +============================================================== .. program:: keystone .. highlight:: bash @@ -18,13 +18,21 @@ DESCRIPTION =========== +.. WARNING:: + + The :program:`keystone` command line utility is pending deprecation. The + `OpenStackClient unified command line utility + `_ 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 @@ .. envvar:: OS_SERVICE_TOKEN - Your keystone administrative token + Your Keystone administrative token .. envvar:: OS_SERVICE_ENDPOINT diff -Nru python-keystoneclient-0.11.2/doc/source/middlewarearchitecture.rst python-keystoneclient-1.0.0/doc/source/middlewarearchitecture.rst --- python-keystoneclient-0.11.2/doc/source/middlewarearchitecture.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/middlewarearchitecture.rst 1970-01-01 00:00:00.000000000 +0000 @@ -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= - - # API version of the admin Identity API endpoint (string - # value) - #auth_version= - - # 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= - - # 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= - - # Keystone account username (string value) - #admin_user= - - # 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= - - # Required if Keystone server requires client certificate - # (string value) - #certfile= - - # Required if Keystone server requires client certificate - # (string value) - #keyfile= - - # A PEM encoded Certificate Authority to use when verifying - # HTTPs connections. Defaults to system CAs. (string value) - #cafile= - - # Verify HTTPS connections. (boolean value) - #insecure=false - - # Directory used to cache files related to PKI tokens (string - # value) - #signing_dir= - - # If defined, the memcache server(s) to use for caching (list - # value) - # Deprecated group/name - [DEFAULT]/memcache_servers - #memcached_servers= - - # 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= - - # (optional, mandatory if memcache_security_strategy is - # defined) this string is used for key derivation. (string - # value) - #memcache_secret_key= - - # (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 -Nru python-keystoneclient-0.11.2/doc/source/using-api-v2.rst python-keystoneclient-1.0.0/doc/source/using-api-v2.rst --- python-keystoneclient-0.11.2/doc/source/using-api-v2.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/using-api-v2.rst 2014-12-18 17:37:35.000000000 +0000 @@ -1,6 +1,6 @@ -================= -The Client v2 API -================= +======================= +Using the V2 Client API +======================= Introduction ============ @@ -13,7 +13,7 @@ * 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 -Nru python-keystoneclient-0.11.2/doc/source/using-api-v3.rst python-keystoneclient-1.0.0/doc/source/using-api-v3.rst --- python-keystoneclient-0.11.2/doc/source/using-api-v3.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/using-api-v3.rst 2014-12-18 17:37:35.000000000 +0000 @@ -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 @@ -80,11 +81,32 @@ ``keystoneclient.exceptions.ClientException`` (see :py:class:`keystoneclient.openstack.common.apiclient.exceptions.ClientException`) -Authenticating -============== +Authenticating Using Sessions +============================= -You can authenticate against Keystone using a username, a user domain -name (which will default to 'Default' if it is not specified) and a +Instantiate a :py:class:`keystoneclient.v3.client.Client` using a +:py:class:`~keystoneclient.session.Session` to provide the authentication +plugin, SSL/TLS certificates, and other data:: + + >>> from keystoneclient.auth.identity import v3 + >>> from keystoneclient import session + >>> from keystoneclient.v3 import client + >>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3', + ... user_id='myuserid', + ... password='mypassword', + ... project_id='myprojectid') + >>> sess = session.Session(auth=auth) + >>> keystone = client.Client(session=sess) + +For more information on Sessions refer to: `Using Sessions`_. + +.. _`Using Sessions`: using-sessions.html + +Non-Session Authentication (deprecated) +======================================= + +The *deprecated* way to authenticate is to pass the username, the user's domain +name (which will default to 'Default' if it is not specified), and a password:: >>> from keystoneclient import client @@ -96,6 +118,11 @@ ... username=username, password=password, ... user_domain_name=user_domain_name) +A :py:class:`~keystoneclient.session.Session` should be passed to the Client +instead. Using a Session you're not limited to authentication using a username +and password but can take advantage of other more secure authentication +methods. + You may optionally specify a domain or project (along with its project domain name), to obtain a scoped token:: @@ -111,23 +138,3 @@ ... user_domain_name=user_domain_name, ... project_name=project_name, ... project_domain_name=project_domain_name) - -Using Sessions -============== - -It's also possible to instantiate a :py:class:`keystoneclient.v3.client.Client` -class by using :py:class:`keystoneclient.session.Session`.:: - - >>> from keystoneclient.auth.identity import v3 - >>> from keystoneclient import session - >>> from keystoneclient.v3 import client - >>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3', - ... user_id='myuserid', - ... password='mypassword', - ... project_id='myprojectid') - >>> sess = session.Session(auth=auth) - >>> keystone = client.Client(session=sess) - -For more information on Sessions refer to: `Using Sessions`_. - -.. _`Using Sessions`: using-sessions.html diff -Nru python-keystoneclient-0.11.2/doc/source/using-sessions.rst python-keystoneclient-1.0.0/doc/source/using-sessions.rst --- python-keystoneclient-0.11.2/doc/source/using-sessions.rst 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/doc/source/using-sessions.rst 2014-12-18 17:37:35.000000000 +0000 @@ -75,13 +75,13 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/access.py python-keystoneclient-1.0.0/keystoneclient/access.py --- python-keystoneclient-0.11.2/keystoneclient/access.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/access.py 2014-12-18 17:37:35.000000000 +0000 @@ -19,6 +19,7 @@ from oslo.utils import timeutils +from keystoneclient.i18n import _ from keystoneclient import service_catalog @@ -63,7 +64,7 @@ else: auth_ref = AccessInfoV2(**kwargs) else: - raise NotImplementedError('Unrecognized auth response') + raise NotImplementedError(_('Unrecognized auth response')) else: auth_ref = AccessInfoV2(**kwargs) @@ -84,7 +85,8 @@ def will_expire_soon(self, stale_duration=None): """Determines if expiration is about to occur. - :returns: boolean : true if expiration is within the given duration + :returns: true if expiration is within the given duration + :rtype: boolean """ stale_duration = (STALE_TOKEN_DURATION if stale_duration is None @@ -101,7 +103,8 @@ """Determines if processing v2 or v3 token given a successful auth body or a user-provided dict. - :returns: boolean : true if auth body matches implementing class + :returns: true if auth body matches implementing class + :rtype: boolean """ raise NotImplementedError() diff -Nru python-keystoneclient-0.11.2/keystoneclient/adapter.py python-keystoneclient-1.0.0/keystoneclient/adapter.py --- python-keystoneclient-0.11.2/keystoneclient/adapter.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/adapter.py 2014-12-18 17:37:35.000000000 +0000 @@ -23,6 +23,23 @@ state such as the service type and region_name that are only relevant to a particular client that is using the session. An adapter provides a wrapper of client local data around the global session object. + + :param session: The session object to wrap. + :type session: keystoneclient.session.Session + :param str service_type: The default service_type for URL discovery. + :param str service_name: The default service_name for URL discovery. + :param str interface: The default interface for URL discovery. + :param str region_name: The default region_name for URL discovery. + :param str endpoint_override: Always use this endpoint URL for requests + for this client. + :param tuple version: The version that this API targets. + :param auth: An auth plugin to use instead of the session one. + :type auth: keystoneclient.auth.base.BaseAuthPlugin + :param str user_agent: The User-Agent string to set. + :param int connect_retries: the maximum number of retries that should + be attempted for connection errors. + Default None - use session default which + is don't retry. """ @utils.positional() @@ -30,24 +47,8 @@ interface=None, region_name=None, endpoint_override=None, version=None, auth=None, user_agent=None, connect_retries=None): - """Create a new adapter. - - :param Session session: The session object to wrap. - :param str service_type: The default service_type for URL discovery. - :param str service_name: The default service_name for URL discovery. - :param str interface: The default interface for URL discovery. - :param str region_name: The default region_name for URL discovery. - :param str endpoint_override: Always use this endpoint URL for requests - for this client. - :param tuple version: The version that this API targets. - :param auth.BaseAuthPlugin auth: An auth plugin to use instead of the - session one. - :param str user_agent: The User-Agent string to set. - :param int connect_retries: the maximum number of retries that should - be attempted for connection errors. - Default None - use session default which - is don't retry. - """ + # NOTE(jamielennox): when adding new parameters to adapter please also + # add them to the adapter call in httpclient.HTTPClient.__init__ self.session = session self.service_type = service_type self.service_name = service_name @@ -94,9 +95,11 @@ 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 string: A valid token. + :returns: A valid token. + :rtype: string """ return self.session.get_token(auth or self.auth) @@ -107,10 +110,15 @@ 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 string: An endpoint if available or None. + :returns: An endpoint if available or None. + :rtype: string """ + if self.endpoint_override: + return self.endpoint_override + self._set_endpoint_filter_kwargs(kwargs) return self.session.get_endpoint(auth or self.auth, **kwargs) diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/base.py python-keystoneclient-1.0.0/keystoneclient/auth/base.py --- python-keystoneclient-0.11.2/keystoneclient/auth/base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/base.py 2014-12-18 17:37:35.000000000 +0000 @@ -17,6 +17,8 @@ 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 @@ :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 @@ -64,7 +67,8 @@ Returning None will indicate that no token was able to be retrieved. :param session: A session object so the plugin can make HTTP calls. - :return string: A token to use. + :return: A token to use. + :rtype: string """ def get_endpoint(self, session, **kwargs): @@ -83,8 +87,9 @@ :param Session session: The session object that the auth_plugin belongs to. - :returns string: The base URL that will be used to talk to the - required service or None if not available. + :returns: The base URL that will be used to talk to the required + service or None if not available. + :rtype: string """ def invalidate(self): @@ -96,9 +101,10 @@ returned to indicate that the token may have been revoked or is otherwise now invalid. - :returns bool: True if there was something that the plugin did to - invalidate. This means that it makes sense to try again. - If nothing happens returns False to indicate give up. + :returns: True if there was something that the plugin did to + invalidate. This means that it makes sense to try again. If + nothing happens returns False to indicate give up. + :rtype: bool """ return False @@ -108,8 +114,9 @@ This list may be used to generate CLI or config arguments. - :returns list: A list of Param objects describing available plugin - parameters. + :returns: A list of Param objects describing available plugin + parameters. + :rtype: list """ return [] @@ -148,14 +155,12 @@ args.append('--os-%s' % o.name) envs.append('OS_%s' % o.name.replace('-', '_').upper()) - default = opt.default - if default is None: - # select the first ENV that is not false-y or return None - env_vars = (os.environ.get(e) for e in envs) - default = six.next(six.moves.filter(None, env_vars), None) + # select the first ENV that is not false-y or return None + env_vars = (os.environ.get(e) for e in envs) + default = six.next(six.moves.filter(None, env_vars), None) parser.add_argument(*args, - default=default, + default=default or opt.default, metavar=opt.metavar, help=opt.help, dest='os_%s' % opt.dest) @@ -198,7 +203,8 @@ :param conf: An oslo.config conf object. :param string group: The group name that options should be read from. - :returns plugin: An authentication Plugin. + :returns: An authentication Plugin. + :rtype: plugin: """ plugin_opts = cls.get_options() diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/cli.py python-keystoneclient-1.0.0/keystoneclient/auth/cli.py --- python-keystoneclient-0.11.2/keystoneclient/auth/cli.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/cli.py 2014-12-18 17:37:35.000000000 +0000 @@ -31,7 +31,8 @@ :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 @@ :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 -Nru python-keystoneclient-0.11.2/keystoneclient/auth/conf.py python-keystoneclient-1.0.0/keystoneclient/auth/conf.py --- python-keystoneclient-0.11.2/keystoneclient/auth/conf.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/conf.py 2014-12-18 17:37:35.000000000 +0000 @@ -88,9 +88,11 @@ :param conf: An oslo.config conf object. :param string group: The group name that options should be read from. - :returns plugin: An authentication Plugin or None if a name is not provided + :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 -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/base.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/base.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/base.py 2014-12-18 17:37:35.000000000 +0000 @@ -19,6 +19,7 @@ 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,10 +74,14 @@ 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 AccessInfo: Token access information. + :returns: Token access information. + :rtype: :py:class:`keystoneclient.access.AccessInfo` """ def get_token(self, session, **kwargs): @@ -84,9 +89,11 @@ 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 string: A valid token. + :return: A valid token. + :rtype: string """ return self.get_access(session).auth_token @@ -118,9 +125,11 @@ 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 AccessInfo: Valid AccessInfo + :returns: Valid AccessInfo + :rtype: :py:class:`keystoneclient.access.AccessInfo` """ if self._needs_reauthenticate(): self.auth_ref = self.get_auth_ref(session) @@ -136,9 +145,10 @@ returned to indicate that the token may have been revoked or is otherwise now invalid. - :returns bool: True if there was something that the plugin did to - invalidate. This means that it makes sense to try again. - If nothing happens returns False to indicate give up. + :returns: True if there was something that the plugin did to + invalidate. This means that it makes sense to try again. If + nothing happens returns False to indicate give up. + :rtype: bool """ if self.auth_ref: self.auth_ref = None @@ -169,9 +179,11 @@ :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 string or None: A valid endpoint URL or None if not available. + :return: A valid endpoint URL or None if not available. + :rtype: string or None """ # NOTE(jamielennox): if you specifically ask for requests to be sent to # the auth url then we can ignore the rest of the checks. Typically if @@ -181,9 +193,9 @@ 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: @@ -216,8 +228,9 @@ # 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) @@ -240,8 +253,10 @@ (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 -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/base.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/base.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/base.py 2014-12-18 17:37:35.000000000 +0000 @@ -17,15 +17,17 @@ import six import six.moves.urllib.parse as urlparse -from keystoneclient import _discover from keystoneclient.auth.identity import base +from keystoneclient import discover from keystoneclient import exceptions +from keystoneclient.i18n import _, _LW + LOG = logging.getLogger(__name__) def get_options(): - return base.get_options() + [ + return [ cfg.StrOpt('domain-id', help='Domain ID to scope to'), cfg.StrOpt('domain-name', help='Domain name to scope to'), cfg.StrOpt('tenant-id', help='Tenant ID to scope to'), @@ -127,9 +129,9 @@ 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() @@ -145,7 +147,7 @@ for data in disc_data: version = data['version'] - if (_discover.version_match((2,), version) and + if (discover.version_match((2,), version) and self._has_domain_scope): # NOTE(jamielennox): if there are domain parameters there # is no point even trying against v2 APIs. @@ -163,7 +165,7 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/password.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/password.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/password.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/password.py 2014-12-18 17:37:35.000000000 +0000 @@ -14,10 +14,10 @@ from oslo.config import cfg -from keystoneclient import _discover from keystoneclient.auth.identity.generic import base from keystoneclient.auth.identity import v2 from keystoneclient.auth.identity import v3 +from keystoneclient import discover from keystoneclient import utils LOG = logging.getLogger(__name__) @@ -25,6 +25,7 @@ def get_options(): return [ + cfg.StrOpt('user-id', help='User id'), cfg.StrOpt('user-name', dest='username', help='Username', deprecated_name='username'), cfg.StrOpt('user-domain-id', help="User's domain id"), @@ -34,19 +35,19 @@ class Password(base.BaseGenericPlugin): - """A common user/password authentication plugin.""" + """A common user/password authentication plugin. + + :param string username: Username for authentication. + :param string user_id: User ID for authentication. + :param string password: Password for authentication. + :param string user_domain_id: User's domain ID for authentication. + :param string user_domain_name: User's domain name for authentication. + + """ @utils.positional() def __init__(self, auth_url, username=None, user_id=None, password=None, user_domain_id=None, user_domain_name=None, **kwargs): - """Construct plugin. - - :param string username: Username for authentication. - :param string user_id: User ID for authentication. - :param string password: Password for authentication. - :param string user_domain_id: User's domain ID for authentication. - :param string user_domain_name: User's domain name for authentication. - """ super(Password, self).__init__(auth_url=auth_url, **kwargs) self._username = username @@ -56,7 +57,7 @@ self._user_domain_name = user_domain_name def create_plugin(self, session, version, url, raw_status=None): - if _discover.version_match((2,), version): + if discover.version_match((2,), version): if self._user_domain_id or self._user_domain_name: # If you specify any domain parameters it won't work so quit. return None @@ -67,7 +68,7 @@ password=self._password, **self._v2_params) - elif _discover.version_match((3,), version): + elif discover.version_match((3,), version): return v3.Password(auth_url=url, user_id=self._user_id, username=self._username, diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/token.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/token.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/generic/token.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/generic/token.py 2014-12-18 17:37:35.000000000 +0000 @@ -14,10 +14,10 @@ from oslo.config import cfg -from keystoneclient import _discover from keystoneclient.auth.identity.generic import base from keystoneclient.auth.identity import v2 from keystoneclient.auth.identity import v3 +from keystoneclient import discover LOG = logging.getLogger(__name__) @@ -29,20 +29,20 @@ class Token(base.BaseGenericPlugin): + """Generic token auth plugin. - def __init__(self, auth_url, token=None, **kwargs): - """Construct a plugin. + :param string token: Token for authentication. + """ - :param string token: Token for authentication. - """ + def __init__(self, auth_url, token=None, **kwargs): super(Token, self).__init__(auth_url, **kwargs) self._token = token def create_plugin(self, session, version, url, raw_status=None): - if _discover.version_match((2,), version): + if discover.version_match((2,), version): return v2.Token(url, self._token, **self._v2_params) - elif _discover.version_match((3,), version): + elif discover.version_match((3,), version): return v3.Token(url, self._token, **self._v3_params) @classmethod diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/v2.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/v2.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/v2.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/v2.py 2014-12-18 17:37:35.000000000 +0000 @@ -26,6 +26,15 @@ @six.add_metaclass(abc.ABCMeta) class Auth(base.BaseIdentityPlugin): + """Identity V2 Authentication Plugin. + + :param string auth_url: Identity service endpoint for authorization. + :param string trust_id: Trust ID for trust scoping. + :param string tenant_id: Tenant ID for project scoping. + :param string tenant_name: Tenant name for project scoping. + :param bool reauthenticate: Allow fetching a new token if the current one + is going to expire. (optional) default True + """ @classmethod def get_options(cls): @@ -45,16 +54,6 @@ tenant_id=None, tenant_name=None, reauthenticate=True): - """Construct an Identity V2 Authentication Plugin. - - :param string auth_url: Identity service endpoint for authorization. - :param string trust_id: Trust ID for trust scoping. - :param string tenant_id: Tenant ID for project scoping. - :param string tenant_name: Tenant name for project scoping. - :param bool reauthenticate: Allow fetching a new token if the current - one is going to expire. - (optional) default True - """ super(Auth, self).__init__(auth_url=auth_url, reauthenticate=reauthenticate) @@ -91,7 +90,8 @@ :param dict headers: The headers that will be sent with the auth request if a plugin needs to add to them. - :return dict: A dict of authentication data for the auth type. + :return: A dict of authentication data for the auth type. + :rtype: dict """ @@ -99,21 +99,21 @@ class Password(Auth): + """A plugin for authenticating with a username and password. - @utils.positional(4) - def __init__(self, auth_url, username=_NOT_PASSED, password=None, - user_id=_NOT_PASSED, **kwargs): - """A plugin for authenticating with a username and password. + A username or user_id must be provided. - A username or user_id must be provided. + :param string auth_url: Identity service endpoint for authorization. + :param string username: Username for authentication. + :param string password: Password for authentication. + :param string user_id: User ID for authentication. - :param string auth_url: Identity service endpoint for authorization. - :param string username: Username for authentication. - :param string password: Password for authentication. - :param string user_id: User ID for authentication. + :raises TypeError: if a user_id or username is not provided. + """ - :raises TypeError: if a user_id or username is not provided. - """ + @utils.positional(4) + def __init__(self, auth_url, username=_NOT_PASSED, password=None, + user_id=_NOT_PASSED, **kwargs): super(Password, self).__init__(auth_url, **kwargs) if username is _NOT_PASSED and user_id is _NOT_PASSED: @@ -156,13 +156,13 @@ class Token(Auth): + """A plugin for authenticating with an existing token. - def __init__(self, auth_url, token, **kwargs): - """A plugin for authenticating with an existing token. + :param string auth_url: Identity service endpoint for authorization. + :param string token: Existing token for authentication. + """ - :param string auth_url: Identity service endpoint for authorization. - :param string token: Existing token for authentication. - """ + def __init__(self, auth_url, token, **kwargs): super(Token, self).__init__(auth_url, **kwargs) self.token = token diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/identity/v3.py python-keystoneclient-1.0.0/keystoneclient/auth/identity/v3.py --- python-keystoneclient-0.11.2/keystoneclient/auth/identity/v3.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/identity/v3.py 2014-12-18 17:37:35.000000000 +0000 @@ -19,12 +19,27 @@ 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__) class Auth(base.BaseIdentityPlugin): + """Identity V3 Authentication Plugin. + + :param string auth_url: Identity service endpoint for authentication. + :param list auth_methods: A collection of methods to authenticate with. + :param string trust_id: Trust ID for trust scoping. + :param string domain_id: Domain ID for domain scoping. + :param string domain_name: Domain name for domain scoping. + :param string project_id: Project ID for project scoping. + :param string project_name: Project name for project scoping. + :param string project_domain_id: Project's domain ID for project. + :param string project_domain_name: Project's domain name for project. + :param bool reauthenticate: Allow fetching a new token if the current one + is going to expire. (optional) default True + """ @utils.positional() def __init__(self, auth_url, auth_methods, @@ -36,22 +51,6 @@ project_domain_id=None, project_domain_name=None, reauthenticate=True): - """Construct an Identity V3 Authentication Plugin. - - :param string auth_url: Identity service endpoint for authentication. - :param list auth_methods: A collection of methods to authenticate with. - :param string trust_id: Trust ID for trust scoping. - :param string domain_id: Domain ID for domain scoping. - :param string domain_name: Domain name for domain scoping. - :param string project_id: Project ID for project scoping. - :param string project_name: Project name for project scoping. - :param string project_domain_id: Project's domain ID for project. - :param string project_domain_name: Project's domain name for project. - :param bool reauthenticate: Allow fetching a new token if the current - one is going to expire. - (optional) default True - """ - super(Auth, self).__init__(auth_url=auth_url, reauthenticate=reauthenticate) @@ -84,18 +83,17 @@ 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 +163,7 @@ 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 @@ -182,8 +180,9 @@ :param Auth auth: The auth plugin calling the method. :param dict headers: The headers that will be sent with the auth request if a plugin needs to add to them. - :return tuple(string, dict): The identifier of this plugin and a dict - of authentication data for the auth type. + :return: The identifier of this plugin and a dict of authentication + data for the auth type. + :rtype: tuple(string, dict) """ @@ -206,6 +205,14 @@ class PasswordMethod(AuthMethod): + """Construct a User/Password based authentication method. + + :param string password: Password for authentication. + :param string username: Username for authentication. + :param string user_id: User ID for authentication. + :param string user_domain_id: User's domain ID for authentication. + :param string user_domain_name: User's domain name for authentication. + """ _method_parameters = ['user_id', 'username', @@ -213,17 +220,6 @@ 'user_domain_name', 'password'] - def __init__(self, **kwargs): - """Construct a User/Password based authentication method. - - :param string password: Password for authentication. - :param string username: Username for authentication. - :param string user_id: User ID for authentication. - :param string user_domain_id: User's domain ID for authentication. - :param string user_domain_name: User's domain name for authentication. - """ - super(PasswordMethod, self).__init__(**kwargs) - def get_auth_data(self, session, auth, headers, **kwargs): user = {'password': self.password} @@ -260,15 +256,12 @@ class TokenMethod(AuthMethod): + """Construct an Auth plugin to fetch a token from a token. - _method_parameters = ['token'] - - def __init__(self, **kwargs): - """Construct an Auth plugin to fetch a token from a token. + :param string token: Token for authentication. + """ - :param string token: Token for authentication. - """ - super(TokenMethod, self).__init__(**kwargs) + _method_parameters = ['token'] def get_auth_data(self, session, auth, headers, **kwargs): headers['X-Auth-Token'] = self.token diff -Nru python-keystoneclient-0.11.2/keystoneclient/auth/token_endpoint.py python-keystoneclient-1.0.0/keystoneclient/auth/token_endpoint.py --- python-keystoneclient-0.11.2/keystoneclient/auth/token_endpoint.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/auth/token_endpoint.py 2014-12-18 17:37:35.000000000 +0000 @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.config import cfg from keystoneclient.auth import base @@ -38,8 +39,9 @@ """ return self.endpoint - def get_options(self): - options = super(Token, self).get_options() + @classmethod + def get_options(cls): + options = super(Token, cls).get_options() options.extend([ cfg.StrOpt('endpoint', diff -Nru python-keystoneclient-0.11.2/keystoneclient/base.py python-keystoneclient-1.0.0/keystoneclient/base.py --- python-keystoneclient-0.11.2/keystoneclient/base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/base.py 2014-12-18 17:37:35.000000000 +0000 @@ -27,6 +27,7 @@ from keystoneclient import auth from keystoneclient import exceptions +from keystoneclient.i18n import _ from keystoneclient.openstack.common.apiclient import base @@ -78,14 +79,13 @@ Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. + + :param client: instance of BaseClient descendant for HTTP requests + """ resource_class = None def __init__(self, client): - """Initializes Manager with `client`. - - :param client: instance of BaseClient descendant for HTTP requests - """ super(Manager, self).__init__() self.client = client @@ -210,16 +210,15 @@ return self.client.delete(url, **kwargs) def _update(self, url, body=None, response_key=None, method="PUT", - management=True, **kwargs): + **kwargs): methods = {"PUT": self.client.put, "POST": self.client.post, "PATCH": self.client.patch} try: resp, body = methods[method](url, body=body, - 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 +243,8 @@ 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 +395,8 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/client.py python-keystoneclient-1.0.0/keystoneclient/client.py --- python-keystoneclient-0.11.2/keystoneclient/client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/client.py 2014-12-18 17:37:35.000000000 +0000 @@ -36,8 +36,10 @@ :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 -Nru python-keystoneclient-0.11.2/keystoneclient/common/cms.py python-keystoneclient-1.0.0/keystoneclient/common/cms.py --- python-keystoneclient-0.11.2/keystoneclient/common/cms.py 2014-10-23 17:42:08.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/common/cms.py 2014-12-18 17:37:35.000000000 +0000 @@ -28,6 +28,7 @@ import six from keystoneclient import exceptions +from keystoneclient.i18n import _, _LE, _LW subprocess = None @@ -38,6 +39,14 @@ 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 @@ 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 @@ 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 @@ 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): @@ -166,12 +171,12 @@ # -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') @@ -296,8 +301,8 @@ 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) @@ -343,14 +348,13 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/contrib/auth/v3/saml2.py python-keystoneclient-1.0.0/keystoneclient/contrib/auth/v3/saml2.py --- python-keystoneclient-0.11.2/keystoneclient/contrib/auth/v3/saml2.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/contrib/auth/v3/saml2.py 2014-12-18 17:37:35.000000000 +0000 @@ -20,6 +20,7 @@ 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 @@ @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,27 +81,45 @@ _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): """Implement authentication plugin for SAML2 protocol. - ECP stands for ``Enhanced Client or Proxy`` and is a SAML2 extension + ECP stands for `Enhanced Client or Proxy` and is a SAML2 extension for federated authentication where a transportation layer consists of HTTP protocol and XML SOAP messages. - Read for more information:: - ``https://wiki.shibboleth.net/confluence/display/SHIB2/ECP`` + `Read for more information + `_ on ECP. - The SAML2 ECP specification can be found at:: - ``https://www.oasis-open.org/committees/download.php/ - 49979/saml-ecp-v2.0-wd09.pdf`` + Reference the `SAML2 ECP specification `_. Currently only HTTPBasicAuth mechanism is available for the IdP authenication. + :param auth_url: URL of the Identity Service + :type auth_url: string + + :param identity_provider: name of the Identity Provider the client will + authenticate against. This parameter will be used + to build a dynamic URL used to obtain unscoped + OpenStack token. + :type identity_provider: string + + :param identity_provider_url: An Identity Provider URL, where the SAML2 + authn request will be sent. + :type identity_provider_url: string + + :param username: User's login + :type username: string + + :param password: User's password + :type password: string + """ _auth_method_class = Saml2UnscopedTokenAuthMethod @@ -148,27 +167,6 @@ identity_provider_url, username, password, **kwargs): - """Class constructor accepting following parameters: - :param auth_url: URL of the Identity Service - :type auth_url: string - - :param identity_provider: name of the Identity Provider the client - will authenticate against. This parameter - will be used to build a dynamic URL used to - obtain unscoped OpenStack token. - :type identity_provider: string - - :param identity_provider_url: An Identity Provider URL, where the SAML2 - authn request will be sent. - :type identity_provider_url: string - - :param username: User's login - :type username: string - - :param password: User's password - :type password: string - - """ super(Saml2UnscopedToken, self).__init__(auth_url=auth_url, **kwargs) self.identity_provider = identity_provider self.identity_provider_url = identity_provider_url @@ -211,9 +209,9 @@ 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 +255,8 @@ 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 +286,8 @@ 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( @@ -426,8 +424,9 @@ :param session : a session object to send out HTTP requests. :type session: keystoneclient.session.Session - :return access.AccessInfoV3: an object with scoped token's id and - unscoped token json included. + :return: an object with scoped token's id and unscoped token json + included. + :rtype: :py:class:`keystoneclient.access.AccessInfoV3` """ token, token_json = self._get_unscoped_token(session) @@ -436,7 +435,32 @@ class ADFSUnscopedToken(_BaseSAMLPlugin): - """Authentication plugin for Microsoft ADFS2.0 IdPs.""" + """Authentication plugin for Microsoft ADFS2.0 IdPs. + + :param auth_url: URL of the Identity Service + :type auth_url: string + + :param identity_provider: name of the Identity Provider the client will + authenticate against. This parameter will be used + to build a dynamic URL used to obtain unscoped + OpenStack token. + :type identity_provider: string + + :param identity_provider_url: An Identity Provider URL, where the SAML2 + authentication request will be sent. + :type identity_provider_url: string + + :param service_provider_endpoint: Endpoint where an assertion is being + sent, for instance: ``https://host.domain/Shibboleth.sso/ADFS`` + :type service_provider_endpoint: string + + :param username: User's login + :type username: string + + :param password: User's password + :type password: string + + """ _auth_method_class = Saml2UnscopedTokenAuthMethod @@ -462,33 +486,6 @@ def __init__(self, auth_url, identity_provider, identity_provider_url, service_provider_endpoint, username, password, **kwargs): - """Constructor for ``ADFSUnscopedToken``. - - :param auth_url: URL of the Identity Service - :type auth_url: string - - :param identity_provider: name of the Identity Provider the client - will authenticate against. This parameter - will be used to build a dynamic URL used to - obtain unscoped OpenStack token. - :type identity_provider: string - - :param identity_provider_url: An Identity Provider URL, where the SAML2 - authentication request will be sent. - :type identity_provider_url: string - - :param service_provider_endpoint: Endpoint where an assertion is being - sent, for instance: ``https://host.domain/Shibboleth.sso/ADFS`` - :type service_provider_endpoint: string - - :param username: User's login - :type username: string - - :param password: User's password - :type password: string - - """ - super(ADFSUnscopedToken, self).__init__(auth_url=auth_url, **kwargs) self.identity_provider = identity_provider self.identity_provider_url = identity_provider_url @@ -505,10 +502,6 @@ ]) return options - @property - def _uuid4(self): - return str(uuid.uuid4()) - def _cookies(self, session): """Check if cookie jar is not empty. @@ -519,7 +512,7 @@ :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: @@ -590,7 +583,7 @@ 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( @@ -631,7 +624,7 @@ '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-' @@ -704,11 +697,12 @@ :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): @@ -736,8 +730,8 @@ 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): @@ -797,14 +791,15 @@ :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) @@ -841,7 +836,7 @@ :param session : a session object to send out HTTP requests. :type session: keystoneclient.session.Session - :returns (Unscoped federated token, token JSON body) + :returns: (Unscoped federated token, token JSON body) """ self._prepare_adfs_request() @@ -883,4 +878,4 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/contrib/ec2/utils.py python-keystoneclient-1.0.0/keystoneclient/contrib/ec2/utils.py --- python-keystoneclient-0.11.2/keystoneclient/contrib/ec2/utils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/contrib/ec2/utils.py 2014-12-18 17:37:35.000000000 +0000 @@ -24,6 +24,8 @@ 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 @@ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/contrib/revoke/model.py python-keystoneclient-1.0.0/keystoneclient/contrib/revoke/model.py --- python-keystoneclient-0.11.2/keystoneclient/contrib/revoke/model.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/contrib/revoke/model.py 2014-12-18 17:37:35.000000000 +0000 @@ -110,9 +110,9 @@ fields of the revocation event. The leaf node will always be set to the latest 'issued_before' for events that are otherwise identical. - :param: Event to add to the tree + :param: Event to add to the tree - :returns: the event that was passed in. + :returns: the event that was passed in. """ revoke_map = self.revoke_map diff -Nru python-keystoneclient-0.11.2/keystoneclient/_discover.py python-keystoneclient-1.0.0/keystoneclient/_discover.py --- python-keystoneclient-0.11.2/keystoneclient/_discover.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/_discover.py 2014-12-18 17:37:35.000000000 +0000 @@ -25,6 +25,7 @@ import re from keystoneclient import exceptions +from keystoneclient.i18n import _, _LI, _LW from keystoneclient import utils @@ -65,7 +66,7 @@ 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 @@ except Exception: pass - raise TypeError('Invalid version specified: %s' % version) + raise TypeError(_('Invalid version specified: %s') % version) def version_match(required, candidate): @@ -114,7 +115,8 @@ :param tuple required: the version that must be met. :param tuple candidate: the version to test against required. - :returns bool: True if candidate is suitable False otherwise. + :returns: True if candidate is suitable False otherwise. + :rtype: bool """ # major versions must be the same (e.g. even though v2 is a lower # version than v3 we can't use it if v2 was requested) @@ -151,16 +153,17 @@ :param bool allow_deprecated: Allow deprecated version endpoints. :param bool allow_unknown: Allow endpoints with an unrecognised status. - :returns list: The endpoints returned from the server that match the - criteria. + :returns: The endpoints returned from the server that match the + criteria. + :rtype: list """ versions = [] for v in self._data: 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() @@ -183,13 +186,14 @@ Return version data in a structured way. - :returns list(dict): A list of version data dictionaries sorted by - version number. Each data element in the returned - list is a dictionary consisting of at least: + :returns: A list of version data dictionaries sorted by version number. + Each data element in the returned list is a dictionary + consisting of at least: :version tuple: The normalized version of the endpoint. :url str: The url for the endpoint. :raw_status str: The status as provided by the server + :rtype: list(dict) """ data = self.raw_version_data(**kwargs) versions = [] @@ -198,13 +202,14 @@ 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) @@ -214,15 +219,15 @@ 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, @@ -239,9 +244,10 @@ same major release as there should be no compatibility issues with using a version newer than the one asked for. - :returns dict: the endpoint data for a URL that matches the required - version (the format is described in version_data) - or None if no match. + :returns: the endpoint data for a URL that matches the required version + (the format is described in version_data) or None if no + match. + :rtype: dict """ version = normalize_version_number(version) version_data = self.version_data(**kwargs) @@ -259,7 +265,8 @@ same major release as there should be no compatibility issues with using a version newer than the one asked for. - :returns str: The url for the specified version or None if no match. + :returns: The url for the specified version or None if no match. + :rtype: str """ data = self.data_for(version, **kwargs) return data['url'] if data else None diff -Nru python-keystoneclient-0.11.2/keystoneclient/discover.py python-keystoneclient-1.0.0/keystoneclient/discover.py --- python-keystoneclient-0.11.2/keystoneclient/discover.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/discover.py 2014-12-18 17:37:35.000000000 +0000 @@ -16,6 +16,7 @@ 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 @@ -29,6 +30,46 @@ 3: v3_client.Client} +# functions needed from the private file that can be made public + +def normalize_version_number(version): + """Turn a version representation into a tuple. + + Takes a string, tuple or float which represent version formats we can + handle and converts them into a (major, minor) version tuple that we can + actually use for discovery. + + e.g. 'v3.3' gives (3, 3) + 3.1 gives (3, 1) + + :param version: Inputted version number to try and convert. + + :returns: A usable version tuple + :rtype: tuple + + :raises TypeError: if the inputted version cannot be converted to tuple. + """ + return _discover.normalize_version_number(version) + + +def version_match(required, candidate): + """Test that an available version is a suitable match for a required + version. + + To be suitable a version must be of the same major version as required + and be at least a match in minor/patch level. + + eg. 3.3 is a match for a required 3.1 but 4.1 is not. + + :param tuple required: the version that must be met. + :param tuple candidate: the version to test against required. + + :returns: True if candidate is suitable False otherwise. + :rtype: bool + """ + return _discover.version_match(required, candidate) + + def available_versions(url, session=None, **kwargs): """Retrieve raw version data from a url.""" if not session: @@ -43,69 +84,63 @@ Querying the server is done on object creation and every subsequent method operates upon the data that was retrieved. + + The connection parameters associated with this method are the same format + and name as those used by a client (see + :py:class:`keystoneclient.v2_0.client.Client` and + :py:class:`keystoneclient.v3.client.Client`). If not overridden in + subsequent methods they will also be what is passed to the constructed + client. + + In the event that auth_url and endpoint is provided then auth_url will be + used in accordance with how the client operates. + + :param session: A session object that will be used for communication. + Clients will also be constructed with this session. + :type session: keystoneclient.session.Session + :param string auth_url: Identity service endpoint for authorization. + (optional) + :param string endpoint: A user-supplied endpoint URL for the identity + service. (optional) + :param string original_ip: The original IP of the requesting user which + will be sent to identity service in a + 'Forwarded' header. (optional) DEPRECATED: use + the session object. This is ignored if a session + is provided. + :param boolean debug: Enables debug logging of all request and responses to + the identity service. default False (optional) + DEPRECATED: use the session object. This is ignored + if a session is provided. + :param string cacert: Path to the Privacy Enhanced Mail (PEM) file which + contains the trusted authority X.509 certificates + needed to established SSL connection with the + identity service. (optional) DEPRECATED: use the + session object. This is ignored if a session is + provided. + :param string key: Path to the Privacy Enhanced Mail (PEM) file which + contains the unencrypted client private key needed to + established two-way SSL connection with the identity + service. (optional) DEPRECATED: use the session object. + This is ignored if a session is provided. + :param string cert: Path to the Privacy Enhanced Mail (PEM) file which + contains the corresponding X.509 client certificate + needed to established two-way SSL connection with the + identity service. (optional) DEPRECATED: use the + session object. This is ignored if a session is + provided. + :param boolean insecure: Does not perform X.509 certificate validation when + establishing SSL connection with identity service. + default: False (optional) DEPRECATED: use the + session object. This is ignored if a session is + provided. + :param bool authenticated: Should a token be used to perform the initial + discovery operations. default: None (attach a + token if an auth plugin is available). + """ @utils.positional(2) def __init__(self, session=None, authenticated=None, **kwargs): - """Construct a new discovery object. - - The connection parameters associated with this method are the same - format and name as those used by a client (see - keystoneclient.v2_0.client.Client and keystoneclient.v3.client.Client). - If not overridden in subsequent methods they will also be what is - passed to the constructed client. - - In the event that auth_url and endpoint is provided then auth_url will - be used in accordance with how the client operates. - - The initialization process also queries the server. - - :param Session session: A session object that will be used for - communication. Clients will also be constructed - with this session. - :param string auth_url: Identity service endpoint for authorization. - (optional) - :param string endpoint: A user-supplied endpoint URL for the identity - service. (optional) - :param string original_ip: The original IP of the requesting user - which will be sent to identity service in a - 'Forwarded' header. (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param boolean debug: Enables debug logging of all request and - responses to the identity service. - default False (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param string cacert: Path to the Privacy Enhanced Mail (PEM) file - which contains the trusted authority X.509 - certificates needed to established SSL connection - with the identity service. (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param string key: Path to the Privacy Enhanced Mail (PEM) file which - contains the unencrypted client private key needed - to established two-way SSL connection with the - identity service. (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param string cert: Path to the Privacy Enhanced Mail (PEM) file which - contains the corresponding X.509 client certificate - needed to established two-way SSL connection with - the identity service. (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param boolean insecure: Does not perform X.509 certificate validation - when establishing SSL connection with identity - service. default: False (optional) - DEPRECATED: use the session object. This is - ignored if a session is provided. - :param bool authenticated: Should a token be used to perform the - initial discovery operations. - default: None (attach a token if an auth - plugin is available). - """ - if not session: session = client_session.Session.construct(kwargs) kwargs['session'] = session @@ -122,9 +157,9 @@ 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, @@ -163,8 +198,9 @@ :param bool allow_deprecated: Allow deprecated version endpoints. :param bool allow_unknown: Allow endpoints with an unrecognised status. - :returns list: The endpoints returned from the server that match the - criteria. + :returns: The endpoints returned from the server that match the + criteria. + :rtype: list Example:: @@ -213,10 +249,11 @@ 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) @@ -228,7 +265,7 @@ 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. @@ -261,8 +298,10 @@ :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 -Nru python-keystoneclient-0.11.2/keystoneclient/exceptions.py python-keystoneclient-1.0.0/keystoneclient/exceptions.py --- python-keystoneclient-0.11.2/keystoneclient/exceptions.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/exceptions.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 @@ 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 NoMatchingPlugin(ClientException): """There were no auth plugins that could be created from the parameters - provided.""" + provided. + """ class InvalidResponse(ClientException): diff -Nru python-keystoneclient-0.11.2/keystoneclient/fixture/discovery.py python-keystoneclient-1.0.0/keystoneclient/fixture/discovery.py --- python-keystoneclient-0.11.2/keystoneclient/fixture/discovery.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/fixture/discovery.py 2014-12-18 17:37:35.000000000 +0000 @@ -28,16 +28,14 @@ """The basic version discovery structure. All version discovery elements should have access to these values. + + :param string id: The version id for this version entry. + :param string status: The status of this entry. + :param DateTime updated: When the API was last updated. """ @utils.positional() def __init__(self, id, status=None, updated=None): - """Create a new structure. - - :param string id: The version id for this version entry. - :param string status: The status of this entry. - :param DateTime updated: When the API was last updated. - """ super(DiscoveryBase, self).__init__() self.id = id @@ -106,20 +104,19 @@ Provides some default values and helper methods for creating a v2.0 endpoint version structure. Clients should use this instead of creating their own structures. + + :param string href: The url that this entry should point to. + :param string id: The version id that should be reported. (optional) + Defaults to 'v2.0'. + :param bool html: Add HTML describedby links to the structure. + :param bool pdf: Add PDF describedby links to the structure. + """ _DESC_URL = 'http://docs.openstack.org/api/openstack-identity-service/2.0/' @utils.positional() def __init__(self, href, id=None, html=True, pdf=True, **kwargs): - """Create a new structure. - - :param string href: The url that this entry should point to. - :param string id: The version id that should be reported. (optional) - Defaults to 'v2.0'. - :param bool html: Add HTML describedby links to the structure. - :param bool pdf: Add PDF describedby links to the structure. - """ super(V2Discovery, self).__init__(id or 'v2.0', **kwargs) self.add_link(href) @@ -156,18 +153,16 @@ Provides some default values and helper methods for creating a v3 endpoint version structure. Clients should use this instead of creating their own structures. + + :param href: The url that this entry should point to. + :param string id: The version id that should be reported. (optional) + Defaults to 'v3.0'. + :param bool json: Add JSON media-type elements to the structure. + :param bool xml: Add XML media-type elements to the structure. """ @utils.positional() def __init__(self, href, id=None, json=True, xml=True, **kwargs): - """Create a new structure. - - :param href: The url that this entry should point to. - :param string id: The version id that should be reported. (optional) - Defaults to 'v3.0'. - :param bool json: Add JSON media-type elements to the structure. - :param bool xml: Add XML media-type elements to the structure. - """ super(V3Discovery, self).__init__(id or 'v3.0', **kwargs) self.add_link(href) @@ -201,6 +196,18 @@ Creates a correctly structured list of identity service endpoints for use in testing with discovery. + + :param string href: The url that this should be based at. + :param bool v2: Add a v2 element. + :param bool v3: Add a v3 element. + :param string v2_status: The status to use for the v2 element. + :param DateTime v2_updated: The update time to use for the v2 element. + :param bool v2_html: True to add a html link to the v2 element. + :param bool v2_pdf: True to add a pdf link to the v2 element. + :param string v3_status: The status to use for the v3 element. + :param DateTime v3_updated: The update time to use for the v3 element. + :param bool v3_json: True to add a html link to the v2 element. + :param bool v3_xml: True to add a pdf link to the v2 element. """ TEST_URL = 'http://keystone.host:5000/' @@ -209,21 +216,6 @@ def __init__(self, href=None, v2=True, v3=True, v2_id=None, v3_id=None, v2_status=None, v2_updated=None, v2_html=True, v2_pdf=True, v3_status=None, v3_updated=None, v3_json=True, v3_xml=True): - """Create a new structure. - - :param string href: The url that this should be based at. - :param bool v2: Add a v2 element. - :param bool v3: Add a v3 element. - :param string v2_status: The status to use for the v2 element. - :param DateTime v2_updated: The update time to use for the v2 element. - :param bool v2_html: True to add a html link to the v2 element. - :param bool v2_pdf: True to add a pdf link to the v2 element. - :param string v3_status: The status to use for the v3 element. - :param DateTime v3_updated: The update time to use for the v3 element. - :param bool v3_json: True to add a html link to the v2 element. - :param bool v3_xml: True to add a pdf link to the v2 element. - """ - super(DiscoveryList, self).__init__(versions={'values': []}) href = href or self.TEST_URL diff -Nru python-keystoneclient-0.11.2/keystoneclient/generic/client.py python-keystoneclient-1.0.0/keystoneclient/generic/client.py --- python-keystoneclient-0.11.2/keystoneclient/generic/client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/generic/client.py 2014-12-18 17:37:35.000000000 +0000 @@ -19,6 +19,7 @@ from keystoneclient import exceptions from keystoneclient import httpclient +from keystoneclient.i18n import _, _LE _logger = logging.getLogger(__name__) @@ -94,7 +95,7 @@ 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 @@ 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 @@ "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 @@ 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 @@ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/generic/shell.py python-keystoneclient-1.0.0/keystoneclient/generic/shell.py --- python-keystoneclient-0.11.2/keystoneclient/generic/shell.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/generic/shell.py 2014-12-18 17:37:35.000000000 +0000 @@ -16,6 +16,7 @@ import six from keystoneclient.generic import client +from keystoneclient.i18n import _ from keystoneclient import utils @@ -37,13 +38,14 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/httpclient.py python-keystoneclient-1.0.0/keystoneclient/httpclient.py --- python-keystoneclient-0.11.2/keystoneclient/httpclient.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/httpclient.py 2014-12-18 17:37:35.000000000 +0000 @@ -52,9 +52,11 @@ from keystoneclient import access +from keystoneclient import adapter from keystoneclient.auth import base from keystoneclient import baseclient from keystoneclient import exceptions +from keystoneclient.i18n import _, _LW from keystoneclient import session as client_session from keystoneclient import utils @@ -83,7 +85,121 @@ return requests.request(*args, **kwargs) +class _KeystoneAdapter(adapter.LegacyJsonAdapter): + """A wrapper layer to interface keystoneclient with a session. + + An adapter provides a generic interface between a client and the session to + provide client specific defaults. This object is passed to the managers. + Keystoneclient managers have some additional requirements of variables that + they expect to be present on the passed object. + + Subclass the existing adapter to provide those values that keystoneclient + managers expect. + """ + + @property + def user_id(self): + """Best effort to retrieve the user_id from the plugin. + + Some managers rely on being able to get the currently authenticated + user id. This is a problem when we are trying to abstract away the + details of an auth plugin. + + For example changing a user's password can require access to the + currently authenticated user_id. + + Perform a best attempt to fetch this data. It will work in the legacy + case and with identity plugins and be None otherwise which is the same + as the historical behavior. + """ + # the identity plugin case + try: + return self.session.auth.get_access(self.session).user_id + except AttributeError: + pass + + # there is a case that we explicity allow (tested by our unit tests) + # that says you should be able to set the user_id on a legacy client + # and it should overwrite the one retrieved via authentication. If it's + # a legacy then self.session.auth is a client and we retrieve user_id. + try: + return self.session.auth.user_id + except AttributeError: + pass + + return None + + class HTTPClient(baseclient.Client, base.BaseAuthPlugin): + """HTTP client + + :param string user_id: User ID for authentication. (optional) + :param string username: Username for authentication. (optional) + :param string user_domain_id: User's domain ID for authentication. + (optional) + :param string user_domain_name: User's domain name for authentication. + (optional) + :param string password: Password for authentication. (optional) + :param string domain_id: Domain ID for domain scoping. (optional) + :param string domain_name: Domain name for domain scoping. (optional) + :param string project_id: Project ID for project scoping. (optional) + :param string project_name: Project name for project scoping. (optional) + :param string project_domain_id: Project's domain ID for project scoping. + (optional) + :param string project_domain_name: Project's domain name for project + scoping. (optional) + :param string auth_url: Identity service endpoint for authorization. + :param string region_name: Name of a region to select when choosing an + endpoint from the service catalog. + :param integer timeout: DEPRECATED: use session. (optional) + :param string endpoint: A user-supplied endpoint URL for the identity + service. Lazy-authentication is possible for API + service calls if endpoint is set at instantiation. + (optional) + :param string token: Token for authentication. (optional) + :param string cacert: DEPRECATED: use session. (optional) + :param string key: DEPRECATED: use session. (optional) + :param string cert: DEPRECATED: use session. (optional) + :param boolean insecure: DEPRECATED: use session. (optional) + :param string original_ip: DEPRECATED: use session. (optional) + :param boolean debug: DEPRECATED: use logging configuration. (optional) + :param dict auth_ref: To allow for consumers of the client to manage their + own caching strategy, you may initialize a client + with a previously captured auth_reference (token). If + there are keyword arguments passed that also exist in + auth_ref, the value from the argument will take + precedence. + :param boolean use_keyring: Enables caching auth_ref into keyring. + default: False (optional) + :param boolean force_new_token: Keyring related parameter, forces request + for new token. default: False (optional) + :param integer stale_duration: Gap in seconds to determine if token from + keyring is about to expire. default: 30 + (optional) + :param string tenant_name: Tenant name. (optional) The tenant_name keyword + argument is deprecated, use project_name + instead. + :param string tenant_id: Tenant id. (optional) The tenant_id keyword + argument is deprecated, use project_id instead. + :param string trust_id: Trust ID for trust scoping. (optional) + :param object session: A Session object to be used for + communicating with the identity service. + :type session: keystoneclient.session.Session + :param string service_name: The default service_name for URL discovery. + default: None (optional) + :param string interface: The default interface for URL discovery. + default: admin (optional) + :param string endpoint_override: Always use this endpoint URL for requests + for this client. (optional) + :param auth: An auth plugin to use instead of the session one. (optional) + :type auth: keystoneclient.auth.base.BaseAuthPlugin + :param string user_agent: The User-Agent string to set. + default: python-keystoneclient (optional) + :param int connect_retries: the maximum number of retries that should + be attempted for connection errors. + Default None - use session default which + is don't retry. (optional) + """ version = None @@ -95,65 +211,9 @@ user_domain_id=None, user_domain_name=None, domain_id=None, domain_name=None, project_id=None, project_name=None, project_domain_id=None, project_domain_name=None, - trust_id=None, session=None, **kwargs): - """Construct a new http client - - :param string user_id: User ID for authentication. (optional) - :param string username: Username for authentication. (optional) - :param string user_domain_id: User's domain ID for authentication. - (optional) - :param string user_domain_name: User's domain name for authentication. - (optional) - :param string password: Password for authentication. (optional) - :param string domain_id: Domain ID for domain scoping. (optional) - :param string domain_name: Domain name for domain scoping. (optional) - :param string project_id: Project ID for project scoping. (optional) - :param string project_name: Project name for project scoping. - (optional) - :param string project_domain_id: Project's domain ID for project - scoping. (optional) - :param string project_domain_name: Project's domain name for project - scoping. (optional) - :param string auth_url: Identity service endpoint for authorization. - :param string region_name: Name of a region to select when choosing an - endpoint from the service catalog. - :param integer timeout: DEPRECATED: use session. (optional) - :param string endpoint: A user-supplied endpoint URL for the identity - service. Lazy-authentication is possible for - API service calls if endpoint is set at - instantiation. (optional) - :param string token: Token for authentication. (optional) - :param string cacert: DEPRECATED: use session. (optional) - :param string key: DEPRECATED: use session. (optional) - :param string cert: DEPRECATED: use session. (optional) - :param boolean insecure: DEPRECATED: use session. (optional) - :param string original_ip: DEPRECATED: use session. (optional) - :param boolean debug: DEPRECATED: use logging configuration. (optional) - :param dict auth_ref: To allow for consumers of the client to manage - their own caching strategy, you may initialize a - client with a previously captured auth_reference - (token). If there are keyword arguments passed - that also exist in auth_ref, the value from the - argument will take precedence. - :param boolean use_keyring: Enables caching auth_ref into keyring. - default: False (optional) - :param boolean force_new_token: Keyring related parameter, forces - request for new token. - default: False (optional) - :param integer stale_duration: Gap in seconds to determine if token - from keyring is about to expire. - default: 30 (optional) - :param string tenant_name: Tenant name. (optional) - The tenant_name keyword argument is - deprecated, use project_name instead. - :param string tenant_id: Tenant id. (optional) - The tenant_id keyword argument is - deprecated, use project_id instead. - :param string trust_id: Trust ID for trust scoping. (optional) - :param object session: A Session object to be used for - communicating with the identity service. - - """ + trust_id=None, session=None, service_name=None, + interface='admin', endpoint_override=None, auth=None, + user_agent=USER_AGENT, connect_retries=None, **kwargs): # set baseline defaults self.user_id = None self.username = None @@ -168,7 +228,6 @@ self.project_domain_id = None self.project_domain_name = None - self.region_name = None self.auth_url = None self._endpoint = None self._management_url = None @@ -192,8 +251,8 @@ self._management_url = self.auth_ref.management_url[0] self.auth_token_from_user = self.auth_ref.auth_token self.trust_id = self.auth_ref.trust_id - if self.auth_ref.has_service_catalog(): - self.region_name = self.auth_ref.service_catalog.region_name + if self.auth_ref.has_service_catalog() and not region_name: + region_name = self.auth_ref.service_catalog.region_name else: self.auth_ref = None @@ -250,8 +309,6 @@ self.auth_token_from_user = None if endpoint: self._endpoint = endpoint.rstrip('/') - if region_name: - self.region_name = region_name self._auth_token = None if not session: @@ -263,9 +320,23 @@ self.domain = '' self.debug_log = debug + # NOTE(jamielennox): unfortunately we can't just use **kwargs here as + # it would incompatibly limit the kwargs that can be passed to __init__ + # try and keep this list in sync with adapter.Adapter.__init__ + self._adapter = _KeystoneAdapter(session, + service_type='identity', + service_name=service_name, + interface=interface, + region_name=region_name, + endpoint_override=endpoint_override, + version=self.version, + auth=auth, + user_agent=user_agent, + connect_retries=connect_retries) + # 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 +383,7 @@ 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 +435,10 @@ 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 @@ -394,7 +466,7 @@ project_domain_name = project_domain_name or self.project_domain_name trust_id = trust_id or self.trust_id - region_name = region_name or self.region_name + region_name = region_name or self._adapter.region_name if not token: token = self.auth_token_from_user @@ -476,7 +548,8 @@ 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 +562,8 @@ 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 +572,7 @@ 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 +585,14 @@ 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 @@ -565,82 +639,110 @@ def serialize(self, entity): return jsonutils.dumps(entity) - @staticmethod - def _decode_body(resp): - if resp.text: - try: - body_resp = jsonutils.loads(resp.text) - except (ValueError, TypeError): - body_resp = None - _logger.debug("Could not decode JSON from body: %s", - resp.text) - else: - _logger.debug("No body was returned.") - body_resp = None - - return body_resp - - def request(self, url, method, **kwargs): + def request(self, *args, **kwargs): """Send an http request with the specified characteristics. Wrapper around requests.request to handle tasks such as setting headers, JSON encoding/decoding, and error handling. - """ - - try: - kwargs['json'] = kwargs.pop('body') - except KeyError: - pass + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used only by the managers and the managers now receive an + adapter so this function is no longer on the standard request path. + """ kwargs.setdefault('authenticated', False) - resp = super(HTTPClient, self).request(url, method, **kwargs) - return resp, self._decode_body(resp) + return self._adapter.request(*args, **kwargs) def _cs_request(self, url, method, management=True, **kwargs): """Makes an authenticated request to keystone endpoint by concatenating self.management_url and url and passing in method and any associated kwargs. """ - # NOTE(jamielennox): remember that if you use the legacy client mode - # (you create a client without a session) then this HTTPClient object - # is the auth plugin you are using. Values in the endpoint_filter may - # be ignored and you should look at get_endpoint to figure out what. - interface = 'admin' if management else 'public' - endpoint_filter = kwargs.setdefault('endpoint_filter', {}) - endpoint_filter.setdefault('service_type', 'identity') - endpoint_filter.setdefault('interface', interface) - - if self.version: - endpoint_filter.setdefault('version', self.version) - - if self.region_name: - endpoint_filter.setdefault('region_name', self.region_name) + # NOTE(jamielennox): This is deprecated and is no longer a part of the + # standard client request path. It now goes via the adapter instead. + if not management: + endpoint_filter = kwargs.setdefault('endpoint_filter', {}) + endpoint_filter.setdefault('interface', 'public') kwargs.setdefault('authenticated', None) - try: - return self.request(url, method, **kwargs) - except exceptions.MissingAuthPlugin: - _logger.info('Cannot get authenticated endpoint without an ' - 'auth plugin') - raise exceptions.AuthorizationFailure( - 'Current authorization does not have a known management url') + return self.request(url, method, **kwargs) def get(self, url, **kwargs): + """Perform an authenticated GET request. + + This calls :py:meth:`.request()` with ``method`` set to ``GET`` and an + authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'GET', **kwargs) def head(self, url, **kwargs): + """Perform an authenticated HEAD request. + + This calls :py:meth:`.request()` with ``method`` set to ``HEAD`` and an + authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'HEAD', **kwargs) def post(self, url, **kwargs): + """Perform an authenticate POST request. + + This calls :py:meth:`.request()` with ``method`` set to ``POST`` and an + authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'POST', **kwargs) def put(self, url, **kwargs): + """Perform an authenticate PUT request. + + This calls :py:meth:`.request()` with ``method`` set to ``PUT`` and an + authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'PUT', **kwargs) def patch(self, url, **kwargs): + """Perform an authenticate PATCH request. + + This calls :py:meth:`.request()` with ``method`` set to ``PATCH`` and + an authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'PATCH', **kwargs) def delete(self, url, **kwargs): + """Perform an authenticate DELETE request. + + This calls :py:meth:`.request()` with ``method`` set to ``DELETE`` and + an authentication token if one is available. + + .. warning:: + *DEPRECATED*: This function is no longer used. It was designed to + be used by the managers and the managers now receive an adapter so + this function is no longer on the standard request path. + """ return self._cs_request(url, 'DELETE', **kwargs) # DEPRECATIONS: The following methods are no longer directly supported @@ -651,20 +753,40 @@ 'timeout': None, 'verify_cert': 'verify'} + deprecated_adapter_variables = {'region_name': None} + def __getattr__(self, name): # FIXME(jamielennox): provide a proper deprecated warning try: var_name = self.deprecated_session_variables[name] except KeyError: - raise AttributeError("Unknown Attribute: %s" % name) + pass + else: + return getattr(self.session, var_name or name) + + try: + var_name = self.deprecated_adapter_variables[name] + except KeyError: + pass + else: + return getattr(self._adapter, var_name or name) - return getattr(self.session, var_name or name) + raise AttributeError(_("Unknown Attribute: %s") % name) def __setattr__(self, name, val): # FIXME(jamielennox): provide a proper deprecated warning try: var_name = self.deprecated_session_variables[name] except KeyError: - super(HTTPClient, self).__setattr__(name, val) + pass else: - setattr(self.session, var_name or name) + return setattr(self.session, var_name or name) + + try: + var_name = self.deprecated_adapter_variables[name] + except KeyError: + pass + else: + return setattr(self._adapter, var_name or name) + + super(HTTPClient, self).__setattr__(name, val) diff -Nru python-keystoneclient-0.11.2/keystoneclient/i18n.py python-keystoneclient-1.0.0/keystoneclient/i18n.py --- python-keystoneclient-0.11.2/keystoneclient/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/i18n.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/middleware/auth_token.py python-keystoneclient-1.0.0/keystoneclient/middleware/auth_token.py --- python-keystoneclient-0.11.2/keystoneclient/middleware/auth_token.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/middleware/auth_token.py 2014-12-18 17:37:35.000000000 +0000 @@ -16,6 +16,12 @@ """ TOKEN-BASED AUTH MIDDLEWARE +.. warning:: + + This module is DEPRECATED. The auth_token middleware has been moved to the + `keystonemiddleware repository + `_. + This WSGI component: * Verifies that incoming client requests have valid tokens by validating @@ -26,9 +32,6 @@ * 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 ------- diff -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/base.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/base.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/base.py 2014-12-18 17:37:35.000000000 +0000 @@ -26,12 +26,12 @@ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/client.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/client.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/client.py 2014-12-18 17:37:35.000000000 +0000 @@ -25,6 +25,7 @@ # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 +import hashlib import logging import time @@ -33,14 +34,15 @@ 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 @@ 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 @@ ] 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 @@ 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 @@ 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 @@ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/exceptions.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/exceptions.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/exceptions.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/exceptions.py 2014-12-18 17:37:35.000000000 +0000 @@ -25,7 +25,7 @@ import six -from keystoneclient.openstack.common.gettextutils import _ +from keystoneclient.openstack.common._i18n import _ class ClientException(Exception): @@ -34,14 +34,6 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/fake_client.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/fake_client.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/fake_client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/fake_client.py 2014-12-18 17:37:35.000000000 +0000 @@ -168,6 +168,8 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/utils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/utils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/apiclient/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/apiclient/utils.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/gettextutils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/gettextutils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/gettextutils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/gettextutils.py 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/_i18n.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/_i18n.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/_i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/_i18n.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/importutils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/importutils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/importutils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/importutils.py 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/__init__.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/__init__.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/__init__.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/__init__.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/memorycache.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/memorycache.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/memorycache.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/memorycache.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/strutils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/strutils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/strutils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/strutils.py 1970-01-01 00:00:00.000000000 +0000 @@ -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>).*?()', - 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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/timeutils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/timeutils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/timeutils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/timeutils.py 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/openstack/common/uuidutils.py python-keystoneclient-1.0.0/keystoneclient/openstack/common/uuidutils.py --- python-keystoneclient-0.11.2/keystoneclient/openstack/common/uuidutils.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/openstack/common/uuidutils.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/service_catalog.py python-keystoneclient-1.0.0/keystoneclient/service_catalog.py --- python-keystoneclient-0.11.2/keystoneclient/service_catalog.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/service_catalog.py 2014-12-18 17:37:35.000000000 +0000 @@ -21,6 +21,7 @@ import six from keystoneclient import exceptions +from keystoneclient.i18n import _ from keystoneclient import utils @@ -36,7 +37,7 @@ 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 @@ """ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/session.py python-keystoneclient-1.0.0/keystoneclient/session.py --- python-keystoneclient-0.11.2/keystoneclient/session.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/session.py 2014-12-18 17:37:35.000000000 +0000 @@ -25,6 +25,7 @@ from six.moves import urllib from keystoneclient import exceptions +from keystoneclient.i18n import _, _LI, _LW from keystoneclient import utils osprofiler_web = importutils.try_import("osprofiler.web") @@ -40,10 +41,10 @@ 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 @@ -53,52 +54,58 @@ class Session(object): + """Maintains client communication state and common functionality. + + As much as possible the parameters to this class reflect and are passed + directly to the requests library. + + :param auth: An authentication plugin to authenticate the session with. + (optional, defaults to None) + :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` + :param requests.Session session: A requests session object that can be used + for issuing requests. (optional) + :param string original_ip: The original IP of the requesting user which + will be sent to identity service in a + 'Forwarded' header. (optional) + :param verify: The verification arguments to pass to requests. These are of + the same form as requests expects, so True or False to + verify (or not) against system certificates or a path to a + bundle or CA certs to check against or None for requests to + attempt to locate and use certificates. (optional, defaults + to True) + :param cert: A client certificate to pass to requests. These are of the + same form as requests expects. Either a single filename + containing both the certificate and key or a tuple containing + the path to the certificate then a path to the key. (optional) + :param float timeout: A timeout to pass to requests. This should be a + numerical value indicating some amount (or fraction) + of seconds or 0 for no timeout. (optional, defaults + to 0) + :param string user_agent: A User-Agent header string to use for the + request. If not provided a default is used. + (optional, defaults to 'python-keystoneclient') + :param int/bool redirect: Controls the maximum number of redirections that + can be followed by a request. Either an integer + for a specific count or True/False for + forever/never. (optional, default to 30) + """ user_agent = None - REDIRECT_STATUSES = (301, 302, 303, 305, 307) - DEFAULT_REDIRECT_LIMIT = 30 + _REDIRECT_STATUSES = (301, 302, 303, 305, 307) + + REDIRECT_STATUSES = _REDIRECT_STATUSES + """This property is deprecated.""" + + _DEFAULT_REDIRECT_LIMIT = 30 + + DEFAULT_REDIRECT_LIMIT = _DEFAULT_REDIRECT_LIMIT + """This property is deprecated.""" @utils.positional(2, enforcement=utils.positional.WARN) def __init__(self, auth=None, session=None, original_ip=None, verify=True, cert=None, timeout=None, user_agent=None, - redirect=DEFAULT_REDIRECT_LIMIT): - """Maintains client communication state and common functionality. - - As much as possible the parameters to this class reflect and are passed - directly to the requests library. - - :param auth: An authentication plugin to authenticate the session with. - (optional, defaults to None) - :param requests.Session session: A requests session object that can be - used for issuing requests. (optional) - :param string original_ip: The original IP of the requesting user - which will be sent to identity service in a - 'Forwarded' header. (optional) - :param verify: The verification arguments to pass to requests. These - are of the same form as requests expects, so True or - False to verify (or not) against system certificates or - a path to a bundle or CA certs to check against or None - for requests to attempt to locate and use certificates. - (optional, defaults to True) - :param cert: A client certificate to pass to requests. These are of the - same form as requests expects. Either a single filename - containing both the certificate and key or a tuple - containing the path to the certificate then a path to the - key. (optional) - :param float timeout: A timeout to pass to requests. This should be a - numerical value indicating some amount - (or fraction) of seconds or 0 for no timeout. - (optional, defaults to 0) - :param string user_agent: A User-Agent header string to use for the - request. If not provided a default is used. - (optional, defaults to - 'python-keystoneclient') - :param int/bool redirect: Controls the maximum number of redirections - that can be followed by a request. Either an - integer for a specific count or True/False - for forever/never. (optional, default to 30) - """ + redirect=_DEFAULT_REDIRECT_LIMIT): if not session: session = requests.Session() @@ -138,12 +145,14 @@ # 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. if self.verify is False: string_parts.append('--insecure') + elif isinstance(self.verify, six.string_types): + string_parts.append('--cacert "%s"' % self.verify) if method: string_parts.extend(['-X', method]) @@ -240,11 +249,11 @@ :param auth: The auth plugin to use when authenticating this request. This will override the plugin that is attached to the session (if any). (optional) - :type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin` + :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` :param requests_auth: A requests library auth plugin that cannot be passed via kwarg because the `auth` kwarg collides with our own auth plugins. (optional) - :type requests_auth: :class:`requests.auth.AuthBase` + :type requests_auth: :py:class:`requests.auth.AuthBase` :param bool raise_exc: If True then raise an appropriate exception for failed HTTP requests. If False then return the request object. (optional, default True) @@ -259,8 +268,8 @@ '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 +283,7 @@ 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 +381,20 @@ 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( @@ -397,7 +406,7 @@ if log: self._http_log_response(response=resp) - if resp.status_code in self.REDIRECT_STATUSES: + if resp.status_code in self._REDIRECT_STATUSES: # be careful here in python True == 1 and False == 0 if isinstance(redirect, bool): redirect_allowed = redirect @@ -411,8 +420,8 @@ 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. @@ -429,35 +438,67 @@ return resp def head(self, url, **kwargs): + """Perform a HEAD request. + + This calls :py:meth:`.request()` with ``method`` set to ``HEAD``. + + """ return self.request(url, 'HEAD', **kwargs) def get(self, url, **kwargs): + """Perform a GET request. + + This calls :py:meth:`.request()` with ``method`` set to ``GET``. + + """ return self.request(url, 'GET', **kwargs) def post(self, url, **kwargs): + """Perform a POST request. + + This calls :py:meth:`.request()` with ``method`` set to ``POST``. + + """ return self.request(url, 'POST', **kwargs) def put(self, url, **kwargs): + """Perform a PUT request. + + This calls :py:meth:`.request()` with ``method`` set to ``PUT``. + + """ return self.request(url, 'PUT', **kwargs) def delete(self, url, **kwargs): + """Perform a DELETE request. + + This calls :py:meth:`.request()` with ``method`` set to ``DELETE``. + + """ return self.request(url, 'DELETE', **kwargs) def patch(self, url, **kwargs): + """Perform a PATCH request. + + This calls :py:meth:`.request()` with ``method`` set to ``PATCH``. + + """ return self.request(url, 'PATCH', **kwargs) @classmethod def construct(cls, kwargs): - """Handles constructing a session from the older HTTPClient args as - well as the new request style arguments. - - *DEPRECATED*: This function is purely for bridging the gap between - older client arguments and the session arguments that they relate to. - It is not intended to be used as a generic Session Factory. + """Handles constructing a session from the older + :py:class:`~keystoneclient.httpclient.HTTPClient` args as well as the + new request-style arguments. + + .. warning:: + *DEPRECATED*: This function is purely for bridging the gap between + older client arguments and the session arguments that they relate + to. It is not intended to be used as a generic Session Factory. This function purposefully modifies the input kwargs dictionary so that the remaining kwargs dict can be reused and passed on to other - functionswithout session arguments. + functions without session arguments. """ params = {} @@ -498,59 +539,72 @@ :param auth: The auth plugin to use for token. Overrides the plugin on the session. (optional) - :type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin` + :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` - :raises AuthorizationFailure: if a new token fetch fails. + :raises keystoneclient.exceptions.AuthorizationFailure: if a new token + fetch fails. + :raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not + available. - :returns string: A valid token. + :returns: A valid token. + :rtype: string """ if not auth: 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. :param auth: The auth plugin to use for token. Overrides the plugin on the session. (optional) - :type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin` + :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` - :raises MissingAuthPlugin: if a plugin is not available. + :raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not + available. - :returns string: An endpoint if available or None. + :returns: An endpoint if available or None. + :rtype: string """ if not auth: 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) def invalidate(self, auth=None): """Invalidate an authentication plugin. + + :param auth: The auth plugin to invalidate. Overrides the plugin on the + session. (optional) + :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` + """ if not auth: 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() @utils.positional.classmethod() def get_conf_options(cls, deprecated_opts=None): - """Get the oslo.config options that are needed for a session. + """Get the oslo.config options that are needed for a + :py:class:`.Session`. These may be useful without being registered for config file generation or to manipulate the options before registering them yourself. @@ -563,12 +617,12 @@ :timeout: The max time to wait for HTTP connections. :param dict deprecated_opts: Deprecated options that should be included - in the definition of new options. This should be a dictionary from - the name of the new option to a list of oslo.DeprecatedOpts that + in the definition of new options. This should be a dict from the + name of the new option to a list of oslo.DeprecatedOpts that correspond to the new option. (optional) - Example to support the 'ca_file' option pointing to the new - 'cafile' option name:: + For example, to support the ``ca_file`` option pointing to the new + ``cafile`` option name:: old_opt = oslo.cfg.DeprecatedOpt('ca_file', 'old_group') deprecated_opts={'cafile': [old_opt]} @@ -611,12 +665,12 @@ :param oslo.config.Cfg conf: config object to register with. :param string group: The ini group to register options in. :param dict deprecated_opts: Deprecated options that should be included - in the definition of new options. This should be a dictionary from - the name of the new option to a list of oslo.DeprecatedOpts that + in the definition of new options. This should be a dict from the + name of the new option to a list of oslo.DeprecatedOpts that correspond to the new option. (optional) - Example to support the 'ca_file' option pointing to the new - 'cafile' option name:: + For example, to support the ``ca_file`` option pointing to the new + ``cafile`` option name:: old_opt = oslo.cfg.DeprecatedOpt('ca_file', 'old_group') deprecated_opts={'cafile': [old_opt]} @@ -640,6 +694,7 @@ :param dict kwargs: Additional parameters to pass to session construction. :returns: A new session object. + :rtype: :py:class:`.Session` """ c = conf[group] @@ -691,13 +746,15 @@ @classmethod def load_from_cli_options(cls, args, **kwargs): - """Create a session object from CLI arguments. + """Create a :py:class:`.Session` object from CLI arguments. - The CLI arguments must have been registered with register_cli_options. + The CLI arguments must have been registered with + :py:meth:`.register_cli_options`. :param Namespace args: result of parsed arguments. :returns: A new session object. + :rtype: :py:class:`.Session` """ kwargs['insecure'] = args.insecure kwargs['cacert'] = args.os_cacert diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_cli.py python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_cli.py --- python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_cli.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_cli.py 2014-12-18 17:37:35.000000000 +0000 @@ -13,6 +13,7 @@ import argparse import uuid +import fixtures import mock from oslo.config import cfg @@ -43,6 +44,13 @@ super(CliTests, self).setUp() self.p = argparse.ArgumentParser() + def env(self, name, value=None): + if value is not None: + # environment variables are always strings + value = str(value) + + return self.useFixture(fixtures.EnvironmentVariable(name, value)) + def test_creating_with_no_args(self): ret = cli.register_argparse_arguments(self.p, []) self.assertIsNone(ret) @@ -140,6 +148,18 @@ self.assertIs(utils.MockPlugin, klass) m.assert_called_once_with(name) + @utils.mock_plugin + def test_env_overrides_default_opt(self, m): + name = uuid.uuid4().hex + val = uuid.uuid4().hex + self.env('OS_A_STR', val) + + klass = cli.register_argparse_arguments(self.p, [], default=name) + opts = self.p.parse_args([]) + a = klass.load_from_argparse_arguments(opts) + + self.assertEqual(val, a['a_str']) + def test_deprecated_cli_options(self): TesterPlugin.register_argparse_arguments(self.p) val = uuid.uuid4().hex diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_password.py python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_password.py --- python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_password.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_password.py 2014-12-18 17:37:35.000000000 +0000 @@ -38,3 +38,26 @@ def test_v3_user_params_v2_url(self): self.stub_discovery(v3=False) self.assertDiscoveryFailure(user_domain_id=uuid.uuid4().hex) + + def test_options(self): + opts = [o.name for o in self.PLUGIN_CLASS.get_options()] + + allowed_opts = ['user-name', + 'user-domain-id', + 'user-domain-name', + 'user-id', + 'password', + + 'domain-id', + 'domain-name', + 'tenant-id', + 'tenant-name', + 'project-id', + 'project-name', + 'project-domain-id', + 'project-domain-name', + 'trust-id', + 'auth-url'] + + self.assertEqual(set(allowed_opts), set(opts)) + self.assertEqual(len(allowed_opts), len(opts)) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_token_endpoint.py python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_token_endpoint.py --- python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_token_endpoint.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_token_endpoint.py 2014-12-18 17:37:35.000000000 +0000 @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +from testtools import matchers + from keystoneclient.auth import token_endpoint from keystoneclient import session from keystoneclient.tests import utils @@ -43,3 +45,11 @@ self.assertEqual(self.TEST_URL, a.get_endpoint(s)) self.assertEqual('body', data.text) self.assertRequestHeaderEqual('X-Auth-Token', self.TEST_TOKEN) + + def test_token_endpoint_options(self): + opt_names = [opt.name for opt in token_endpoint.Token.get_options()] + + self.assertThat(opt_names, matchers.HasLength(2)) + + self.assertIn('token', opt_names) + self.assertIn('endpoint', opt_names) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_token.py python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_token.py --- python-keystoneclient-0.11.2/keystoneclient/tests/auth/test_token.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/auth/test_token.py 2014-12-18 17:37:35.000000000 +0000 @@ -27,3 +27,21 @@ def new_plugin(self, **kwargs): kwargs.setdefault('token', uuid.uuid4().hex) return super(TokenTests, self).new_plugin(**kwargs) + + def test_options(self): + opts = [o.name for o in self.PLUGIN_CLASS.get_options()] + + allowed_opts = ['token', + 'domain-id', + 'domain-name', + 'tenant-id', + 'tenant-name', + 'project-id', + 'project-name', + 'project-domain-id', + 'project-domain-name', + 'trust-id', + 'auth-url'] + + self.assertEqual(set(allowed_opts), set(opts)) + self.assertEqual(len(allowed_opts), len(opts)) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/auth/utils.py python-keystoneclient-1.0.0/keystoneclient/tests/auth/utils.py --- python-keystoneclient-0.11.2/keystoneclient/tests/auth/utils.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/auth/utils.py 2014-12-18 17:37:35.000000000 +0000 @@ -30,6 +30,8 @@ INT_DESC = 'test int' FLOAT_DESC = 'test float' BOOL_DESC = 'test bool' + STR_DESC = 'test str' + STR_DEFAULT = uuid.uuid4().hex def __init__(self, **kwargs): self._data = kwargs @@ -49,6 +51,7 @@ cfg.IntOpt('a-int', default='3', help=cls.INT_DESC), cfg.BoolOpt('a-bool', help=cls.BOOL_DESC), cfg.FloatOpt('a-float', help=cls.FLOAT_DESC), + cfg.StrOpt('a-str', help=cls.STR_DESC, default=cls.STR_DEFAULT), ] diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/test_auth_token_middleware.py python-keystoneclient-1.0.0/keystoneclient/tests/test_auth_token_middleware.py --- python-keystoneclient-0.11.2/keystoneclient/tests/test_auth_token_middleware.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/test_auth_token_middleware.py 2014-12-18 17:37:35.000000000 +0000 @@ -1007,7 +1007,7 @@ 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 diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/test_base.py python-keystoneclient-1.0.0/keystoneclient/tests/test_base.py --- python-keystoneclient-0.11.2/keystoneclient/tests/test_base.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/test_base.py 2014-12-18 17:37:35.000000000 +0000 @@ -40,8 +40,8 @@ auth_url='http://127.0.0.1:5000', endpoint='http://127.0.0.1:5000') - self.client.get = self.mox.CreateMockAnything() - self.client.get('/OS-KSADM/roles/1').AndRaise(AttributeError) + self.client._adapter.get = self.mox.CreateMockAnything() + self.client._adapter.get('/OS-KSADM/roles/1').AndRaise(AttributeError) self.mox.ReplayAll() f = roles.Role(self.client.roles, {'id': 1, 'name': 'Member'}) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/test_session.py python-keystoneclient-1.0.0/keystoneclient/tests/test_session.py --- python-keystoneclient-0.11.2/keystoneclient/tests/test_session.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/test_session.py 2014-12-18 17:37:35.000000000 +0000 @@ -172,6 +172,16 @@ self.assertEqual(v, resp.headers[k]) self.assertNotIn(v, self.logger.output) + def test_logging_cacerts(self): + path_to_certs = '/path/to/certs' + session = client_session.Session(verify=path_to_certs) + + self.stub_url('GET', text='text') + session.get(self.TEST_URL) + + self.assertIn('--cacert', self.logger.output) + self.assertIn(path_to_certs, self.logger.output) + def test_connect_retries(self): def _timeout_error(request, context): @@ -689,6 +699,8 @@ self.assertEqual(response, resp.text) self.assertEqual(endpoint_url, self.requests.last_request.url) + self.assertEqual(endpoint_override, adpt.get_endpoint()) + def test_adapter_invalidate(self): auth = CalledAuthPlugin() sess = client_session.Session() diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/v2_0/test_client.py python-keystoneclient-1.0.0/keystoneclient/tests/v2_0/test_client.py --- python-keystoneclient-0.11.2/keystoneclient/tests/v2_0/test_client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/v2_0/test_client.py 2014-12-18 17:37:35.000000000 +0000 @@ -11,9 +11,14 @@ # under the License. import json +import uuid +import six + +from keystoneclient.auth import token_endpoint from keystoneclient import exceptions from keystoneclient import fixture +from keystoneclient import session from keystoneclient.tests.v2_0 import client_fixtures from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import client @@ -151,3 +156,22 @@ client.Client, tenant_name='exampleproject', auth_url=self.TEST_URL) + + def test_client_params(self): + opts = {'auth': token_endpoint.Token('a', 'b'), + 'connect_retries': 50, + 'endpoint_override': uuid.uuid4().hex, + 'interface': uuid.uuid4().hex, + 'region_name': uuid.uuid4().hex, + 'service_name': uuid.uuid4().hex, + 'user_agent': uuid.uuid4().hex, + } + + sess = session.Session() + cl = client.Client(session=sess, **opts) + + for k, v in six.iteritems(opts): + self.assertEqual(v, getattr(cl._adapter, k)) + + self.assertEqual('identity', cl._adapter.service_type) + self.assertEqual('v2.0', cl._adapter.version) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/v3/test_client.py python-keystoneclient-1.0.0/keystoneclient/tests/v3/test_client.py --- python-keystoneclient-0.11.2/keystoneclient/tests/v3/test_client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/v3/test_client.py 2014-12-18 17:37:35.000000000 +0000 @@ -12,8 +12,13 @@ import copy import json +import uuid +import six + +from keystoneclient.auth import token_endpoint from keystoneclient import exceptions +from keystoneclient import session from keystoneclient.tests.v3 import client_fixtures from keystoneclient.tests.v3 import utils from keystoneclient.v3 import client @@ -33,6 +38,7 @@ 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()) @@ -195,3 +201,22 @@ client.Client, project_name='exampleproject', auth_url=self.TEST_URL) + + def test_client_params(self): + opts = {'auth': token_endpoint.Token('a', 'b'), + 'connect_retries': 50, + 'endpoint_override': uuid.uuid4().hex, + 'interface': uuid.uuid4().hex, + 'region_name': uuid.uuid4().hex, + 'service_name': uuid.uuid4().hex, + 'user_agent': uuid.uuid4().hex, + } + + sess = session.Session() + cl = client.Client(session=sess, **opts) + + for k, v in six.iteritems(opts): + self.assertEqual(v, getattr(cl._adapter, k)) + + self.assertEqual('identity', cl._adapter.service_type) + self.assertEqual('v3', cl._adapter.version) diff -Nru python-keystoneclient-0.11.2/keystoneclient/tests/v3/test_oauth1.py python-keystoneclient-1.0.0/keystoneclient/tests/v3/test_oauth1.py --- python-keystoneclient-0.11.2/keystoneclient/tests/v3/test_oauth1.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/tests/v3/test_oauth1.py 2014-12-18 17:37:35.000000000 +0000 @@ -182,7 +182,7 @@ self.assertEqual(request_secret, request_token.secret) # Assert that the project id is in the header - self.assertRequestHeaderEqual('requested_project_id', project_id) + self.assertRequestHeaderEqual('requested-project-id', project_id) req_headers = self.requests.last_request.headers oauth_client = oauth1.Client(consumer_key, diff -Nru python-keystoneclient-0.11.2/keystoneclient/v2_0/client.py python-keystoneclient-1.0.0/keystoneclient/v2_0/client.py --- python-keystoneclient-0.11.2/keystoneclient/v2_0/client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v2_0/client.py 2014-12-18 17:37:35.000000000 +0000 @@ -18,6 +18,7 @@ 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 @@ -129,17 +130,19 @@ def __init__(self, **kwargs): """Initialize a new client for the Keystone v2.0 API.""" super(Client, self).__init__(**kwargs) - self.endpoints = endpoints.EndpointManager(self) - self.extensions = extensions.ExtensionManager(self) - self.roles = roles.RoleManager(self) - self.services = services.ServiceManager(self) - self.tokens = tokens.TokenManager(self) - self.users = users.UserManager(self, self.roles) - self.tenants = tenants.TenantManager(self, self.roles, self.users) + self.endpoints = endpoints.EndpointManager(self._adapter) + self.extensions = extensions.ExtensionManager(self._adapter) + self.roles = roles.RoleManager(self._adapter) + self.services = services.ServiceManager(self._adapter) + self.tokens = tokens.TokenManager(self._adapter) + self.users = users.UserManager(self._adapter, self.roles) + + self.tenants = tenants.TenantManager(self._adapter, + self.roles, self.users) # extensions - self.ec2 = ec2.CredentialsManager(self) + self.ec2 = ec2.CredentialsManager(self._adapter) # DEPRECATED: if session is passed then we go to the new behaviour of # authenticating on the first required call. @@ -158,12 +161,12 @@ 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 +178,7 @@ 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 +186,9 @@ _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 -Nru python-keystoneclient-0.11.2/keystoneclient/v2_0/__init__.py python-keystoneclient-1.0.0/keystoneclient/v2_0/__init__.py --- python-keystoneclient-0.11.2/keystoneclient/v2_0/__init__.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v2_0/__init__.py 2014-12-18 17:37:35.000000000 +0000 @@ -1,5 +1,4 @@ -# flake8: noqa -from keystoneclient.v2_0.client import Client +from keystoneclient.v2_0.client import Client # noqa __all__ = [ diff -Nru python-keystoneclient-0.11.2/keystoneclient/v2_0/shell.py python-keystoneclient-1.0.0/keystoneclient/v2_0/shell.py --- python-keystoneclient-0.11.2/keystoneclient/v2_0/shell.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v2_0/shell.py 2014-12-18 17:37:35.000000000 +0000 @@ -29,6 +29,7 @@ from oslo.utils import strutils import six +from keystoneclient.i18n import _ from keystoneclient import utils from keystoneclient.v2_0 import client @@ -38,9 +39,9 @@ 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 @@ 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='', dest='passwd', required=False, @@ -141,8 +142,8 @@ 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 @@ 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='', help='Name or ID of user to delete.') @@ -234,7 +235,7 @@ 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 @@ 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='', default=None, @@ -463,7 +464,7 @@ 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 @@ 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 @@ """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='', default=0, diff -Nru python-keystoneclient-0.11.2/keystoneclient/v2_0/tokens.py python-keystoneclient-1.0.0/keystoneclient/v2_0/tokens.py --- python-keystoneclient-0.11.2/keystoneclient/v2_0/tokens.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v2_0/tokens.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v2_0/users.py python-keystoneclient-1.0.0/keystoneclient/v2_0/users.py --- python-keystoneclient-0.11.2/keystoneclient/v2_0/users.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v2_0/users.py 2014-12-18 17:37:35.000000000 +0000 @@ -78,7 +78,7 @@ return self._update("/OS-KSCRUD/users/%s" % self.api.user_id, params, response_key="access", method="PATCH", - management=False, + endpoint_filter={'interface': 'public'}, log=False) def update_tenant(self, user, tenant): diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/client.py python-keystoneclient-1.0.0/keystoneclient/v3/client.py --- python-keystoneclient-0.11.2/keystoneclient/v3/client.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/client.py 2014-12-18 17:37:35.000000000 +0000 @@ -20,6 +20,7 @@ from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import exceptions from keystoneclient import httpclient +from keystoneclient.i18n import _ from keystoneclient.v3.contrib import endpoint_filter from keystoneclient.v3.contrib import endpoint_policy from keystoneclient.v3.contrib import federation @@ -168,23 +169,26 @@ """Initialize a new client for the Keystone v3 API.""" super(Client, self).__init__(**kwargs) - self.credentials = credentials.CredentialManager(self) - self.endpoint_filter = endpoint_filter.EndpointFilterManager(self) - self.endpoint_policy = endpoint_policy.EndpointPolicyManager(self) - self.endpoints = endpoints.EndpointManager(self) - self.domains = domains.DomainManager(self) - self.federation = federation.FederationManager(self) - self.groups = groups.GroupManager(self) - self.oauth1 = oauth1.create_oauth_manager(self) - self.policies = policies.PolicyManager(self) - self.projects = projects.ProjectManager(self) - self.regions = regions.RegionManager(self) - self.role_assignments = role_assignments.RoleAssignmentManager(self) - self.roles = roles.RoleManager(self) - self.services = services.ServiceManager(self) - self.tokens = tokens.TokenManager(self) - self.trusts = trusts.TrustManager(self) - self.users = users.UserManager(self) + self.credentials = credentials.CredentialManager(self._adapter) + self.endpoint_filter = endpoint_filter.EndpointFilterManager( + self._adapter) + self.endpoint_policy = endpoint_policy.EndpointPolicyManager( + self._adapter) + self.endpoints = endpoints.EndpointManager(self._adapter) + self.domains = domains.DomainManager(self._adapter) + self.federation = federation.FederationManager(self._adapter) + self.groups = groups.GroupManager(self._adapter) + self.oauth1 = oauth1.create_oauth_manager(self._adapter) + self.policies = policies.PolicyManager(self._adapter) + self.projects = projects.ProjectManager(self._adapter) + self.regions = regions.RegionManager(self._adapter) + self.role_assignments = ( + role_assignments.RoleAssignmentManager(self._adapter)) + self.roles = roles.RoleManager(self._adapter) + self.services = services.ServiceManager(self._adapter) + self.tokens = tokens.TokenManager(self._adapter) + self.trusts = trusts.TrustManager(self._adapter) + self.users = users.UserManager(self._adapter) # DEPRECATED: if session is passed then we go to the new behaviour of # authenticating on the first required call. @@ -203,7 +207,7 @@ 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 @@ -228,14 +232,15 @@ 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 = [] @@ -251,7 +256,7 @@ 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, @@ -268,8 +273,9 @@ _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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/endpoint_filter.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/endpoint_filter.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/endpoint_filter.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/endpoint_filter.py 2014-12-18 17:37:35.000000000 +0000 @@ -14,6 +14,9 @@ from keystoneclient import base from keystoneclient import exceptions +from keystoneclient.i18n import _ +from keystoneclient.v3 import endpoints +from keystoneclient.v3 import projects class EndpointFilterManager(base.Manager): @@ -31,7 +34,7 @@ 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 +42,7 @@ 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 +51,7 @@ 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 +60,7 @@ 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,21 +69,21 @@ 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( base_url, - self.client.endpoints.collection_key, - obj_class=self.client.endpoints.resource_class) + endpoints.EndpointManager.collection_key, + obj_class=endpoints.EndpointManager.resource_class) 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( base_url, - self.client.projects.collection_key, - obj_class=self.client.projects.resource_class) + projects.ProjectManager.collection_key, + obj_class=projects.ProjectManager.resource_class) diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/endpoint_policy.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/endpoint_policy.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/endpoint_policy.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/endpoint_policy.py 2014-12-18 17:37:35.000000000 +0000 @@ -13,6 +13,8 @@ # under the License. from keystoneclient import base +from keystoneclient.i18n import _ +from keystoneclient.v3 import endpoints from keystoneclient.v3 import policies @@ -24,7 +26,7 @@ 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 +54,7 @@ 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 +83,7 @@ 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 +123,7 @@ """ 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 +143,7 @@ """ 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') % { @@ -149,5 +151,5 @@ 'ext_name': self.OS_EP_POLICY_EXT} return self._list( url, - self.client.endpoints.collection_key, - obj_class=self.client.endpoints.resource_class) + endpoints.EndpointManager.collection_key, + obj_class=endpoints.EndpointManager.resource_class) diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/federation/__init__.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/federation/__init__.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/federation/__init__.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/federation/__init__.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/access_tokens.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/access_tokens.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/access_tokens.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/access_tokens.py 2014-12-18 17:37:35.000000000 +0000 @@ -13,6 +13,7 @@ from __future__ import unicode_literals +from keystoneclient import auth from keystoneclient import base from keystoneclient.v3.contrib.oauth1 import utils @@ -39,8 +40,9 @@ resource_owner_secret=request_secret, signature_method=oauth1.SIGNATURE_HMAC, verifier=verifier) - url = self.client.auth_url.rstrip("/") + endpoint - url, headers, body = oauth_client.sign(url, http_method='POST') + url = self.api.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip('/') + url, headers, body = oauth_client.sign(url + endpoint, + http_method='POST') resp, body = self.client.post(endpoint, headers=headers) token = utils.get_oauth_token_from_body(resp.content) return self.resource_class(self, token) diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/auth.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/auth.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/auth.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/auth.py 2014-12-18 17:37:35.000000000 +0000 @@ -20,18 +20,18 @@ class OAuthMethod(v3.AuthMethod): + """OAuth based authentication method. + + :param string consumer_key: Consumer key. + :param string consumer_secret: Consumer secret. + :param string access_key: Access token key. + :param string access_secret: Access token secret. + """ _method_parameters = ['consumer_key', 'consumer_secret', 'access_key', 'access_secret'] def __init__(self, **kwargs): - """Construct an OAuth based authentication method. - - :param string consumer_key: Consumer key. - :param string consumer_secret: Consumer secret. - :param string access_key: Access token key. - :param string access_secret: Access token secret. - """ super(OAuthMethod, self).__init__(**kwargs) if oauth1 is None: raise NotImplementedError('optional package oauthlib' diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/core.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/core.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/core.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/core.py 2014-12-18 17:37:35.000000000 +0000 @@ -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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/request_tokens.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/request_tokens.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/oauth1/request_tokens.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/oauth1/request_tokens.py 2014-12-18 17:37:35.000000000 +0000 @@ -15,6 +15,7 @@ from six.moves.urllib import parse as urlparse +from keystoneclient import auth from keystoneclient import base from keystoneclient.v3.contrib.oauth1 import utils @@ -57,13 +58,14 @@ def create(self, consumer_key, consumer_secret, project): endpoint = utils.OAUTH_PATH + '/request_token' - headers = {'requested_project_id': base.getid(project)} + headers = {'requested-project-id': base.getid(project)} oauth_client = oauth1.Client(consumer_key, client_secret=consumer_secret, signature_method=oauth1.SIGNATURE_HMAC, callback_uri="oob") - url = self.client.auth_url.rstrip("/") + endpoint - url, headers, body = oauth_client.sign(url, http_method='POST', + url = self.api.get_endpoint(interface=auth.AUTH_INTERFACE).rstrip("/") + url, headers, body = oauth_client.sign(url + endpoint, + http_method='POST', headers=headers) resp, body = self.client.post(endpoint, headers=headers) token = utils.get_oauth_token_from_body(resp.content) diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/contrib/trusts.py python-keystoneclient-1.0.0/keystoneclient/v3/contrib/trusts.py --- python-keystoneclient-0.11.2/keystoneclient/v3/contrib/trusts.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/contrib/trusts.py 2014-12-18 17:37:35.000000000 +0000 @@ -14,6 +14,7 @@ from keystoneclient import base from keystoneclient import exceptions +from keystoneclient.i18n import _ class Trust(base.Resource): @@ -75,8 +76,8 @@ **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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/credentials.py python-keystoneclient-1.0.0/keystoneclient/v3/credentials.py --- python-keystoneclient-0.11.2/keystoneclient/v3/credentials.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/credentials.py 2014-12-18 17:37:35.000000000 +0000 @@ -15,6 +15,7 @@ # under the License. from keystoneclient import base +from keystoneclient.i18n import _ from keystoneclient import utils @@ -46,7 +47,7 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/endpoints.py python-keystoneclient-1.0.0/keystoneclient/v3/endpoints.py --- python-keystoneclient-0.11.2/keystoneclient/v3/endpoints.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/endpoints.py 2014-12-18 17:37:35.000000000 +0000 @@ -16,6 +16,7 @@ from keystoneclient import base from keystoneclient import exceptions +from keystoneclient.i18n import _ from keystoneclient import utils @@ -45,7 +46,7 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/__init__.py python-keystoneclient-1.0.0/keystoneclient/v3/__init__.py --- python-keystoneclient-0.11.2/keystoneclient/v3/__init__.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/__init__.py 2014-12-18 17:37:35.000000000 +0000 @@ -1,5 +1,5 @@ -# flake8: noqa -from keystoneclient.v3.client import Client + +from keystoneclient.v3.client import Client # noqa __all__ = [ diff -Nru python-keystoneclient-0.11.2/keystoneclient/v3/role_assignments.py python-keystoneclient-1.0.0/keystoneclient/v3/role_assignments.py --- python-keystoneclient-0.11.2/keystoneclient/v3/role_assignments.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/role_assignments.py 2014-12-18 17:37:35.000000000 +0000 @@ -12,6 +12,7 @@ from keystoneclient import base from keystoneclient import exceptions +from keystoneclient.i18n import _ class RoleAssignment(base.Resource): @@ -37,12 +38,12 @@ 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 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/roles.py python-keystoneclient-1.0.0/keystoneclient/v3/roles.py --- python-keystoneclient-0.11.2/keystoneclient/v3/roles.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/roles.py 2014-12-18 17:37:35.000000000 +0000 @@ -16,6 +16,7 @@ from keystoneclient import base from keystoneclient import exceptions +from keystoneclient.i18n import _ from keystoneclient import utils @@ -59,18 +60,18 @@ 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 -Nru python-keystoneclient-0.11.2/keystoneclient/v3/users.py python-keystoneclient-1.0.0/keystoneclient/v3/users.py --- python-keystoneclient-0.11.2/keystoneclient/v3/users.py 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/keystoneclient/v3/users.py 2014-12-18 17:37:35.000000000 +0000 @@ -18,6 +18,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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, @@ -157,8 +158,8 @@ base_url = '/users/%s/password' % self.api.user_id - return self._update(base_url, params, method='POST', management=False, - log=False) + return self._update(base_url, params, method='POST', log=False, + endpoint_filter={'interface': 'public'}) def add_to_group(self, user, group): self._require_user_and_group(user, group) diff -Nru python-keystoneclient-0.11.2/PKG-INFO python-keystoneclient-1.0.0/PKG-INFO --- python-keystoneclient-0.11.2/PKG-INFO 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/PKG-INFO 2014-12-18 17:39:20.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-keystoneclient -Version: 0.11.2 +Version: 1.0.0 Summary: Client Library for OpenStack Identity Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru python-keystoneclient-0.11.2/python_keystoneclient.egg-info/pbr.json python-keystoneclient-1.0.0/python_keystoneclient.egg-info/pbr.json --- python-keystoneclient-0.11.2/python_keystoneclient.egg-info/pbr.json 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-1.0.0/python_keystoneclient.egg-info/pbr.json 2014-12-18 17:39:19.000000000 +0000 @@ -0,0 +1 @@ +{"git_version": "10860db", "is_release": true} \ No newline at end of file diff -Nru python-keystoneclient-0.11.2/python_keystoneclient.egg-info/PKG-INFO python-keystoneclient-1.0.0/python_keystoneclient.egg-info/PKG-INFO --- python-keystoneclient-0.11.2/python_keystoneclient.egg-info/PKG-INFO 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/python_keystoneclient.egg-info/PKG-INFO 2014-12-18 17:39:19.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-keystoneclient -Version: 0.11.2 +Version: 1.0.0 Summary: Client Library for OpenStack Identity Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru python-keystoneclient-0.11.2/python_keystoneclient.egg-info/requires.txt python-keystoneclient-1.0.0/python_keystoneclient.egg-info/requires.txt --- python-keystoneclient-0.11.2/python_keystoneclient.egg-info/requires.txt 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/python_keystoneclient.egg-info/requires.txt 2014-12-18 17:39:19.000000000 +0000 @@ -4,9 +4,10 @@ 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 +oslo.utils>=1.1.0 # Apache-2.0 PrettyTable>=0.7,<0.8 requests>=2.2.0,!=2.4.0 six>=1.7.0 -stevedore>=1.0.0 # Apache-2.0 \ No newline at end of file +stevedore>=1.1.0 # Apache-2.0 \ No newline at end of file diff -Nru python-keystoneclient-0.11.2/python_keystoneclient.egg-info/SOURCES.txt python-keystoneclient-1.0.0/python_keystoneclient.egg-info/SOURCES.txt --- python-keystoneclient-0.11.2/python_keystoneclient.egg-info/SOURCES.txt 2014-10-23 17:44:07.000000000 +0000 +++ python-keystoneclient-1.0.0/python_keystoneclient.egg-info/SOURCES.txt 2014-12-18 17:39:19.000000000 +0000 @@ -23,7 +23,6 @@ doc/source/authentication-plugins.rst doc/source/conf.py doc/source/index.rst -doc/source/middlewarearchitecture.rst doc/source/using-api-v2.rst doc/source/using-api-v3.rst doc/source/using-sessions.rst @@ -72,6 +71,7 @@ keystoneclient/discover.py keystoneclient/exceptions.py keystoneclient/httpclient.py +keystoneclient/i18n.py keystoneclient/service_catalog.py keystoneclient/session.py keystoneclient/shell.py @@ -118,17 +118,16 @@ keystoneclient/middleware/s3_token.py keystoneclient/openstack/__init__.py keystoneclient/openstack/common/__init__.py -keystoneclient/openstack/common/gettextutils.py -keystoneclient/openstack/common/importutils.py +keystoneclient/openstack/common/_i18n.py keystoneclient/openstack/common/memorycache.py -keystoneclient/openstack/common/strutils.py -keystoneclient/openstack/common/timeutils.py +keystoneclient/openstack/common/uuidutils.py keystoneclient/openstack/common/apiclient/__init__.py keystoneclient/openstack/common/apiclient/auth.py keystoneclient/openstack/common/apiclient/base.py keystoneclient/openstack/common/apiclient/client.py keystoneclient/openstack/common/apiclient/exceptions.py keystoneclient/openstack/common/apiclient/fake_client.py +keystoneclient/openstack/common/apiclient/utils.py keystoneclient/tests/__init__.py keystoneclient/tests/client_fixtures.py keystoneclient/tests/test_auth_token_middleware.py @@ -256,9 +255,9 @@ python_keystoneclient.egg-info/dependency_links.txt python_keystoneclient.egg-info/entry_points.txt python_keystoneclient.egg-info/not-zip-safe +python_keystoneclient.egg-info/pbr.json python_keystoneclient.egg-info/requires.txt python_keystoneclient.egg-info/top_level.txt -tools/debug_helper.sh tools/install_venv.py tools/install_venv_common.py tools/keystone.bash_completion diff -Nru python-keystoneclient-0.11.2/requirements.txt python-keystoneclient-1.0.0/requirements.txt --- python-keystoneclient-0.11.2/requirements.txt 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/requirements.txt 2014-12-18 17:37:35.000000000 +0000 @@ -9,9 +9,10 @@ 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 +oslo.utils>=1.1.0 # Apache-2.0 PrettyTable>=0.7,<0.8 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 -Nru python-keystoneclient-0.11.2/test-requirements.txt python-keystoneclient-1.0.0/test-requirements.txt --- python-keystoneclient-0.11.2/test-requirements.txt 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/test-requirements.txt 2014-12-18 17:37:35.000000000 +0000 @@ -13,10 +13,11 @@ 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 +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,!=1.2.0 WebOb>=1.2.3 diff -Nru python-keystoneclient-0.11.2/tools/debug_helper.sh python-keystoneclient-1.0.0/tools/debug_helper.sh --- python-keystoneclient-0.11.2/tools/debug_helper.sh 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/tools/debug_helper.sh 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru python-keystoneclient-0.11.2/tox.ini python-keystoneclient-1.0.0/tox.ini --- python-keystoneclient-0.11.2/tox.ini 2014-10-23 17:42:06.000000000 +0000 +++ python-keystoneclient-1.0.0/tox.ini 2014-12-18 17:37:35.000000000 +0000 @@ -28,9 +28,7 @@ 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 @@ commands= python setup.py build_sphinx +[hacking] +import_exceptions = + keystoneclient.i18n