summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.rst17
-rw-r--r--MANIFEST.in1
-rwxr-xr-xbin/keystone-all29
-rwxr-xr-xbin/keystone-manage4
-rw-r--r--doc/source/apache-httpd.rst7
-rw-r--r--doc/source/configuration.rst44
-rw-r--r--doc/source/developing.rst32
-rw-r--r--doc/source/external-auth.rst2
-rw-r--r--doc/source/installing.rst1
-rw-r--r--doc/source/man/keystone-manage.rst1
-rw-r--r--etc/keystone-paste.ini85
-rw-r--r--etc/keystone.conf.sample89
-rw-r--r--[-rwxr-xr-x]httpd/keystone.py10
-rw-r--r--httpd/wsgi-keystone.conf (renamed from httpd/keystone.conf)0
-rw-r--r--keystone/auth/__init__.py3
-rw-r--r--keystone/auth/controllers.py24
-rw-r--r--keystone/auth/core.py2
-rw-r--r--keystone/auth/plugins/password.py9
-rw-r--r--keystone/auth/plugins/token.py3
-rw-r--r--keystone/auth/token_factory.py14
-rw-r--r--keystone/catalog/__init__.py1
-rw-r--r--keystone/catalog/backends/sql.py10
-rw-r--r--keystone/catalog/core.py2
-rw-r--r--keystone/clean.py12
-rw-r--r--keystone/cli.py23
-rw-r--r--keystone/common/bufferedhttp.py63
-rw-r--r--keystone/common/cms.py16
-rw-r--r--keystone/common/config.py16
-rw-r--r--keystone/common/controller.py9
-rw-r--r--keystone/common/ldap/__init__.py1
-rw-r--r--keystone/common/ldap/core.py53
-rw-r--r--keystone/common/ldap/fakeldap.py18
-rw-r--r--keystone/common/logging.py7
-rw-r--r--keystone/common/openssl.py7
-rw-r--r--keystone/common/sql/__init__.py1
-rw-r--r--keystone/common/sql/core.py9
-rw-r--r--keystone/common/sql/legacy.py2
-rw-r--r--keystone/common/sql/migrate_repo/versions/003_token_valid.py16
-rw-r--r--keystone/common/sql/migrate_repo/versions/008_create_default_domain.py1
-rw-r--r--keystone/common/sql/migrate_repo/versions/009_normalize_identity.py1
-rw-r--r--keystone/common/sql/migrate_repo/versions/015_tenant_to_project.py4
-rw-r--r--keystone/common/sql/migrate_repo/versions/016_normalize_domain_ids.py13
-rw-r--r--keystone/common/sql/migrate_repo/versions/017_membership_role.py17
-rw-r--r--keystone/common/sql/migrate_repo/versions/018_add_trust_tables.py7
-rw-r--r--keystone/common/sql/migrate_repo/versions/019_fixup_role.py5
-rw-r--r--keystone/common/sql/migrate_repo/versions/020_migrate_metadata_table_roles.py22
-rw-r--r--keystone/common/sql/migrate_repo/versions/021_add_trust_to_token.py4
-rw-r--r--keystone/common/sql/migrate_repo/versions/023_drop_credential_constraints.py77
-rw-r--r--keystone/common/sql/migrate_repo/versions/024_add_index_to_expires.py17
-rw-r--r--keystone/common/sql/nova.py19
-rw-r--r--keystone/common/utils.py21
-rw-r--r--keystone/common/wsgi.py145
-rw-r--r--keystone/common/wsgi_server.py120
-rw-r--r--keystone/config.py31
-rw-r--r--keystone/contrib/access/__init__.py1
-rw-r--r--keystone/contrib/admin_crud/__init__.py1
-rw-r--r--keystone/contrib/ec2/__init__.py1
-rw-r--r--keystone/contrib/s3/__init__.py1
-rw-r--r--keystone/contrib/s3/core.py6
-rw-r--r--keystone/contrib/stats/__init__.py1
-rw-r--r--keystone/contrib/user_crud/__init__.py1
-rw-r--r--keystone/contrib/user_crud/core.py6
-rw-r--r--keystone/controllers.py3
-rw-r--r--keystone/credential/__init__.py20
-rw-r--r--keystone/credential/backends/__init__.py0
-rw-r--r--keystone/credential/backends/sql.py88
-rw-r--r--keystone/credential/controllers.py52
-rw-r--r--keystone/credential/core.py87
-rw-r--r--keystone/credential/routers.py26
-rw-r--r--keystone/exception.py64
-rw-r--r--keystone/identity/__init__.py1
-rw-r--r--keystone/identity/backends/kvs.py3
-rw-r--r--keystone/identity/backends/ldap/__init__.py1
-rw-r--r--keystone/identity/backends/ldap/core.py213
-rw-r--r--keystone/identity/backends/sql.py273
-rw-r--r--keystone/identity/controllers.py59
-rw-r--r--keystone/identity/core.py44
-rw-r--r--keystone/identity/routers.py4
-rw-r--r--keystone/locale/bg_BG/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/ca/LC_MESSAGES/keystone.po551
-rw-r--r--keystone/locale/cs/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/da/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/de/LC_MESSAGES/keystone.po576
-rw-r--r--keystone/locale/es/LC_MESSAGES/keystone.po583
-rw-r--r--keystone/locale/fi_FI/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/fr/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/hu/LC_MESSAGES/keystone.po445
-rw-r--r--keystone/locale/it/LC_MESSAGES/keystone.po572
-rw-r--r--keystone/locale/ja/LC_MESSAGES/keystone.po551
-rw-r--r--keystone/locale/ka_GE/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/keystone.pot146
-rw-r--r--keystone/locale/ko_KR/LC_MESSAGES/keystone.po172
-rw-r--r--keystone/locale/pt_BR/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/ru/LC_MESSAGES/keystone.po559
-rw-r--r--keystone/locale/vi_VN/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/zh_CN/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/locale/zh_TW/LC_MESSAGES/keystone.po558
-rw-r--r--keystone/middleware/__init__.py1
-rw-r--r--keystone/middleware/ec2_token.py4
-rw-r--r--keystone/openstack/common/gettextutils.py25
-rw-r--r--keystone/openstack/common/setup.py367
-rw-r--r--keystone/openstack/common/version.py94
-rw-r--r--keystone/policy/__init__.py1
-rw-r--r--keystone/policy/backends/rules.py6
-rw-r--r--keystone/policy/backends/sql.py6
-rw-r--r--keystone/service.py8
-rw-r--r--keystone/test.py55
-rw-r--r--keystone/token/__init__.py1
-rw-r--r--keystone/token/backends/kvs.py8
-rw-r--r--keystone/token/backends/sql.py22
-rw-r--r--keystone/token/controllers.py31
-rw-r--r--keystone/token/core.py5
-rw-r--r--keystone/trust/__init__.py3
-rw-r--r--keystone/trust/backends/kvs.py3
-rw-r--r--keystone/trust/backends/sql.py6
-rw-r--r--keystone/trust/controllers.py18
-rw-r--r--openstack-common.conf2
-rw-r--r--setup.cfg31
-rw-r--r--setup.py58
-rw-r--r--tests/backend_pam.conf1
-rw-r--r--tests/test_auth.py50
-rw-r--r--tests/test_backend.py206
-rw-r--r--tests/test_backend_kvs.py1
-rw-r--r--tests/test_backend_ldap.py97
-rw-r--r--tests/test_backend_memcache.py5
-rw-r--r--tests/test_backend_sql.py16
-rw-r--r--tests/test_catalog.py14
-rw-r--r--tests/test_config.py18
-rw-r--r--tests/test_content_types.py395
-rw-r--r--tests/test_contrib_s3_core.py4
-rw-r--r--tests/test_exception.py9
-rw-r--r--tests/test_ipv6.py4
-rw-r--r--tests/test_keystoneclient.py35
-rw-r--r--tests/test_keystoneclient_sql.py8
-rw-r--r--tests/test_policy.py2
-rw-r--r--tests/test_setup.py56
-rw-r--r--tests/test_singular_plural.py2
-rw-r--r--tests/test_sql_upgrade.py17
-rw-r--r--tests/test_ssl.py22
-rw-r--r--tests/test_v3.py34
-rw-r--r--tests/test_v3_auth.py190
-rw-r--r--tests/test_v3_catalog.py32
-rw-r--r--tests/test_v3_credential.py78
-rw-r--r--tests/test_v3_identity.py184
-rw-r--r--tests/test_v3_policy.py14
-rw-r--r--tests/test_v3_protection.py70
-rw-r--r--tests/test_wsgi.py33
-rw-r--r--tools/flakes.py24
-rw-r--r--tools/pip-requires2
-rwxr-xr-xtools/sample_data.sh21
-rw-r--r--tools/test-requires8
-rw-r--r--tox.ini16
152 files changed, 11895 insertions, 2366 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 00000000..9e6f3752
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,17 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps in the "If you're a developer, start here"
+section of this page:
+
+ http://wiki.openstack.org/HowToContribute
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+ http://wiki.openstack.org/GerritWorkflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+ https://bugs.launchpad.net/keystone
diff --git a/MANIFEST.in b/MANIFEST.in
index 2d867f43..93b762f7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,7 @@
include AUTHORS
include babel.cfg
include ChangeLog
+include CONTRIBUTING.txt
include LICENSE
include HACKING.rst
include README.rst
diff --git a/bin/keystone-all b/bin/keystone-all
index 2fdc8c7a..7e6711b5 100755
--- a/bin/keystone-all
+++ b/bin/keystone-all
@@ -2,7 +2,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import greenlet
-import eventlet
import logging
import os
import signal
@@ -20,12 +19,13 @@ if os.path.exists(os.path.join(possible_topdir,
from paste import deploy
+import pbr.version
-from keystone import config
-from keystone.common import wsgi
from keystone.common import utils
+from keystone.common import wsgi_server
+from keystone import config
+from keystone.openstack.common import gettextutils
from keystone.openstack.common import importutils
-from keystone.openstack.common import version
CONF = config.CONF
@@ -33,7 +33,7 @@ CONF = config.CONF
def create_server(conf, name, host, port):
app = deploy.loadapp('config:%s' % conf, name=name)
- server = wsgi.Server(app, host=host, port=port)
+ server = wsgi_server.Server(app, host=host, port=port)
if CONF.ssl.enable:
server.set_ssl(CONF.ssl.certfile, CONF.ssl.keyfile,
CONF.ssl.ca_certs, CONF.ssl.cert_required)
@@ -71,6 +71,8 @@ def serve(*servers):
if __name__ == '__main__':
+ gettextutils.install('keystone')
+
dev_conf = os.path.join(possible_topdir,
'etc',
'keystone.conf')
@@ -79,7 +81,7 @@ if __name__ == '__main__':
config_files = [dev_conf]
CONF(project='keystone',
- version=version.VersionInfo('keystone').version_string(),
+ version=pbr.version.VersionInfo('keystone').version_string(),
default_config_files=config_files)
config.setup_logging(CONF)
@@ -88,15 +90,7 @@ if __name__ == '__main__':
if CONF.debug:
CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
- if CONF.config_file:
- paste_config = CONF.config_file[0]
- else:
- paste_config = CONF.find_file('keystone.conf')
- if not paste_config:
- print ("The keystone.conf file could not be found in the "
- "configuration directories.")
- CONF.print_help()
- sys.exit(1)
+ paste_config = config.find_paste_config()
monkeypatch_thread = not CONF.standard_threads
pydev_debug_url = utils.setup_remote_pydev_debug()
@@ -106,10 +100,7 @@ if __name__ == '__main__':
# http://lists.openstack.org/pipermail/openstack-dev/2012-August/
# 000794.html
monkeypatch_thread = False
- eventlet.patcher.monkey_patch(all=False, socket=True, time=True,
- thread=monkeypatch_thread)
-
- options = deploy.appconfig('config:%s' % paste_config)
+ wsgi_server.monkey_patch_eventlet(monkeypatch_thread=monkeypatch_thread)
servers = []
servers.append(create_server(paste_config,
diff --git a/bin/keystone-manage b/bin/keystone-manage
index b440ad15..89ada5bd 100755
--- a/bin/keystone-manage
+++ b/bin/keystone-manage
@@ -13,11 +13,13 @@ if os.path.exists(os.path.join(possible_topdir,
'__init__.py')):
sys.path.insert(0, possible_topdir)
-
from keystone import cli
+from keystone.openstack.common import gettextutils
if __name__ == '__main__':
+ gettextutils.install('keystone')
+
dev_conf = os.path.join(possible_topdir,
'etc',
'keystone.conf')
diff --git a/doc/source/apache-httpd.rst b/doc/source/apache-httpd.rst
index 47b3b62a..41437780 100644
--- a/doc/source/apache-httpd.rst
+++ b/doc/source/apache-httpd.rst
@@ -63,14 +63,17 @@ it goes right before::
Files
-----
-Copy the file keystone.conf to the appropriate location for your apache server, most likely::
+Copy the file httpd/wsgi-keystone.conf to the appropriate location for your apache server, most likely::
- /etc/httpd/conf.d/keystone.conf
+ /etc/httpd/conf.d/wsgi-keystone.conf
Create the directory ``/var/www/cgi-bin/keystone/``. You can either hardlink or softlink the files ``main`` and ``admin`` to the file ``keystone.py`` in this directory. For a distribution appropriate place, it should probably be copied to::
/usr/share/openstack/keystone/httpd/keystone.py
+Keystone's primary configuration file (``etc/keystone.conf``) and the PasteDeploy
+configuration file (``etc/keystone-paste.ini``) must be readable to HTTPD in
+one of the default locations described in :doc:`configuration`.
SELinux
-------
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 4b09f2c4..661723da 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -25,8 +25,8 @@ Configuring Keystone
man/keystone-all
Once Keystone is installed, it is configured via a primary configuration file
-(``etc/keystone.conf``), possibly a separate logging configuration file, and
-initializing data into keystone using the command line client.
+(``etc/keystone.conf``), a PasteDeploy configuration file (``etc/keystone-paste.ini``),
+possibly a separate logging configuration file, and initializing data into Keystone using the command line client.
Starting and Stopping Keystone
==============================
@@ -37,7 +37,7 @@ Start Keystone services using the command::
Invoking this command starts up two ``wsgi.Server`` instances, ``admin`` (the
administration API) and ``main`` (the primary/public API interface). Both
-services are configured by ``keystone.conf`` as run in a single process.
+services are configured to run in a single process.
Stop the process using ``Control-C``.
@@ -60,10 +60,13 @@ match if key expiry is to behave as expected.
Configuration Files
===================
-The keystone configuration file is an ``ini`` file based on Paste_, a
-common system used to configure python WSGI based applications. In addition to
-the paste configuration entries, general and driver-specific configuration
-values are organized into the following sections:
+The Keystone configuration files are an ``ini`` file format based on Paste_, a
+common system used to configure Python WSGI based applications.
+The PasteDeploy configuration entries (WSGI pipeline definitions)
+can be provided in a separate ``keystone-paste.ini`` file, while general and
+driver-specific configuration parameters are in the primary configuration file
+``keystone.conf``. The primary configuration file is organized into the
+following sections:
* ``[DEFAULT]`` - general configuration
* ``[sql]`` - optional storage backend configuration
@@ -76,11 +79,12 @@ values are organized into the following sections:
* ``[signing]`` - cryptographic signatures for PKI based tokens
* ``[ssl]`` - SSL configuration
* ``[auth]`` - Authentication plugin configuration
+* ``[paste_deploy]`` - Pointer to the PasteDeploy configuration file
-The Keystone configuration file is expected to be named ``keystone.conf``.
-When starting keystone, you can specify a different configuration file to
+The Keystone primary configuration file is expected to be named ``keystone.conf``.
+When starting Keystone, you can specify a different configuration file to
use with ``--config-file``. If you do **not** specify a configuration file,
-keystone will look in the following directories for a configuration file, in
+Keystone will look in the following directories for a configuration file, in
order:
* ``~/.keystone/``
@@ -88,6 +92,8 @@ order:
* ``/etc/keystone/``
* ``/etc/``
+PasteDeploy configuration file is specified by the ``config_file`` parameter in ``[paste_deploy]`` section of the primary configuration file. If the parameter
+is not an absolute path, then Keystone looks for it in the same directories as above. If not specified, WSGI pipeline definitions are loaded from the primary configuration file.
Authentication Plugins
----------------------
@@ -440,7 +446,7 @@ pipeline. This user crud filter allows users to use a HTTP PATCH to change
their own password. To enable this extension you should define a
user_crud_extension filter, insert it after the ``*_body`` middleware
and before the ``public_service`` app in the public_api WSGI pipeline in
-keystone.conf e.g.::
+``keystone-paste.ini`` e.g.::
[filter:user_crud_extension]
paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory
@@ -463,7 +469,8 @@ Sample Configuration Files
The ``etc/`` folder distributed with Keystone contains example configuration
files for each Server application.
-* ``etc/keystone.conf``
+* ``etc/keystone.conf.sample``
+* ``etc/keystone-paste.ini``
* ``etc/logging.conf.sample``
* ``etc/default_catalog.templates``
@@ -975,6 +982,19 @@ example::
$ keystone service-delete 08741d8ed88242ca88d1f61484a0fe3b
+
+Removing Expired Tokens
+===========================================================
+
+In the SQL and KVS token stores expired tokens are not automatically
+removed. These tokens can be removed with::
+
+ $ keystone-manage token_flush
+
+The memcache backend automatically discards expired tokens and so flushing
+is unnecessary and if attempted will fail with a NotImplemented error.
+
+
Configuring the LDAP Identity Provider
===========================================================
diff --git a/doc/source/developing.rst b/doc/source/developing.rst
index f04f5e61..e5751981 100644
--- a/doc/source/developing.rst
+++ b/doc/source/developing.rst
@@ -43,6 +43,18 @@ virtualenv. If you chose not to, simply exclude "tools/with_venv.sh" from the
example commands below.
+Configuring Keystone
+--------------------
+
+keystone requires a configuration file. There is a sample configuration file
+that can be used to get started::
+
+ $ cp etc/keystone.conf.sample etc/keystone.conf
+
+The defaults are enough to get you going, but you can make any changes if
+needed.
+
+
Running Keystone
----------------
@@ -60,8 +72,7 @@ Interacting with Keystone
-------------------------
You can interact with Keystone through the command line using
-keystonemanage_ which allows you to establish tenants, users, etc.
-
+keystonemanage_ which allows you to initialize keystone, etc.
You can also interact with Keystone through its REST API. There is a python
keystone client library `python-keystoneclient`_ which interacts exclusively
@@ -76,6 +87,23 @@ place::
.. _`python-keystoneclient`: https://github.com/openstack/python-keystoneclient
+Initial Sample Data
+-------------------
+
+There is an included script which is helpful in setting up some initial sample
+data for use with keystone::
+
+ $ SERVICE_TOKEN=ADMIN tools/with_venv.sh tools/sample_data.sh
+
+Notice it requires a service token read from an environment variable for
+authentication. The default value "ADMIN" is from the ``admin_token``
+option in the ``[DEFAULT]`` section in ``etc/keystone.conf``.
+
+Once run, you can see the sample data that has been created by using the
+`python-keystoneclient`_ command-line interface::
+
+ $ tools/with_venv.sh keystone --token ADMIN --endpoint http://127.0.0.1:35357/v2.0/ user-list
+
Running Tests
=============
diff --git a/doc/source/external-auth.rst b/doc/source/external-auth.rst
index b7767416..2262f631 100644
--- a/doc/source/external-auth.rst
+++ b/doc/source/external-auth.rst
@@ -96,7 +96,7 @@ Pipeline configuration
Once you have your WSGI middleware component developed you have to add it to
your pipeline. The first step is to add the middleware to your configuration file.
Assuming that your middleware module is ``keystone.middleware.MyMiddlewareAuth``,
-you can configure it in your ``keystone.conf`` as::
+you can configure it in your ``keystone-paste.ini`` as::
[filter:my_auth]
paste.filter_factory = keystone.middleware.MyMiddlewareAuth.factory
diff --git a/doc/source/installing.rst b/doc/source/installing.rst
index eeb4158b..f5380f24 100644
--- a/doc/source/installing.rst
+++ b/doc/source/installing.rst
@@ -62,6 +62,7 @@ commandline path:
You will find sample configuration files in ``etc/``
* keystone.conf
+* keystone-paste.ini
* logging.conf
* policy.json
* default_catalog.templates
diff --git a/doc/source/man/keystone-manage.rst b/doc/source/man/keystone-manage.rst
index b7c2131c..84a3ec9f 100644
--- a/doc/source/man/keystone-manage.rst
+++ b/doc/source/man/keystone-manage.rst
@@ -49,6 +49,7 @@ Available commands:
* ``import_nova_auth``: Import a dump of nova auth data into keystone.
* ``pki_setup``: Initialize the certificates used to sign tokens.
* ``ssl_setup``: Generate certificates for SSL.
+* ``token_flush``: Purge expired tokens.
OPTIONS
diff --git a/etc/keystone-paste.ini b/etc/keystone-paste.ini
new file mode 100644
index 00000000..0f4590a2
--- /dev/null
+++ b/etc/keystone-paste.ini
@@ -0,0 +1,85 @@
+# Keystone PasteDeploy configuration file.
+
+[filter:debug]
+paste.filter_factory = keystone.common.wsgi:Debug.factory
+
+[filter:token_auth]
+paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory
+
+[filter:admin_token_auth]
+paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory
+
+[filter:xml_body]
+paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory
+
+[filter:json_body]
+paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory
+
+[filter:user_crud_extension]
+paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory
+
+[filter:crud_extension]
+paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory
+
+[filter:ec2_extension]
+paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory
+
+[filter:s3_extension]
+paste.filter_factory = keystone.contrib.s3:S3Extension.factory
+
+[filter:url_normalize]
+paste.filter_factory = keystone.middleware:NormalizingFilter.factory
+
+[filter:sizelimit]
+paste.filter_factory = keystone.middleware:RequestBodySizeLimiter.factory
+
+[filter:stats_monitoring]
+paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory
+
+[filter:stats_reporting]
+paste.filter_factory = keystone.contrib.stats:StatsExtension.factory
+
+[filter:access_log]
+paste.filter_factory = keystone.contrib.access:AccessLogMiddleware.factory
+
+[app:public_service]
+paste.app_factory = keystone.service:public_app_factory
+
+[app:service_v3]
+paste.app_factory = keystone.service:v3_app_factory
+
+[app:admin_service]
+paste.app_factory = keystone.service:admin_app_factory
+
+[pipeline:public_api]
+pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension user_crud_extension public_service
+
+[pipeline:admin_api]
+pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension s3_extension crud_extension admin_service
+
+[pipeline:api_v3]
+pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension s3_extension service_v3
+
+[app:public_version_service]
+paste.app_factory = keystone.service:public_version_app_factory
+
+[app:admin_version_service]
+paste.app_factory = keystone.service:admin_version_app_factory
+
+[pipeline:public_version_api]
+pipeline = access_log sizelimit url_normalize xml_body public_version_service
+
+[pipeline:admin_version_api]
+pipeline = access_log sizelimit url_normalize xml_body admin_version_service
+
+[composite:main]
+use = egg:Paste#urlmap
+/v2.0 = public_api
+/v3 = api_v3
+/ = public_version_api
+
+[composite:admin]
+use = egg:Paste#urlmap
+/v2.0 = admin_api
+/v3 = api_v3
+/ = admin_version_api
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample
index 5344cdf3..cd2bda81 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -91,6 +91,9 @@
# exist to order to maintain support for your v2 clients.
# default_domain_id = default
+[credential]
+# driver = keystone.credential.backends.sql.Credential
+
[trust]
# driver = keystone.trust.backends.sql.Trust
@@ -243,86 +246,6 @@ methods = password,token
password = keystone.auth.plugins.password.Password
token = keystone.auth.plugins.token.Token
-[filter:debug]
-paste.filter_factory = keystone.common.wsgi:Debug.factory
-
-[filter:token_auth]
-paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory
-
-[filter:admin_token_auth]
-paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory
-
-[filter:xml_body]
-paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory
-
-[filter:json_body]
-paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory
-
-[filter:user_crud_extension]
-paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory
-
-[filter:crud_extension]
-paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory
-
-[filter:ec2_extension]
-paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory
-
-[filter:s3_extension]
-paste.filter_factory = keystone.contrib.s3:S3Extension.factory
-
-[filter:url_normalize]
-paste.filter_factory = keystone.middleware:NormalizingFilter.factory
-
-[filter:sizelimit]
-paste.filter_factory = keystone.middleware:RequestBodySizeLimiter.factory
-
-[filter:stats_monitoring]
-paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory
-
-[filter:stats_reporting]
-paste.filter_factory = keystone.contrib.stats:StatsExtension.factory
-
-[filter:access_log]
-paste.filter_factory = keystone.contrib.access:AccessLogMiddleware.factory
-
-[app:public_service]
-paste.app_factory = keystone.service:public_app_factory
-
-[app:service_v3]
-paste.app_factory = keystone.service:v3_app_factory
-
-[app:admin_service]
-paste.app_factory = keystone.service:admin_app_factory
-
-[pipeline:public_api]
-pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension user_crud_extension public_service
-
-[pipeline:admin_api]
-pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension s3_extension crud_extension admin_service
-
-[pipeline:api_v3]
-pipeline = access_log sizelimit url_normalize token_auth admin_token_auth xml_body json_body ec2_extension s3_extension service_v3
-
-[app:public_version_service]
-paste.app_factory = keystone.service:public_version_app_factory
-
-[app:admin_version_service]
-paste.app_factory = keystone.service:admin_version_app_factory
-
-[pipeline:public_version_api]
-pipeline = access_log sizelimit url_normalize xml_body public_version_service
-
-[pipeline:admin_version_api]
-pipeline = access_log sizelimit url_normalize xml_body admin_version_service
-
-[composite:main]
-use = egg:Paste#urlmap
-/v2.0 = public_api
-/v3 = api_v3
-/ = public_version_api
-
-[composite:admin]
-use = egg:Paste#urlmap
-/v2.0 = admin_api
-/v3 = api_v3
-/ = admin_version_api
+[paste_deploy]
+# Name of the paste configuration file that defines the available pipelines
+config_file = keystone-paste.ini
diff --git a/httpd/keystone.py b/httpd/keystone.py
index 7935f56f..8b1ab740 100755..100644
--- a/httpd/keystone.py
+++ b/httpd/keystone.py
@@ -2,20 +2,16 @@ import os
from paste import deploy
-from keystone import config
from keystone.common import logging
+from keystone import config
LOG = logging.getLogger(__name__)
CONF = config.CONF
-config_files = ['/etc/keystone/keystone.conf']
-CONF(project='keystone', default_config_files=config_files)
+CONF(project='keystone')
-conf = CONF.config_file[0]
name = os.path.basename(__file__)
if CONF.debug:
CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
-options = deploy.appconfig('config:%s' % CONF.config_file[0])
-
-application = deploy.loadapp('config:%s' % conf, name=name)
+deploy.loadapp('config:%s' % config.find_paste_config(), name=name)
diff --git a/httpd/keystone.conf b/httpd/wsgi-keystone.conf
index d542a878..d542a878 100644
--- a/httpd/keystone.conf
+++ b/httpd/wsgi-keystone.conf
diff --git a/keystone/auth/__init__.py b/keystone/auth/__init__.py
index 614f3634..570e0384 100644
--- a/keystone/auth/__init__.py
+++ b/keystone/auth/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2013 OpenStack LLC
#
@@ -14,6 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystone.auth.core import AuthMethodHandler
from keystone.auth import controllers
+from keystone.auth.core import *
from keystone.auth import routers
diff --git a/keystone/auth/controllers.py b/keystone/auth/controllers.py
index 0cb070bc..938b9cf6 100644
--- a/keystone/auth/controllers.py
+++ b/keystone/auth/controllers.py
@@ -17,15 +17,15 @@
import json
from keystone.auth import token_factory
-from keystone.common import controller
from keystone.common import cms
+from keystone.common import controller
from keystone.common import logging
from keystone import config
from keystone import exception
from keystone import identity
+from keystone.openstack.common import importutils
from keystone import token
from keystone import trust
-from keystone.openstack.common import importutils
LOG = logging.getLogger(__name__)
@@ -51,7 +51,7 @@ def get_auth_method(method_name):
class AuthInfo(object):
- """ Encapsulation of "auth" request. """
+ """Encapsulation of "auth" request."""
def __init__(self, context, auth=None):
self.identity_api = identity.Manager()
@@ -166,7 +166,7 @@ class AuthInfo(object):
return user_ref
def _validate_and_normalize_scope_data(self):
- """ Validate and normalize scope data """
+ """Validate and normalize scope data."""
if 'scope' not in self.auth:
return
if sum(['project' in self.auth['scope'],
@@ -187,7 +187,7 @@ class AuthInfo(object):
raise exception.Forbidden('Trusts are disabled.')
trust_ref = self._lookup_trust(
self.auth['scope']['OS-TRUST:trust'])
- #TODO ayoung when trusts support domain, Fill in domain data here
+ # TODO(ayoung): when trusts support domains, fill in domain data
if 'project_id' in trust_ref:
project_ref = self._lookup_project(
{'id': trust_ref['project_id']})
@@ -213,7 +213,7 @@ class AuthInfo(object):
raise exception.AuthMethodNotSupported()
def _validate_and_normalize_auth_data(self):
- """ Make sure "auth" is valid. """
+ """Make sure "auth" is valid."""
# make sure "auth" exist
if not self.auth:
raise exception.ValidationError(attribute='auth',
@@ -223,7 +223,7 @@ class AuthInfo(object):
self._validate_and_normalize_scope_data()
def get_method_names(self):
- """ Returns the identity method names.
+ """Returns the identity method names.
:returns: list of auth method names
@@ -231,7 +231,7 @@ class AuthInfo(object):
return self.auth['identity']['methods']
def get_method_data(self, method):
- """ Get the auth method payload.
+ """Get the auth method payload.
:returns: auth method payload
@@ -242,7 +242,7 @@ class AuthInfo(object):
return self.auth['identity'][method]
def get_scope(self):
- """ Get scope information.
+ """Get scope information.
Verify and return the scoping information.
@@ -260,7 +260,7 @@ class AuthInfo(object):
return self._scope_data
def set_scope(self, domain_id=None, project_id=None, trust=None):
- """ Set scope information. """
+ """Set scope information."""
if domain_id and project_id:
msg = _('Scoping to both domain and project is not allowed')
raise ValueError(msg)
@@ -280,7 +280,7 @@ class Auth(controller.V3Controller):
config.setup_authentication()
def authenticate_for_token(self, context, auth=None):
- """ Authenticate user and issue a token. """
+ """Authenticate user and issue a token."""
try:
auth_info = AuthInfo(context, auth=auth)
auth_context = {'extras': {}, 'method_names': []}
@@ -334,7 +334,7 @@ class Auth(controller.V3Controller):
raise exception.Unauthorized(msg)
def authenticate(self, context, auth_info, auth_context):
- """ Authenticate user. """
+ """Authenticate user."""
# user have been authenticated externally
if 'REMOTE_USER' in context:
diff --git a/keystone/auth/core.py b/keystone/auth/core.py
index da70c43c..b7bdb7c6 100644
--- a/keystone/auth/core.py
+++ b/keystone/auth/core.py
@@ -14,8 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystone import exception
from keystone.common import dependency
+from keystone import exception
@dependency.requires('identity_api')
diff --git a/keystone/auth/plugins/password.py b/keystone/auth/plugins/password.py
index 9d924059..68d9623a 100644
--- a/keystone/auth/plugins/password.py
+++ b/keystone/auth/plugins/password.py
@@ -14,9 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-from keystone.common import logging
from keystone import auth
+from keystone.common import logging
from keystone import exception
from keystone import identity
@@ -104,12 +103,12 @@ class UserAuthInfo(object):
class Password(auth.AuthMethodHandler):
def authenticate(self, context, auth_payload, user_context):
- """ Try to authenticate against the identity backend. """
+ """Try to authenticate against the identity backend."""
user_info = UserAuthInfo(context, auth_payload)
- # FIXME: identity.authenticate() can use some refactoring since
+ # FIXME(gyee): identity.authenticate() can use some refactoring since
# all we care is password matches
- user_auth_data = self.identity_api.authenticate(
+ self.identity_api.authenticate(
context=context,
user_id=user_info.user_id,
password=user_info.password)
diff --git a/keystone/auth/plugins/token.py b/keystone/auth/plugins/token.py
index c84a6da3..f91fa4f1 100644
--- a/keystone/auth/plugins/token.py
+++ b/keystone/auth/plugins/token.py
@@ -14,9 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-from keystone.common import logging
from keystone import auth
+from keystone.common import logging
from keystone import exception
from keystone import token
diff --git a/keystone/auth/token_factory.py b/keystone/auth/token_factory.py
index a67c973a..43a23ce6 100644
--- a/keystone/auth/token_factory.py
+++ b/keystone/auth/token_factory.py
@@ -21,17 +21,17 @@ import subprocess
import uuid
import webob
+from keystone import catalog
from keystone.common import cms
from keystone.common import logging
from keystone.common import utils
-from keystone import catalog
from keystone import config
from keystone import exception
from keystone import identity
-from keystone import token as token_module
-from keystone import trust
from keystone.openstack.common import jsonutils
from keystone.openstack.common import timeutils
+from keystone import token as token_module
+from keystone import trust
CONF = config.CONF
@@ -178,11 +178,11 @@ class TokenDataHelper(object):
try:
service_catalog = self.catalog_api.get_v3_catalog(
self.context, user_id, project_id)
- #TODO KVS backend needs a sample implementation
+ # TODO(ayoung): KVS backend needs a sample implementation
except exception.NotImplemented:
service_catalog = {}
# TODO(gyee): v3 service catalog is not quite completed yet
- #TODO Enforce Endpoints for trust
+ # TODO(ayoung): Enforce Endpoints for trust
token_data['catalog'] = service_catalog
def _populate_token(self, token_data, expires=None, trust=None):
@@ -219,7 +219,7 @@ class TokenDataHelper(object):
def recreate_token_data(context, token_data=None, expires=None,
user_ref=None, project_ref=None):
- """ Recreate token from an existing token.
+ """Recreate token from an existing token.
Repopulate the ephemeral data and return the new token data.
@@ -351,7 +351,7 @@ def create_token(context, auth_context, auth_info):
def render_token_data_response(token_id, token_data, created=False):
- """ Render token data HTTP response.
+ """Render token data HTTP response.
Stash token ID into the X-Auth-Token header.
diff --git a/keystone/catalog/__init__.py b/keystone/catalog/__init__.py
index d067b981..4aaa2e12 100644
--- a/keystone/catalog/__init__.py
+++ b/keystone/catalog/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py
index 9175a9e6..63a2ec01 100644
--- a/keystone/catalog/backends/sql.py
+++ b/keystone/catalog/backends/sql.py
@@ -60,10 +60,10 @@ class Catalog(sql.Base, catalog.Driver):
return [s.to_dict() for s in list(services)]
def _get_service(self, session, service_id):
- try:
- return session.query(Service).filter_by(id=service_id).one()
- except sql.NotFound:
+ ref = session.query(Service).get(service_id)
+ if not ref:
raise exception.ServiceNotFound(service_id=service_id)
+ return ref
def get_service(self, service_id):
session = self.get_session()
@@ -112,8 +112,8 @@ class Catalog(sql.Base, catalog.Driver):
def delete_endpoint(self, endpoint_id):
session = self.get_session()
with session.begin():
- if not session.query(Endpoint).filter_by(id=endpoint_id).delete():
- raise exception.EndpointNotFound(endpoint_id=endpoint_id)
+ ref = self._get_endpoint(session, endpoint_id)
+ session.delete(ref)
session.flush()
def _get_endpoint(self, session, endpoint_id):
diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py
index 5ce3c38c..df9e3514 100644
--- a/keystone/catalog/core.py
+++ b/keystone/catalog/core.py
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
def format_url(url, data):
- """Helper Method for all Backend Catalog's to Deal with URLS"""
+ """Safely string formats a user-defined URL with the given data."""
try:
result = url.replace('$(', '%(') % data
except AttributeError:
diff --git a/keystone/clean.py b/keystone/clean.py
index 8b8f032a..be647382 100644
--- a/keystone/clean.py
+++ b/keystone/clean.py
@@ -33,11 +33,17 @@ def check_length(property_name, value, min_length=1, max_length=64):
def check_type(property_name, value, expected_type, display_expected_type):
if not isinstance(value, expected_type):
- msg = _("%(property_name)s is not a"
+ msg = _("%(property_name)s is not a "
"%(display_expected_type)s") % locals()
raise exception.ValidationError(msg)
+def check_enabled(property_name, enabled):
+ # Allow int and it's subclass bool
+ check_type('%s enabled' % property_name, enabled, int, 'boolean')
+ return bool(enabled)
+
+
def check_name(property_name, name):
check_type('%s name' % property_name, name, basestring, 'str or unicode')
name = name.strip()
@@ -57,5 +63,9 @@ def user_name(name):
return check_name('User', name)
+def user_enabled(enabled):
+ return check_enabled('User', enabled)
+
+
def group_name(name):
return check_name('Group', name)
diff --git a/keystone/cli.py b/keystone/cli.py
index 5dace7fa..45787f15 100644
--- a/keystone/cli.py
+++ b/keystone/cli.py
@@ -20,12 +20,13 @@ import grp
import pwd
from oslo.config import cfg
+import pbr.version
from keystone.common import openssl
from keystone import config
from keystone.openstack.common import importutils
from keystone.openstack.common import jsonutils
-from keystone.openstack.common import version
+from keystone import token
CONF = config.CONF
@@ -48,14 +49,14 @@ class DbSync(BaseApp):
@staticmethod
def main():
- for k in ['identity', 'catalog', 'policy', 'token']:
+ for k in ['identity', 'catalog', 'policy', 'token', 'credential']:
driver = importutils.import_object(getattr(CONF, k).driver)
if hasattr(driver, 'db_sync'):
driver.db_sync()
class BaseCertificateSetup(BaseApp):
- """Common user/group setup for PKI and SSL generation"""
+ """Common user/group setup for PKI and SSL generation."""
@classmethod
def add_argument_parser(cls, subparsers):
@@ -100,7 +101,7 @@ class PKISetup(BaseCertificateSetup):
class SSLSetup(BaseCertificateSetup):
- """Create key pairs and certificates for HTTPS connections"""
+ """Create key pairs and certificates for HTTPS connections."""
name = 'ssl_setup'
@@ -111,6 +112,17 @@ class SSLSetup(BaseCertificateSetup):
conf_ssl.run()
+class TokenFlush(BaseApp):
+ """Flush expired tokens from the backend."""
+
+ name = 'token_flush'
+
+ @classmethod
+ def main(cls):
+ token_manager = token.Manager()
+ token_manager.driver.flush_expired_tokens()
+
+
class ImportLegacy(BaseApp):
"""Import a legacy database."""
@@ -173,6 +185,7 @@ CMDS = [
ImportNovaAuth,
PKISetup,
SSLSetup,
+ TokenFlush,
]
@@ -191,7 +204,7 @@ def main(argv=None, config_files=None):
CONF.register_cli_opt(command_opt)
CONF(args=argv[1:],
project='keystone',
- version=version.VersionInfo('keystone').version_string(),
+ version=pbr.version.VersionInfo('keystone').version_string(),
usage='%(prog)s [' + '|'.join([cmd.name for cmd in CMDS]) + ']',
default_config_files=config_files)
config.setup_logging(CONF)
diff --git a/keystone/common/bufferedhttp.py b/keystone/common/bufferedhttp.py
index 70786a8c..554758cd 100644
--- a/keystone/common/bufferedhttp.py
+++ b/keystone/common/bufferedhttp.py
@@ -29,10 +29,9 @@ BufferedHTTPResponse.
"""
import time
-from urllib import quote
+import urllib
-from eventlet.green.httplib import (CONTINUE, HTTPConnection, HTTPMessage,
- HTTPResponse, HTTPSConnection, _UNKNOWN)
+from eventlet.green import httplib
from keystone.common import logging
@@ -40,8 +39,8 @@ from keystone.common import logging
LOG = logging.getLogger(__name__)
-class BufferedHTTPResponse(HTTPResponse):
- """HTTPResponse class that buffers reading of headers"""
+class BufferedHTTPResponse(httplib.HTTPResponse):
+ """HTTPResponse class that buffers reading of headers."""
def __init__(self, sock, debuglevel=0, strict=0,
method=None): # pragma: no cover
@@ -54,42 +53,42 @@ class BufferedHTTPResponse(HTTPResponse):
self.msg = None
# from the Status-Line of the response
- self.version = _UNKNOWN # HTTP-Version
- self.status = _UNKNOWN # Status-Code
- self.reason = _UNKNOWN # Reason-Phrase
+ self.version = httplib._UNKNOWN # HTTP-Version
+ self.status = httplib._UNKNOWN # Status-Code
+ self.reason = httplib._UNKNOWN # Reason-Phrase
- self.chunked = _UNKNOWN # is "chunked" being used?
- self.chunk_left = _UNKNOWN # bytes left to read in current chunk
- self.length = _UNKNOWN # number of bytes left in response
- self.will_close = _UNKNOWN # conn will close at end of response
+ self.chunked = httplib._UNKNOWN # is "chunked" being used?
+ self.chunk_left = httplib._UNKNOWN # bytes left to read in chunk
+ self.length = httplib._UNKNOWN # number of bytes left in response
+ self.will_close = httplib._UNKNOWN # conn will close at end of resp
def expect_response(self):
self.fp = self.sock.makefile('rb', 0)
version, status, reason = self._read_status()
- if status != CONTINUE:
+ if status != httplib.CONTINUE:
self._read_status = lambda: (version, status, reason)
self.begin()
else:
self.status = status
self.reason = reason.strip()
self.version = 11
- self.msg = HTTPMessage(self.fp, 0)
+ self.msg = httplib.HTTPMessage(self.fp, 0)
self.msg.fp = None
-class BufferedHTTPConnection(HTTPConnection):
- """HTTPConnection class that uses BufferedHTTPResponse"""
+class BufferedHTTPConnection(httplib.HTTPConnection):
+ """HTTPConnection class that uses BufferedHTTPResponse."""
response_class = BufferedHTTPResponse
def connect(self):
self._connected_time = time.time()
- return HTTPConnection.connect(self)
+ return httplib.HTTPConnection.connect(self)
def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
self._method = method
self._path = url
- return HTTPConnection.putrequest(self, method, url, skip_host,
- skip_accept_encoding)
+ return httplib.HTTPConnection.putrequest(self, method, url, skip_host,
+ skip_accept_encoding)
def getexpect(self):
response = BufferedHTTPResponse(self.sock, strict=self.strict,
@@ -98,7 +97,7 @@ class BufferedHTTPConnection(HTTPConnection):
return response
def getresponse(self):
- response = HTTPConnection.getresponse(self)
+ response = httplib.HTTPConnection.getresponse(self)
LOG.debug(_('HTTP PERF: %(time).5f seconds to %(method)s '
'%(host)s:%(port)s %(path)s)'),
{'time': time.time() - self._connected_time,
@@ -112,10 +111,11 @@ class BufferedHTTPConnection(HTTPConnection):
def http_connect(ipaddr, port, device, partition, method, path,
headers=None, query_string=None, ssl=False, key_file=None,
cert_file=None):
- """
- Helper function to create an HTTPConnection object. If ssl is set True,
- HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
- will be used, which is buffered for backend Swift services.
+ """Helper function to create an HTTPConnection object.
+
+ If ssl is set True, HTTPSConnection will be used. However, if ssl=False,
+ BufferedHTTPConnection will be used, which is buffered for backend Swift
+ services.
:param ipaddr: IPv4 address to connect to
:param port: port to connect to
@@ -131,7 +131,7 @@ def http_connect(ipaddr, port, device, partition, method, path,
:returns: HTTPConnection object
"""
- path = quote('/' + device + '/' + str(partition) + path)
+ path = urllib.quote('/' + device + '/' + str(partition) + path)
return http_connect_raw(ipaddr, port, device, partition, method, path,
headers, query_string, ssl, key_file, cert_file)
@@ -139,10 +139,11 @@ def http_connect(ipaddr, port, device, partition, method, path,
def http_connect_raw(ipaddr, port, method, path, headers=None,
query_string=None, ssl=False, key_file=None,
cert_file=None):
- """
- Helper function to create an HTTPConnection object. If ssl is set True,
- HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
- will be used, which is buffered for backend Swift services.
+ """Helper function to create an HTTPConnection object.
+
+ If ssl is set True, HTTPSConnection will be used. However, if ssl=False,
+ BufferedHTTPConnection will be used, which is buffered for backend Swift
+ services.
:param ipaddr: IPv4 address to connect to
:param port: port to connect to
@@ -157,8 +158,8 @@ def http_connect_raw(ipaddr, port, method, path, headers=None,
"""
if ssl:
- conn = HTTPSConnection('%s:%s' % (ipaddr, port), key_file=key_file,
- cert_file=cert_file)
+ conn = httplib.HTTPSConnection(
+ '%s:%s' % (ipaddr, port), key_file=key_file, cert_file=cert_file)
else:
conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
if query_string:
diff --git a/keystone/common/cms.py b/keystone/common/cms.py
index ed0fa60c..307542b4 100644
--- a/keystone/common/cms.py
+++ b/keystone/common/cms.py
@@ -21,13 +21,11 @@ def _ensure_subprocess():
else:
import subprocess
except ImportError:
- import subprocess
+ import subprocess # nopep8
def cms_verify(formatted, signing_cert_file_name, ca_file_name):
- """
- verifies the signature of the contents IAW CMS syntax
- """
+ """Verifies the signature of the contents IAW CMS syntax."""
_ensure_subprocess()
process = subprocess.Popen(["openssl", "cms", "-verify",
"-certfile", signing_cert_file_name,
@@ -72,7 +70,8 @@ def verify_token(token, signing_cert_file_name, ca_file_name):
def is_ans1_token(token):
- '''
+ """Determine if a token appears to be PKI-based.
+
thx to ayoung for sorting this out.
base64 decoded hex representation of MII is 3082
@@ -109,12 +108,12 @@ def is_ans1_token(token):
Max length of the content using 2 octets is 7FFF or 32767
It's not practical to support a token of this length or greater in http
therefore, we will check for MII only and ignore the case of larger tokens
- '''
+ """
return token[:3] == PKI_ANS1_PREFIX
def cms_sign_text(text, signing_cert_file_name, signing_key_file_name):
- """ Uses OpenSSL to sign a document
+ """Uses OpenSSL to sign a document
Produces a Base64 encoding of a DER formatted CMS Document
http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax
"""
@@ -160,7 +159,8 @@ def cms_to_token(cms_text):
def cms_hash_token(token_id):
- """
+ """Hash PKI tokens.
+
return: for ans1_token, returns the hash of the passed in token
otherwise, returns what it was passed in.
"""
diff --git a/keystone/common/config.py b/keystone/common/config.py
index e2ff7e14..e2f5055a 100644
--- a/keystone/common/config.py
+++ b/keystone/common/config.py
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import gettext
import os
import sys
@@ -23,8 +22,6 @@ from oslo.config import cfg
from keystone.common import logging
-gettext.install('keystone', unicode=1)
-
_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
_DEFAULT_AUTH_METHODS = ['password', 'token']
@@ -78,8 +75,7 @@ CONF = cfg.CONF
def setup_logging(conf):
- """
- Sets up the logging options for a log with supplied name
+ """Sets up the logging options for a log with supplied name
:param conf: a cfg.ConfOpts object
"""
@@ -270,6 +266,10 @@ def configure():
default='keystone.identity.backends.sql.Identity')
register_str(
'driver',
+ group='credential',
+ default='keystone.credential.backends.sql.Credential')
+ register_str(
+ 'driver',
group='policy',
default='keystone.policy.backends.sql.Policy')
register_str(
@@ -384,12 +384,11 @@ def configure():
'domain_additional_attribute_mapping', group='ldap', default=None)
register_str('tls_cacertfile', group='ldap', default=None)
- register_str('tls_cacertdir', group='ldap', default=None)
+ register_str('tls_cacertdir', group='ldap', default=None)
register_bool('use_tls', group='ldap', default=False)
register_str('tls_req_cert', group='ldap', default='demand')
# pam
- register_str('url', group='pam', default=None)
register_str('userid', group='pam', default=None)
register_str('password', group='pam', default=None)
@@ -405,3 +404,6 @@ def configure():
for method_name in CONF.auth.methods:
if method_name not in _DEFAULT_AUTH_METHODS:
register_str(method_name, group='auth')
+
+ # PasteDeploy config file
+ register_str('config_file', group='paste_deploy', default=None)
diff --git a/keystone/common/controller.py b/keystone/common/controller.py
index daed966a..3452f85e 100644
--- a/keystone/common/controller.py
+++ b/keystone/common/controller.py
@@ -15,10 +15,9 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
def _build_policy_check_credentials(self, action, context, kwargs):
-
- LOG.debug(_('RBAC: Authorizing %s(%s)') % (
- action,
- ', '.join(['%s=%s' % (k, kwargs[k]) for k in kwargs])))
+ LOG.debug(_('RBAC: Authorizing %(action)s(%(kwargs)s)') % {
+ 'action': action,
+ 'kwargs': ', '.join(['%s=%s' % (k, kwargs[k]) for k in kwargs])})
try:
token_ref = self.token_api.get_token(
@@ -149,7 +148,7 @@ def filterprotected(*filters):
@dependency.requires('identity_api', 'policy_api', 'token_api',
- 'trust_api', 'catalog_api')
+ 'trust_api', 'catalog_api', 'credential_api')
class V2Controller(wsgi.Application):
"""Base controller class for Identity API v2."""
diff --git a/keystone/common/ldap/__init__.py b/keystone/common/ldap/__init__.py
index 17aca3b1..adc3a276 100644
--- a/keystone/common/ldap/__init__.py
+++ b/keystone/common/ldap/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py
index 7113fbbb..7a2dfee7 100644
--- a/keystone/common/ldap/core.py
+++ b/keystone/common/ldap/core.py
@@ -84,17 +84,21 @@ def parse_tls_cert(opt):
try:
return LDAP_TLS_CERTS[opt]
except KeyError:
- raise ValueError((_('Invalid LDAP tls certs option: %s. '
- 'Choose one of: ') %
- opt) + ', '.join(LDAP_TLS_CERTS.keys()))
+ raise ValueError(_(
+ 'Invalid LDAP TLS certs option: %(option). '
+ 'Choose one of: %(options)s') % {
+ 'option': opt,
+ 'options': ', '.join(LDAP_TLS_CERTS.keys())})
def ldap_scope(scope):
try:
return LDAP_SCOPES[scope]
except KeyError:
- raise ValueError(_('Invalid LDAP scope: %s. Choose one of: ' % scope) +
- ', '.join(LDAP_SCOPES.keys()))
+ raise ValueError(
+ _('Invalid LDAP scope: %(scope)s. Choose one of: %(options)s') % {
+ 'scope': scope,
+ 'options': ', '.join(LDAP_SCOPES.keys())})
class BaseLdap(object):
@@ -182,9 +186,10 @@ class BaseLdap(object):
try:
ldap_attr, attr_map = item.split(':')
except Exception:
- LOG.warn(_('Invalid additional attribute mapping: "%s". '
- 'Format must be ' +
- '<ldap_attribute>:<keystone_attribute>') % item)
+ LOG.warn(_(
+ 'Invalid additional attribute mapping: "%s". '
+ 'Format must be <ldap_attribute>:<keystone_attribute>')
+ % item)
continue
if attr_map not in self.attribute_mapping:
LOG.warn(_('Invalid additional attribute mapping: "%(item)s". '
@@ -393,6 +398,8 @@ class BaseLdap(object):
except ldap.NO_SUCH_OBJECT:
raise self._not_found(id)
+ return self.get(id)
+
def delete(self, id):
if not self.allow_delete:
action = _('LDAP %s delete') % self.options_name
@@ -444,7 +451,7 @@ class LdapWrapper(object):
if use_tls:
if not ldap.TLS_AVAIL:
- raise ValueError(_('Invalid LDAP TLS_AVAIL option: %s. TLS'
+ raise ValueError(_('Invalid LDAP TLS_AVAIL option: %s. TLS '
'not available') % ldap.TLS_AVAIL)
if tls_cacertfile:
#NOTE(topol)
@@ -498,16 +505,19 @@ class LdapWrapper(object):
if kind != 'userPassword'
else ['****'])
for kind, values in ldap_attrs]
- LOG.debug(_('LDAP add: dn=%s, attrs=%s'), dn, sane_attrs)
+ LOG.debug(_('LDAP add: dn=%(dn)s, attrs=%(attrs)s') % {
+ 'dn': dn, 'attrs': sane_attrs})
return self.conn.add_s(dn, ldap_attrs)
def search_s(self, dn, scope, query, attrlist=None):
if LOG.isEnabledFor(logging.DEBUG):
- LOG.debug(_('LDAP search: dn=%s, scope=%s, query=%s, attrs=%s'),
- dn,
- scope,
- query,
- attrlist)
+ LOG.debug(_(
+ 'LDAP search: dn=%(dn)s, scope=%(scope)s, query=%(query)s, '
+ 'attrs=%(attrlist)s') % {
+ 'dn': dn,
+ 'scope': scope,
+ 'query': query,
+ 'attrlist': attrlist})
if self.page_size:
res = self.paged_search_s(dn, scope, query, attrlist)
else:
@@ -571,7 +581,8 @@ class LdapWrapper(object):
sane_modlist = [(op, kind, (values if kind != 'userPassword'
else ['****']))
for op, kind, values in ldap_modlist]
- LOG.debug(_("LDAP modify: dn=%s, modlist=%s"), dn, sane_modlist)
+ LOG.debug(_('LDAP modify: dn=%(dn)s, modlist=%(modlist)s') % {
+ 'dn': dn, 'modlist': sane_modlist})
return self.conn.modify_s(dn, ldap_modlist)
@@ -580,7 +591,9 @@ class LdapWrapper(object):
return self.conn.delete_s(dn)
def delete_ext_s(self, dn, serverctrls):
- LOG.debug(_("LDAP delete_ext: dn=%s, serverctrls=%s"), dn, serverctrls)
+ LOG.debug(
+ _('LDAP delete_ext: dn=%(dn)s, serverctrls=%(serverctrls)s') % {
+ 'dn': dn, 'serverctrls': serverctrls})
return self.conn.delete_ext_s(dn, serverctrls)
def _disable_paging(self):
@@ -688,14 +701,16 @@ class EnabledEmuMixIn(BaseLdap):
if 'enabled' not in self.attribute_ignore and self.enabled_emulation:
data = values.copy()
enabled_value = data.pop('enabled', None)
- super(EnabledEmuMixIn, self).update(object_id, data, old_obj)
+ ref = super(EnabledEmuMixIn, self).update(object_id, data, old_obj)
if enabled_value is not None:
if enabled_value:
self._add_enabled(object_id)
else:
self._remove_enabled(object_id)
+ return ref
else:
- super(EnabledEmuMixIn, self).update(object_id, values, old_obj)
+ return super(EnabledEmuMixIn, self).update(
+ object_id, values, old_obj)
def delete(self, object_id):
if self.enabled_emulation:
diff --git a/keystone/common/ldap/fakeldap.py b/keystone/common/ldap/fakeldap.py
index 56eedee1..f6c95895 100644
--- a/keystone/common/ldap/fakeldap.py
+++ b/keystone/common/ldap/fakeldap.py
@@ -187,7 +187,8 @@ class FakeLdap(object):
raise ldap.SERVER_DOWN
key = '%s%s' % (self.__prefix, dn)
- LOG.debug(_('FakeLdap add item: dn=%s, attrs=%s'), dn, attrs)
+ LOG.debug(_('FakeLdap add item: dn=%(dn)s, attrs=%(attrs)s') % {
+ 'dn': dn, 'attrs': attrs})
if key in self.db:
LOG.debug(_('FakeLdap add item failed: dn=%s is'
' already in store.'), dn)
@@ -236,7 +237,8 @@ class FakeLdap(object):
raise ldap.SERVER_DOWN
key = '%s%s' % (self.__prefix, dn)
- LOG.debug(_('FakeLdap modify item: dn=%s attrs=%s'), dn, attrs)
+ LOG.debug(_('FakeLdap modify item: dn=%(dn)s attrs=%(attrs)s') % {
+ 'dn': dn, 'attrs': attrs})
try:
entry = self.db[key]
except KeyError:
@@ -268,9 +270,10 @@ class FakeLdap(object):
try:
values.remove(val)
except ValueError:
- LOG.debug(_('FakeLdap modify item failed:'
- ' item has no attribute "%s" with'
- ' value "%s" to delete'), k, val)
+ LOG.debug(_('FakeLdap modify item failed: '
+ 'item has no attribute "%(k)s" with '
+ 'value "%(v)s" to delete') % {
+ 'k': k, 'v': val})
raise ldap.NO_SUCH_ATTRIBUTE
else:
LOG.debug(_('FakeLdap modify item failed: unknown'
@@ -293,8 +296,9 @@ class FakeLdap(object):
if server_fail:
raise ldap.SERVER_DOWN
- LOG.debug(_('FakeLdap search at dn=%s scope=%s query=%s'),
- dn, SCOPE_NAMES.get(scope, scope), query)
+ LOG.debug(
+ _('FakeLdap search at dn=%(dn)s scope=%(scope)s query=%(query)s') %
+ {'dn': dn, 'scope': SCOPE_NAMES.get(scope, scope), 'query': query})
if scope == ldap.SCOPE_BASE:
try:
item_dict = self.db['%s%s' % (self.__prefix, dn)]
diff --git a/keystone/common/logging.py b/keystone/common/logging.py
index 9d8bee88..8c036f02 100644
--- a/keystone/common/logging.py
+++ b/keystone/common/logging.py
@@ -21,11 +21,10 @@ from __future__ import absolute_import
import functools
import logging
import logging.config
+import logging.handlers
import pprint
import traceback
-from logging.handlers import SysLogHandler
-from logging.handlers import WatchedFileHandler
# A list of things we want to replicate from logging.
# levels
@@ -57,8 +56,8 @@ Formatter = logging.Formatter
# handlers
StreamHandler = logging.StreamHandler
-WatchedFileHandler = WatchedFileHandler
-SysLogHandler = SysLogHandler
+WatchedFileHandler = logging.handlers.WatchedFileHandler
+SysLogHandler = logging.handlers.SysLogHandler
def log_debug(f):
diff --git a/keystone/common/openssl.py b/keystone/common/openssl.py
index dd31fb71..3e08ed1e 100644
--- a/keystone/common/openssl.py
+++ b/keystone/common/openssl.py
@@ -36,8 +36,11 @@ def file_exists(file_path):
class BaseCertificateConfigure(object):
- """Create a certificate signing environment from a config section
- and reasonable OpenSSL defaults"""
+ """Create a certificate signing environment.
+
+ This is based on a config section and reasonable OpenSSL defaults.
+
+ """
def __init__(self, conf_obj, keystone_user, keystone_group, **kwargs):
self.conf_dir = os.path.dirname(conf_obj.ca_certs)
diff --git a/keystone/common/sql/__init__.py b/keystone/common/sql/__init__.py
index 285a7ee0..b069482f 100644
--- a/keystone/common/sql/__init__.py
+++ b/keystone/common/sql/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/common/sql/core.py b/keystone/common/sql/core.py
index fb29298a..694d30bd 100644
--- a/keystone/common/sql/core.py
+++ b/keystone/common/sql/core.py
@@ -22,9 +22,9 @@ import sqlalchemy.engine.url
from sqlalchemy.exc import DisconnectionError
from sqlalchemy.ext import declarative
import sqlalchemy.orm
+from sqlalchemy.orm.attributes import InstrumentedAttribute
import sqlalchemy.pool
from sqlalchemy import types as sql_types
-from sqlalchemy.orm.attributes import InstrumentedAttribute
from keystone.common import logging
from keystone import config
@@ -47,6 +47,7 @@ String = sql.String
ForeignKey = sql.ForeignKey
DateTime = sql.DateTime
IntegrityError = sql.exc.IntegrityError
+OperationalError = sql.exc.OperationalError
NotFound = sql.orm.exc.NoResultFound
Boolean = sql.Boolean
Text = sql.Text
@@ -181,9 +182,7 @@ class DictBase(object):
class MySQLPingListener(object):
- """
- Ensures that MySQL connections checked out of the
- pool are alive.
+ """Ensures that MySQL connections checked out of the pool are alive.
Borrowed from:
http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f
@@ -272,7 +271,7 @@ def handle_conflicts(type='object'):
def wrapper(*args, **kwargs):
try:
return method(*args, **kwargs)
- except IntegrityError as e:
+ except (IntegrityError, OperationalError) as e:
raise exception.Conflict(type=type, details=str(e.orig))
return wrapper
return decorator
diff --git a/keystone/common/sql/legacy.py b/keystone/common/sql/legacy.py
index 82dda2cf..f2e0b994 100644
--- a/keystone/common/sql/legacy.py
+++ b/keystone/common/sql/legacy.py
@@ -20,9 +20,9 @@ import sqlalchemy
from sqlalchemy import exc
from keystone.common import logging
+from keystone import config
from keystone.contrib.ec2.backends import sql as ec2_sql
from keystone.identity.backends import sql as identity_sql
-from keystone import config
LOG = logging.getLogger(__name__)
diff --git a/keystone/common/sql/migrate_repo/versions/003_token_valid.py b/keystone/common/sql/migrate_repo/versions/003_token_valid.py
index efc1fd4e..c8df800c 100644
--- a/keystone/common/sql/migrate_repo/versions/003_token_valid.py
+++ b/keystone/common/sql/migrate_repo/versions/003_token_valid.py
@@ -14,25 +14,25 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-from sqlalchemy import *
+import sqlalchemy as sql
def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine; bind
- meta = MetaData()
+ meta = sql.MetaData()
meta.bind = migrate_engine
- token = Table('token', meta, autoload=True)
+ token = sql.Table('token', meta, autoload=True)
# creating the column immediately with nullable=False fails with
# PostgreSQL (LP 1068181), so do it in two steps instead
- valid = Column("valid", Boolean(), ColumnDefault(True), nullable=True)
+ valid = sql.Column(
+ 'valid', sql.Boolean(), sql.ColumnDefault(True), nullable=True)
valid.create(token, populate_default=True)
- valid.alter(type=Boolean(), default=True, nullable=False)
+ valid.alter(type=sql.Boolean(), default=True, nullable=False)
def downgrade(migrate_engine):
- meta = MetaData()
+ meta = sql.MetaData()
meta.bind = migrate_engine
- token = Table('token', meta, autoload=True)
+ token = sql.Table('token', meta, autoload=True)
token.drop_column('valid')
diff --git a/keystone/common/sql/migrate_repo/versions/008_create_default_domain.py b/keystone/common/sql/migrate_repo/versions/008_create_default_domain.py
index 3a88f897..387d75ed 100644
--- a/keystone/common/sql/migrate_repo/versions/008_create_default_domain.py
+++ b/keystone/common/sql/migrate_repo/versions/008_create_default_domain.py
@@ -18,6 +18,7 @@ import json
import sqlalchemy as sql
from sqlalchemy import orm
+
from keystone import config
diff --git a/keystone/common/sql/migrate_repo/versions/009_normalize_identity.py b/keystone/common/sql/migrate_repo/versions/009_normalize_identity.py
index 8260764e..7941ca02 100644
--- a/keystone/common/sql/migrate_repo/versions/009_normalize_identity.py
+++ b/keystone/common/sql/migrate_repo/versions/009_normalize_identity.py
@@ -17,7 +17,6 @@
from sqlalchemy import Column, MetaData, String, Table, Text, types
from sqlalchemy.orm import sessionmaker
-from keystone import config
#sqlite doesn't support dropping columns. Copy to a new table instead
diff --git a/keystone/common/sql/migrate_repo/versions/015_tenant_to_project.py b/keystone/common/sql/migrate_repo/versions/015_tenant_to_project.py
index a024eb0e..5f276c68 100644
--- a/keystone/common/sql/migrate_repo/versions/015_tenant_to_project.py
+++ b/keystone/common/sql/migrate_repo/versions/015_tenant_to_project.py
@@ -17,7 +17,7 @@ def downgrade_with_rename(meta, migrate_engine):
def upgrade_with_copy(meta, migrate_engine):
- legacy_table = sql.Table('user', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
project_table = sql.Table(
'project',
meta,
@@ -69,7 +69,7 @@ def upgrade_with_copy(meta, migrate_engine):
def downgrade_with_copy(meta, migrate_engine):
- legacy_table = sql.Table('user', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
tenant_table = sql.Table(
'tenant',
meta,
diff --git a/keystone/common/sql/migrate_repo/versions/016_normalize_domain_ids.py b/keystone/common/sql/migrate_repo/versions/016_normalize_domain_ids.py
index 618c738b..cfab79d4 100644
--- a/keystone/common/sql/migrate_repo/versions/016_normalize_domain_ids.py
+++ b/keystone/common/sql/migrate_repo/versions/016_normalize_domain_ids.py
@@ -39,6 +39,7 @@ of integrity constraints.
import sqlalchemy as sql
from sqlalchemy.orm import sessionmaker
+
from keystone import config
@@ -94,7 +95,7 @@ def upgrade_user_table_with_copy(meta, migrate_engine, session):
# different version of the user table
meta2 = sql.MetaData()
meta2.bind = migrate_engine
- domain_table = sql.Table('domain', meta2, autoload=True)
+ sql.Table('domain', meta2, autoload=True)
user_table = sql.Table(
'user',
meta2,
@@ -163,7 +164,7 @@ def upgrade_project_table_with_copy(meta, migrate_engine, session):
# different version of the project table
meta2 = sql.MetaData()
meta2.bind = migrate_engine
- domain_table = sql.Table('domain', meta2, autoload=True)
+ sql.Table('domain', meta2, autoload=True)
project_table = sql.Table(
'project',
meta2,
@@ -326,7 +327,7 @@ def upgrade_user_table_with_col_create(meta, migrate_engine, session):
# bat since any existing rows would cause an Integrity Error.
# We therefore create it nullable, fill the column with the
# default data and then set it to non nullable.
- domain_table = sql.Table('domain', meta, autoload=True)
+ sql.Table('domain', meta, autoload=True)
user_table = sql.Table('user', meta, autoload=True)
user_table.create_column(
sql.Column('domain_id', sql.String(64),
@@ -353,7 +354,7 @@ def upgrade_project_table_with_col_create(meta, migrate_engine, session):
# bat since any existing rows would cause an Integrity Error.
# We therefore create it nullable, fill the column with the
# default data and then set it to non nullable.
- domain_table = sql.Table('domain', meta, autoload=True)
+ sql.Table('domain', meta, autoload=True)
project_table = sql.Table('project', meta, autoload=True)
project_table.create_column(
sql.Column('domain_id', sql.String(64),
@@ -381,7 +382,7 @@ def downgrade_user_table_with_col_drop(meta, migrate_engine, session):
session.execute('ALTER TABLE "user" ADD UNIQUE (name);')
session.commit()
# And now go ahead an drop the domain_id column
- domain_table = sql.Table('domain', meta, autoload=True)
+ sql.Table('domain', meta, autoload=True)
user_table = sql.Table('user', meta, autoload=True)
column = sql.Column('domain_id', sql.String(64),
sql.ForeignKey('domain.id'), nullable=False)
@@ -396,7 +397,7 @@ def downgrade_project_table_with_col_drop(meta, migrate_engine, session):
'UNIQUE (name);')
session.commit()
# And now go ahead an drop the domain_id column
- domain_table = sql.Table('domain', meta, autoload=True)
+ sql.Table('domain', meta, autoload=True)
project_table = sql.Table('project', meta, autoload=True)
column = sql.Column('domain_id', sql.String(64),
sql.ForeignKey('domain.id'), nullable=False)
diff --git a/keystone/common/sql/migrate_repo/versions/017_membership_role.py b/keystone/common/sql/migrate_repo/versions/017_membership_role.py
index 98ed9a2a..85333057 100644
--- a/keystone/common/sql/migrate_repo/versions/017_membership_role.py
+++ b/keystone/common/sql/migrate_repo/versions/017_membership_role.py
@@ -1,11 +1,8 @@
import json
-import uuid
import sqlalchemy as sql
-from sqlalchemy import orm
from keystone import config
-from keystone import exception
CONF = config.CONF
@@ -15,9 +12,9 @@ def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
- user_table = sql.Table('user', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
+ sql.Table('project', meta, autoload=True)
role_table = sql.Table('role', meta, autoload=True)
- project_table = sql.Table('project', meta, autoload=True)
user_project_role_table = sql.Table(
'user_project_metadata',
@@ -59,8 +56,8 @@ def downgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
- user_table = sql.Table('user', meta, autoload=True)
- project_table = sql.Table('project', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
+ sql.Table('project', meta, autoload=True)
user_project_membership_table = sql.Table(
'user_project_membership',
@@ -87,9 +84,9 @@ def downgrade(migrate_engine):
if 'roles' in membership:
roles = membership['roles']
if config.CONF.member_role_id in roles:
- ins = (user_project_membership_table.insert()
- .values(user_id=membership.user_id,
- tenant_id=membership.project_id))
+ user_project_membership_table.insert().values(
+ user_id=membership.user_id,
+ tenant_id=membership.project_id)
session.close()
role_table = sql.Table('role', meta, autoload=True)
conn = migrate_engine.connect()
diff --git a/keystone/common/sql/migrate_repo/versions/018_add_trust_tables.py b/keystone/common/sql/migrate_repo/versions/018_add_trust_tables.py
index 77c42ba1..46ed5ce0 100644
--- a/keystone/common/sql/migrate_repo/versions/018_add_trust_tables.py
+++ b/keystone/common/sql/migrate_repo/versions/018_add_trust_tables.py
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import migrate
import sqlalchemy as sql
@@ -24,9 +23,9 @@ def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
- user_table = sql.Table('user', meta, autoload=True)
- role_table = sql.Table('role', meta, autoload=True)
- tenant_table = sql.Table('project', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
+ sql.Table('role', meta, autoload=True)
+ sql.Table('project', meta, autoload=True)
trust_table = sql.Table(
'trust',
diff --git a/keystone/common/sql/migrate_repo/versions/019_fixup_role.py b/keystone/common/sql/migrate_repo/versions/019_fixup_role.py
index 11fa0a40..21da5544 100644
--- a/keystone/common/sql/migrate_repo/versions/019_fixup_role.py
+++ b/keystone/common/sql/migrate_repo/versions/019_fixup_role.py
@@ -1,11 +1,6 @@
-import json
-import uuid
-
import sqlalchemy as sql
-from sqlalchemy import orm
from keystone import config
-from keystone import exception
CONF = config.CONF
diff --git a/keystone/common/sql/migrate_repo/versions/020_migrate_metadata_table_roles.py b/keystone/common/sql/migrate_repo/versions/020_migrate_metadata_table_roles.py
index b60dacb1..ca744122 100644
--- a/keystone/common/sql/migrate_repo/versions/020_migrate_metadata_table_roles.py
+++ b/keystone/common/sql/migrate_repo/versions/020_migrate_metadata_table_roles.py
@@ -1,11 +1,8 @@
import json
-import uuid
import sqlalchemy as sql
-from sqlalchemy import orm
from keystone import config
-from keystone import exception
CONF = config.CONF
@@ -15,9 +12,9 @@ def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
- user_table = sql.Table('user', meta, autoload=True)
- role_table = sql.Table('role', meta, autoload=True)
- project_table = sql.Table('project', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
+ sql.Table('role', meta, autoload=True)
+ sql.Table('project', meta, autoload=True)
new_metadata_table = sql.Table('user_project_metadata',
meta,
autoload=True)
@@ -28,7 +25,7 @@ def upgrade(migrate_engine):
session = sql.orm.sessionmaker(bind=migrate_engine)()
for metadata in session.query(old_metadata_table):
- if not config.CONF.member_role_id in metadata.data:
+ if config.CONF.member_role_id not in metadata.data:
data = json.loads(metadata.data)
data['roles'].append(config.CONF.member_role_id)
else:
@@ -63,8 +60,8 @@ def downgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
- user_table = sql.Table('user', meta, autoload=True)
- project_table = sql.Table('project', meta, autoload=True)
+ sql.Table('user', meta, autoload=True)
+ sql.Table('project', meta, autoload=True)
metadata_table = sql.Table(
'metadata',
@@ -95,9 +92,8 @@ def downgrade(migrate_engine):
for metadata in session.query(user_project_metadata_table):
if 'roles' in metadata:
- roles = json.loads(metadata.data)
- ins = (metadata_table.insert()
- .values(user_id=metadata.user_id,
- tenant_id=metadata.project_id))
+ metadata_table.insert().values(
+ user_id=metadata.user_id,
+ tenant_id=metadata.project_id)
session.close()
diff --git a/keystone/common/sql/migrate_repo/versions/021_add_trust_to_token.py b/keystone/common/sql/migrate_repo/versions/021_add_trust_to_token.py
index caad8674..444435b2 100644
--- a/keystone/common/sql/migrate_repo/versions/021_add_trust_to_token.py
+++ b/keystone/common/sql/migrate_repo/versions/021_add_trust_to_token.py
@@ -14,12 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-
import sqlalchemy
from sqlalchemy import exc
-from sqlalchemy.orm import sessionmaker
-
-from keystone import config
def downgrade_token_table_with_column_drop(meta, migrate_engine):
diff --git a/keystone/common/sql/migrate_repo/versions/023_drop_credential_constraints.py b/keystone/common/sql/migrate_repo/versions/023_drop_credential_constraints.py
new file mode 100644
index 00000000..ee1d34eb
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/023_drop_credential_constraints.py
@@ -0,0 +1,77 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# 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 migrate import ForeignKeyConstraint
+import sqlalchemy
+from sqlalchemy.orm import sessionmaker
+
+MYSQL_FKEY_QUERY = ("select CONSTRAINT_NAME from "
+ "INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS "
+ "where table_name = 'credential'")
+
+
+def drop_constraint_mysql(migrate_engine):
+ session = sessionmaker(bind=migrate_engine)()
+ #http://bugs.mysql.com/bug.php?id=10333
+ #MySQL varies from the SQL norm in naming
+ #Foreign Keys. The mapping from the column name
+ #to the actual foreign key is stored in
+ #INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ #SQLAlchemy expects the constraint name to be
+ # the column name.
+ for constraint in session.execute(MYSQL_FKEY_QUERY):
+ session.execute('ALTER TABLE credential DROP FOREIGN KEY %s;'
+ % constraint[0])
+ session.commit()
+
+
+def remove_constraints(migrate_engine):
+ if migrate_engine.name == 'sqlite':
+ return
+ if migrate_engine.name == 'mysql':
+ drop_constraint_mysql(migrate_engine)
+ return
+ meta = sqlalchemy.MetaData()
+ meta.bind = migrate_engine
+ user_table = sqlalchemy.Table('user', meta, autoload=True)
+ proj_table = sqlalchemy.Table('project', meta, autoload=True)
+ cred_table = sqlalchemy.Table('credential', meta, autoload=True)
+ ForeignKeyConstraint(columns=[cred_table.c.user_id],
+ refcolumns=[user_table.c.id]).drop()
+ ForeignKeyConstraint(columns=[cred_table.c.project_id],
+ refcolumns=[proj_table.c.id]).drop()
+
+
+def add_constraints(migrate_engine):
+ if migrate_engine.name == 'sqlite':
+ return
+ meta = sqlalchemy.MetaData()
+ meta.bind = migrate_engine
+ user_table = sqlalchemy.Table('user', meta, autoload=True)
+ proj_table = sqlalchemy.Table('project', meta, autoload=True)
+ cred_table = sqlalchemy.Table('credential', meta, autoload=True)
+ ForeignKeyConstraint(columns=[cred_table.c.user_id],
+ refcolumns=[user_table.c.id]).create()
+ ForeignKeyConstraint(columns=[cred_table.c.project_id],
+ refcolumns=[proj_table.c.id]).create()
+
+
+def upgrade(migrate_engine):
+ remove_constraints(migrate_engine)
+
+
+def downgrade(migrate_engine):
+ add_constraints(migrate_engine)
diff --git a/keystone/common/sql/migrate_repo/versions/024_add_index_to_expires.py b/keystone/common/sql/migrate_repo/versions/024_add_index_to_expires.py
new file mode 100644
index 00000000..51073bf3
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/024_add_index_to_expires.py
@@ -0,0 +1,17 @@
+import sqlalchemy as sql
+
+
+def upgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+ token = sql.Table('token', meta, autoload=True)
+ idx = sql.Index('ix_token_expires', token.c.expires)
+ idx.create(migrate_engine)
+
+
+def downgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+ token = sql.Table('token', meta, autoload=True)
+ idx = sql.Index('ix_token_expires', token.c.expires)
+ idx.drop(migrate_engine)
diff --git a/keystone/common/sql/nova.py b/keystone/common/sql/nova.py
index 968c542f..c6d452cd 100644
--- a/keystone/common/sql/nova.py
+++ b/keystone/common/sql/nova.py
@@ -18,8 +18,8 @@
import uuid
-from keystone import config
from keystone.common import logging
+from keystone import config
from keystone.contrib.ec2.backends import sql as ec2_sql
from keystone.identity.backends import sql as identity_sql
@@ -85,7 +85,8 @@ def _create_memberships(api, memberships, user_map, tenant_map):
for membership in memberships:
user_id = user_map[membership['user_id']]
tenant_id = tenant_map[membership['tenant_id']]
- LOG.debug(_('Add user %s to tenant %s') % (user_id, tenant_id))
+ LOG.debug(_('Add user %(user_id)s to tenant %(tenant_id)s') % {
+ 'user_id': user_id, 'tenant_id': tenant_id})
api.add_user_to_project(tenant_id, user_id)
@@ -110,8 +111,12 @@ def _assign_roles(api, assignments, role_map, user_map, tenant_map):
role_id = role_map[assignment['role']]
user_id = user_map[assignment['user_id']]
tenant_id = tenant_map[assignment['tenant_id']]
- LOG.debug(_('Assign role %s to user %s on tenant %s') %
- (role_id, user_id, tenant_id))
+ LOG.debug(_(
+ 'Assign role %(role_id)s to user %(user_id)s on tenant '
+ '%(tenant_id)s') % {
+ 'role_id': role_id,
+ 'user_id': user_id,
+ 'tenant_id': tenant_id})
api.add_role_to_user_and_project(user_id, tenant_id, role_id)
@@ -125,6 +130,8 @@ def _create_ec2_creds(ec2_api, identity_api, ec2_creds, user_map):
'user_id': user_id,
'tenant_id': tenant_id,
}
- LOG.debug(_('Creating ec2 cred for user %s and tenant %s') %
- (user_id, tenant_id))
+ LOG.debug(_(
+ 'Creating ec2 cred for user %(user_id)s and tenant '
+ '%(tenant_id)s') % {
+ 'user_id': user_id, 'tenant_id': tenant_id})
ec2_api.create_credential(None, cred_dict)
diff --git a/keystone/common/utils.py b/keystone/common/utils.py
index 0bc38985..687d4cd2 100644
--- a/keystone/common/utils.py
+++ b/keystone/common/utils.py
@@ -79,7 +79,7 @@ def trunc_password(password):
def hash_user_password(user):
- """Hash a user dict's password without modifying the passed-in dict"""
+ """Hash a user dict's password without modifying the passed-in dict."""
try:
password = user['password']
except KeyError:
@@ -89,7 +89,7 @@ def hash_user_password(user):
def hash_ldap_user_password(user):
- """Hash a user dict's password without modifying the passed-in dict"""
+ """Hash a user dict's password without modifying the passed-in dict."""
try:
password = user['password']
except KeyError:
@@ -151,9 +151,10 @@ def check_output(*popenargs, **kwargs):
The stdout argument is not allowed as it is used internally.
To capture standard error in the result, use stderr=STDOUT.
+ >>> import sys
>>> check_output(['/bin/sh', '-c',
... 'ls -l non_existent_file ; exit 0'],
- ... stderr=STDOUT)
+ ... stderr=sys.STDOUT)
'ls: non_existent_file: No such file or directory\n'
"""
if 'stdout' in kwargs:
@@ -216,10 +217,6 @@ def hash_signed_token(signed_text):
def setup_remote_pydev_debug():
if CONF.pydev_debug_host and CONF.pydev_debug_port:
- error_msg = ('Error setting up the debug environment. Verify that the'
- ' option --debug-url has the format <host>:<port> and '
- 'that a debugger processes is listening on that port.')
-
try:
try:
from pydevd import pydevd
@@ -231,15 +228,19 @@ def setup_remote_pydev_debug():
stdoutToServer=True,
stderrToServer=True)
return True
- except:
- LOG.exception(_(error_msg))
+ except Exception:
+ LOG.exception(_(
+ 'Error setting up the debug environment. Verify that the '
+ 'option --debug-url has the format <host>:<port> and that a '
+ 'debugger processes is listening on that port.'))
raise
class LimitingReader(object):
"""Reader to limit the size of an incoming request."""
def __init__(self, data, limit):
- """
+ """Create an iterator on the underlying data.
+
:param data: Underlying data object
:param limit: maximum number of bytes the reader should allow
"""
diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py
index 08540b6a..a07ae117 100644
--- a/keystone/common/wsgi.py
+++ b/keystone/common/wsgi.py
@@ -20,12 +20,9 @@
"""Utility methods for working with WSGI servers."""
-import socket
-import sys
+import re
-import eventlet.wsgi
import routes.middleware
-import ssl
import webob.dec
import webob.exc
@@ -48,6 +45,34 @@ CONTEXT_ENV = 'openstack.context'
PARAMS_ENV = 'openstack.params'
+_RE_PASS = re.compile(r'([\'"].*?password[\'"]\s*:\s*u?[\'"]).*?([\'"])',
+ re.DOTALL)
+
+
+def mask_password(message, is_unicode=False, secret="***"):
+ """Replace password with 'secret' in message.
+
+ :param message: The string which include security information.
+ :param is_unicode: Is unicode string ?
+ :param secret: substitution string default to "***".
+ :returns: The string
+
+ For example:
+ >>> mask_password('"password" : "aaaaa"')
+ '"password" : "***"'
+ >>> mask_password("'original_password' : 'aaaaa'")
+ "'original_password' : '***'"
+ >>> mask_password("u'original_password' : u'aaaaa'")
+ "u'original_password' : u'***'"
+ """
+ if is_unicode:
+ message = unicode(message)
+ # Match the group 1,2 and replace all others with 'secret'
+ secret = r"\g<1>" + secret + r"\g<2>"
+ result = _RE_PASS.sub(secret, message)
+ return result
+
+
class WritableLogger(object):
"""A thin wrapper that responds to `write` and logs."""
@@ -59,85 +84,6 @@ class WritableLogger(object):
self.logger.log(self.level, msg)
-class Server(object):
- """Server class to manage multiple WSGI sockets and applications."""
-
- def __init__(self, application, host=None, port=None, threads=1000):
- self.application = application
- self.host = host or '0.0.0.0'
- self.port = port or 0
- self.pool = eventlet.GreenPool(threads)
- self.socket_info = {}
- self.greenthread = None
- self.do_ssl = False
- self.cert_required = False
-
- def start(self, key=None, backlog=128):
- """Run a WSGI server with the given application."""
- LOG.debug(_('Starting %(arg0)s on %(host)s:%(port)s') %
- {'arg0': sys.argv[0],
- 'host': self.host,
- 'port': self.port})
-
- # TODO(dims): eventlet's green dns/socket module does not actually
- # support IPv6 in getaddrinfo(). We need to get around this in the
- # future or monitor upstream for a fix
- info = socket.getaddrinfo(self.host,
- self.port,
- socket.AF_UNSPEC,
- socket.SOCK_STREAM)[0]
- _socket = eventlet.listen(info[-1],
- family=info[0],
- backlog=backlog)
- if key:
- self.socket_info[key] = _socket.getsockname()
- # SSL is enabled
- if self.do_ssl:
- if self.cert_required:
- cert_reqs = ssl.CERT_REQUIRED
- else:
- cert_reqs = ssl.CERT_NONE
- sslsocket = eventlet.wrap_ssl(_socket, certfile=self.certfile,
- keyfile=self.keyfile,
- server_side=True,
- cert_reqs=cert_reqs,
- ca_certs=self.ca_certs)
- _socket = sslsocket
-
- self.greenthread = self.pool.spawn(self._run,
- self.application,
- _socket)
-
- def set_ssl(self, certfile, keyfile=None, ca_certs=None,
- cert_required=True):
- self.certfile = certfile
- self.keyfile = keyfile
- self.ca_certs = ca_certs
- self.cert_required = cert_required
- self.do_ssl = True
-
- def kill(self):
- if self.greenthread:
- self.greenthread.kill()
-
- def wait(self):
- """Wait until all servers have completed running."""
- try:
- self.pool.waitall()
- except KeyboardInterrupt:
- pass
-
- def _run(self, application, socket):
- """Start a WSGI server in a new green thread."""
- log = logging.getLogger('eventlet.wsgi.server')
- try:
- eventlet.wsgi.server(socket, application, custom_pool=self.pool,
- log=WritableLogger(log))
- except Exception:
- LOG.exception(_('Server error'))
- raise
-
-
class Request(webob.Request):
pass
@@ -235,8 +181,9 @@ class Application(BaseApplication):
try:
result = method(context, **params)
except exception.Unauthorized as e:
- LOG.warning(_("Authorization failed. %s from %s")
- % (e, req.environ['REMOTE_ADDR']))
+ LOG.warning(
+ _('Authorization failed. %(exception)s from %(remote_addr)s') %
+ {'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']})
return render_exception(e)
except exception.Error as e:
LOG.warning(e)
@@ -389,21 +336,23 @@ class Debug(Middleware):
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
- LOG.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20))
- for key, value in req.environ.items():
- LOG.debug('%s = %s', key, value)
- LOG.debug('')
- LOG.debug('%s %s %s', ('*' * 20), 'REQUEST BODY', ('*' * 20))
- for line in req.body_file:
- LOG.debug(line)
- LOG.debug('')
+ if LOG.isEnabledFor(logging.DEBUG):
+ LOG.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20))
+ for key, value in req.environ.items():
+ LOG.debug('%s = %s', key, mask_password(value,
+ is_unicode=True))
+ LOG.debug('')
+ LOG.debug('%s %s %s', ('*' * 20), 'REQUEST BODY', ('*' * 20))
+ for line in req.body_file:
+ LOG.debug(mask_password(line))
+ LOG.debug('')
resp = req.get_response(self.application)
-
- LOG.debug('%s %s %s', ('*' * 20), 'RESPONSE HEADERS', ('*' * 20))
- for (key, value) in resp.headers.iteritems():
- LOG.debug('%s = %s', key, value)
- LOG.debug('')
+ if LOG.isEnabledFor(logging.DEBUG):
+ LOG.debug('%s %s %s', ('*' * 20), 'RESPONSE HEADERS', ('*' * 20))
+ for (key, value) in resp.headers.iteritems():
+ LOG.debug('%s = %s', key, value)
+ LOG.debug('')
resp.app_iter = self.print_generator(resp.app_iter)
diff --git a/keystone/common/wsgi_server.py b/keystone/common/wsgi_server.py
new file mode 100644
index 00000000..f3c22907
--- /dev/null
+++ b/keystone/common/wsgi_server.py
@@ -0,0 +1,120 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2010 OpenStack LLC.
+# 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 os
+import socket
+import ssl
+import sys
+
+import eventlet
+import eventlet.wsgi
+
+from keystone.common import logging
+from keystone.common import wsgi
+
+
+LOG = logging.getLogger(__name__)
+
+
+def monkey_patch_eventlet(monkeypatch_thread=None):
+ if monkeypatch_thread is None:
+ monkeypatch_thread = not os.getenv('STANDARD_THREADS')
+
+ eventlet.patcher.monkey_patch(all=False, socket=True, time=True,
+ thread=monkeypatch_thread)
+
+
+class Server(object):
+ """Server class to manage multiple WSGI sockets and applications."""
+
+ def __init__(self, application, host=None, port=None, threads=1000):
+ self.application = application
+ self.host = host or '0.0.0.0'
+ self.port = port or 0
+ self.pool = eventlet.GreenPool(threads)
+ self.socket_info = {}
+ self.greenthread = None
+ self.do_ssl = False
+ self.cert_required = False
+
+ def start(self, key=None, backlog=128):
+ """Run a WSGI server with the given application."""
+ LOG.debug(_('Starting %(arg0)s on %(host)s:%(port)s') %
+ {'arg0': sys.argv[0],
+ 'host': self.host,
+ 'port': self.port})
+
+ # TODO(dims): eventlet's green dns/socket module does not actually
+ # support IPv6 in getaddrinfo(). We need to get around this in the
+ # future or monitor upstream for a fix
+ info = socket.getaddrinfo(self.host,
+ self.port,
+ socket.AF_UNSPEC,
+ socket.SOCK_STREAM)[0]
+ _socket = eventlet.listen(info[-1],
+ family=info[0],
+ backlog=backlog)
+ if key:
+ self.socket_info[key] = _socket.getsockname()
+ # SSL is enabled
+ if self.do_ssl:
+ if self.cert_required:
+ cert_reqs = ssl.CERT_REQUIRED
+ else:
+ cert_reqs = ssl.CERT_NONE
+ sslsocket = eventlet.wrap_ssl(_socket, certfile=self.certfile,
+ keyfile=self.keyfile,
+ server_side=True,
+ cert_reqs=cert_reqs,
+ ca_certs=self.ca_certs)
+ _socket = sslsocket
+
+ self.greenthread = self.pool.spawn(self._run,
+ self.application,
+ _socket)
+
+ def set_ssl(self, certfile, keyfile=None, ca_certs=None,
+ cert_required=True):
+ self.certfile = certfile
+ self.keyfile = keyfile
+ self.ca_certs = ca_certs
+ self.cert_required = cert_required
+ self.do_ssl = True
+
+ def kill(self):
+ if self.greenthread:
+ self.greenthread.kill()
+
+ def wait(self):
+ """Wait until all servers have completed running."""
+ try:
+ self.pool.waitall()
+ except KeyboardInterrupt:
+ pass
+
+ def _run(self, application, socket):
+ """Start a WSGI server in a new green thread."""
+ log = logging.getLogger('eventlet.wsgi.server')
+ try:
+ eventlet.wsgi.server(socket, application, custom_pool=self.pool,
+ log=wsgi.WritableLogger(log))
+ except Exception:
+ LOG.exception(_('Server error'))
+ raise
diff --git a/keystone/config.py b/keystone/config.py
index e2ff6f4e..28f1cf2c 100644
--- a/keystone/config.py
+++ b/keystone/config.py
@@ -15,7 +15,10 @@
# under the License.
"""Wrapper for keystone.common.config that configures itself on import."""
+import os
+
from keystone.common import config
+from keystone import exception
config.configure()
@@ -31,3 +34,31 @@ register_cli_bool = config.register_cli_bool
register_int = config.register_int
register_cli_int = config.register_cli_int
setup_authentication = config.setup_authentication
+
+
+def find_paste_config():
+ """Selects Keystone paste.deploy configuration file.
+
+ Keystone paste.deploy configuration file is selectd in [paste_deploy]
+ section of the main Keystone configuration file.
+ For example:
+ [paste_deploy]
+ config_file = keystone-paste.ini
+
+ :returns: The selected configuration filename
+ :raises: exception.PasteConfigNotFound
+ """
+ if CONF.paste_deploy.config_file:
+ paste_config = CONF.paste_deploy.config_file
+ paste_config_value = paste_config
+ if not os.path.isabs(paste_config):
+ paste_config = CONF.find_file(paste_config)
+ elif CONF.config_file:
+ paste_config = CONF.config_file[0]
+ paste_config_value = paste_config
+ else:
+ paste_config = CONF.find_file('keystone.conf')
+ paste_config_value = 'keystone.conf'
+ if not paste_config or not os.path.exists(paste_config):
+ raise exception.PasteConfigNotFound(config_file=paste_config_value)
+ return paste_config
diff --git a/keystone/contrib/access/__init__.py b/keystone/contrib/access/__init__.py
index 4b782183..6eb9aa43 100644
--- a/keystone/contrib/access/__init__.py
+++ b/keystone/contrib/access/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2013 OpenStack LLC
#
diff --git a/keystone/contrib/admin_crud/__init__.py b/keystone/contrib/admin_crud/__init__.py
index 4fb4bab0..e0d488f3 100644
--- a/keystone/contrib/admin_crud/__init__.py
+++ b/keystone/contrib/admin_crud/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/contrib/ec2/__init__.py b/keystone/contrib/ec2/__init__.py
index 50c4039f..3fbaa09b 100644
--- a/keystone/contrib/ec2/__init__.py
+++ b/keystone/contrib/ec2/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/contrib/s3/__init__.py b/keystone/contrib/s3/__init__.py
index 84de6dcf..c77b04e9 100644
--- a/keystone/contrib/s3/__init__.py
+++ b/keystone/contrib/s3/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/contrib/s3/core.py b/keystone/contrib/s3/core.py
index 56e10ca4..44b038d4 100644
--- a/keystone/contrib/s3/core.py
+++ b/keystone/contrib/s3/core.py
@@ -24,10 +24,9 @@ for the EC2 module for how to generate the required credentials.
"""
import base64
+import hashlib
import hmac
-from hashlib import sha1
-
from keystone.common import utils
from keystone.common import wsgi
from keystone import config
@@ -51,7 +50,8 @@ class S3Controller(ec2.Ec2Controller):
def check_signature(self, creds_ref, credentials):
msg = base64.urlsafe_b64decode(str(credentials['token']))
key = str(creds_ref['secret'])
- signed = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip()
+ signed = base64.encodestring(
+ hmac.new(key, msg, hashlib.sha1).digest()).strip()
if not utils.auth_str_equal(credentials['signature'], signed):
raise exception.Unauthorized('Credential signature mismatch')
diff --git a/keystone/contrib/stats/__init__.py b/keystone/contrib/stats/__init__.py
index c4a07cbc..74eaa95e 100644
--- a/keystone/contrib/stats/__init__.py
+++ b/keystone/contrib/stats/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/contrib/user_crud/__init__.py b/keystone/contrib/user_crud/__init__.py
index 8f4a83f0..a07019f1 100644
--- a/keystone/contrib/user_crud/__init__.py
+++ b/keystone/contrib/user_crud/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 Red Hat, Inc
#
diff --git a/keystone/contrib/user_crud/core.py b/keystone/contrib/user_crud/core.py
index 0df5f9de..4ccb486b 100644
--- a/keystone/contrib/user_crud/core.py
+++ b/keystone/contrib/user_crud/core.py
@@ -70,11 +70,7 @@ class UserController(identity.controllers.User):
class CrudExtension(wsgi.ExtensionRouter):
- """
-
- Provides a subset of CRUD operations for internal data types.
-
- """
+ """Provides a subset of CRUD operations for internal data types."""
def add_routes(self, mapper):
user_controller = UserController()
diff --git a/keystone/controllers.py b/keystone/controllers.py
index 150971ba..6dd303e1 100644
--- a/keystone/controllers.py
+++ b/keystone/controllers.py
@@ -14,11 +14,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystone.common import wsgi
from keystone.common import logging
+from keystone.common import wsgi
from keystone import config
from keystone import exception
+
LOG = logging.getLogger(__name__)
CONF = config.CONF
diff --git a/keystone/credential/__init__.py b/keystone/credential/__init__.py
new file mode 100644
index 00000000..4891a210
--- /dev/null
+++ b/keystone/credential/__init__.py
@@ -0,0 +1,20 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
+
+# Copyright 2013 OpenStack LLC
+#
+# 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 keystone.credential import controllers
+from keystone.credential.core import *
+from keystone.credential import routers
diff --git a/keystone/credential/backends/__init__.py b/keystone/credential/backends/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/credential/backends/__init__.py
diff --git a/keystone/credential/backends/sql.py b/keystone/credential/backends/sql.py
new file mode 100644
index 00000000..cf8d4bd3
--- /dev/null
+++ b/keystone/credential/backends/sql.py
@@ -0,0 +1,88 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# 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 keystone.common import sql
+from keystone.common.sql import migration
+from keystone import credential
+from keystone import exception
+
+
+class CredentialModel(sql.ModelBase, sql.DictBase):
+ __tablename__ = 'credential'
+ attributes = ['id', 'user_id', 'project_id', 'blob', 'type']
+ id = sql.Column(sql.String(64), primary_key=True)
+ user_id = sql.Column(sql.String(64),
+ nullable=False)
+ project_id = sql.Column(sql.String(64))
+ blob = sql.Column(sql.JsonBlob(), nullable=False)
+ type = sql.Column(sql.String(255), nullable=False)
+ extra = sql.Column(sql.JsonBlob())
+
+
+class Credential(sql.Base, credential.Driver):
+ # Internal interface to manage the database
+ def db_sync(self):
+ migration.db_sync()
+
+ # credential crud
+
+ @sql.handle_conflicts(type='credential')
+ def create_credential(self, credential_id, credential):
+ session = self.get_session()
+ with session.begin():
+ ref = CredentialModel.from_dict(credential)
+ session.add(ref)
+ session.flush()
+ return ref.to_dict()
+
+ def list_credentials(self):
+ session = self.get_session()
+ refs = session.query(CredentialModel).all()
+ return [ref.to_dict() for ref in refs]
+
+ def _get_credential(self, session, credential_id):
+ ref = session.query(CredentialModel).get(credential_id)
+ if ref is None:
+ raise exception.CredentialNotFound(credential_id=credential_id)
+ return ref
+
+ def get_credential(self, credential_id):
+ session = self.get_session()
+ return self._get_credential(session, credential_id).to_dict()
+
+ @sql.handle_conflicts(type='credential')
+ def update_credential(self, credential_id, credential):
+ session = self.get_session()
+ with session.begin():
+ ref = self._get_credential(session, credential_id)
+ old_dict = ref.to_dict()
+ for k in credential:
+ old_dict[k] = credential[k]
+ new_credential = CredentialModel.from_dict(old_dict)
+ for attr in CredentialModel.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_credential, attr))
+ ref.extra = new_credential.extra
+ session.flush()
+ return ref.to_dict()
+
+ def delete_credential(self, credential_id):
+ session = self.get_session()
+
+ with session.begin():
+ ref = self._get_credential(session, credential_id)
+ session.delete(ref)
+ session.flush()
diff --git a/keystone/credential/controllers.py b/keystone/credential/controllers.py
new file mode 100644
index 00000000..8743456d
--- /dev/null
+++ b/keystone/credential/controllers.py
@@ -0,0 +1,52 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# 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 keystone.common import controller
+
+
+class CredentialV3(controller.V3Controller):
+ collection_name = 'credentials'
+ member_name = 'credential'
+
+ @controller.protected
+ def create_credential(self, context, credential):
+ ref = self._assign_unique_id(self._normalize_dict(credential))
+ ref = self.credential_api.create_credential(context, ref['id'], ref)
+ return CredentialV3.wrap_member(context, ref)
+
+ @controller.protected
+ def list_credentials(self, context):
+ refs = self.credential_api.list_credentials(context)
+ return CredentialV3.wrap_collection(context, refs)
+
+ @controller.protected
+ def get_credential(self, context, credential_id):
+ ref = self.credential_api.get_credential(context, credential_id)
+ return CredentialV3.wrap_member(context, ref)
+
+ @controller.protected
+ def update_credential(self, context, credential_id, credential):
+ self._require_matching_id(credential_id, credential)
+
+ ref = self.credential_api.update_credential(
+ context,
+ credential_id,
+ credential)
+ return CredentialV3.wrap_member(context, ref)
+
+ @controller.protected
+ def delete_credential(self, context, credential_id):
+ return self.credential_api.delete_credential(context, credential_id)
diff --git a/keystone/credential/core.py b/keystone/credential/core.py
new file mode 100644
index 00000000..a8921ba0
--- /dev/null
+++ b/keystone/credential/core.py
@@ -0,0 +1,87 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# 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.
+
+"""Main entry point into the Credentials service."""
+
+from keystone.common import dependency
+from keystone.common import logging
+from keystone.common import manager
+from keystone import config
+from keystone import exception
+
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+@dependency.provider('credential_api')
+class Manager(manager.Manager):
+ """Default pivot point for the Credential backend.
+
+ See :mod:`keystone.common.manager.Manager` for more details on how this
+ dynamically calls the backend.
+
+ """
+
+ def __init__(self):
+ super(Manager, self).__init__(CONF.credential.driver)
+
+
+class Driver(object):
+ # credential crud
+
+ def create_credential(self, credential_id, credential):
+ """Creates a new credential.
+
+ :raises: keystone.exception.Conflict
+
+ """
+ raise exception.NotImplemented()
+
+ def list_credentials(self):
+ """List all credentials in the system.
+
+ :returns: a list of credential_refs or an empty list.
+
+ """
+ raise exception.NotImplemented()
+
+ def get_credential(self, credential_id):
+ """Get a credential by ID.
+
+ :returns: credential_ref
+ :raises: keystone.exception.CredentialNotFound
+
+ """
+ raise exception.NotImplemented()
+
+ def update_credential(self, credential_id, credential):
+ """Updates an existing credential.
+
+ :raises: keystone.exception.CredentialNotFound,
+ keystone.exception.Conflict
+
+ """
+ raise exception.NotImplemented()
+
+ def delete_credential(self, credential_id):
+ """Deletes an existing credential.
+
+ :raises: keystone.exception.CredentialNotFound
+
+ """
+ raise exception.NotImplemented()
diff --git a/keystone/credential/routers.py b/keystone/credential/routers.py
new file mode 100644
index 00000000..2b27a039
--- /dev/null
+++ b/keystone/credential/routers.py
@@ -0,0 +1,26 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# 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.
+
+"""WSGI Routers for the Credentials service."""
+
+from keystone.common import router
+from keystone.credential import controllers
+
+
+def append_v3_routers(mapper, routers):
+ routers.append(
+ router.Router(controllers.CredentialV3(),
+ 'credentials', 'credential'))
diff --git a/keystone/exception.py b/keystone/exception.py
index eaacfcf0..39a08d4a 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -56,14 +56,10 @@ class Error(StandardError):
:raises: KeyError given insufficient kwargs
"""
- return message or self.__doc__ % kwargs
-
- def __str__(self):
- """Cleans up line breaks and indentation from doc strings."""
- string = super(Error, self).__str__()
- string = re.sub('[ \n]+', ' ', string)
- string = string.strip()
- return string
+ if not message:
+ message = re.sub('[ \n]+', ' ', self.__doc__ % kwargs)
+ message = message.strip()
+ return message
class ValidationError(Error):
@@ -80,8 +76,12 @@ class ValidationError(Error):
class StringLengthExceeded(ValidationError):
- """The length of string "%(string)s" exceeded the limit of column
- %(type)s(CHAR(%(length)d))."""
+ """String length exceeded.
+
+ The length of string "%(string)s" exceeded the limit of column
+ %(type)s(CHAR(%(length)d)).
+
+ """
class ValidationSizeError(Error):
@@ -115,7 +115,7 @@ class Unauthorized(SecurityError):
class AuthPluginException(Unauthorized):
- """ Authentication plugin error. """
+ """Authentication plugin error."""
def __init__(self, *args, **kwargs):
super(AuthPluginException, self).__init__(*args, **kwargs)
@@ -123,7 +123,7 @@ class AuthPluginException(Unauthorized):
class AuthMethodNotSupported(AuthPluginException):
- """ Attempted to authenticate with an unsupported method. """
+ """Attempted to authenticate with an unsupported method."""
def __init__(self, *args, **kwargs):
super(AuthMethodNotSupported, self).__init__(*args, **kwargs)
@@ -131,7 +131,7 @@ class AuthMethodNotSupported(AuthPluginException):
class AdditionalAuthRequired(AuthPluginException):
- """ Additional authentications steps required. """
+ """Additional authentications steps required."""
def __init__(self, auth_response=None, **kwargs):
super(AdditionalAuthRequired, self).__init__(message=None, **kwargs)
@@ -141,21 +141,21 @@ class AdditionalAuthRequired(AuthPluginException):
class Forbidden(SecurityError):
"""You are not authorized to perform the requested action."""
code = 403
- title = 'Not Authorized'
+ title = 'Forbidden'
class ForbiddenAction(Forbidden):
- """You are not authorized to perform the requested action: %(action)s"""
+ """You are not authorized to perform the requested action, %(action)s."""
class NotFound(Error):
- """Could not find: %(target)s"""
+ """Could not find, %(target)s."""
code = 404
title = 'Not Found'
class EndpointNotFound(NotFound):
- """Could not find endpoint: %(endpoint_id)s"""
+ """Could not find endpoint, %(endpoint_id)s."""
class MetadataNotFound(NotFound):
@@ -165,47 +165,47 @@ class MetadataNotFound(NotFound):
class PolicyNotFound(NotFound):
- """Could not find policy: %(policy_id)s"""
+ """Could not find policy, %(policy_id)s."""
class RoleNotFound(NotFound):
- """Could not find role: %(role_id)s"""
+ """Could not find role, %(role_id)s."""
class ServiceNotFound(NotFound):
- """Could not find service: %(service_id)s"""
+ """Could not find service, %(service_id)s."""
class DomainNotFound(NotFound):
- """Could not find domain: %(domain_id)s"""
+ """Could not find domain, %(domain_id)s."""
class ProjectNotFound(NotFound):
- """Could not find project: %(project_id)s"""
+ """Could not find project, %(project_id)s."""
class TokenNotFound(NotFound):
- """Could not find token: %(token_id)s"""
+ """Could not find token, %(token_id)s."""
class UserNotFound(NotFound):
- """Could not find user: %(user_id)s"""
+ """Could not find user, %(user_id)s."""
class GroupNotFound(NotFound):
- """Could not find group: %(group_id)s"""
+ """Could not find group, %(group_id)s."""
class TrustNotFound(NotFound):
- """Could not find trust: %(trust_id)s"""
+ """Could not find trust, %(trust_id)s."""
class CredentialNotFound(NotFound):
- """Could not find credential: %(credential_id)s"""
+ """Could not find credential, %(credential_id)s."""
class VersionNotFound(NotFound):
- """Could not find version: %(version)s"""
+ """Could not find version, %(version)s."""
class Conflict(Error):
@@ -235,10 +235,16 @@ class UnexpectedError(Error):
class MalformedEndpoint(UnexpectedError):
- """Malformed endpoint URL (see ERROR log for details): %(endpoint)s"""
+ """Malformed endpoint URL (%(endpoint)s), see ERROR log for details."""
class NotImplemented(Error):
"""The action you have requested has not been implemented."""
code = 501
title = 'Not Implemented'
+
+
+class PasteConfigNotFound(UnexpectedError):
+ """The Keystone paste configuration file %(config_file)s could not be
+ found.
+ """
diff --git a/keystone/identity/__init__.py b/keystone/identity/__init__.py
index 58853cc6..514db788 100644
--- a/keystone/identity/__init__.py
+++ b/keystone/identity/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py
index 3b03c930..101ceb9e 100644
--- a/keystone/identity/backends/kvs.py
+++ b/keystone/identity/backends/kvs.py
@@ -190,6 +190,7 @@ class Identity(kvs.Base, identity.Driver):
# CRUD
def create_user(self, user_id, user):
user['name'] = clean.user_name(user['name'])
+ user['enabled'] = clean.user_enabled(user.get('enabled', True))
try:
self.get_user(user_id)
except exception.UserNotFound:
@@ -225,6 +226,8 @@ class Identity(kvs.Base, identity.Driver):
if existing and user_id != existing['id']:
msg = 'Duplicate name, %s.' % user['name']
raise exception.Conflict(type='user', details=msg)
+ if 'enabled' in user:
+ user['enabled'] = clean.user_enabled(user['enabled'])
# get the old name and delete it too
try:
old_user = self.db.get('user-%s' % user_id)
diff --git a/keystone/identity/backends/ldap/__init__.py b/keystone/identity/backends/ldap/__init__.py
index 17903c93..043f9e8f 100644
--- a/keystone/identity/backends/ldap/__init__.py
+++ b/keystone/identity/backends/ldap/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py
index 58ab3bd0..65330149 100644
--- a/keystone/identity/backends/ldap/core.py
+++ b/keystone/identity/backends/ldap/core.py
@@ -28,10 +28,16 @@ from keystone import config
from keystone import exception
from keystone import identity
-CONF = config.CONF
+CONF = config.CONF
LOG = logging.getLogger(__name__)
+DEFAULT_DOMAIN = {
+ 'id': CONF.identity.default_domain_id,
+ 'name': 'Default',
+ 'enabled': True
+}
+
class Identity(identity.Driver):
def __init__(self):
@@ -45,7 +51,6 @@ class Identity(identity.Driver):
self.project = ProjectApi(CONF)
self.role = RoleApi(CONF)
self.group = GroupApi(CONF)
- self.domain = DomainApi(CONF)
def get_connection(self, user=None, password=None):
if self.LDAP_URL.startswith('fake://'):
@@ -59,6 +64,36 @@ class Identity(identity.Driver):
conn.simple_bind_s(user, password)
return conn
+ def _validate_domain(self, ref):
+ """Validate that either the default domain or nothing is specified.
+
+ Also removes the domain from the ref so that LDAP doesn't have to
+ persist the attribute.
+
+ """
+ ref = ref.copy()
+ domain_id = ref.pop('domain_id', CONF.identity.default_domain_id)
+ self._validate_domain_id(domain_id)
+ return ref
+
+ def _validate_domain_id(self, domain_id):
+ """Validate that the domain ID specified belongs to the default domain.
+
+ """
+ if domain_id != CONF.identity.default_domain_id:
+ raise exception.DomainNotFound(domain_id=domain_id)
+
+ def _set_default_domain(self, ref):
+ """Overrides any domain reference with the default domain."""
+ if isinstance(ref, dict):
+ ref = ref.copy()
+ ref['domain_id'] = CONF.identity.default_domain_id
+ return ref
+ elif isinstance(ref, list):
+ return [self._set_default_domain(x) for x in ref]
+ else:
+ raise ValueError(_('Expected dict or list: %s') % type(ref))
+
# Identity interface
def authenticate(self, user_id=None, tenant_id=None, password=None):
"""Authenticate based on a user, tenant and password.
@@ -97,38 +132,38 @@ class Identity(identity.Driver):
except exception.MetadataNotFound:
metadata_ref = {}
- return (identity.filter_user(user_ref), tenant_ref, metadata_ref)
+ user_ref = self._set_default_domain(identity.filter_user(user_ref))
+ return (user_ref, tenant_ref, metadata_ref)
def get_project(self, tenant_id):
- return self.project.get(tenant_id)
+ return self._set_default_domain(self.project.get(tenant_id))
def list_projects(self):
- return self.project.get_all()
+ return self._set_default_domain(self.project.get_all())
def get_project_by_name(self, tenant_name, domain_id):
- # TODO(henry-nash): Use domain_id once domains are implemented
- # in LDAP backend
- return self.project.get_by_name(tenant_name)
+ self._validate_domain_id(domain_id)
+ return self._set_default_domain(self.project.get_by_name(tenant_name))
def _get_user(self, user_id):
return self.user.get(user_id)
def get_user(self, user_id):
- return identity.filter_user(self._get_user(user_id))
+ ref = identity.filter_user(self._get_user(user_id))
+ return self._set_default_domain(ref)
def list_users(self):
- return self.user.get_all()
+ return self._set_default_domain(self.user.get_all())
def get_user_by_name(self, user_name, domain_id):
- # TODO(henry-nash): Use domain_id once domains are implemented
- # in LDAP backend
- return identity.filter_user(self.user.get_by_name(user_name))
+ self._validate_domain_id(domain_id)
+ ref = identity.filter_user(self.user.get_by_name(user_name))
+ return self._set_default_domain(ref)
def get_metadata(self, user_id=None, tenant_id=None,
domain_id=None, group_id=None):
- # FIXME(henry-nash): Use domain_id and group_id once domains
- # and groups are implemented in LDAP backend
-
+ if domain_id is not None:
+ raise NotImplemented('Domain metadata not supported by LDAP.')
if not self.get_project(tenant_id) or not self.get_user(user_id):
return {}
@@ -149,7 +184,7 @@ class Identity(identity.Driver):
def get_project_users(self, tenant_id):
self.get_project(tenant_id)
- return self.project.get_users(tenant_id)
+ return self._set_default_domain(self.project.get_users(tenant_id))
def get_roles_for_user_and_project(self, user_id, tenant_id):
self.get_user(user_id)
@@ -165,27 +200,35 @@ class Identity(identity.Driver):
# CRUD
def create_user(self, user_id, user):
+ user = self._validate_domain(user)
user['name'] = clean.user_name(user['name'])
- return identity.filter_user(self.user.create(user))
+ user['enabled'] = clean.user_enabled(user.get('enabled', True))
+ user_ref = self.user.create(user)
+ return self._set_default_domain(identity.filter_user(user_ref))
def update_user(self, user_id, user):
+ user = self._validate_domain(user)
if 'name' in user:
user['name'] = clean.user_name(user['name'])
- return self.user.update(user_id, user)
+ if 'enabled' in user:
+ user['enabled'] = clean.user_enabled(user['enabled'])
+ return self._set_default_domain(self.user.update(user_id, user))
def create_project(self, tenant_id, tenant):
+ tenant = self._validate_domain(tenant)
tenant['name'] = clean.project_name(tenant['name'])
data = tenant.copy()
if 'id' not in data or data['id'] is None:
data['id'] = str(uuid.uuid4().hex)
if 'description' in data and data['description'] in ['', None]:
data.pop('description')
- return self.project.create(data)
+ return self._set_default_domain(self.project.create(data))
def update_project(self, tenant_id, tenant):
+ tenant = self._validate_domain(tenant)
if 'name' in tenant:
tenant['name'] = clean.project_name(tenant['name'])
- return self.project.update(tenant_id, tenant)
+ return self._set_default_domain(self.project.update(tenant_id, tenant))
def create_metadata(self, user_id, tenant_id, metadata):
return {}
@@ -226,16 +269,18 @@ class Identity(identity.Driver):
self.role.update(role_id, role)
def create_group(self, group_id, group):
+ group = self._validate_domain(group)
group['name'] = clean.group_name(group['name'])
- return self.group.create(group)
+ return self._set_default_domain(self.group.create(group))
def get_group(self, group_id):
- return self.group.get(group_id)
+ return self._set_default_domain(self.group.get(group_id))
def update_group(self, group_id, group):
+ group = self._validate_domain(group)
if 'name' in group:
group['name'] = clean.group_name(group['name'])
- return self.group.update(group_id, group)
+ return self._set_default_domain(self.group.update(group_id, group))
def delete_group(self, group_id):
return self.group.delete(group_id)
@@ -252,14 +297,14 @@ class Identity(identity.Driver):
def list_groups_for_user(self, user_id):
self.get_user(user_id)
- return self.group.list_user_groups(user_id)
+ return self._set_default_domain(self.group.list_user_groups(user_id))
def list_groups(self):
- return self.group.get_all()
+ return self._set_default_domain(self.group.get_all())
def list_users_in_group(self, group_id):
self.get_group(group_id)
- return self.group.list_group_users(group_id)
+ return self._set_default_domain(self.group.list_group_users(group_id))
def check_user_in_group(self, user_id, group_id):
self.get_user(user_id)
@@ -273,28 +318,25 @@ class Identity(identity.Driver):
return found
def create_domain(self, domain_id, domain):
- domain['name'] = clean.domain_name(domain['name'])
- return self.domain.create(domain)
+ if domain_id == CONF.identity.default_domain_id:
+ msg = 'Duplicate ID, %s.' % domain_id
+ raise exception.Conflict(type='domain', details=msg)
+ raise exception.Forbidden('Domains are read-only against LDAP')
def get_domain(self, domain_id):
- try:
- return self.domain.get(domain_id)
- except exception.NotFound:
- raise exception.DomainNotFound(domain_id=domain_id)
+ self._validate_domain_id(domain_id)
+ return DEFAULT_DOMAIN
def update_domain(self, domain_id, domain):
- if 'name' in domain:
- domain['name'] = clean.domain_name(domain['name'])
- return self.domain.update(domain_id, domain)
+ self._validate_domain_id(domain_id)
+ raise exception.Forbidden('Domains are read-only against LDAP')
def delete_domain(self, domain_id):
- try:
- return self.domain.delete(domain_id)
- except ldap.NO_SUCH_OBJECT:
- raise exception.DomainNotFound(domain_id=domain_id)
+ self._validate_domain_id(domain_id)
+ raise exception.Forbidden('Domains are read-only against LDAP')
def list_domains(self):
- return self.domain.get_all()
+ return [DEFAULT_DOMAIN]
# TODO(termie): remove this and move cross-api calls into driver
@@ -338,12 +380,6 @@ class ApiShim(object):
self._group = GroupApi(self.conf)
return self._group
- @property
- def domain(self):
- if not self._domain:
- self._domain = DomainApi(self.conf)
- return self._domain
-
# TODO(termie): remove this and move cross-api calls into driver
class ApiShimMixin(object):
@@ -577,7 +613,7 @@ class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
if old_obj['name'] != values['name']:
msg = 'Changing Name not supported by LDAP'
raise exception.NotImplemented(message=msg)
- super(ProjectApi, self).update(id, values, old_obj)
+ return super(ProjectApi, self).update(id, values, old_obj)
class UserRoleAssociation(object):
@@ -636,8 +672,6 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
return model
def create(self, values):
- #values['id'] = values['name']
- #delattr(values, 'name')
return super(RoleApi, self).create(values)
def add_user(self, role_id, user_id, tenant_id=None):
@@ -788,7 +822,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
raise exception.Conflict('Cannot duplicate name %s' % old_name)
except exception.NotFound:
pass
- super(RoleApi, self).update(role_id, role)
+ return super(RoleApi, self).update(role_id, role)
def delete(self, id):
conn = self.get_connection()
@@ -804,7 +838,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
pass
super(RoleApi, self).delete(id)
-# TODO (spzala) - this is only placeholder for group and domain role support
+# TODO(spzala): this is only placeholder for group and domain role support
# which will be added under bug 1101287
def roles_delete_subtree_by_type(self, id, type):
conn = self.get_connection()
@@ -819,10 +853,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
roles = conn.search_s(dn, ldap.SCOPE_ONELEVEL,
query, ['%s' % '1.1'])
for role_dn, _ in roles:
- try:
- conn.delete_s(role_dn)
- except:
- raise Exception
+ conn.delete_s(role_dn)
except ldap.NO_SUCH_OBJECT:
pass
@@ -875,7 +906,7 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
if old_obj['name'] != values['name']:
msg = _('Changing Name not supported by LDAP')
raise exception.NotImplemented(message=msg)
- super(GroupApi, self).update(id, values, old_obj)
+ return super(GroupApi, self).update(id, values, old_obj)
def add_user(self, user_id, group_id):
conn = self.get_connection()
@@ -886,9 +917,9 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
self.member_attribute,
self.user_api._id_to_dn(user_id))])
except ldap.TYPE_OR_VALUE_EXISTS:
- msg = _('User %s is already a member of group %s'
- % (user_id, group_id))
- raise exception.Conflict(msg)
+ raise exception.Conflict(_(
+ 'User %(user_id)s is already a member of group %(group_id)s') %
+ {'user_id': user_id, 'group_id': group_id})
def remove_user(self, user_id, group_id):
conn = self.get_connection()
@@ -902,14 +933,14 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
raise exception.UserNotFound(user_id=user_id)
def list_user_groups(self, user_id):
- """Returns a list of groups a user has access to"""
+ """Return a list of groups for which the user is a member."""
user_dn = self.user_api._id_to_dn(user_id)
query = '(%s=%s)' % (self.member_attribute, user_dn)
memberships = self.get_all(query)
return memberships
def list_group_users(self, group_id):
- """Returns a list of users that belong to a group"""
+ """Return a list of users which are members of a group."""
query = '(objectClass=%s)' % self.object_class
conn = self.get_connection()
group_dn = self._id_to_dn(group_id)
@@ -934,61 +965,3 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
" from the group. The user will be ignored.") %
dict(user_dn=user_dn, group_dn=group_dn))
return users
-
-
-class DomainApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
- ApiShimMixin):
- DEFAULT_OU = 'ou=Domains'
- DEFAULT_STRUCTURAL_CLASSES = []
- DEFAULT_OBJECTCLASS = 'groupOfNames'
- DEFAULT_ID_ATTR = 'cn'
- DEFAULT_MEMBER_ATTRIBUTE = 'member'
- DEFAULT_ATTRIBUTE_IGNORE = []
- options_name = 'domain'
- attribute_mapping = {'name': 'ou',
- 'description': 'description',
- 'domainId': 'cn',
- 'enabled': 'enabled'}
- model = models.Domain
-
- def __init__(self, conf):
- super(DomainApi, self).__init__(conf)
- self.api = ApiShim(conf)
- self.attribute_mapping['name'] = conf.ldap.domain_name_attribute
- self.attribute_mapping['description'] = conf.ldap.domain_desc_attribute
- self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute
- self.member_attribute = (getattr(conf.ldap, 'domain_member_attribute')
- or self.DEFAULT_MEMBER_ATTRIBUTE)
- self.attribute_ignore = (getattr(conf.ldap, 'domain_attribute_ignore')
- or self.DEFAULT_ATTRIBUTE_IGNORE)
-
- def get(self, id, filter=None):
- """Replaces exception.NotFound with exception.DomainNotFound."""
- try:
- return super(DomainApi, self).get(id, filter)
- except exception.NotFound:
- raise exception.DomainNotFound(domain_id=id)
-
- def create(self, values):
- self.affirm_unique(values)
- data = values.copy()
- if data.get('id') is None:
- data['id'] = uuid.uuid4().hex
- return super(DomainApi, self).create(data)
-
- def delete(self, id):
- if self.subtree_delete_enabled:
- super(DomainApi, self).deleteTree(id)
- else:
- self.role_api.roles_delete_subtree_by_type(id, 'Domain')
- super(DomainApi, self).delete(id)
-
- def update(self, id, values):
- try:
- old_obj = self.get(id)
- except exception.NotFound:
- raise exception.DomainNotFound(domain_id=id)
- if old_obj['name'] != values['name']:
- msg = _('Changing Name not supported by LDAP')
- raise exception.NotImplemented(message=msg)
- super(DomainApi, self).update(id, values, old_obj)
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py
index 048a0b2b..670ebb65 100644
--- a/keystone/identity/backends/sql.py
+++ b/keystone/identity/backends/sql.py
@@ -51,19 +51,6 @@ class Group(sql.ModelBase, sql.DictBase):
__table_args__ = (sql.UniqueConstraint('domain_id', 'name'), {})
-class Credential(sql.ModelBase, sql.DictBase):
- __tablename__ = 'credential'
- attributes = ['id', 'user_id', 'project_id', 'blob', 'type']
- id = sql.Column(sql.String(64), primary_key=True)
- user_id = sql.Column(sql.String(64),
- sql.ForeignKey('user.id'),
- nullable=False)
- project_id = sql.Column(sql.String(64), sql.ForeignKey('project.id'))
- blob = sql.Column(sql.JsonBlob(), nullable=False)
- type = sql.Column(sql.String(255), nullable=False)
- extra = sql.Column(sql.JsonBlob())
-
-
class Domain(sql.ModelBase, sql.DictBase):
__tablename__ = 'domain'
attributes = ['id', 'name', 'enabled']
@@ -166,7 +153,7 @@ class Identity(sql.Base, identity.Driver):
https://blueprints.launchpad.net/keystone/+spec/sql-identiy-pam
"""
- return utils.check_password(password, user_ref.get('password'))
+ return utils.check_password(password, user_ref.password)
# Identity interface
def authenticate(self, user_id=None, tenant_id=None, password=None):
@@ -176,12 +163,14 @@ class Identity(sql.Base, identity.Driver):
in the list of tenants on the user.
"""
+ session = self.get_session()
+
user_ref = None
tenant_ref = None
metadata_ref = {}
try:
- user_ref = self._get_user(user_id)
+ user_ref = self._get_user(session, user_id)
except exception.UserNotFound:
raise AssertionError('Invalid user / password')
@@ -201,14 +190,18 @@ class Identity(sql.Base, identity.Driver):
metadata_ref = {}
except exception.MetadataNotFound:
metadata_ref = {}
- return (identity.filter_user(user_ref), tenant_ref, metadata_ref)
+ user_ref = identity.filter_user(user_ref.to_dict())
+ return (user_ref, tenant_ref, metadata_ref)
+
+ def _get_project(self, session, project_id):
+ project_ref = session.query(Project).get(project_id)
+ if project_ref is None:
+ raise exception.ProjectNotFound(project_id=project_id)
+ return project_ref
def get_project(self, tenant_id):
session = self.get_session()
- tenant_ref = session.query(Project).filter_by(id=tenant_id).first()
- if tenant_ref is None:
- raise exception.ProjectNotFound(project_id=tenant_id)
- return tenant_ref.to_dict()
+ return self._get_project(session, tenant_id).to_dict()
def get_project_by_name(self, tenant_name, domain_id):
session = self.get_session()
@@ -258,16 +251,16 @@ class Identity(sql.Base, identity.Driver):
def create_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None):
-
- self.get_role(role_id)
+ session = self.get_session()
+ self._get_role(session, role_id)
if user_id:
- self.get_user(user_id)
+ self._get_user(session, user_id)
if group_id:
- self.get_group(group_id)
+ self._get_group(session, group_id)
if domain_id:
- self.get_domain(domain_id)
+ self._get_domain(session, domain_id)
if project_id:
- self.get_project(project_id)
+ self._get_project(session, project_id)
try:
metadata_ref = self.get_metadata(user_id, project_id,
@@ -288,14 +281,15 @@ class Identity(sql.Base, identity.Driver):
def list_grants(self, user_id=None, group_id=None,
domain_id=None, project_id=None):
+ session = self.get_session()
if user_id:
- self.get_user(user_id)
+ self._get_user(session, user_id)
if group_id:
- self.get_group(group_id)
+ self._get_group(session, group_id)
if domain_id:
- self.get_domain(domain_id)
+ self._get_domain(session, domain_id)
if project_id:
- self.get_project(project_id)
+ self._get_project(session, project_id)
try:
metadata_ref = self.get_metadata(user_id, project_id,
@@ -306,15 +300,16 @@ class Identity(sql.Base, identity.Driver):
def get_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None):
- self.get_role(role_id)
+ session = self.get_session()
+ role_ref = self._get_role(session, role_id)
if user_id:
- self.get_user(user_id)
+ self._get_user(session, user_id)
if group_id:
- self.get_group(group_id)
+ self._get_group(session, group_id)
if domain_id:
- self.get_domain(domain_id)
+ self._get_domain(session, domain_id)
if project_id:
- self.get_project(project_id)
+ self._get_project(session, project_id)
try:
metadata_ref = self.get_metadata(user_id, project_id,
@@ -324,19 +319,20 @@ class Identity(sql.Base, identity.Driver):
role_ids = set(metadata_ref.get('roles', []))
if role_id not in role_ids:
raise exception.RoleNotFound(role_id=role_id)
- return self.get_role(role_id)
+ return role_ref.to_dict()
def delete_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None):
- self.get_role(role_id)
+ session = self.get_session()
+ self._get_role(session, role_id)
if user_id:
- self.get_user(user_id)
+ self._get_user(session, user_id)
if group_id:
- self.get_group(group_id)
+ self._get_group(session, group_id)
if domain_id:
- self.get_domain(domain_id)
+ self._get_domain(session, domain_id)
if project_id:
- self.get_project(project_id)
+ self._get_project(session, project_id)
try:
metadata_ref = self.get_metadata(user_id, project_id,
@@ -365,7 +361,7 @@ class Identity(sql.Base, identity.Driver):
def get_projects_for_user(self, user_id):
session = self.get_session()
- self.get_user(user_id)
+ self._get_user(session, user_id)
query = session.query(UserProjectGrant)
query = query.filter_by(user_id=user_id)
membership_refs = query.all()
@@ -389,17 +385,19 @@ class Identity(sql.Base, identity.Driver):
pass
def get_roles_for_user_and_project(self, user_id, tenant_id):
- self.get_user(user_id)
- self.get_project(tenant_id)
+ session = self.get_session()
+ self._get_user(session, user_id)
+ self._get_project(session, tenant_id)
metadata_ref = {}
self._get_user_project_roles(metadata_ref, user_id, tenant_id)
self._get_user_group_project_roles(metadata_ref, user_id, tenant_id)
return list(set(metadata_ref.get('roles', [])))
def add_role_to_user_and_project(self, user_id, tenant_id, role_id):
- self.get_user(user_id)
- self.get_project(tenant_id)
- self.get_role(role_id)
+ session = self.get_session()
+ self._get_user(session, user_id)
+ self._get_project(session, tenant_id)
+ self._get_role(session, role_id)
try:
metadata_ref = self.get_metadata(user_id, tenant_id)
is_new = False
@@ -423,9 +421,9 @@ class Identity(sql.Base, identity.Driver):
metadata_ref = self.get_metadata(user_id, tenant_id)
roles = set(metadata_ref.get('roles', []))
if role_id not in roles:
- msg = _('Cannot remove role that has not been granted, %s' %
- role_id)
- raise exception.RoleNotFound(message=msg)
+ raise exception.RoleNotFound(message=_(
+ 'Cannot remove role that has not been granted, %s') %
+ role_id)
roles.remove(role_id)
metadata_ref['roles'] = list(roles)
if len(roles):
@@ -456,12 +454,9 @@ class Identity(sql.Base, identity.Driver):
if 'name' in tenant:
tenant['name'] = clean.project_name(tenant['name'])
- try:
- tenant_ref = session.query(Project).filter_by(id=tenant_id).one()
- except sql.NotFound:
- raise exception.ProjectNotFound(project_id=tenant_id)
with session.begin():
+ tenant_ref = self._get_project(session, tenant_id)
old_project_dict = tenant_ref.to_dict()
for k in tenant:
old_project_dict[k] = tenant[k]
@@ -477,12 +472,9 @@ class Identity(sql.Base, identity.Driver):
def delete_project(self, tenant_id):
session = self.get_session()
- try:
- tenant_ref = session.query(Project).filter_by(id=tenant_id).one()
- except sql.NotFound:
- raise exception.ProjectNotFound(project_id=tenant_id)
-
with session.begin():
+ tenant_ref = self._get_project(session, tenant_id)
+
q = session.query(UserProjectGrant)
q = q.filter_by(project_id=tenant_id)
q.delete(False)
@@ -495,10 +487,6 @@ class Identity(sql.Base, identity.Driver):
q = q.filter_by(project_id=tenant_id)
q.delete(False)
- delete_query = session.query(Project).filter_by(id=tenant_id)
- if not delete_query.delete(False):
- raise exception.ProjectNotFound(project_id=tenant_id)
-
session.delete(tenant_ref)
session.flush()
@@ -574,14 +562,16 @@ class Identity(sql.Base, identity.Driver):
refs = session.query(Domain).all()
return [ref.to_dict() for ref in refs]
- def get_domain(self, domain_id):
- session = self.get_session()
- ref = session.query(Domain).filter_by(id=domain_id).first()
+ def _get_domain(self, session, domain_id):
+ ref = session.query(Domain).get(domain_id)
if ref is None:
raise exception.DomainNotFound(domain_id=domain_id)
- return ref.to_dict()
+ return ref
+
+ def get_domain(self, domain_id):
+ session = self.get_session()
+ return self._get_domain(session, domain_id).to_dict()
- @sql.handle_conflicts(type='domain')
def get_domain_by_name(self, domain_name):
session = self.get_session()
try:
@@ -594,9 +584,7 @@ class Identity(sql.Base, identity.Driver):
def update_domain(self, domain_id, domain):
session = self.get_session()
with session.begin():
- ref = session.query(Domain).filter_by(id=domain_id).first()
- if ref is None:
- raise exception.DomainNotFound(domain_id=domain_id)
+ ref = self._get_domain(session, domain_id)
old_dict = ref.to_dict()
for k in domain:
old_dict[k] = domain[k]
@@ -610,10 +598,8 @@ class Identity(sql.Base, identity.Driver):
def delete_domain(self, domain_id):
session = self.get_session()
- ref = session.query(Domain).filter_by(id=domain_id).first()
- if not ref:
- raise exception.DomainNotFound(domain_id=domain_id)
with session.begin():
+ ref = self._get_domain(session, domain_id)
session.delete(ref)
session.flush()
@@ -639,6 +625,7 @@ class Identity(sql.Base, identity.Driver):
@sql.handle_conflicts(type='user')
def create_user(self, user_id, user):
user['name'] = clean.user_name(user['name'])
+ user['enabled'] = clean.user_enabled(user.get('enabled', True))
user = utils.hash_user_password(user)
session = self.get_session()
with session.begin():
@@ -652,14 +639,17 @@ class Identity(sql.Base, identity.Driver):
user_refs = session.query(User)
return [identity.filter_user(x.to_dict()) for x in user_refs]
- def _get_user(self, user_id):
- session = self.get_session()
- user_ref = session.query(User).filter_by(id=user_id).first()
+ def _get_user(self, session, user_id):
+ user_ref = session.query(User).get(user_id)
if not user_ref:
raise exception.UserNotFound(user_id=user_id)
- return user_ref.to_dict()
+ return user_ref
- def _get_user_by_name(self, user_name, domain_id):
+ def get_user(self, user_id):
+ session = self.get_session()
+ return identity.filter_user(self._get_user(session, user_id).to_dict())
+
+ def get_user_by_name(self, user_name, domain_id):
session = self.get_session()
query = session.query(User)
query = query.filter_by(name=user_name)
@@ -668,27 +658,20 @@ class Identity(sql.Base, identity.Driver):
user_ref = query.one()
except sql.NotFound:
raise exception.UserNotFound(user_id=user_name)
- return user_ref.to_dict()
-
- def get_user(self, user_id):
- return identity.filter_user(self._get_user(user_id))
-
- def get_user_by_name(self, user_name, domain_id):
- return identity.filter_user(
- self._get_user_by_name(user_name, domain_id))
+ return identity.filter_user(user_ref.to_dict())
@sql.handle_conflicts(type='user')
def update_user(self, user_id, user):
if 'name' in user:
user['name'] = clean.user_name(user['name'])
+ if 'enabled' in user:
+ user['enabled'] = clean.user_enabled(user['enabled'])
session = self.get_session()
if 'id' in user and user_id != user['id']:
raise exception.ValidationError('Cannot change user ID')
with session.begin():
- user_ref = session.query(User).filter_by(id=user_id).first()
- if user_ref is None:
- raise exception.UserNotFound(user_id=user_id)
+ user_ref = self._get_user(session, user_id)
old_user_dict = user_ref.to_dict()
user = utils.hash_user_password(user)
for k in user:
@@ -760,12 +743,8 @@ class Identity(sql.Base, identity.Driver):
def delete_user(self, user_id):
session = self.get_session()
- try:
- ref = session.query(User).filter_by(id=user_id).one()
- except sql.NotFound:
- raise exception.UserNotFound(user_id=user_id)
-
with session.begin():
+ ref = self._get_user(session, user_id)
q = session.query(UserProjectGrant)
q = q.filter_by(user_id=user_id)
@@ -779,9 +758,6 @@ class Identity(sql.Base, identity.Driver):
q = q.filter_by(user_id=user_id)
q.delete(False)
- if not session.query(User).filter_by(id=user_id).delete(False):
- raise exception.UserNotFound(user_id=user_id)
-
session.delete(ref)
session.flush()
@@ -801,24 +777,22 @@ class Identity(sql.Base, identity.Driver):
refs = session.query(Group).all()
return [ref.to_dict() for ref in refs]
- def _get_group(self, group_id):
- session = self.get_session()
- ref = session.query(Group).filter_by(id=group_id).first()
+ def _get_group(self, session, group_id):
+ ref = session.query(Group).get(group_id)
if not ref:
raise exception.GroupNotFound(group_id=group_id)
- return ref.to_dict()
+ return ref
def get_group(self, group_id):
- return self._get_group(group_id)
+ session = self.get_session()
+ return self._get_group(session, group_id).to_dict()
@sql.handle_conflicts(type='group')
def update_group(self, group_id, group):
session = self.get_session()
with session.begin():
- ref = session.query(Group).filter_by(id=group_id).first()
- if ref is None:
- raise exception.GroupNotFound(group_id=group_id)
+ ref = self._get_group(session, group_id)
old_dict = ref.to_dict()
for k in group:
old_dict[k] = group[k]
@@ -833,12 +807,9 @@ class Identity(sql.Base, identity.Driver):
def delete_group(self, group_id):
session = self.get_session()
- try:
- ref = session.query(Group).filter_by(id=group_id).one()
- except sql.NotFound:
- raise exception.GroupNotFound(group_id=group_id)
-
with session.begin():
+ ref = self._get_group(session, group_id)
+
q = session.query(GroupProjectGrant)
q = q.filter_by(group_id=group_id)
q.delete(False)
@@ -851,62 +822,6 @@ class Identity(sql.Base, identity.Driver):
q = q.filter_by(group_id=group_id)
q.delete(False)
- if not session.query(Group).filter_by(id=group_id).delete(False):
- raise exception.GroupNotFound(group_id=group_id)
-
- session.delete(ref)
- session.flush()
-
- # credential crud
-
- @sql.handle_conflicts(type='credential')
- def create_credential(self, credential_id, credential):
- session = self.get_session()
- with session.begin():
- ref = Credential.from_dict(credential)
- session.add(ref)
- session.flush()
- return ref.to_dict()
-
- def list_credentials(self):
- session = self.get_session()
- refs = session.query(Credential).all()
- return [ref.to_dict() for ref in refs]
-
- def get_credential(self, credential_id):
- session = self.get_session()
- ref = session.query(Credential).filter_by(id=credential_id).first()
- if ref is None:
- raise exception.CredentialNotFound(credential_id=credential_id)
- return ref.to_dict()
-
- @sql.handle_conflicts(type='credential')
- def update_credential(self, credential_id, credential):
- session = self.get_session()
- with session.begin():
- ref = session.query(Credential).filter_by(id=credential_id).first()
- if ref is None:
- raise exception.CredentialNotFound(credential_id=credential_id)
- old_dict = ref.to_dict()
- for k in credential:
- old_dict[k] = credential[k]
- new_credential = Credential.from_dict(old_dict)
- for attr in Credential.attributes:
- if attr != 'id':
- setattr(ref, attr, getattr(new_credential, attr))
- ref.extra = new_credential.extra
- session.flush()
- return ref.to_dict()
-
- def delete_credential(self, credential_id):
- session = self.get_session()
-
- try:
- ref = session.query(Credential).filter_by(id=credential_id).one()
- except sql.NotFound:
- raise exception.CredentialNotFound(credential_id=credential_id)
-
- with session.begin():
session.delete(ref)
session.flush()
@@ -926,20 +841,21 @@ class Identity(sql.Base, identity.Driver):
refs = session.query(Role).all()
return [ref.to_dict() for ref in refs]
- def get_role(self, role_id):
- session = self.get_session()
- ref = session.query(Role).filter_by(id=role_id).first()
+ def _get_role(self, session, role_id):
+ ref = session.query(Role).get(role_id)
if ref is None:
raise exception.RoleNotFound(role_id=role_id)
- return ref.to_dict()
+ return ref
+
+ def get_role(self, role_id):
+ session = self.get_session()
+ return self._get_role(session, role_id).to_dict()
@sql.handle_conflicts(type='role')
def update_role(self, role_id, role):
session = self.get_session()
with session.begin():
- ref = session.query(Role).filter_by(id=role_id).first()
- if ref is None:
- raise exception.RoleNotFound(role_id=role_id)
+ ref = self._get_role(session, role_id)
old_dict = ref.to_dict()
for k in role:
old_dict[k] = role[k]
@@ -954,12 +870,8 @@ class Identity(sql.Base, identity.Driver):
def delete_role(self, role_id):
session = self.get_session()
- try:
- ref = session.query(Role).filter_by(id=role_id).one()
- except sql.NotFound:
- raise exception.RoleNotFound(role_id=role_id)
-
with session.begin():
+ ref = self._get_role(session, role_id)
for metadata_ref in session.query(UserProjectGrant):
try:
self.delete_grant(role_id, user_id=metadata_ref.user_id,
@@ -985,8 +897,5 @@ class Identity(sql.Base, identity.Driver):
except exception.RoleNotFound:
pass
- if not session.query(Role).filter_by(id=role_id).delete():
- raise exception.RoleNotFound(role_id=role_id)
-
session.delete(ref)
session.flush()
diff --git a/keystone/identity/controllers.py b/keystone/identity/controllers.py
index e82b81f4..3e60272d 100644
--- a/keystone/identity/controllers.py
+++ b/keystone/identity/controllers.py
@@ -194,6 +194,9 @@ class User(controller.V2Controller):
if 'name' not in user or not user['name']:
msg = 'Name field is required and cannot be empty'
raise exception.ValidationError(message=msg)
+ if 'enabled' in user and not isinstance(user['enabled'], bool):
+ msg = 'Enabled field must be a boolean'
+ raise exception.ValidationError(message=msg)
default_tenant_id = user.get('tenantId', None)
if (default_tenant_id is not None
@@ -213,6 +216,11 @@ class User(controller.V2Controller):
def update_user(self, context, user_id, user):
# NOTE(termie): this is really more of a patch than a put
self.assert_admin(context)
+
+ if 'enabled' in user and not isinstance(user['enabled'], bool):
+ msg = 'Enabled field should be a boolean'
+ raise exception.ValidationError(message=msg)
+
user_ref = self.identity_api.update_user(context, user_id, user)
if user.get('password') or not user.get('enabled', True):
@@ -223,6 +231,7 @@ class User(controller.V2Controller):
def delete_user(self, context, user_id):
self.assert_admin(context)
self.identity_api.delete_user(context, user_id)
+ self._delete_tokens_for_user(context, user_id)
def set_user_enabled(self, context, user_id, user):
return self.update_user(context, user_id, user)
@@ -386,8 +395,6 @@ class Role(controller.V2Controller):
role_id = role_ref_ref.get('roleId')[0]
self.identity_api.remove_role_from_user_and_project(
context, user_id, tenant_id, role_id)
- roles = self.identity_api.get_roles_for_user_and_project(
- context, user_id, tenant_id)
self._delete_tokens_for_user(context, user_id)
@@ -465,7 +472,7 @@ class DomainV3(controller.V3Controller):
"""
# Start by disabling all the users in this domain, to minimize the
# the risk that things are changing under our feet.
- # TODO(henry-nash) In theory this step should not be necessary, since
+ # TODO(henry-nash): In theory this step should not be necessary, since
# users of a disabled domain are prevented from authenticating.
# However there are some existing bugs in this area (e.g. 1130236).
# Consider removing this code once these have been fixed.
@@ -571,9 +578,9 @@ class ProjectV3(controller.V3Controller):
def _delete_project(self, context, project_id):
# Delete any credentials that reference this project
- for cred in self.identity_api.list_credentials(context):
+ for cred in self.credential_api.list_credentials(context):
if cred['project_id'] == project_id:
- self.identity_api.delete_credential(context, cred['id'])
+ self.credential_api.delete_credential(context, cred['id'])
# Finally delete the project itself - the backend is
# responsible for deleting any role assignments related
# to this project
@@ -642,9 +649,9 @@ class UserV3(controller.V3Controller):
def _delete_user(self, context, user_id):
# Delete any credentials that reference this user
- for cred in self.identity_api.list_credentials(context):
+ for cred in self.credential_api.list_credentials(context):
if cred['user_id'] == user_id:
- self.identity_api.delete_credential(context, cred['id'])
+ self.credential_api.delete_credential(context, cred['id'])
# Make sure any tokens are marked as deleted
self._delete_tokens_for_user(context, user_id)
@@ -708,44 +715,6 @@ class GroupV3(controller.V3Controller):
return self._delete_group(context, group_id)
-class CredentialV3(controller.V3Controller):
- collection_name = 'credentials'
- member_name = 'credential'
-
- @controller.protected
- def create_credential(self, context, credential):
- ref = self._assign_unique_id(self._normalize_dict(credential))
- ref = self.identity_api.create_credential(context, ref['id'], ref)
- return CredentialV3.wrap_member(context, ref)
-
- @controller.protected
- def list_credentials(self, context):
- refs = self.identity_api.list_credentials(context)
- return CredentialV3.wrap_collection(context, refs)
-
- @controller.protected
- def get_credential(self, context, credential_id):
- ref = self.identity_api.get_credential(context, credential_id)
- return CredentialV3.wrap_member(context, ref)
-
- @controller.protected
- def update_credential(self, context, credential_id, credential):
- self._require_matching_id(credential_id, credential)
-
- ref = self.identity_api.update_credential(
- context,
- credential_id,
- credential)
- return CredentialV3.wrap_member(context, ref)
-
- def _delete_credential(self, context, credential_id):
- return self.identity_api.delete_credential(context, credential_id)
-
- @controller.protected
- def delete_credential(self, context, credential_id):
- return self._delete_credential(context, credential_id)
-
-
class RoleV3(controller.V3Controller):
collection_name = 'roles'
member_name = 'role'
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index 2231b4a7..fde7ac8d 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -432,50 +432,6 @@ class Driver(object):
"""
raise exception.NotImplemented()
- # credential crud
-
- def create_credential(self, credential_id, credential):
- """Creates a new credential.
-
- :raises: keystone.exception.Conflict
-
- """
- raise exception.NotImplemented()
-
- def list_credentials(self):
- """List all credentials in the system.
-
- :returns: a list of credential_refs or an empty list.
-
- """
- raise exception.NotImplemented()
-
- def get_credential(self, credential_id):
- """Get a credential by ID.
-
- :returns: credential_ref
- :raises: keystone.exception.CredentialNotFound
-
- """
- raise exception.NotImplemented()
-
- def update_credential(self, credential_id, credential):
- """Updates an existing credential.
-
- :raises: keystone.exception.CredentialNotFound,
- keystone.exception.Conflict
-
- """
- raise exception.NotImplemented()
-
- def delete_credential(self, credential_id):
- """Deletes an existing credential.
-
- :raises: keystone.exception.CredentialNotFound
-
- """
- raise exception.NotImplemented()
-
# role crud
def create_role(self, role_id, role):
diff --git a/keystone/identity/routers.py b/keystone/identity/routers.py
index caa84149..32eada5e 100644
--- a/keystone/identity/routers.py
+++ b/keystone/identity/routers.py
@@ -107,10 +107,6 @@ def append_v3_routers(mapper, routers):
action='list_groups_for_user',
conditions=dict(method=['GET']))
- routers.append(
- router.Router(controllers.CredentialV3(),
- 'credentials', 'credential'))
-
role_controller = controllers.RoleV3()
routers.append(router.Router(role_controller, 'roles', 'role'))
mapper.connect('/projects/{project_id}/users/{user_id}/roles/{role_id}',
diff --git a/keystone/locale/bg_BG/LC_MESSAGES/keystone.po b/keystone/locale/bg_BG/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..e0cbafba
--- /dev/null
+++ b/keystone/locale/bg_BG/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Bulgarian (Bulgaria) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Bulgarian (Bulgaria) "
+"(http://www.transifex.com/projects/p/openstack/language/bg_BG/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/ca/LC_MESSAGES/keystone.po b/keystone/locale/ca/LC_MESSAGES/keystone.po
index 532a554a..f6168492 100644
--- a/keystone/locale/ca/LC_MESSAGES/keystone.po
+++ b/keystone/locale/ca/LC_MESSAGES/keystone.po
@@ -1,4 +1,4 @@
-# Translations template for OpenStack Keystone.
+# Catalan translations for OpenStack Keystone.
# Copyright (C) 2012 OpenStack Foundation
# This file is distributed under the same license as the keystone project.
#
@@ -6,35 +6,554 @@
# Sergi Almacellas <pokoli@gmail.com>, 2012.
msgid ""
msgstr ""
-"Project-Id-Version: Keystone\n"
+"Project-Id-Version: Keystone\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
-"POT-Creation-Date: 2012-11-15 23:10+0000\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
"PO-Revision-Date: 2012-11-03 03:08+0000\n"
"Last-Translator: Sergi Almacellas <pokoli@gmail.com>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language-Team: ca <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-"Language: ca\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: keystone/config.py:62
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, fuzzy, python-format
+msgid "Unable to lookup user %s"
+msgstr "No es pot afegir el token a la llista d'usuaris."
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+#, fuzzy
+msgid "Unable to sign token."
+msgstr "No es pot afegir el token a la llista d'usuaris."
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
msgid "Invalid syslog facility"
msgstr "Fitxer syslog invàlid"
-#: keystone/policy/backends/rules.py:34
-msgid "JSON file representing policy"
-msgstr "Fitxer JSON que repsenta la politica"
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
-#: keystone/policy/backends/rules.py:37
-msgid "Rule checked when requested rule is not found"
-msgstr "Marca la regla quan aquesta no es troba"
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
-#: keystone/token/backends/memcache.py:81
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
msgid "Unable to add token user list."
msgstr "No es pot afegir el token a la llista d'usuaris."
-#: keystone/token/backends/memcache.py:91
+#: keystone/token/backends/memcache.py:93
msgid "Unable to add token to revocation list."
msgstr "No es pot afegir el token a la llista de revocats."
+
diff --git a/keystone/locale/cs/LC_MESSAGES/keystone.po b/keystone/locale/cs/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..2922672e
--- /dev/null
+++ b/keystone/locale/cs/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Czech translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Czech "
+"(http://www.transifex.com/projects/p/openstack/language/cs/)\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/da/LC_MESSAGES/keystone.po b/keystone/locale/da/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..31303513
--- /dev/null
+++ b/keystone/locale/da/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Danish translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Danish "
+"(http://www.transifex.com/projects/p/openstack/language/da/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/de/LC_MESSAGES/keystone.po b/keystone/locale/de/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..d25fea7f
--- /dev/null
+++ b/keystone/locale/de/LC_MESSAGES/keystone.po
@@ -0,0 +1,576 @@
+# German translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-22 03:45+0000\n"
+"Last-Translator: daisy.ycguo <daisy.ycguo@gmail.com>\n"
+"Language-Team: German "
+"(http://www.transifex.com/projects/p/openstack/language/de/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s darf nicht leer sein."
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr "%(property_name)s darf nicht kleiner als %(min_length)s Zeichen sein."
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr "%(property_name)s sollte nicht größer als %(max_length)s Zeichen sein."
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr "%s wurde nicht ausgecheckt"
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr "Projekt ist inaktiviert: %s"
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "Domäne ist inaktiviert: %s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr "Benutzer ist inaktiviert: %s"
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr "Scoping sowohl auf 'domain' als auch auf 'project' ist nicht zulässig"
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr "Scoping sowohl auf 'domain' als auch auf 'trust' ist nicht zulässig"
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr "Scoping sowohl auf 'project' als auch auf 'trust' ist nicht zulässig"
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr "Suche nach Benutzer %s nicht möglich"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "Benutzer nicht gefunden"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "Benutzer hat keinen Zugriff auf Projekt"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr "Benutzer hat keinen Zugriff auf Domäne"
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr "Token kann nicht unterzeichnet werden."
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+"Ungültiger Wert für 'token_format': %s. Zulässige Werte sind PKI oder "
+"UUID."
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr "Fehlerhafter Endpunkt %(url)s - unbekannter Schlüssel %(keyerror)s"
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+"Fehlerhafter Endpunkt %(url)s - unbekannter Schlüssel %(keyerror)s "
+"(fehlende Klammer?)"
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+"Fehlerhafter Endpunkt %s - unvollständiges Format "
+"(fehlender Typenhinweis?)"
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr "Vorlagendatei %s kann nicht geöffnet werden"
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr "HTTP PERF: %(time).5f Sekunden für %(method)s %(host)s:%(port)s %(path)s)"
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr "Fehler überprüfen: %s"
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+"Unterzeichnungsfehler: Zertifikat kann nicht geladen werden - stellen Sie"
+" sicher, dass Sie PKI mit 'keystone-manage pki_setup' konfiguriert haben"
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr "Unterzeichnungsfehler: %s"
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr "Angegebene Protokollkonfigurationsdatei kann nicht gefunden werden: %s"
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "Ungültige 'syslog'-Funktion"
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr "Rollenbasierte Zugriffssteuerung: Autorisierung von %s(%s)"
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr "Rollenbasierte Zugriffssteuerung: Ungültiges Token"
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr "Rollenbasierte Zugriffssteuerung: Ungültiger Benutzer"
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr "Rollenbasierte Zugriffssteuerung: Fortfahren ohne Projekt"
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr "Rollenbasierte Zugriffssteuerung: Fortsetzung ohne Nutzer"
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr "Rollenbasierte Zugriffssteuerung: Umgehen von Autorisierung"
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr "Rollenbasierte Zugriffssteuerung: Autorisierung erteilt"
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+"Rollenbasierte Zugriffssteuerung: Hinzufügen von Abfragefilterparametern "
+"(%s)"
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr "Ungültiges Token in 'normalize_domain_id'"
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr "Starten von %(arg0)s auf %(host)s:%(port)s"
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr "Serverfehler"
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr "arg_dict: %s"
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr "Autorisierung fehlgeschlagen. %s von %s"
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr "Die Ressource konnte nicht gefunden werden."
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr "Ungültige LDAP-deref-Option: %s. Wählen Sie aus: "
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr "Ungültiger LDAP-Umfang: %s. Wählen Sie aus: "
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr "Doppelter Name, %s."
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr "Doppelte ID, %s."
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "LDAP %s erstellen"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "LDAP %s aktualisieren"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "LDAP %s löschen"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr "LDAP starten: url=%s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "LDAP binden: dn=%s"
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "LDAP hinzufügen: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "LDAP-Suche: dn=%s, scope=%s, query=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+"LDAP-Server unterstützt Paging nicht. Inaktivieren Sie das Paging in "
+"keystone.conf, um diese Nachricht zu verhindern."
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "LDAP ändern: dn=%s, modlist=%s"
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "LDAP löschen: dn=%s"
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "LDAP-'delete_ext': dn=%s, serverctrls=%s"
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr "FakeLdap starten, url=%s"
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr "FakeLdap binden, dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr "FakeLdap binden fehlgeschlagen: dn=%s nicht gefunden"
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr "FakeLdap binden fehlgeschlagen: Kennwort für dn=%s nicht gefunden"
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr "FakeLdap binden fehlgeschlagen: Kennwort für dn=%s stimmt nicht überein"
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr "FakeLdap-Element hinzufügen: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr "FakeLdap-Element hinzufügen fehlgeschlagen: dn=%s ist bereits im Speicher."
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr "FakeLdap-Element löschen: dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr "FakeLdap-Element löschen fehlgeschlagen: dn=%s nicht gefunden."
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr "FakeLdap-Element ändern: dn=%s attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr "FakeLdap-Element ändern fehlgeschlagen: dn=%s nicht gefunden."
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+"FakeLdap-Element ändern fehlgeschlagen: Element hat kein zu löschendes "
+"Attribut \"%s\""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+"FakeLdap-Element ändern fehlgeschlagen: Element hat kein zu löschendes "
+"Attribut \"%s\" mit Wert \"%s\""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr "FakeLdap-Element ändern fehlgeschlagen: unbekannter Befehl %s"
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr "'modify_s'-Aktion %s nicht implementiert"
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr "FakeLdap-Suche unter dn=%s scope=%s query=%s"
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr "FakeLdap-Suche fehlgeschlagen: dn für 'SCOPE_BASE' nicht gefunden"
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr "Suchbereich %s nicht implementiert."
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+"Es wurde festgestellt, dass keine Verbindung zum mysql-Server mehr "
+"vorhanden ist: %s"
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr "EC2-Berechtigungsnachweis kann nicht migriert werden: %s"
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr "Version sollte eine Ganzzahl sein"
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr "Nutzer %s erstellen"
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "Benutzer %s erstellen"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr "Benutzer %s zu Nutzer %s hinzufügen"
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr "Vorhandene Rolle %s ignorieren"
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr "Rolle %s erstellen"
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr "Rolle %s Benutzer %s auf Nutzer %s zuweisen"
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr "Erstellen von EC2-Berechtigungsnachweis für Benutzer %s und Nutzer %s"
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr "Benutzer nicht in Gruppe gefunden"
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr "Nicht gewährte Rolle kann nicht entfernt werden, %s"
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "Rolle %s nicht gefunden"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr "Änderung von Namen wird von LDAP nicht unterstützt"
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr "Benutzer %s ist bereits Mitglied der Gruppe %s."
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr "Regel '%(rule)s' konnte nicht verstanden werden"
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr "Kein Handler für Übereinstimmungen des Typs %s"
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr "Regel '%(rule)r' konnte nicht verstanden werden"
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr "%s erzwingen: %s"
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr "Token gehört nicht zu angegebenem Nutzer."
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr "Nicht-Standard-Domäne wird nicht unterstützt"
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr "Bereichsorientiertes Token der Domäne wird nicht unterstützt"
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr "Token kann nicht zu Benutzerliste hinzugefügt werden."
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr "Token kann nicht zu Widerrufsliste hinzugefügt werden."
+
diff --git a/keystone/locale/es/LC_MESSAGES/keystone.po b/keystone/locale/es/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..090c21e7
--- /dev/null
+++ b/keystone/locale/es/LC_MESSAGES/keystone.po
@@ -0,0 +1,583 @@
+# Spanish translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-21 09:13+0000\n"
+"Last-Translator: daisy.ycguo <daisy.ycguo@gmail.com>\n"
+"Language-Team: Spanish "
+"(http://www.transifex.com/projects/p/openstack/language/es/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s no puede estar vacío."
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr "%(property_name)s no puede tener menos de %(min_length)s caracteres."
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr "%(property_name)s no debe tener más de %(max_length)s caracteres."
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr "No se ha podido extraer %s"
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr "El proyecto está inhabilitado: %s"
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "El dominio está inhabilitado: %s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr "El usuario está inhabilitado: %s"
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr "El ámbito para dominio y proyecto no está permitido"
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr "El ámbito para dominio y confianza no está permitido"
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr "El ámbito para proyecto y confianza no está permitido"
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr "No se ha podido buscar el usuario %s"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "Usuario no encontrado"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "El usuario no tiene acceso al proyecto"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr "El usuario no tiene acceso al dominio"
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr "No se ha podido firmar la señal."
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+"Valor no válido para token_format: %s. Los valores permitidos son PKI o "
+"UUID."
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+"Punto final formado incorrectamente %(url)s - clave desconocida "
+"%(keyerror)s"
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+"Punto final con formato incorrecto %(url)s - clave desconocida "
+"%(keyerror)s (¿Faltan corchetes?)"
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+"Punto final con formato incorrecto %s - formato incompleto"
+" (¿Le falta un notificador de tipo?)"
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr "No se puede abrir el archivo de plantilla %s"
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr "PERF HTTP: %(time).5f segundos en %(method)s %(host)s:%(port)s %(path)s)"
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr "Verificar error: %s"
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+"Error de firma: no se ha podido cargar el certificado; asegúrese de haber"
+" configurado la PKI con 'keystone-manage pki_setup'"
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr "Error de firma: %s"
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+"No se puede localizar el archivo de configuración de registro "
+"especificado: %s"
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "Recurso syslog no válido"
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr "RBAC: Autorizando %s(%s)"
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr "RBAC: Señal no válida"
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr "RBAC: Usuario no válido"
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr "RBAC: continuando sin proyecto"
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr "RBAC: Continuando sin arrendatario"
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr "RBAC: Eludiendo autorización"
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr "RBAC: Autorización otorgada"
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr "RBAC: añadiendo parámetros de filtro de consultas (%s)"
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr "Señal no válida en normalize_domain_id"
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr "Iniciando %(arg0)s en %(host)s:%(port)s"
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr "Error de servidor"
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr "arg_dict: %s"
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr "Ha fallado la autorización. %s de %s"
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr "El recurso no se ha podido encontrar."
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr "Opción deref de LDAP no válida: %s. Elija una de las siguientes: "
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr "Ámbito de LDAP no válido: %s. Elija uno de los siguientes: "
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr "Nombre duplicado, %s."
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr "ID duplicado, %s."
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "Creación de LDAP %s"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "Actualización de LDAP %s"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "Supresión de LDAP %s"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr "Inicialización de LDAP: url=%s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "Enlace de LDAP: dn=%s"
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "Adición de LDAP: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "Búsqueda LDAP: dn=%s, scope=%s, query=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+"El servidor LDAP no soporta la paginación. Inhabilite la paginación en "
+"keystone.conf para evitar este mensaje."
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "Modificación de LDAP: dn=%s, modlist=%s"
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "Supresión de LDAP: dn=%s"
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "LDAP delete_ext: dn=%s, serverctrls=%s"
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr "Inicialización de FakeLDAP url=%s"
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr "Enlace de FakeLDAP dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr "Error de enlace de FakeLDAP: dn=%s no encontrado"
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr "Error de enlace FakeLDAP: contraseña de dn=%s no encontrada"
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr "Error de enlace FakeLDAP: contraseña de dn=%s no coincide"
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr "Añadir elemento de FakeLDAP: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+"Ha fallado la adición de elemento de FakeLDAP: dn=%s ya está en el "
+"almacén."
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr "Supresión de elemento de FakeLDAP: dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr "Ha fallado la supresión de elemento de FakeLDAP: dn=%s no encontrado."
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr "Modificación de elemento de FakeLDAP: dn=%s attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr "Ha fallado la modificación de elemento de FakeLDAP: dn=%s no encontrado."
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+"Ha fallado la modificación de elemento de FakeLDAP: el elemento no tiene "
+"atributos \"%s\" a suprimir"
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+"Se ha encontrado un error en la modificación de elemento de FakeLdap: el "
+"elemento no tiene ningún atributo \"%s\" con el valor \"%s\" para "
+"suprimir"
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+"Ha fallado la modificación de elemento de FakeLDAP: mandato desconocido "
+"%s "
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr "Acción modify_s %s no implementada"
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr "Búsqueda de FakeLDAP en dn=%s scope=%s query=%s"
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+"Error de búsqueda de FakeLDAP: nombre distinguido no encontrado para "
+"SCOPE_BASE"
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr "Ámbito de búsqueda %s no implementado."
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr "Se ha notificado que mysql server ha desaparecido: %s"
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr "No se puede migrar la credencial EC2: %s "
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr "la versión debe ser un entero"
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr "Crear el arrendatario %s"
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "Crear el usuario %s"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr "Añadir el usuario %s al arrendatario %s"
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr "Ignorando el rol existente %s"
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr "Crear el rol %s"
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr "Asignar el rol %s al usuario %s en el arrendatario %s"
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr "Creando credencial ec2 para el usuario %s y el arrendatario %s"
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr "Usuario no encontrado en grupo"
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr "No se puede eliminar un rol que no se ha otorgado, %s"
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "No se ha encontrado el rol %s"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr "LDAP no soporta el cambio de nombre"
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr "El usuario %s ya es miembro del grupo %s"
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr "No se ha podido comprender la regla %(rule)s"
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr "No hay manejador para coincidencias de clase %s"
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr "No se ha podido comprender la regla %(rule)r"
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr "imponer %s: %s"
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr "La señal no pertenece al arrendatario especificado."
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr "El dominio no predeterminado no está soportado"
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr "La señal con ámbito de dominio no está soportada"
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr "No se puede añadir lista de usuarios de señal."
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr "No se puede añadir señal a lista de revocación. "
+
diff --git a/keystone/locale/fi_FI/LC_MESSAGES/keystone.po b/keystone/locale/fi_FI/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..4db6bfa0
--- /dev/null
+++ b/keystone/locale/fi_FI/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Finnish (Finland) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Finnish (Finland) "
+"(http://www.transifex.com/projects/p/openstack/language/fi_FI/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/fr/LC_MESSAGES/keystone.po b/keystone/locale/fr/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..6438a8c1
--- /dev/null
+++ b/keystone/locale/fr/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# French translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: French "
+"(http://www.transifex.com/projects/p/openstack/language/fr/)\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/hu/LC_MESSAGES/keystone.po b/keystone/locale/hu/LC_MESSAGES/keystone.po
index 0abf77c8..a0b5a1bd 100644
--- a/keystone/locale/hu/LC_MESSAGES/keystone.po
+++ b/keystone/locale/hu/LC_MESSAGES/keystone.po
@@ -1,24 +1,23 @@
-# Translations template for keystone.
+# Hungarian translations for keystone.
# Copyright (C) 2013 ORGANIZATION
# This file is distributed under the same license as the keystone project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
-# <kelemeng@gnome.hu>, 2012.
+# <kelemeng@gnome.hu>, 2012.
msgid ""
msgstr ""
-"Project-Id-Version: Keystone\n"
+"Project-Id-Version: Keystone\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
-"POT-Creation-Date: 2013-01-01 00:00+0000\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
"PO-Revision-Date: 2012-12-15 14:14+0000\n"
"Last-Translator: kelemeng <kelemeng@gnome.hu>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language-Team: hu <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-"Language: hu\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: keystone/clean.py:23
#, python-format
@@ -32,28 +31,72 @@ msgstr "%(property_name)s nem lehet kevesebb, mint %(min_length)s karakter."
#: keystone/clean.py:29
#, python-format
-msgid ""
-"%(property_name)s should not be greater than %(max_length)s characters."
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
msgstr "%(property_name)s nem lehet több, mint %(max_length)s karakter."
#: keystone/clean.py:36
-#, python-format
-msgid "%(property_name)s is not a%(display_expected_type)s"
+#, fuzzy, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
msgstr "%(property_name)s nem %(display_expected_type)s"
-#: keystone/config.py:44
+#: keystone/test.py:115
#, python-format
-msgid "Unable to locate specified logging config file: %s"
-msgstr "Nem található a megadott naplózásbeállító fájl: %s"
+msgid "Failed to checkout %s"
+msgstr "Nem sikerült %s kiiktatása"
-#: keystone/config.py:62
-msgid "Invalid syslog facility"
-msgstr "Érvénytelen rendszernapló szolgáltatás"
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
-#: keystone/test.py:103
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
#, python-format
-msgid "Failed to checkout %s"
-msgstr "Nem sikerült %s kiiktatása"
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, fuzzy, python-format
+msgid "Unable to lookup user %s"
+msgstr "Nem vehető fel a token felhasználólistája."
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+#, fuzzy
+msgid "Unable to sign token."
+msgstr "Nem vehető fel a token felhasználólistája."
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
#: keystone/catalog/core.py:38
#, python-format
@@ -65,13 +108,15 @@ msgstr "Rosszul formázott végpont: %(url)s - ismeretlen kulcs: %(keyerror)s"
msgid ""
"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
"brackets ?)"
-msgstr "Rosszul formázott végpont: %(url)s - ismeretlen kulcs: %(keyerror)s (hiányoznak a zárójelek?)"
+msgstr ""
+"Rosszul formázott végpont: %(url)s - ismeretlen kulcs: %(keyerror)s "
+"(hiányoznak a zárójelek?)"
#: keystone/catalog/core.py:49
#, python-format
msgid ""
-"Malformed endpoint %s - incomplete format (are you missing"
-" a type notifier ?)"
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
msgstr "Rosszul formázott végpont: %s hiányos formátum (hiányzik egy típusjelölő?)"
#: keystone/catalog/backends/templated.py:109
@@ -81,233 +126,443 @@ msgstr "Nem nyitható meg a sablonfájl: %s"
#: keystone/common/bufferedhttp.py:102
#, python-format
-msgid ""
-"HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
-msgstr "HTTP PERF: %(time).5f másodperc eddig: %(method)s %(host)s:%(port)s %(path)s)"
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+"HTTP PERF: %(time).5f másodperc eddig: %(method)s %(host)s:%(port)s "
+"%(path)s)"
#: keystone/common/cms.py:44
#, python-format
msgid "Verify error: %s"
msgstr "Ellenőrzési hiba: %s"
-#: keystone/common/cms.py:134
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
#, python-format
msgid "Signing error: %s"
msgstr "Aláírási hiba: %s"
-#: keystone/common/utils.py:124
-msgid "using _calc_signature_2"
-msgstr "a _calc_signature_2 használata"
-
-#: keystone/common/utils.py:140
+#: keystone/common/config.py:93
#, python-format
-msgid "query string: %s"
-msgstr "lekérdezési karakterlánc: %s"
+msgid "Unable to locate specified logging config file: %s"
+msgstr "Nem található a megadott naplózásbeállító fájl: %s"
-#: keystone/common/utils.py:142
-#, python-format
-msgid "string_to_sign: %s"
-msgstr "string_to_sign: %s"
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "Érvénytelen rendszernapló szolgáltatás"
-#: keystone/common/utils.py:145
+#: keystone/common/controller.py:19
#, python-format
-msgid "len(b64)=%d"
-msgstr "len(b64)=%d"
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
-#: keystone/common/utils.py:146
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
#, python-format
-msgid "base64 encoded digest: %s"
-msgstr "base64 kódolási kivonat: %s"
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
-#: keystone/common/wsgi.py:73
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
#, python-format
msgid "Starting %(arg0)s on %(host)s:%(port)s"
msgstr "%(arg0)s indítása ezen: %(host)s:%(port)s"
-#: keystone/common/wsgi.py:196
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
#, python-format
msgid "arg_dict: %s"
msgstr "arg_dict: %s"
-#: keystone/common/wsgi.py:217
+#: keystone/common/wsgi.py:267
#, python-format
msgid "Authorization failed. %s from %s"
msgstr "Hitelesítés sikertelen. %s innen: %s"
-#: keystone/common/wsgi.py:430
+#: keystone/common/wsgi.py:510
msgid "The resource could not be found."
msgstr "Az erőforrás nem található."
-#: keystone/common/ldap/core.py:171
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
#, python-format
msgid "Duplicate name, %s."
msgstr "Többszörös név: %s."
-#: keystone/common/ldap/core.py:181
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
#, python-format
msgid "Duplicate ID, %s."
msgstr "Többszörös azonosító: %s."
-#: keystone/common/ldap/core.py:186
+#: keystone/common/ldap/core.py:289
#, python-format
-msgid "LDAP backend does not allow %s create"
-msgstr "Az LDAP háttérprogram nem engedélyezi %s létrehozását"
+msgid "LDAP %s create"
+msgstr ""
-#: keystone/common/ldap/core.py:293
+#: keystone/common/ldap/core.py:367
#, python-format
-msgid "LDAP backend does not allow %s update"
-msgstr "Az LDAP háttérprogram nem engedélyezi %s frissítését"
+msgid "LDAP %s update"
+msgstr ""
-#: keystone/common/ldap/core.py:321
+#: keystone/common/ldap/core.py:400
#, python-format
-msgid "LDAP backend does not allow %s delete"
-msgstr "Az LDAP háttérprogram nem engedélyezi %s törlését"
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, fuzzy, python-format
+msgid "LDAP init: url=%s"
+msgstr "FakeLdap előkészítési url=%s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
-#: keystone/common/ldap/fakeldap.py:148
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, fuzzy, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "FakeLdap bind dn=%s"
+
+#: keystone/common/ldap/core.py:503
+#, fuzzy, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "FakeLdap elem hozzáadása: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:508
+#, fuzzy, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "FakeLdap keresés itt: dn=%s hatókör=%s lekérdezés=%s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, fuzzy, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "FakeLdap elem módosítása: dn=%s attrs=%s"
+
+#: keystone/common/ldap/core.py:581
+#, fuzzy, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "FakeLdap elem törlése: dn=%s"
+
+#: keystone/common/ldap/core.py:585
+#, fuzzy, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "FakeLdap elem hozzáadása: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:146
#, python-format
msgid "FakeLdap initialize url=%s"
msgstr "FakeLdap előkészítési url=%s"
-#: keystone/common/ldap/fakeldap.py:158
+#: keystone/common/ldap/fakeldap.py:156
#, python-format
msgid "FakeLdap bind dn=%s"
msgstr "FakeLdap bind dn=%s"
-#: keystone/common/ldap/fakeldap.py:165
+#: keystone/common/ldap/fakeldap.py:163
#, python-format
msgid "FakeLdap bind fail: dn=%s not found"
msgstr "FakeLdap bind hiba: dn=%s nem található"
-#: keystone/common/ldap/fakeldap.py:172
+#: keystone/common/ldap/fakeldap.py:170
#, python-format
msgid "FakeLdap bind fail: password for dn=%s not found"
msgstr "FakeLdap bind hiba: a jelszó nem található ehhez: dn=%s"
-#: keystone/common/ldap/fakeldap.py:177
+#: keystone/common/ldap/fakeldap.py:175
#, python-format
msgid "FakeLdap bind fail: password for dn=%s does not match"
msgstr "FakeLdap bind hiba: a jelszó nem egyezik ehhez: dn=%s"
-#: keystone/common/ldap/fakeldap.py:192
+#: keystone/common/ldap/fakeldap.py:190
#, python-format
msgid "FakeLdap add item: dn=%s, attrs=%s"
msgstr "FakeLdap elem hozzáadása: dn=%s, attrs=%s"
-#: keystone/common/ldap/fakeldap.py:194
+#: keystone/common/ldap/fakeldap.py:192
#, python-format
msgid "FakeLdap add item failed: dn=%s is already in store."
msgstr "FakeLdap elem hozzáadása sikertelen: dn=%s már a tárolóban van."
-#: keystone/common/ldap/fakeldap.py:208 keystone/common/ldap/fakeldap.py:222
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
#, python-format
msgid "FakeLdap delete item: dn=%s"
msgstr "FakeLdap elem törlése: dn=%s"
-#: keystone/common/ldap/fakeldap.py:212 keystone/common/ldap/fakeldap.py:226
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
#, python-format
msgid "FakeLdap delete item failed: dn=%s not found."
msgstr "FakeLdap elem törlése sikertelen: dn=%s nem található."
-#: keystone/common/ldap/fakeldap.py:241
+#: keystone/common/ldap/fakeldap.py:239
#, python-format
msgid "FakeLdap modify item: dn=%s attrs=%s"
msgstr "FakeLdap elem módosítása: dn=%s attrs=%s"
-#: keystone/common/ldap/fakeldap.py:245
+#: keystone/common/ldap/fakeldap.py:243
#, python-format
msgid "FakeLdap modify item failed: dn=%s not found."
msgstr "FakeLdap elem módosítása sikertelen: dn=%s nem található."
-#: keystone/common/ldap/fakeldap.py:262
+#: keystone/common/ldap/fakeldap.py:260
#, python-format
msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
-msgstr "FakeLdap elem módosítása sikertelen: az elemnek nincs törölhető „%s” attribútuma"
+msgstr ""
+"FakeLdap elem módosítása sikertelen: az elemnek nincs törölhető „%s” "
+"attribútuma"
-#: keystone/common/ldap/fakeldap.py:273
+#: keystone/common/ldap/fakeldap.py:271
#, python-format
msgid ""
-"FakeLdap modify item failed: item has no attribute \"%s\" with value \"%s\" "
-"to delete"
-msgstr "FakeLdap elem módosítása sikertelen: az elemnek nincs törölhető „%s” attribútuma „%s” értékkel"
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+"FakeLdap elem módosítása sikertelen: az elemnek nincs törölhető „%s” "
+"attribútuma „%s” értékkel"
-#: keystone/common/ldap/fakeldap.py:278
+#: keystone/common/ldap/fakeldap.py:276
#, python-format
msgid "FakeLdap modify item failed: unknown command %s"
msgstr "FakeLdap elem módosítása sikertelen: ismeretlen parancs: %s"
-#: keystone/common/ldap/fakeldap.py:280
+#: keystone/common/ldap/fakeldap.py:278
#, python-format
msgid "modify_s action %s not implemented"
msgstr "modify_s %s művelet nincs megvalósítva"
-#: keystone/common/ldap/fakeldap.py:298
+#: keystone/common/ldap/fakeldap.py:296
#, python-format
msgid "FakeLdap search at dn=%s scope=%s query=%s"
msgstr "FakeLdap keresés itt: dn=%s hatókör=%s lekérdezés=%s"
-#: keystone/common/ldap/fakeldap.py:304
+#: keystone/common/ldap/fakeldap.py:302
msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
msgstr "FakeLdap keresés sikertelen: a dn nem található a SCOPE_BASE-hez"
-#: keystone/common/ldap/fakeldap.py:318
+#: keystone/common/ldap/fakeldap.py:316
#, python-format
msgid "Search scope %s not implemented."
msgstr "A(z) %s keresési hatókör nincs megvalósítva."
-#: keystone/common/sql/core.py:163
+#: keystone/common/sql/core.py:207
#, python-format
msgid "Got mysql server has gone away: %s"
msgstr "A kapott MySQL szerver eltűnt: %s"
-#: keystone/common/sql/legacy.py:174
+#: keystone/common/sql/legacy.py:180
#, python-format
msgid "Cannot migrate EC2 credential: %s"
msgstr "Nem migrálhatók az EC2 hitelesítési adatok: %s"
-#: keystone/common/sql/nova.py:58
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
#, python-format
msgid "Create tenant %s"
msgstr "%s bérlő létrehozása"
-#: keystone/common/sql/nova.py:74
+#: keystone/common/sql/nova.py:79
#, python-format
msgid "Create user %s"
msgstr "%s felhasználó létrehozása"
-#: keystone/common/sql/nova.py:83
+#: keystone/common/sql/nova.py:88
#, python-format
msgid "Add user %s to tenant %s"
msgstr "%s felhasználó hozzáadása %s bérlőhöz"
-#: keystone/common/sql/nova.py:91
+#: keystone/common/sql/nova.py:96
#, python-format
msgid "Ignoring existing role %s"
msgstr "Meglévő %s szerep figyelmen kívül hagyása"
-#: keystone/common/sql/nova.py:98
+#: keystone/common/sql/nova.py:103
#, python-format
msgid "Create role %s"
msgstr "%s szerep létrehozása"
-#: keystone/common/sql/nova.py:108
+#: keystone/common/sql/nova.py:113
#, python-format
msgid "Assign role %s to user %s on tenant %s"
msgstr "%s szerep hozzárendelése %s felhasználóhoz %s bérlőben"
-#: keystone/common/sql/nova.py:123
+#: keystone/common/sql/nova.py:128
#, python-format
msgid "Creating ec2 cred for user %s and tenant %s"
msgstr "EC2 hitelesítési adatok létrehozása %s felhasználóhoz és %s bérlőhöz"
-#: keystone/policy/backends/rules.py:95
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
#, python-format
msgid "enforce %s: %s"
msgstr "%s kikényszerítése: %s"
-#: keystone/token/backends/memcache.py:81
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
msgid "Unable to add token user list."
msgstr "Nem vehető fel a token felhasználólistája."
-#: keystone/token/backends/memcache.py:91
+#: keystone/token/backends/memcache.py:93
msgid "Unable to add token to revocation list."
msgstr "A token nem adható a visszavonási listához."
+
diff --git a/keystone/locale/it/LC_MESSAGES/keystone.po b/keystone/locale/it/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..1ee00550
--- /dev/null
+++ b/keystone/locale/it/LC_MESSAGES/keystone.po
@@ -0,0 +1,572 @@
+# Italian translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Italian "
+"(http://www.transifex.com/projects/p/openstack/language/it/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s non può essere vuoto."
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr "%(property_name)s non può essere inferiore a %(min_length)s caratteri."
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr "%(property_name)s non può essere superiore a %(max_length)s caratteri."
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr "Impossibile eseguire il checkout %s"
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr "Il progetto è disabilitato: %s"
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "Il dominio è disabilitato: %s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr "L'utente è disabilitato: %s"
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr "Il controllo sia del dominio che del progetto non è consentito"
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr "Il controllo sia del dominio che di trust non è consentito"
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr "Il controllo sia delprogetto che di trust non è consentito"
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr "Impossibile eseguire la ricerca dell'utente %s"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "Utente non trovato"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "L'utente non ha accesso al progetto"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr "L'utente non ha accesso al dominio"
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr "Impossibile firmare il token."
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+"Valore non valido per token_format: %s. I valori consentiti sono PKI o "
+"UUID."
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr "Endpoint %(url)s non valdio - chiave sconosciuta %(keyerror)s"
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+"Endpoint %(url)s non corretto - chiave sconosciuta %(keyerror)s(mancano "
+"le parentesi?)"
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+"Endpoint %s non corretto - formato non completo (manca "
+"un notifier del tipo ?)"
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr "Impossibile aprire il file di template %s"
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr "HTTP PERF: %(time).5f secondi a %(method)s %(host)s:%(port)s %(path)s)"
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr "Verifica errore: %s"
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+"errore di firma: impossibile caricare il certificato - assicurarsi che "
+"sia stato configurato PKI con 'keystone-manage pki_setup'"
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr "Errore di firma: %s"
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr "Impossibile individuare il file config di registrazione specificato: %s"
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "Funzione syslog non valida"
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr "RBAC: autorizzazione %s(%s)"
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr "RBAC: token non valido"
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr "RBAC: utente non valido"
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr "RBAC: si procede senza progetto"
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr "RBAC: si procede senza tenant"
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr "RBAC: autorizzazione oltrepassata"
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr "RBAC: autorizzazione concessa"
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr "RBAC: aggiunta parametri del filtro della query (%s)"
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr "Token non valido in normalize_domain_id"
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr "Avvio %(arg0)s in %(host)s:%(port)s"
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr "Errore del server"
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr "arg_dict: %s"
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr "Autorizzazione non riuscita. %s da %s"
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr "Impossibile trovare la risorsa."
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr "Opzione deref LDAP non valida: %s. Sceglierne una di: "
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr "Ambito LDAP non valido: %s. Sceglierne uno di: "
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr "Nome duplicato, %s."
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr "ID duplicato, %s."
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "LDAP %s crea"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "LDAP %s aggiorna"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "LDAP %s elimina"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr "LDAP inizializza: url=%s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "LDAP bind: dn=%s"
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "LDAP aggiunge: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "Ricerca LDAP: dn=%s, scope=%s, query=%s, attrs=%s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+"Il server LDAP non supporta la paginazione. Disabilitare la paginazione "
+"in keystone.conf per evitare questo messaggio."
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "LDAP modifica: dn=%s, modlist=%s"
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "LDAP elimina: dn=%s"
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "LDAP delete_ext: dn=%s, serverctrls=%s"
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr "FakeLdap inizializza url=%s"
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr "FakeLdap bind dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr "FakeLdap bind non riuscito: dn=%s non trovato"
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr "FakeLdap bind non riuscito: password per dn=%s non tovato"
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr "FakeLdap bind non riuscito: password per dn=%s non corrisponde"
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr "FakeLdap aggiunge elemento: dn=%s, attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr "FakeLdap aggiunta elemento non riuscita: dn=%s è già nell'archivio."
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr "FakeLdap elimina elemento: dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr "FakeLdap elimina elemento non riuscito: dn=%s non trovato."
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr "FakeLdap modifica elemento: dn=%s attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr "FakeLdap modifica elemento: dn=%s non trovato."
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+"FakeLdap modifica elemento non riuscito: l'elemento non ha nessun "
+"attributo \"%s\" da eliminare"
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+"Modifica elemento FakeLdap non riuscita: l'elemento non ha nessun "
+"attributo \"%s\" con il valore \"%s\" da eliminare"
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr "FakeLdap modifica elemento non riuscito: comando %s sconosciuto"
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr "modify_s azione %s non implementata"
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr "FakeLdap ricerca in dn=%s scope=%s query=%s"
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr "FakeLdap ricerca non riuscita: dn non trovato per SCOPE_BASE"
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr "Ambito di ricerca %s non implementato."
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr "Ricevuto messaggio di interruzione della connessione del server mysql: %s"
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr "Impossibile migrare la credenziale EC2: %s"
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr "la versione deve essere un numero intero"
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr "Crea tenant %s"
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "Crea utente %s"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr "Aggiungi utente %s al tenant %s"
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr "Il ruolo esistente viene ignorato %s"
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr "Crea ruolo %s"
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr "Assegna il ruolo %s all'utente %s nel tenant %s"
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr "Creazione credenziale ec2 per l'utente %s e del tenant %s"
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr "Utente non trovato nel gruppo"
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr "Impossibile rimuovere un ruolo che non è stato concesso, %s"
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "Ruolo %s non trovato"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr "Modifica nome non supportato da LDAP"
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr "L'utente %s è già membro del gruppo %s"
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr "Impossibile comprendere la regola %(rule)s"
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr "Nessun gestore per le corrispondenze di tipo %s"
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr "Impossibile comprendere la regola %(rule)r"
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr "applica %s: %s"
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr "Il token non appartiene al tenant specificato."
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr "Il dominio non predefinito non è supportato"
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr "L'ambito del dominio token non è supportato"
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr "Impossibile aggiungere un elenco utenti token."
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr "Impossibile aggiungere un token ad un elenco di revoca."
+
diff --git a/keystone/locale/ja/LC_MESSAGES/keystone.po b/keystone/locale/ja/LC_MESSAGES/keystone.po
index 20cbfdae..705c932f 100644
--- a/keystone/locale/ja/LC_MESSAGES/keystone.po
+++ b/keystone/locale/ja/LC_MESSAGES/keystone.po
@@ -1,4 +1,3 @@
-Translations template for OpenStack Keystone.
# Copyright (C) 2012 OpenStack Foundation
# This file is distributed under the same license as the keystone project.
#
@@ -6,35 +5,555 @@ Translations template for OpenStack Keystone.
# Tomoyuki KATO <tomo@dream.daynight.jp>, 2012.
msgid ""
msgstr ""
-"Project-Id-Version: Keystone\n"
+"Project-Id-Version: Keystone\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
-"POT-Creation-Date: 2012-11-15 23:10+0000\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
"PO-Revision-Date: 2012-11-03 01:25+0000\n"
"Last-Translator: Tomoyuki KATO <tomo@dream.daynight.jp>\n"
-"Language-Team: Japanese (http://www.transifex.com/projects/p/openstack/language/ja/)\n"
+"Language-Team: Japanese "
+"(http://www.transifex.com/projects/p/openstack/language/ja/)\n"
+"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-"Language: ja\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-#: keystone/config.py:62
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, fuzzy, python-format
+msgid "Unable to lookup user %s"
+msgstr "ユーザーリストにトークンを追加できません。"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+#, fuzzy
+msgid "Unable to sign token."
+msgstr "ユーザーリストにトークンを追加できません。"
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
msgid "Invalid syslog facility"
msgstr "無効な syslog ファシリティ"
-#: keystone/policy/backends/rules.py:34
-msgid "JSON file representing policy"
-msgstr "\t\nJSON ファイル表現ポリシー"
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
-#: keystone/policy/backends/rules.py:37
-msgid "Rule checked when requested rule is not found"
-msgstr "要求されたルールが見つからないときにチェックされるルール"
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
-#: keystone/token/backends/memcache.py:81
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
msgid "Unable to add token user list."
msgstr "ユーザーリストにトークンを追加できません。"
-#: keystone/token/backends/memcache.py:91
+#: keystone/token/backends/memcache.py:93
msgid "Unable to add token to revocation list."
msgstr "失効リストにトークンを追加できません。"
+
diff --git a/keystone/locale/ka_GE/LC_MESSAGES/keystone.po b/keystone/locale/ka_GE/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..6ba6dbb4
--- /dev/null
+++ b/keystone/locale/ka_GE/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Georgian (Georgia) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+# Nika Chkhikvishvili <frrrredo@gmail.com>, 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: ka_GE <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s არ შეიძლება იყოს ცარიელი."
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "დომენი გათიშულია: %s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "მომხმარებელი ვერ მოიძებნა"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "მომხმარებელს არ აქვს წვდომა პროექტზე"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "LDAP %s შექმნა"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "LDAP %s განახლება"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "LDAP %s წაშლა"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "მომხმარებლის შექმნა %s"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "როლი %s ვერ მოიძებნა"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/keystone.pot b/keystone/locale/keystone.pot
index 95dba04c..585ca6bf 100644
--- a/keystone/locale/keystone.pot
+++ b/keystone/locale/keystone.pot
@@ -7,9 +7,9 @@
msgid ""
msgstr ""
"Project-Id-Version: keystone "
-"jenkins.keystone.propose.translation.update.148\n"
+"jenkins.keystone.propose.translation.update.209\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2013-03-22 00:02+0000\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -35,10 +35,10 @@ msgstr ""
#: keystone/clean.py:36
#, python-format
-msgid "%(property_name)s is not a%(display_expected_type)s"
+msgid "%(property_name)s is not a %(display_expected_type)s"
msgstr ""
-#: keystone/test.py:112
+#: keystone/test.py:115
#, python-format
msgid "Failed to checkout %s"
msgstr ""
@@ -70,28 +70,28 @@ msgstr ""
msgid "Scoping to both project and trust is not allowed"
msgstr ""
-#: keystone/auth/controllers.py:332
+#: keystone/auth/controllers.py:333
#, python-format
msgid "Unable to lookup user %s"
msgstr ""
-#: keystone/auth/controllers.py:362
+#: keystone/auth/controllers.py:363
msgid "User not found"
msgstr ""
-#: keystone/auth/token_factory.py:78
+#: keystone/auth/token_factory.py:81
msgid "User have no access to project"
msgstr ""
-#: keystone/auth/token_factory.py:93
+#: keystone/auth/token_factory.py:96
msgid "User have no access to domain"
msgstr ""
-#: keystone/auth/token_factory.py:264 keystone/token/controllers.py:121
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
msgid "Unable to sign token."
msgstr ""
-#: keystone/auth/token_factory.py:267 keystone/token/controllers.py:124
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
#, python-format
msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
msgstr ""
@@ -188,102 +188,155 @@ msgstr ""
msgid "Invalid token in normalize_domain_id"
msgstr ""
-#: keystone/common/wsgi.py:77
+#: keystone/common/wsgi.py:106
#, python-format
msgid "Starting %(arg0)s on %(host)s:%(port)s"
msgstr ""
-#: keystone/common/wsgi.py:137
+#: keystone/common/wsgi.py:166
msgid "Server error"
msgstr ""
-#: keystone/common/wsgi.py:216
+#: keystone/common/wsgi.py:245
#, python-format
msgid "arg_dict: %s"
msgstr ""
-#: keystone/common/wsgi.py:238
+#: keystone/common/wsgi.py:267
#, python-format
msgid "Authorization failed. %s from %s"
msgstr ""
-#: keystone/common/wsgi.py:469
+#: keystone/common/wsgi.py:510
msgid "The resource could not be found."
msgstr ""
-#: keystone/common/ldap/core.py:74
+#: keystone/common/ldap/core.py:79
#, python-format
msgid "Invalid LDAP deref option: %s. Choose one of: "
msgstr ""
-#: keystone/common/ldap/core.py:82
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
#, python-format
msgid "Invalid LDAP scope: %s. Choose one of: "
msgstr ""
-#: keystone/common/ldap/core.py:226 keystone/identity/backends/kvs.py:588
-#: keystone/identity/backends/kvs.py:616
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
#, python-format
msgid "Duplicate name, %s."
msgstr ""
-#: keystone/common/ldap/core.py:236 keystone/identity/backends/kvs.py:581
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
#, python-format
msgid "Duplicate ID, %s."
msgstr ""
-#: keystone/common/ldap/core.py:241
+#: keystone/common/ldap/core.py:289
#, python-format
msgid "LDAP %s create"
msgstr ""
-#: keystone/common/ldap/core.py:313
+#: keystone/common/ldap/core.py:367
#, python-format
msgid "LDAP %s update"
msgstr ""
-#: keystone/common/ldap/core.py:344
+#: keystone/common/ldap/core.py:400
#, python-format
msgid "LDAP %s delete"
msgstr ""
-#: keystone/common/ldap/core.py:367
+#: keystone/common/ldap/core.py:425
#, python-format
msgid "LDAP init: url=%s"
msgstr ""
-#: keystone/common/ldap/core.py:374
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
#, python-format
msgid "LDAP bind: dn=%s"
msgstr ""
-#: keystone/common/ldap/core.py:385
+#: keystone/common/ldap/core.py:503
#, python-format
msgid "LDAP add: dn=%s, attrs=%s"
msgstr ""
-#: keystone/common/ldap/core.py:390
+#: keystone/common/ldap/core.py:508
#, python-format
msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
msgstr ""
-#: keystone/common/ldap/core.py:441
+#: keystone/common/ldap/core.py:559
msgid ""
"LDAP Server does not support paging. Disable paging in keystone.conf to "
"avoid this message."
msgstr ""
-#: keystone/common/ldap/core.py:458
+#: keystone/common/ldap/core.py:576
#, python-format
msgid "LDAP modify: dn=%s, modlist=%s"
msgstr ""
-#: keystone/common/ldap/core.py:463
+#: keystone/common/ldap/core.py:581
#, python-format
msgid "LDAP delete: dn=%s"
msgstr ""
-#: keystone/common/ldap/core.py:467
+#: keystone/common/ldap/core.py:585
#, python-format
msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
msgstr ""
@@ -379,7 +432,7 @@ msgstr ""
msgid "Search scope %s not implemented."
msgstr ""
-#: keystone/common/sql/core.py:206
+#: keystone/common/sql/core.py:207
#, python-format
msgid "Got mysql server has gone away: %s"
msgstr ""
@@ -428,30 +481,41 @@ msgstr ""
msgid "Creating ec2 cred for user %s and tenant %s"
msgstr ""
-#: keystone/identity/backends/kvs.py:254 keystone/identity/backends/kvs.py:263
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
msgid "User not found in group"
msgstr ""
-#: keystone/identity/backends/sql.py:427
+#: keystone/identity/backends/sql.py:424
#, python-format
msgid "Cannot remove role that has not been granted, %s"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:654
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
#, python-format
msgid "Role %s not found"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:874
-#: keystone/identity/backends/ldap/core.py:984
+#: keystone/identity/backends/ldap/core.py:912
msgid "Changing Name not supported by LDAP"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:888
+#: keystone/identity/backends/ldap/core.py:926
#, python-format
msgid "User %s is already a member of group %s"
msgstr ""
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
#: keystone/openstack/common/policy.py:394
#, python-format
msgid "Failed to understand rule %(rule)s"
@@ -472,15 +536,15 @@ msgstr ""
msgid "enforce %s: %s"
msgstr ""
-#: keystone/token/controllers.py:470 keystone/token/controllers.py:473
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
msgid "Token does not belong to specified tenant."
msgstr ""
-#: keystone/token/controllers.py:480
+#: keystone/token/controllers.py:476
msgid "Non-default domain is not supported"
msgstr ""
-#: keystone/token/controllers.py:488
+#: keystone/token/controllers.py:484
msgid "Domain scoped token is not supported"
msgstr ""
diff --git a/keystone/locale/ko_KR/LC_MESSAGES/keystone.po b/keystone/locale/ko_KR/LC_MESSAGES/keystone.po
index 00437797..160972f1 100644
--- a/keystone/locale/ko_KR/LC_MESSAGES/keystone.po
+++ b/keystone/locale/ko_KR/LC_MESSAGES/keystone.po
@@ -1,23 +1,22 @@
-# Translations template for keystone.
+# Korean (South Korea) translations for keystone.
# Copyright (C) 2013 ORGANIZATION
# This file is distributed under the same license as the keystone project.
-#
+#
# Translators:
# Sung Jin Gang <potopro@gmail.com>, 2013.
msgid ""
msgstr ""
-"Project-Id-Version: Keystone\n"
+"Project-Id-Version: Keystone\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
-"POT-Creation-Date: 2013-03-21 21:29+0000\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
"PO-Revision-Date: 2013-03-21 18:34+0000\n"
"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language-Team: ko_KR <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-"Language: ko_KR\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
#: keystone/clean.py:23
#, python-format
@@ -31,16 +30,15 @@ msgstr ""
#: keystone/clean.py:29
#, python-format
-msgid ""
-"%(property_name)s should not be greater than %(max_length)s characters."
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
msgstr ""
#: keystone/clean.py:36
#, python-format
-msgid "%(property_name)s is not a%(display_expected_type)s"
+msgid "%(property_name)s is not a %(display_expected_type)s"
msgstr ""
-#: keystone/test.py:112
+#: keystone/test.py:115
#, python-format
msgid "Failed to checkout %s"
msgstr ""
@@ -72,28 +70,28 @@ msgstr ""
msgid "Scoping to both project and trust is not allowed"
msgstr ""
-#: keystone/auth/controllers.py:332
+#: keystone/auth/controllers.py:333
#, python-format
msgid "Unable to lookup user %s"
msgstr ""
-#: keystone/auth/controllers.py:362
+#: keystone/auth/controllers.py:363
msgid "User not found"
msgstr ""
-#: keystone/auth/token_factory.py:78
+#: keystone/auth/token_factory.py:81
msgid "User have no access to project"
msgstr ""
-#: keystone/auth/token_factory.py:93
+#: keystone/auth/token_factory.py:96
msgid "User have no access to domain"
msgstr ""
-#: keystone/auth/token_factory.py:264 keystone/token/controllers.py:121
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
msgid "Unable to sign token."
msgstr ""
-#: keystone/auth/token_factory.py:267 keystone/token/controllers.py:124
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
#, python-format
msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
msgstr ""
@@ -113,8 +111,8 @@ msgstr ""
#: keystone/catalog/core.py:49
#, python-format
msgid ""
-"Malformed endpoint %s - incomplete format (are you missing"
-" a type notifier ?)"
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
msgstr ""
#: keystone/catalog/backends/templated.py:109
@@ -124,8 +122,7 @@ msgstr ""
#: keystone/common/bufferedhttp.py:102
#, python-format
-msgid ""
-"HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
msgstr ""
#: keystone/common/cms.py:44
@@ -191,102 +188,155 @@ msgstr ""
msgid "Invalid token in normalize_domain_id"
msgstr ""
-#: keystone/common/wsgi.py:77
+#: keystone/common/wsgi.py:106
#, python-format
msgid "Starting %(arg0)s on %(host)s:%(port)s"
msgstr ""
-#: keystone/common/wsgi.py:137
+#: keystone/common/wsgi.py:166
msgid "Server error"
msgstr ""
-#: keystone/common/wsgi.py:216
+#: keystone/common/wsgi.py:245
#, python-format
msgid "arg_dict: %s"
msgstr ""
-#: keystone/common/wsgi.py:238
+#: keystone/common/wsgi.py:267
#, python-format
msgid "Authorization failed. %s from %s"
msgstr ""
-#: keystone/common/wsgi.py:469
+#: keystone/common/wsgi.py:510
msgid "The resource could not be found."
msgstr ""
-#: keystone/common/ldap/core.py:74
+#: keystone/common/ldap/core.py:79
#, python-format
msgid "Invalid LDAP deref option: %s. Choose one of: "
msgstr ""
-#: keystone/common/ldap/core.py:82
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
#, python-format
msgid "Invalid LDAP scope: %s. Choose one of: "
msgstr ""
-#: keystone/common/ldap/core.py:226 keystone/identity/backends/kvs.py:588
-#: keystone/identity/backends/kvs.py:616
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
#, python-format
msgid "Duplicate name, %s."
msgstr ""
-#: keystone/common/ldap/core.py:236 keystone/identity/backends/kvs.py:581
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
#, python-format
msgid "Duplicate ID, %s."
msgstr ""
-#: keystone/common/ldap/core.py:241
+#: keystone/common/ldap/core.py:289
#, python-format
msgid "LDAP %s create"
msgstr ""
-#: keystone/common/ldap/core.py:313
+#: keystone/common/ldap/core.py:367
#, python-format
msgid "LDAP %s update"
msgstr ""
-#: keystone/common/ldap/core.py:344
+#: keystone/common/ldap/core.py:400
#, python-format
msgid "LDAP %s delete"
msgstr ""
-#: keystone/common/ldap/core.py:367
+#: keystone/common/ldap/core.py:425
#, python-format
msgid "LDAP init: url=%s"
msgstr ""
-#: keystone/common/ldap/core.py:374
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
#, python-format
msgid "LDAP bind: dn=%s"
msgstr ""
-#: keystone/common/ldap/core.py:385
+#: keystone/common/ldap/core.py:503
#, python-format
msgid "LDAP add: dn=%s, attrs=%s"
msgstr ""
-#: keystone/common/ldap/core.py:390
+#: keystone/common/ldap/core.py:508
#, python-format
msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
msgstr ""
-#: keystone/common/ldap/core.py:441
+#: keystone/common/ldap/core.py:559
msgid ""
"LDAP Server does not support paging. Disable paging in keystone.conf to "
"avoid this message."
msgstr ""
-#: keystone/common/ldap/core.py:458
+#: keystone/common/ldap/core.py:576
#, python-format
msgid "LDAP modify: dn=%s, modlist=%s"
msgstr ""
-#: keystone/common/ldap/core.py:463
+#: keystone/common/ldap/core.py:581
#, python-format
msgid "LDAP delete: dn=%s"
msgstr ""
-#: keystone/common/ldap/core.py:467
+#: keystone/common/ldap/core.py:585
#, python-format
msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
msgstr ""
@@ -354,8 +404,8 @@ msgstr ""
#: keystone/common/ldap/fakeldap.py:271
#, python-format
msgid ""
-"FakeLdap modify item failed: item has no attribute \"%s\" with value \"%s\" "
-"to delete"
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
msgstr ""
#: keystone/common/ldap/fakeldap.py:276
@@ -382,7 +432,7 @@ msgstr ""
msgid "Search scope %s not implemented."
msgstr ""
-#: keystone/common/sql/core.py:206
+#: keystone/common/sql/core.py:207
#, python-format
msgid "Got mysql server has gone away: %s"
msgstr ""
@@ -431,30 +481,41 @@ msgstr ""
msgid "Creating ec2 cred for user %s and tenant %s"
msgstr ""
-#: keystone/identity/backends/kvs.py:254 keystone/identity/backends/kvs.py:263
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
msgid "User not found in group"
msgstr ""
-#: keystone/identity/backends/sql.py:427
+#: keystone/identity/backends/sql.py:424
#, python-format
msgid "Cannot remove role that has not been granted, %s"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:654
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
#, python-format
msgid "Role %s not found"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:874
-#: keystone/identity/backends/ldap/core.py:984
+#: keystone/identity/backends/ldap/core.py:912
msgid "Changing Name not supported by LDAP"
msgstr ""
-#: keystone/identity/backends/ldap/core.py:888
+#: keystone/identity/backends/ldap/core.py:926
#, python-format
msgid "User %s is already a member of group %s"
msgstr ""
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
#: keystone/openstack/common/policy.py:394
#, python-format
msgid "Failed to understand rule %(rule)s"
@@ -475,15 +536,15 @@ msgstr ""
msgid "enforce %s: %s"
msgstr ""
-#: keystone/token/controllers.py:470 keystone/token/controllers.py:473
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
msgid "Token does not belong to specified tenant."
msgstr ""
-#: keystone/token/controllers.py:480
+#: keystone/token/controllers.py:476
msgid "Non-default domain is not supported"
msgstr ""
-#: keystone/token/controllers.py:488
+#: keystone/token/controllers.py:484
msgid "Domain scoped token is not supported"
msgstr ""
@@ -494,3 +555,4 @@ msgstr ""
#: keystone/token/backends/memcache.py:93
msgid "Unable to add token to revocation list."
msgstr ""
+
diff --git a/keystone/locale/pt_BR/LC_MESSAGES/keystone.po b/keystone/locale/pt_BR/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..ee54d3fe
--- /dev/null
+++ b/keystone/locale/pt_BR/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Portuguese (Brazil) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2012-11-02 18:30+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Portuguese (Brazil) "
+"(http://www.transifex.com/projects/p/openstack/language/pt_BR/)\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/ru/LC_MESSAGES/keystone.po b/keystone/locale/ru/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..f6148dc0
--- /dev/null
+++ b/keystone/locale/ru/LC_MESSAGES/keystone.po
@@ -0,0 +1,559 @@
+# Russian translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Russian "
+"(http://www.transifex.com/projects/p/openstack/language/ru/)\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/vi_VN/LC_MESSAGES/keystone.po b/keystone/locale/vi_VN/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..85568202
--- /dev/null
+++ b/keystone/locale/vi_VN/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Vietnamese (Vietnam) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-17 16:06+0000\n"
+"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
+"Language-Team: Vietnamese (Viet Nam) "
+"(http://www.transifex.com/projects/p/openstack/language/vi_VN/)\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr ""
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr ""
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr ""
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr ""
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr ""
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr ""
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr ""
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr ""
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr ""
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr ""
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr ""
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr ""
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr ""
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr ""
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr ""
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr ""
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr ""
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr ""
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr ""
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr ""
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr ""
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr ""
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr ""
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr ""
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr ""
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr ""
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr ""
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr ""
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr ""
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr ""
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr ""
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr ""
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr ""
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr ""
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr ""
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr ""
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr ""
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr ""
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr ""
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr ""
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr ""
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr ""
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr ""
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr ""
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr ""
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr ""
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr ""
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr ""
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr ""
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr ""
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr ""
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr ""
+
diff --git a/keystone/locale/zh_CN/LC_MESSAGES/keystone.po b/keystone/locale/zh_CN/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..e2a4e6ac
--- /dev/null
+++ b/keystone/locale/zh_CN/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Chinese (China) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-21 06:08+0000\n"
+"Last-Translator: daisy.ycguo <daisy.ycguo@gmail.com>\n"
+"Language-Team: Chinese (China) "
+"(http://www.transifex.com/projects/p/openstack/language/zh_CN/)\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s 不能为空。"
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr "%(property_name)s 不能少于 %(min_length)s 个字符。"
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr "%(property_name)s 不应该超过 %(max_length)s 个字符。"
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr "未能检出 %s"
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr "项目已禁用:%s"
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "域已禁用:%s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr "用户已禁用:%s"
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr "不允许同时将作用域限定到域和项目"
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr "不允许同时将作用域限定到域和信任"
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr "不允许同时将作用域限定到项目和信任"
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr "无法查找用户 %s"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "找不到用户"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "用户对项目没有任何访问权限"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr "用户对域没有任何访问权限"
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr "无法对令牌进行签名。"
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr "token_format 的值 %s 无效。允许值是 PKI 或 UUID。"
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr "端点 %(url)s 的格式不正确 - 键 %(keyerror)s 未知"
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr "端点 %(url)s 的格式不正确 - 键 %(keyerror)s 未知(您遗漏了方括号吗?)"
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr "端点 %s 的格式不正确 - 格式不完整(您遗漏了类型通知器吗?)"
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr "无法打开模板文件 %s"
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr "HTTP 性能:%(time).5f 秒,至 %(method)s %(host)s:%(port)s %(path)s)"
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr "发生验证错误:%s"
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr "发生签名错误:无法装入证书 - 请确保您已使用“keystone-manage pki_setup”配置 PKI"
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr "发生签名错误:%s"
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr "找不到指定的日志记录配置文件:%s"
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "系统日志工具无效"
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr "RBAC:正在授权 %s(%s)"
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr "RBAC:令牌无效"
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr "RBAC:用户无效"
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr "RBAC:正在没有项目的情况下继续"
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr "RBAC:正在没有租户的情况下继续"
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr "RBAC:正在绕过授权"
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr "RBAC:已授予权限"
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr "RBAC:正在添加查询过滤器参数 (%s)"
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr "normalize_domain_id 中的令牌无效"
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr "正在 %(host)s:%(port)s 上启动 %(arg0)s"
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr "服务器错误"
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr "arg_dict:%s"
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr "授权失败。%s 来自 %s"
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr "找不到该资源。"
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr "LDAP deref 选项 %s 无效。请选择下列其中一项:"
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr "LDAP 作用域 %s 无效。请选择下列其中一项:"
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr "名称 %s 重复。"
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr "标识 %s 重复。"
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "LDAP %s 创建"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "LDAP %s 更新"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "LDAP %s 删除"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr "LDAP 初始化:url=%s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "LDAP 绑定:dn=%s"
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "LDAP 添加:dn=%s,attrs=%s"
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "LDAP 搜索:dn=%s,scope=%s,query=%s,attrs=%s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr "LDAP 服务器不支持页面调度。请在 keystone.conf 中禁用页面调度以避免出现此消息。"
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "LDAP 修改:dn=%s,modlist=%s"
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "LDAP 删除:dn=%s"
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "LDAP delete_ext:dn=%s,serverctrls=%s"
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr "FakeLdap initialize url=%s"
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr "FakeLdap bind dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr "FakeLdap bind 失败:找不到 dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr "FakeLdap bind 失败:找不到 dn=%s 的密码"
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr "FakeLdap bind 失败:dn=%s 的密码不匹配"
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr "FakeLdap add item:dn=%s,attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr "FakeLdap add item 失败:dn=%s 已在存储中。"
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr "FakeLdap delete item:dn=%s"
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr "FakeLdap delete item 失败:找不到 dn=%s。"
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr "FakeLdap modify item:dn=%s attrs=%s"
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr "FakeLdap modify item 失败:找不到 dn=%s。"
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr "FakeLdap modify item 失败:项没有要删除的任何属性“%s”"
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr "FakeLdap modify item 失败:项没有任何符合以下条件的属性“%s”:具有要删除的值“%s”"
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr "FakeLdap modify item 失败:命令 %s 未知"
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr "未实现 modify_s 操作 %s"
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr "FakeLdap search(在以下位置:dn=%s scope=%s query=%s)"
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr "FakeLdap search 失败:对于 SCOPE_BASE,找不到 dn"
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr "未实现搜索范围 %s。"
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr "mysql 服务器已不存在:%s"
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr "无法迁移 EC2 凭证:%s"
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr "版本应该为整数"
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr "请创建租户 %s"
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "请创建用户 %s"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr "请将用户 %s 添加至租户 %s"
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr "正在忽略现有角色 %s"
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr "请创建角色 %s"
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr "请将角色 %s 分配给用户 %s(在租户 %s 上)"
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr "正在为用户 %s 和租户 %s 创建 ec2 凭证"
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr "在组中找不到用户"
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr "无法除去尚未授予的角色 %s"
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "找不到角色 %s"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr "更改名称不受 LDAP 支持"
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr "用户 %s 已是组 %s 的成员"
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr "未能理解规则 %(rule)s"
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr "对于类型为 %s 的匹配项,不存在任何处理程序"
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr "未能理解规则 %(rule)r"
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr "请强制执行 %s:%s"
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr "令牌不属于指定的租户。"
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr "非缺省域不受支持"
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr "作用域限定到域的令牌不受支持"
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr "无法添加令牌用户列表。"
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr "无法将令牌添加至撤销列表。"
+
diff --git a/keystone/locale/zh_TW/LC_MESSAGES/keystone.po b/keystone/locale/zh_TW/LC_MESSAGES/keystone.po
new file mode 100644
index 00000000..96b5e1dc
--- /dev/null
+++ b/keystone/locale/zh_TW/LC_MESSAGES/keystone.po
@@ -0,0 +1,558 @@
+# Chinese (Taiwan) translations for keystone.
+# Copyright (C) 2013 ORGANIZATION
+# This file is distributed under the same license as the keystone project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Keystone\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n"
+"POT-Creation-Date: 2013-05-22 17:05+0000\n"
+"PO-Revision-Date: 2013-05-22 03:11+0000\n"
+"Last-Translator: daisy.ycguo <daisy.ycguo@gmail.com>\n"
+"Language-Team: Chinese (Taiwan) "
+"(http://www.transifex.com/projects/p/openstack/language/zh_TW/)\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: keystone/clean.py:23
+#, python-format
+msgid "%s cannot be empty."
+msgstr "%s 不能是空的。"
+
+#: keystone/clean.py:25
+#, python-format
+msgid "%(property_name)s cannot be less than %(min_length)s characters."
+msgstr "%(property_name)s 不能少於 %(min_length)s 個字元。"
+
+#: keystone/clean.py:29
+#, python-format
+msgid "%(property_name)s should not be greater than %(max_length)s characters."
+msgstr "%(property_name)s 不應超過 %(max_length)s 個字元。"
+
+#: keystone/clean.py:36
+#, python-format
+msgid "%(property_name)s is not a %(display_expected_type)s"
+msgstr ""
+
+#: keystone/test.py:115
+#, python-format
+msgid "Failed to checkout %s"
+msgstr "無法移出 %s"
+
+#: keystone/auth/controllers.py:72
+#, python-format
+msgid "Project is disabled: %s"
+msgstr "已停用專案:%s"
+
+#: keystone/auth/controllers.py:78 keystone/auth/plugins/password.py:40
+#, python-format
+msgid "Domain is disabled: %s"
+msgstr "已停用網域:%s"
+
+#: keystone/auth/controllers.py:84 keystone/auth/plugins/password.py:46
+#, python-format
+msgid "User is disabled: %s"
+msgstr "已停用使用者:%s"
+
+#: keystone/auth/controllers.py:265
+msgid "Scoping to both domain and project is not allowed"
+msgstr "不容許將範圍同時設定為網域及專案"
+
+#: keystone/auth/controllers.py:268
+msgid "Scoping to both domain and trust is not allowed"
+msgstr "不容許將範圍同時設定為網域及信任"
+
+#: keystone/auth/controllers.py:271
+msgid "Scoping to both project and trust is not allowed"
+msgstr "不容許將範圍同時設定為專案及信任"
+
+#: keystone/auth/controllers.py:333
+#, python-format
+msgid "Unable to lookup user %s"
+msgstr "無法查閱使用者 %s"
+
+#: keystone/auth/controllers.py:363
+msgid "User not found"
+msgstr "找不到使用者"
+
+#: keystone/auth/token_factory.py:81
+msgid "User have no access to project"
+msgstr "使用者無法存取專案"
+
+#: keystone/auth/token_factory.py:96
+msgid "User have no access to domain"
+msgstr "使用者無法存取網域"
+
+#: keystone/auth/token_factory.py:314 keystone/token/controllers.py:121
+msgid "Unable to sign token."
+msgstr "無法簽署記號。"
+
+#: keystone/auth/token_factory.py:317 keystone/token/controllers.py:124
+#, python-format
+msgid "Invalid value for token_format: %s. Allowed values are PKI or UUID."
+msgstr "token_format 的值無效:%s。接受的值為 PKI 或 UUID。"
+
+#: keystone/catalog/core.py:38
+#, python-format
+msgid "Malformed endpoint %(url)s - unknown key %(keyerror)s"
+msgstr "端點 %(url)s 的格式不正確 - 不明的索引鍵 %(keyerror)s"
+
+#: keystone/catalog/core.py:43
+#, python-format
+msgid ""
+"Malformed endpoint %(url)s - unknown key %(keyerror)s(are you missing "
+"brackets ?)"
+msgstr "端點 %(url)s 的格式不正確 - 不明的索引鍵 %(keyerror)s(遺漏了方括弧嗎?)"
+
+#: keystone/catalog/core.py:49
+#, python-format
+msgid ""
+"Malformed endpoint %s - incomplete format (are you "
+"missing a type notifier ?)"
+msgstr "端點 %s 的格式不正確 - 格式不完整(遺漏了類型通知符嗎?)"
+
+#: keystone/catalog/backends/templated.py:109
+#, python-format
+msgid "Unable to open template file %s"
+msgstr "無法開啟範本檔 %s"
+
+#: keystone/common/bufferedhttp.py:102
+#, python-format
+msgid "HTTP PERF: %(time).5f seconds to %(method)s %(host)s:%(port)s %(path)s)"
+msgstr "HTTP PERF:%(time).5f 秒鐘以 (%(method)s %(host)s:%(port)s %(path)s)"
+
+#: keystone/common/cms.py:44
+#, python-format
+msgid "Verify error: %s"
+msgstr "驗證發生錯誤:%s"
+
+#: keystone/common/cms.py:135
+msgid ""
+"Signing error: Unable to load certificate - ensure you've configured PKI "
+"with 'keystone-manage pki_setup'"
+msgstr "簽署發生錯誤:無法載入憑證 - 請確保已使用 'keystone-manage pki_setup' 來配置 PKI"
+
+#: keystone/common/cms.py:139
+#, python-format
+msgid "Signing error: %s"
+msgstr "簽署發生錯誤:%s"
+
+#: keystone/common/config.py:93
+#, python-format
+msgid "Unable to locate specified logging config file: %s"
+msgstr "找不到指定的記載配置檔:%s"
+
+#: keystone/common/config.py:111
+msgid "Invalid syslog facility"
+msgstr "無效的 Syslog 機能"
+
+#: keystone/common/controller.py:19
+#, python-format
+msgid "RBAC: Authorizing %s(%s)"
+msgstr "RBAC:正在授權 %s(%s)"
+
+#: keystone/common/controller.py:27
+msgid "RBAC: Invalid token"
+msgstr "RBAC:無效的記號"
+
+#: keystone/common/controller.py:37 keystone/common/controller.py:58
+msgid "RBAC: Invalid user"
+msgstr "RBAC:無效的使用者"
+
+#: keystone/common/controller.py:43
+msgid "RBAC: Proceeding without project"
+msgstr "RBAC:在沒有專案的情況下繼續作業"
+
+#: keystone/common/controller.py:63
+msgid "RBAC: Proceeding without tenant"
+msgstr "RBAC:在沒有 Tenant 的情況下繼續作業"
+
+#: keystone/common/controller.py:93 keystone/common/controller.py:145
+msgid "RBAC: Bypassing authorization"
+msgstr "RBAC:正在略過授權"
+
+#: keystone/common/controller.py:102 keystone/common/controller.py:143
+msgid "RBAC: Authorization granted"
+msgstr "RBAC:已授與權限"
+
+#: keystone/common/controller.py:132
+#, python-format
+msgid "RBAC: Adding query filter params (%s)"
+msgstr "RBAC:正在新增查詢過濾器參數 (%s)"
+
+#: keystone/common/controller.py:332
+msgid "Invalid token in normalize_domain_id"
+msgstr "normalize_domain_id 中的記號無效"
+
+#: keystone/common/wsgi.py:106
+#, python-format
+msgid "Starting %(arg0)s on %(host)s:%(port)s"
+msgstr "正在 %(host)s:%(port)s 上啟動 %(arg0)s"
+
+#: keystone/common/wsgi.py:166
+msgid "Server error"
+msgstr "伺服器錯誤"
+
+#: keystone/common/wsgi.py:245
+#, python-format
+msgid "arg_dict: %s"
+msgstr "arg_dict:%s"
+
+#: keystone/common/wsgi.py:267
+#, python-format
+msgid "Authorization failed. %s from %s"
+msgstr "授權失敗。%s(自 %s)"
+
+#: keystone/common/wsgi.py:510
+msgid "The resource could not be found."
+msgstr "找不到資源。"
+
+#: keystone/common/ldap/core.py:79
+#, python-format
+msgid "Invalid LDAP deref option: %s. Choose one of: "
+msgstr "無效的 LDAP deref 選項:%s。請選擇下列其中一個:"
+
+#: keystone/common/ldap/core.py:87
+#, python-format
+msgid "Invalid LDAP tls certs option: %s. Choose one of: "
+msgstr ""
+
+#: keystone/common/ldap/core.py:96
+#, python-format
+msgid "Invalid LDAP scope: %s. Choose one of: "
+msgstr "無效的 LDAP 範圍:%s。請選擇下列其中一個:"
+
+#: keystone/common/ldap/core.py:185
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%s\". Format must be "
+"<ldap_attribute>:<keystone_attribute>"
+msgstr ""
+
+#: keystone/common/ldap/core.py:190
+#, python-format
+msgid ""
+"Invalid additional attribute mapping: \"%(item)s\". Value "
+"\"%(attr_map)s\" must use one of %(keys)s."
+msgstr ""
+
+#: keystone/common/ldap/core.py:274 keystone/identity/backends/kvs.py:603
+#: keystone/identity/backends/kvs.py:631
+#, python-format
+msgid "Duplicate name, %s."
+msgstr "重複的名稱,%s。"
+
+#: keystone/common/ldap/core.py:284 keystone/identity/backends/kvs.py:596
+#, python-format
+msgid "Duplicate ID, %s."
+msgstr "重複的 ID,%s。"
+
+#: keystone/common/ldap/core.py:289
+#, python-format
+msgid "LDAP %s create"
+msgstr "LDAP %s 建立"
+
+#: keystone/common/ldap/core.py:367
+#, python-format
+msgid "LDAP %s update"
+msgstr "LDAP %s 更新"
+
+#: keystone/common/ldap/core.py:400
+#, python-format
+msgid "LDAP %s delete"
+msgstr "LDAP %s 刪除"
+
+#: keystone/common/ldap/core.py:425
+#, python-format
+msgid "LDAP init: url=%s"
+msgstr "LDAP 起始設定:URL = %s"
+
+#: keystone/common/ldap/core.py:426
+#, python-format
+msgid ""
+"LDAP init: use_tls=%(use_tls)s\n"
+"tls_cacertfile=%(tls_cacertfile)s\n"
+"tls_cacertdir=%(tls_cacertdir)s\n"
+"tls_req_cert=%(tls_req_cert)s\n"
+"tls_avail=%(tls_avail)s\n"
+msgstr ""
+
+#: keystone/common/ldap/core.py:445
+msgid "Invalid TLS / LDAPS combination"
+msgstr ""
+
+#: keystone/common/ldap/core.py:449
+#, python-format
+msgid "Invalid LDAP TLS_AVAIL option: %s. TLSnot available"
+msgstr ""
+
+#: keystone/common/ldap/core.py:459
+#, python-format
+msgid "tls_cacertfile %s not found or is not a file"
+msgstr ""
+
+#: keystone/common/ldap/core.py:471
+#, python-format
+msgid "tls_cacertdir %s not found or is not a directory"
+msgstr ""
+
+#: keystone/common/ldap/core.py:478
+#, python-format
+msgid "LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s"
+msgstr ""
+
+#: keystone/common/ldap/core.py:492
+#, python-format
+msgid "LDAP bind: dn=%s"
+msgstr "LDAP 連結:DN = %s"
+
+#: keystone/common/ldap/core.py:503
+#, python-format
+msgid "LDAP add: dn=%s, attrs=%s"
+msgstr "LDAP 新增:DN = %s,屬性 = %s"
+
+#: keystone/common/ldap/core.py:508
+#, python-format
+msgid "LDAP search: dn=%s, scope=%s, query=%s, attrs=%s"
+msgstr "LDAP 搜尋:DN = %s,範圍 = %s,查詢 = %s,屬性 = %s"
+
+#: keystone/common/ldap/core.py:559
+msgid ""
+"LDAP Server does not support paging. Disable paging in keystone.conf to "
+"avoid this message."
+msgstr "「LDAP 伺服器」不支援分頁。請在 keystone.conf 中停用分頁以避免此訊息。"
+
+#: keystone/common/ldap/core.py:576
+#, python-format
+msgid "LDAP modify: dn=%s, modlist=%s"
+msgstr "LDAP 修改:DN = %s,Modlist = %s"
+
+#: keystone/common/ldap/core.py:581
+#, python-format
+msgid "LDAP delete: dn=%s"
+msgstr "LDAP 刪除:DN = %s"
+
+#: keystone/common/ldap/core.py:585
+#, python-format
+msgid "LDAP delete_ext: dn=%s, serverctrls=%s"
+msgstr "LDAP delete_ext:DN = %s,Serverctrls = %s"
+
+#: keystone/common/ldap/fakeldap.py:146
+#, python-format
+msgid "FakeLdap initialize url=%s"
+msgstr "FakeLdap 起始設定 URL = %s"
+
+#: keystone/common/ldap/fakeldap.py:156
+#, python-format
+msgid "FakeLdap bind dn=%s"
+msgstr "FakeLdap 連結 DN = %s"
+
+#: keystone/common/ldap/fakeldap.py:163
+#, python-format
+msgid "FakeLdap bind fail: dn=%s not found"
+msgstr "FakeLdap 連結失敗:找不到 DN = %s"
+
+#: keystone/common/ldap/fakeldap.py:170
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s not found"
+msgstr "FakeLdap 連結失敗:找不到 DN = %s 的密碼"
+
+#: keystone/common/ldap/fakeldap.py:175
+#, python-format
+msgid "FakeLdap bind fail: password for dn=%s does not match"
+msgstr "FakeLdap 連結失敗:DN = %s 的密碼不符"
+
+#: keystone/common/ldap/fakeldap.py:190
+#, python-format
+msgid "FakeLdap add item: dn=%s, attrs=%s"
+msgstr "FakeLdap 新增項目:DN = %s,屬性 = %s"
+
+#: keystone/common/ldap/fakeldap.py:192
+#, python-format
+msgid "FakeLdap add item failed: dn=%s is already in store."
+msgstr "FakeLdap 新增項目失敗:DN = %s 已在儲存庫中。"
+
+#: keystone/common/ldap/fakeldap.py:206 keystone/common/ldap/fakeldap.py:220
+#, python-format
+msgid "FakeLdap delete item: dn=%s"
+msgstr "FakeLdap 刪除項目:DN = %s"
+
+#: keystone/common/ldap/fakeldap.py:210 keystone/common/ldap/fakeldap.py:224
+#, python-format
+msgid "FakeLdap delete item failed: dn=%s not found."
+msgstr "FakeLdap 刪除項目失敗:找不到 DN = %s。"
+
+#: keystone/common/ldap/fakeldap.py:239
+#, python-format
+msgid "FakeLdap modify item: dn=%s attrs=%s"
+msgstr "FakeLdap 修改項目:DN = %s 屬性 = %s"
+
+#: keystone/common/ldap/fakeldap.py:243
+#, python-format
+msgid "FakeLdap modify item failed: dn=%s not found."
+msgstr "FakeLdap 修改項目失敗:找不到 DN = %s。"
+
+#: keystone/common/ldap/fakeldap.py:260
+#, python-format
+msgid "FakeLdap modify item failed: item has no attribute \"%s\" to delete"
+msgstr "FakeLdap 修改項目失敗:項目沒有要刪除的屬性 \"%s\""
+
+#: keystone/common/ldap/fakeldap.py:271
+#, python-format
+msgid ""
+"FakeLdap modify item failed: item has no attribute \"%s\" with value "
+"\"%s\" to delete"
+msgstr "FakeLdap 修改項目失敗:項目沒有要刪除的屬性 \"%s\"(值為 \"%s\")"
+
+#: keystone/common/ldap/fakeldap.py:276
+#, python-format
+msgid "FakeLdap modify item failed: unknown command %s"
+msgstr "FakeLdap 修改項目失敗:不明的指令 %s"
+
+#: keystone/common/ldap/fakeldap.py:278
+#, python-format
+msgid "modify_s action %s not implemented"
+msgstr "未實作 modify_s 動作 %s"
+
+#: keystone/common/ldap/fakeldap.py:296
+#, python-format
+msgid "FakeLdap search at dn=%s scope=%s query=%s"
+msgstr "FakeLdap 搜尋(DN = %s 範圍 = %s 查詢 = %s)"
+
+#: keystone/common/ldap/fakeldap.py:302
+msgid "FakeLdap search fail: dn not found for SCOPE_BASE"
+msgstr "FakeLdap 搜尋失敗:找不到 SCOPE_BASE 的 DN"
+
+#: keystone/common/ldap/fakeldap.py:316
+#, python-format
+msgid "Search scope %s not implemented."
+msgstr "未實作搜尋範圍 %s。"
+
+#: keystone/common/sql/core.py:207
+#, python-format
+msgid "Got mysql server has gone away: %s"
+msgstr "已取得 mysql 伺服器已斷線的訊息:%s"
+
+#: keystone/common/sql/legacy.py:180
+#, python-format
+msgid "Cannot migrate EC2 credential: %s"
+msgstr "無法移轉 EC2 認證:%s"
+
+#: keystone/common/sql/migration.py:47
+msgid "version should be an integer"
+msgstr "版本應該是整數"
+
+#: keystone/common/sql/nova.py:62
+#, python-format
+msgid "Create tenant %s"
+msgstr "建立 Tenant %s"
+
+#: keystone/common/sql/nova.py:79
+#, python-format
+msgid "Create user %s"
+msgstr "建立使用者 %s"
+
+#: keystone/common/sql/nova.py:88
+#, python-format
+msgid "Add user %s to tenant %s"
+msgstr "將使用者 %s 新增至 Tenant %s"
+
+#: keystone/common/sql/nova.py:96
+#, python-format
+msgid "Ignoring existing role %s"
+msgstr "正在忽略現有角色 %s"
+
+#: keystone/common/sql/nova.py:103
+#, python-format
+msgid "Create role %s"
+msgstr "建立角色 %s"
+
+#: keystone/common/sql/nova.py:113
+#, python-format
+msgid "Assign role %s to user %s on tenant %s"
+msgstr "將角色 %s 指派給使用者 %s(在 Tenant %s 上)"
+
+#: keystone/common/sql/nova.py:128
+#, python-format
+msgid "Creating ec2 cred for user %s and tenant %s"
+msgstr "正在給使用者 %s 及 Tenant %s 建立 EC2 Cred"
+
+#: keystone/identity/backends/kvs.py:257 keystone/identity/backends/kvs.py:266
+msgid "User not found in group"
+msgstr "在群組中找不到使用者"
+
+#: keystone/identity/backends/sql.py:424
+#, python-format
+msgid "Cannot remove role that has not been granted, %s"
+msgstr "無法移除尚未授權的角色,%s"
+
+#: keystone/identity/backends/ldap/core.py:95
+#, python-format
+msgid "Expected dict or list: %s"
+msgstr ""
+
+#: keystone/identity/backends/ldap/core.py:692
+#, python-format
+msgid "Role %s not found"
+msgstr "找不到角色 %s"
+
+#: keystone/identity/backends/ldap/core.py:912
+msgid "Changing Name not supported by LDAP"
+msgstr "LDAP 不支援變更名稱"
+
+#: keystone/identity/backends/ldap/core.py:926
+#, python-format
+msgid "User %s is already a member of group %s"
+msgstr "使用者 %s 已是群組 %s 的成員"
+
+#: keystone/identity/backends/ldap/core.py:968
+#, python-format
+msgid ""
+"Group member '%(user_dn)s' not found in '%(group_dn)s'. The user should "
+"be removed from the group. The user will be ignored."
+msgstr ""
+
+#: keystone/openstack/common/policy.py:394
+#, python-format
+msgid "Failed to understand rule %(rule)s"
+msgstr "無法理解規則 %(rule)s"
+
+#: keystone/openstack/common/policy.py:404
+#, python-format
+msgid "No handler for matches of kind %s"
+msgstr "類型為 %s 的相符項沒有處理程式"
+
+#: keystone/openstack/common/policy.py:679
+#, python-format
+msgid "Failed to understand rule %(rule)r"
+msgstr "無法理解規則 %(rule)r"
+
+#: keystone/policy/backends/rules.py:93
+#, python-format
+msgid "enforce %s: %s"
+msgstr "施行 %s:%s"
+
+#: keystone/token/controllers.py:466 keystone/token/controllers.py:469
+msgid "Token does not belong to specified tenant."
+msgstr "記號不屬於所指定的 Tenant。"
+
+#: keystone/token/controllers.py:476
+msgid "Non-default domain is not supported"
+msgstr "不支援非預設網域"
+
+#: keystone/token/controllers.py:484
+msgid "Domain scoped token is not supported"
+msgstr "不支援網域範圍的記號"
+
+#: keystone/token/backends/memcache.py:83
+msgid "Unable to add token user list."
+msgstr "無法新增記號使用者清單。"
+
+#: keystone/token/backends/memcache.py:93
+msgid "Unable to add token to revocation list."
+msgstr "無法將記號新增至撤銷清冊。"
+
diff --git a/keystone/middleware/__init__.py b/keystone/middleware/__init__.py
index 85ed395c..1135f784 100644
--- a/keystone/middleware/__init__.py
+++ b/keystone/middleware/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/middleware/ec2_token.py b/keystone/middleware/ec2_token.py
index daac10aa..7cd007c7 100644
--- a/keystone/middleware/ec2_token.py
+++ b/keystone/middleware/ec2_token.py
@@ -22,7 +22,7 @@ Starting point for routing EC2 requests.
"""
-from urlparse import urlparse
+import urlparse
from eventlet.green import httplib
import webob.dec
@@ -73,7 +73,7 @@ class EC2Token(wsgi.Middleware):
# Disable 'has no x member' pylint error
# for httplib and urlparse
# pylint: disable-msg=E1101
- o = urlparse(FLAGS.keystone_ec2_url)
+ o = urlparse.urlparse(FLAGS.keystone_ec2_url)
if o.scheme == 'http':
conn = httplib.HTTPConnection(o.netloc)
else:
diff --git a/keystone/openstack/common/gettextutils.py b/keystone/openstack/common/gettextutils.py
index d52309e6..55ba3387 100644
--- a/keystone/openstack/common/gettextutils.py
+++ b/keystone/openstack/common/gettextutils.py
@@ -20,14 +20,31 @@ gettext for openstack-common modules.
Usual usage in an openstack.common module:
- from nova.openstack.common.gettextutils import _
+ from keystone.openstack.common.gettextutils import _
"""
import gettext
+import os
-
-t = gettext.translation('openstack-common', 'locale', fallback=True)
+_localedir = os.environ.get('keystone'.upper() + '_LOCALEDIR')
+_t = gettext.translation('keystone', localedir=_localedir, fallback=True)
def _(msg):
- return t.ugettext(msg)
+ return _t.ugettext(msg)
+
+
+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).
+ """
+ gettext.install(domain,
+ localedir=os.environ.get(domain.upper() + '_LOCALEDIR'),
+ unicode=True)
diff --git a/keystone/openstack/common/setup.py b/keystone/openstack/common/setup.py
deleted file mode 100644
index ba6b54af..00000000
--- a/keystone/openstack/common/setup.py
+++ /dev/null
@@ -1,367 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack Foundation.
-# Copyright 2012-2013 Hewlett-Packard Development Company, L.P.
-# 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.
-
-"""
-Utilities with minimum-depends for use in setup.py
-"""
-
-import email
-import os
-import re
-import subprocess
-import sys
-
-from setuptools.command import sdist
-
-
-def parse_mailmap(mailmap='.mailmap'):
- mapping = {}
- if os.path.exists(mailmap):
- with open(mailmap, 'r') as fp:
- for l in fp:
- try:
- canonical_email, alias = re.match(
- r'[^#]*?(<.+>).*(<.+>).*', l).groups()
- except AttributeError:
- continue
- mapping[alias] = canonical_email
- return mapping
-
-
-def _parse_git_mailmap(git_dir, mailmap='.mailmap'):
- mailmap = os.path.join(os.path.dirname(git_dir), mailmap)
- return parse_mailmap(mailmap)
-
-
-def canonicalize_emails(changelog, mapping):
- """Takes in a string and an email alias mapping and replaces all
- instances of the aliases in the string with their real email.
- """
- for alias, email_address in mapping.iteritems():
- changelog = changelog.replace(alias, email_address)
- return changelog
-
-
-# Get requirements from the first file that exists
-def get_reqs_from_files(requirements_files):
- for requirements_file in requirements_files:
- if os.path.exists(requirements_file):
- with open(requirements_file, 'r') as fil:
- return fil.read().split('\n')
- return []
-
-
-def parse_requirements(requirements_files=['requirements.txt',
- 'tools/pip-requires']):
- requirements = []
- for line in get_reqs_from_files(requirements_files):
- # For the requirements list, we need to inject only the portion
- # after egg= so that distutils knows the package it's looking for
- # such as:
- # -e git://github.com/openstack/nova/master#egg=nova
- if re.match(r'\s*-e\s+', line):
- requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1',
- line))
- # such as:
- # http://github.com/openstack/nova/zipball/master#egg=nova
- elif re.match(r'\s*https?:', line):
- requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1',
- line))
- # -f lines are for index locations, and don't get used here
- elif re.match(r'\s*-f\s+', line):
- pass
- # argparse is part of the standard library starting with 2.7
- # adding it to the requirements list screws distro installs
- elif line == 'argparse' and sys.version_info >= (2, 7):
- pass
- else:
- requirements.append(line)
-
- return requirements
-
-
-def parse_dependency_links(requirements_files=['requirements.txt',
- 'tools/pip-requires']):
- dependency_links = []
- # dependency_links inject alternate locations to find packages listed
- # in requirements
- for line in get_reqs_from_files(requirements_files):
- # skip comments and blank lines
- if re.match(r'(\s*#)|(\s*$)', line):
- continue
- # lines with -e or -f need the whole line, minus the flag
- if re.match(r'\s*-[ef]\s+', line):
- dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line))
- # lines that are only urls can go in unmolested
- elif re.match(r'\s*https?:', line):
- dependency_links.append(line)
- return dependency_links
-
-
-def _run_shell_command(cmd, throw_on_error=False):
- if os.name == 'nt':
- output = subprocess.Popen(["cmd.exe", "/C", cmd],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- else:
- output = subprocess.Popen(["/bin/sh", "-c", cmd],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- out = output.communicate()
- if output.returncode and throw_on_error:
- raise Exception("%s returned %d" % cmd, output.returncode)
- if len(out) == 0:
- return None
- if len(out[0].strip()) == 0:
- return None
- return out[0].strip()
-
-
-def _get_git_directory():
- parent_dir = os.path.dirname(__file__)
- while True:
- git_dir = os.path.join(parent_dir, '.git')
- if os.path.exists(git_dir):
- return git_dir
- parent_dir, child = os.path.split(parent_dir)
- if not child: # reached to root dir
- return None
-
-
-def write_git_changelog():
- """Write a changelog based on the git changelog."""
- new_changelog = 'ChangeLog'
- git_dir = _get_git_directory()
- if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
- if git_dir:
- git_log_cmd = 'git --git-dir=%s log' % git_dir
- changelog = _run_shell_command(git_log_cmd)
- mailmap = _parse_git_mailmap(git_dir)
- with open(new_changelog, "w") as changelog_file:
- changelog_file.write(canonicalize_emails(changelog, mailmap))
- else:
- open(new_changelog, 'w').close()
-
-
-def generate_authors():
- """Create AUTHORS file using git commits."""
- jenkins_email = 'jenkins@review.(openstack|stackforge).org'
- old_authors = 'AUTHORS.in'
- new_authors = 'AUTHORS'
- git_dir = _get_git_directory()
- if not os.getenv('SKIP_GENERATE_AUTHORS'):
- if git_dir:
- # don't include jenkins email address in AUTHORS file
- git_log_cmd = ("git --git-dir=" + git_dir +
- " log --format='%aN <%aE>' | sort -u | "
- "egrep -v '" + jenkins_email + "'")
- changelog = _run_shell_command(git_log_cmd)
- signed_cmd = ("git --git-dir=" + git_dir +
- " log | grep -i Co-authored-by: | sort -u")
- signed_entries = _run_shell_command(signed_cmd)
- if signed_entries:
- new_entries = "\n".join(
- [signed.split(":", 1)[1].strip()
- for signed in signed_entries.split("\n") if signed])
- changelog = "\n".join((changelog, new_entries))
- mailmap = _parse_git_mailmap(git_dir)
- with open(new_authors, 'w') as new_authors_fh:
- new_authors_fh.write(canonicalize_emails(changelog, mailmap))
- if os.path.exists(old_authors):
- with open(old_authors, "r") as old_authors_fh:
- new_authors_fh.write('\n' + old_authors_fh.read())
- else:
- open(new_authors, 'w').close()
-
-
-_rst_template = """%(heading)s
-%(underline)s
-
-.. automodule:: %(module)s
- :members:
- :undoc-members:
- :show-inheritance:
-"""
-
-
-def get_cmdclass():
- """Return dict of commands to run from setup.py."""
-
- cmdclass = dict()
-
- def _find_modules(arg, dirname, files):
- for filename in files:
- if filename.endswith('.py') and filename != '__init__.py':
- arg["%s.%s" % (dirname.replace('/', '.'),
- filename[:-3])] = True
-
- class LocalSDist(sdist.sdist):
- """Builds the ChangeLog and Authors files from VC first."""
-
- def run(self):
- write_git_changelog()
- generate_authors()
- # sdist.sdist is an old style class, can't use super()
- sdist.sdist.run(self)
-
- cmdclass['sdist'] = LocalSDist
-
- # If Sphinx is installed on the box running setup.py,
- # enable setup.py to build the documentation, otherwise,
- # just ignore it
- try:
- from sphinx.setup_command import BuildDoc
-
- class LocalBuildDoc(BuildDoc):
-
- builders = ['html', 'man']
-
- def generate_autoindex(self):
- print "**Autodocumenting from %s" % os.path.abspath(os.curdir)
- modules = {}
- option_dict = self.distribution.get_option_dict('build_sphinx')
- source_dir = os.path.join(option_dict['source_dir'][1], 'api')
- if not os.path.exists(source_dir):
- os.makedirs(source_dir)
- for pkg in self.distribution.packages:
- if '.' not in pkg:
- os.path.walk(pkg, _find_modules, modules)
- module_list = modules.keys()
- module_list.sort()
- autoindex_filename = os.path.join(source_dir, 'autoindex.rst')
- with open(autoindex_filename, 'w') as autoindex:
- autoindex.write(""".. toctree::
- :maxdepth: 1
-
-""")
- for module in module_list:
- output_filename = os.path.join(source_dir,
- "%s.rst" % module)
- heading = "The :mod:`%s` Module" % module
- underline = "=" * len(heading)
- values = dict(module=module, heading=heading,
- underline=underline)
-
- print "Generating %s" % output_filename
- with open(output_filename, 'w') as output_file:
- output_file.write(_rst_template % values)
- autoindex.write(" %s.rst\n" % module)
-
- def run(self):
- if not os.getenv('SPHINX_DEBUG'):
- self.generate_autoindex()
-
- for builder in self.builders:
- self.builder = builder
- self.finalize_options()
- self.project = self.distribution.get_name()
- self.version = self.distribution.get_version()
- self.release = self.distribution.get_version()
- BuildDoc.run(self)
-
- class LocalBuildLatex(LocalBuildDoc):
- builders = ['latex']
-
- cmdclass['build_sphinx'] = LocalBuildDoc
- cmdclass['build_sphinx_latex'] = LocalBuildLatex
- except ImportError:
- pass
-
- return cmdclass
-
-
-def _get_revno(git_dir):
- """Return the number of commits since the most recent tag.
-
- We use git-describe to find this out, but if there are no
- tags then we fall back to counting commits since the beginning
- of time.
- """
- describe = _run_shell_command(
- "git --git-dir=%s describe --always" % git_dir)
- if "-" in describe:
- return describe.rsplit("-", 2)[-2]
-
- # no tags found
- revlist = _run_shell_command(
- "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir)
- return len(revlist.splitlines())
-
-
-def _get_version_from_git(pre_version):
- """Return a version which is equal to the tag that's on the current
- revision if there is one, or tag plus number of additional revisions
- if the current revision has no tag."""
-
- git_dir = _get_git_directory()
- if git_dir:
- if pre_version:
- try:
- return _run_shell_command(
- "git --git-dir=" + git_dir + " describe --exact-match",
- throw_on_error=True).replace('-', '.')
- except Exception:
- sha = _run_shell_command(
- "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h")
- return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha)
- else:
- return _run_shell_command(
- "git --git-dir=" + git_dir + " describe --always").replace(
- '-', '.')
- return None
-
-
-def _get_version_from_pkg_info(package_name):
- """Get the version from PKG-INFO file if we can."""
- try:
- pkg_info_file = open('PKG-INFO', 'r')
- except (IOError, OSError):
- return None
- try:
- pkg_info = email.message_from_file(pkg_info_file)
- except email.MessageError:
- return None
- # Check to make sure we're in our own dir
- if pkg_info.get('Name', None) != package_name:
- return None
- return pkg_info.get('Version', None)
-
-
-def get_version(package_name, pre_version=None):
- """Get the version of the project. First, try getting it from PKG-INFO, if
- it exists. If it does, that means we're in a distribution tarball or that
- install has happened. Otherwise, if there is no PKG-INFO file, pull the
- version from git.
-
- We do not support setup.py version sanity in git archive tarballs, nor do
- we support packagers directly sucking our git repo into theirs. We expect
- that a source tarball be made from our git repo - or that if someone wants
- to make a source tarball from a fork of our repo with additional tags in it
- that they understand and desire the results of doing that.
- """
- version = os.environ.get("OSLO_PACKAGE_VERSION", None)
- if version:
- return version
- version = _get_version_from_pkg_info(package_name)
- if version:
- return version
- version = _get_version_from_git(pre_version)
- if version:
- return version
- raise Exception("Versioning for this project requires either an sdist"
- " tarball, or access to an upstream git repository.")
diff --git a/keystone/openstack/common/version.py b/keystone/openstack/common/version.py
deleted file mode 100644
index 4ad85157..00000000
--- a/keystone/openstack/common/version.py
+++ /dev/null
@@ -1,94 +0,0 @@
-
-# Copyright 2012 OpenStack Foundation
-# Copyright 2012-2013 Hewlett-Packard Development Company, L.P.
-#
-# 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.
-
-"""
-Utilities for consuming the version from pkg_resources.
-"""
-
-import pkg_resources
-
-
-class VersionInfo(object):
-
- def __init__(self, package):
- """Object that understands versioning for a package
- :param package: name of the python package, such as glance, or
- python-glanceclient
- """
- self.package = package
- self.release = None
- self.version = None
- self._cached_version = None
-
- def __str__(self):
- """Make the VersionInfo object behave like a string."""
- return self.version_string()
-
- def __repr__(self):
- """Include the name."""
- return "VersionInfo(%s:%s)" % (self.package, self.version_string())
-
- def _get_version_from_pkg_resources(self):
- """Get the version of the package from the pkg_resources record
- associated with the package."""
- try:
- requirement = pkg_resources.Requirement.parse(self.package)
- provider = pkg_resources.get_provider(requirement)
- return provider.version
- except pkg_resources.DistributionNotFound:
- # The most likely cause for this is running tests in a tree
- # produced from a tarball where the package itself has not been
- # installed into anything. Revert to setup-time logic.
- from keystone.openstack.common import setup
- return setup.get_version(self.package)
-
- def release_string(self):
- """Return the full version of the package including suffixes indicating
- VCS status.
- """
- if self.release is None:
- self.release = self._get_version_from_pkg_resources()
-
- return self.release
-
- def version_string(self):
- """Return the short version minus any alpha/beta tags."""
- if self.version is None:
- parts = []
- for part in self.release_string().split('.'):
- if part[0].isdigit():
- parts.append(part)
- else:
- break
- self.version = ".".join(parts)
-
- return self.version
-
- # Compatibility functions
- canonical_version_string = version_string
- version_string_with_vcs = release_string
-
- def cached_version_string(self, prefix=""):
- """Generate an object which will expand in a string context to
- the results of version_string(). We do this so that don't
- call into pkg_resources every time we start up a program when
- passing version information into the CONF constructor, but
- rather only do the calculation when and if a version is requested
- """
- if not self._cached_version:
- self._cached_version = "%s%s" % (prefix,
- self.version_string())
- return self._cached_version
diff --git a/keystone/policy/__init__.py b/keystone/policy/__init__.py
index 121905e4..5ed700eb 100644
--- a/keystone/policy/__init__.py
+++ b/keystone/policy/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/policy/backends/rules.py b/keystone/policy/backends/rules.py
index aa0228cc..63110e69 100644
--- a/keystone/policy/backends/rules.py
+++ b/keystone/policy/backends/rules.py
@@ -20,10 +20,10 @@
import os.path
from keystone.common import logging
-from keystone.openstack.common import policy as common_policy
from keystone.common import utils
from keystone import config
from keystone import exception
+from keystone.openstack.common import policy as common_policy
from keystone import policy
@@ -90,5 +90,7 @@ def enforce(credentials, action, target, do_raise=True):
class Policy(policy.Driver):
def enforce(self, credentials, action, target):
- LOG.debug(_('enforce %s: %s'), action, credentials)
+ LOG.debug(_('enforce %(action)s: %(credentials)s') % {
+ 'action': action,
+ 'credentials': credentials})
enforce(credentials, action, target)
diff --git a/keystone/policy/backends/sql.py b/keystone/policy/backends/sql.py
index 2472c1ed..c1eff268 100644
--- a/keystone/policy/backends/sql.py
+++ b/keystone/policy/backends/sql.py
@@ -53,10 +53,10 @@ class Policy(sql.Base, rules.Policy):
def _get_policy(self, session, policy_id):
"""Private method to get a policy model object (NOT a dictionary)."""
- try:
- return session.query(PolicyModel).filter_by(id=policy_id).one()
- except sql.NotFound:
+ ref = session.query(PolicyModel).get(policy_id)
+ if not ref:
raise exception.PolicyNotFound(policy_id=policy_id)
+ return ref
def get_policy(self, policy_id):
session = self.get_session()
diff --git a/keystone/service.py b/keystone/service.py
index 0b3338fb..406004d6 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -18,11 +18,12 @@ import routes
from keystone import auth
from keystone import catalog
-from keystone import config
-from keystone import controllers
from keystone.common import logging
from keystone.common import wsgi
+from keystone import config
from keystone.contrib import ec2
+from keystone import controllers
+from keystone import credential
from keystone import identity
from keystone import policy
from keystone import routers
@@ -35,6 +36,7 @@ LOG = logging.getLogger(__name__)
DRIVERS = dict(
catalog_api=catalog.Manager(),
+ credentials_api=credential.Manager(),
ec2_api=ec2.Manager(),
identity_api=identity.Manager(),
policy_api=policy.Manager(),
@@ -88,7 +90,7 @@ def v3_app_factory(global_conf, **local_conf):
conf.update(local_conf)
mapper = routes.Mapper()
v3routers = []
- for module in [auth, catalog, identity, policy]:
+ for module in [auth, catalog, credential, identity, policy]:
module.routers.append_v3_routers(mapper, v3routers)
if CONF.trust.enabled:
diff --git a/keystone/test.py b/keystone/test.py
index 77d7f7cd..3cfbd01c 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -22,7 +22,7 @@ import subprocess
import sys
import time
-import eventlet
+import gettext
import mox
import nose.exc
from paste import deploy
@@ -34,7 +34,9 @@ from keystone.common import kvs
from keystone.common import logging
from keystone.common import utils
from keystone.common import wsgi
+from keystone.common import wsgi_server
from keystone import config
+from keystone import credential
from keystone import exception
from keystone import identity
from keystone.openstack.common import timeutils
@@ -43,9 +45,10 @@ from keystone import token
from keystone import trust
-do_monkeypatch = not os.getenv('STANDARD_THREADS')
-eventlet.patcher.monkey_patch(all=False, socket=True, time=True,
- thread=do_monkeypatch)
+wsgi_server.monkey_patch_eventlet()
+
+
+gettext.install('keystone', unicode=1)
LOG = logging.getLogger(__name__)
ROOTDIR = os.path.dirname(os.path.abspath(os.curdir))
@@ -76,6 +79,7 @@ def testsdir(*p):
def initialize_drivers():
DRIVERS['catalog_api'] = catalog.Manager()
+ DRIVERS['credential_api'] = credential.Manager()
DRIVERS['identity_api'] = identity.Manager()
DRIVERS['policy_api'] = policy.Manager()
DRIVERS['token_api'] = token.Manager()
@@ -301,8 +305,8 @@ class TestCase(NoModule, unittest.TestCase):
test_path = os.path.join(TESTSDIR, config)
etc_path = os.path.join(ROOTDIR, 'etc', config)
for path in [test_path, etc_path]:
- if os.path.exists('%s.conf.sample' % path):
- return 'config:%s.conf.sample' % path
+ if os.path.exists('%s-paste.ini' % path):
+ return 'config:%s-paste.ini' % path
return config
def loadapp(self, config, name='main'):
@@ -314,7 +318,7 @@ class TestCase(NoModule, unittest.TestCase):
def serveapp(self, config, name=None, cert=None, key=None, ca=None,
cert_required=None, host="127.0.0.1", port=0):
app = self.loadapp(config, name=name)
- server = wsgi.Server(app, host, port)
+ server = wsgi_server.Server(app, host, port)
if cert is not None and ca is not None and key is not None:
server.set_ssl(certfile=cert, keyfile=key, ca_certs=ca,
cert_required=cert_required)
@@ -339,14 +343,35 @@ class TestCase(NoModule, unittest.TestCase):
"""
self.assertAlmostEqual(a, b, delta=datetime.timedelta(seconds=delta))
- def assertDictContainsSubset(self, dict1, dict2):
- if len(dict1) < len(dict2):
- (subset, fullset) = dict1, dict2
- else:
- (subset, fullset) = dict2, dict1
- for x in subset:
- self.assertIn(x, fullset)
- self.assertEquals(subset.get(x), fullset.get(x))
+ def assertNotEmpty(self, l):
+ self.assertTrue(len(l))
+
+ def assertDictContainsSubset(self, expected, actual, msg=None):
+ """Checks whether actual is a superset of expected."""
+ safe_repr = unittest.util.safe_repr
+ missing = []
+ mismatched = []
+ for key, value in expected.iteritems():
+ if key not in actual:
+ missing.append(key)
+ elif value != actual[key]:
+ mismatched.append('%s, expected: %s, actual: %s' %
+ (safe_repr(key), safe_repr(value),
+ safe_repr(actual[key])))
+
+ if not (missing or mismatched):
+ return
+
+ standardMsg = ''
+ if missing:
+ standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
+ missing)
+ if mismatched:
+ if standardMsg:
+ standardMsg += '; '
+ standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
+
+ self.fail(self._formatMessage(msg, standardMsg))
@staticmethod
def skip_if_no_ipv6():
diff --git a/keystone/token/__init__.py b/keystone/token/__init__.py
index eb4f012a..889cd39a 100644
--- a/keystone/token/__init__.py
+++ b/keystone/token/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py
index 49f15ad8..c16dd61b 100644
--- a/keystone/token/backends/kvs.py
+++ b/keystone/token/backends/kvs.py
@@ -43,6 +43,7 @@ class Token(kvs.Base, token.Driver):
def create_token(self, token_id, data):
token_id = token.unique_id(token_id)
data_copy = copy.deepcopy(data)
+ data_copy['id'] = token_id
if not data_copy.get('expires'):
data_copy['expires'] = token.default_expire_time()
if not data_copy.get('user_id'):
@@ -74,7 +75,6 @@ class Token(kvs.Base, token.Driver):
for token, ref in self.db.items():
if not token.startswith('token-') or self.is_expired(now, ref):
continue
- ref_trust_id = ref.get('trust_id')
if self.trust_matches(trust_id, ref):
tokens.append(token.split('-', 1)[1])
return tokens
@@ -115,3 +115,9 @@ class Token(kvs.Base, token.Driver):
record['expires'] = token_ref['expires']
tokens.append(record)
return tokens
+
+ def flush_expired_tokens(self):
+ now = timeutils.utcnow()
+ for token, token_ref in self.db.items():
+ if self.is_expired(now, token_ref):
+ self.db.delete(token)
diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py
index fef3b81b..df1bc958 100644
--- a/keystone/token/backends/sql.py
+++ b/keystone/token/backends/sql.py
@@ -41,11 +41,9 @@ class Token(sql.Base, token.Driver):
if token_id is None:
raise exception.TokenNotFound(token_id=token_id)
session = self.get_session()
- query = session.query(TokenModel)
- query = query.filter_by(id=token.unique_id(token_id), valid=True)
- token_ref = query.first()
+ token_ref = session.query(TokenModel).get(token.unique_id(token_id))
now = datetime.datetime.utcnow()
- if not token_ref:
+ if not token_ref or not token_ref.valid:
raise exception.TokenNotFound(token_id=token_id)
if not token_ref.expires:
raise exception.TokenNotFound(token_id=token_id)
@@ -73,9 +71,8 @@ class Token(sql.Base, token.Driver):
session = self.get_session()
key = token.unique_id(token_id)
with session.begin():
- token_ref = session.query(TokenModel).filter_by(id=key,
- valid=True).first()
- if not token_ref:
+ token_ref = session.query(TokenModel).get(key)
+ if not token_ref or not token_ref.valid:
raise exception.TokenNotFound(token_id=token_id)
token_ref.valid = False
session.flush()
@@ -91,7 +88,7 @@ class Token(sql.Base, token.Driver):
token_references = query.filter_by(valid=True)
for token_ref in token_references:
token_ref_dict = token_ref.to_dict()
- tokens.append(token_ref['id'])
+ tokens.append(token_ref_dict['id'])
return tokens
def _list_tokens_for_user(self, user_id, tenant_id=None):
@@ -134,3 +131,12 @@ class Token(sql.Base, token.Driver):
}
tokens.append(record)
return tokens
+
+ def flush_expired_tokens(self):
+ session = self.get_session()
+
+ query = session.query(TokenModel)
+ query = query.filter(TokenModel.expires < timeutils.utcnow())
+ query.delete(synchronize_session=False)
+
+ session.flush()
diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py
index 3e2ac670..2d429742 100644
--- a/keystone/token/controllers.py
+++ b/keystone/token/controllers.py
@@ -18,7 +18,7 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
class ExternalAuthNotApplicable(Exception):
- """External authentication is not applicable"""
+ """External authentication is not applicable."""
pass
@@ -215,8 +215,8 @@ class Auth(controller.V2Controller):
tenant_ref = self._get_project_ref(context, user_id, tenant_id)
metadata_ref = self._get_metadata_ref(context, user_id, tenant_id)
- # TODO (henry-nash) If no tenant was specified, instead check
- # for a domain and find any related user/group roles
+ # TODO(henry-nash): If no tenant was specified, instead check for a
+ # domain and find any related user/group roles
self._append_roles(metadata_ref,
self._get_group_metadata_ref(
@@ -227,7 +227,7 @@ class Auth(controller.V2Controller):
trust_id = auth['trust_id']
trust_roles = []
for role in trust_ref['roles']:
- if not 'roles' in metadata_ref:
+ if 'roles' not in metadata_ref:
raise exception.Forbidden()()
if role['id'] in metadata_ref['roles']:
trust_roles.append(role['id'])
@@ -303,8 +303,8 @@ class Auth(controller.V2Controller):
# specified, we will have obtained its metadata. In this case
# we just need to add in any group roles.
#
- # TODO (henry-nash) If no tenant was specified, instead check
- # for a domain and find any related user/group roles
+ # TODO(henry-nash): If no tenant was specified, instead check for a
+ # domain and find any related user/group roles
self._append_roles(metadata_ref,
self._get_group_metadata_ref(
@@ -335,8 +335,8 @@ class Auth(controller.V2Controller):
tenant_ref = self._get_project_ref(context, user_id, tenant_id)
metadata_ref = self._get_metadata_ref(context, user_id, tenant_id)
- # TODO (henry-nash) If no tenant was specified, instead check
- # for a domain and find any related user/group roles
+ # TODO(henry-nash): If no tenant was specified, instead check for a
+ # domain and find any related user/group roles
self._append_roles(metadata_ref,
self._get_group_metadata_ref(
@@ -396,7 +396,7 @@ class Auth(controller.V2Controller):
return domain_id
def _get_project_ref(self, context, user_id, tenant_id):
- """Returns the tenant_ref for the user's tenant"""
+ """Returns the tenant_ref for the user's tenant."""
tenant_ref = None
if tenant_id:
tenants = self.identity_api.get_projects_for_user(context, user_id)
@@ -415,7 +415,7 @@ class Auth(controller.V2Controller):
def _get_metadata_ref(self, context, user_id=None, tenant_id=None,
domain_id=None, group_id=None):
- """Returns metadata_ref for a user or group in a tenant or domain"""
+ """Returns metadata_ref for a user or group in a tenant or domain."""
metadata_ref = {}
if (user_id or group_id) and (tenant_id or domain_id):
@@ -429,7 +429,7 @@ class Auth(controller.V2Controller):
def _get_group_metadata_ref(self, context, user_id,
tenant_id=None, domain_id=None):
- """Return any metadata for this project/domain due to group grants"""
+ """Return any metadata for this project/domain due to group grants."""
group_refs = self.identity_api.list_groups_for_user(context=context,
user_id=user_id)
metadata_ref = {}
@@ -441,9 +441,10 @@ class Auth(controller.V2Controller):
return metadata_ref
def _append_roles(self, metadata, additional_metadata):
- """
- Update the roles in metadata to be the union of the roles from
- both of the passed metadatas
+ """Add additional roles to the roles in metadata.
+
+ The final set of roles represents the union of existing roles and
+ additional roles.
"""
first = set(metadata.get('roles', []))
@@ -468,7 +469,7 @@ class Auth(controller.V2Controller):
return data
def _assert_default_domain(self, context, token_ref):
- """ Make sure we are operating on default domain only. """
+ """Make sure we are operating on default domain only."""
if token_ref.get('token_data'):
# this is a V3 token
msg = _('Non-default domain is not supported')
diff --git a/keystone/token/core.py b/keystone/token/core.py
index 5c3830da..5a47d027 100644
--- a/keystone/token/core.py
+++ b/keystone/token/core.py
@@ -187,3 +187,8 @@ class Driver(object):
"""
raise exception.NotImplemented()
+
+ def flush_expired_tokens(self):
+ """Archive or delete tokens that have expired.
+ """
+ raise exception.NotImplemented()
diff --git a/keystone/trust/__init__.py b/keystone/trust/__init__.py
index 9c6a22f0..6b47460e 100644
--- a/keystone/trust/__init__.py
+++ b/keystone/trust/__init__.py
@@ -1,4 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# flake8: noqa
# Copyright 2012 OpenStack LLC
#
@@ -14,6 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from keystone.trust.core import Manager, Driver
from keystone.trust import controllers
+from keystone.trust.core import *
from keystone.trust import routers
diff --git a/keystone/trust/backends/kvs.py b/keystone/trust/backends/kvs.py
index 40b7c9e4..96995e37 100644
--- a/keystone/trust/backends/kvs.py
+++ b/keystone/trust/backends/kvs.py
@@ -19,10 +19,9 @@ only to be used for testing purposes
"""
import copy
-
from keystone.common import kvs
-from keystone.openstack.common import timeutils
from keystone import exception
+from keystone.openstack.common import timeutils
from keystone import trust
diff --git a/keystone/trust/backends/sql.py b/keystone/trust/backends/sql.py
index cd68b0bc..daa8e3f7 100644
--- a/keystone/trust/backends/sql.py
+++ b/keystone/trust/backends/sql.py
@@ -114,10 +114,8 @@ class Trust(sql.Base, trust.Driver):
def delete_trust(self, trust_id):
session = self.get_session()
with session.begin():
- try:
- trust_ref = (session.query(TrustModel).
- filter_by(id=trust_id).one())
- except sql.NotFound:
+ trust_ref = session.query(TrustModel).get(trust_id)
+ if not trust_ref:
raise exception.TrustNotFound(trust_id=trust_id)
trust_ref.deleted_at = timeutils.utcnow()
session.flush()
diff --git a/keystone/trust/controllers.py b/keystone/trust/controllers.py
index aecdbd8a..a910065e 100644
--- a/keystone/trust/controllers.py
+++ b/keystone/trust/controllers.py
@@ -1,11 +1,11 @@
import uuid
-from keystone import config
-from keystone import exception
-from keystone import identity
from keystone.common import controller
from keystone.common import dependency
from keystone.common import logging
+from keystone import config
+from keystone import exception
+from keystone import identity
from keystone.openstack.common import timeutils
@@ -64,7 +64,7 @@ class TrustV3(controller.V3Controller):
(trust['expires_at'],
subsecond=True))
- if not 'roles' in trust:
+ if 'roles' not in trust:
trust['roles'] = []
trust_full_roles = []
for trust_role in trust['roles']:
@@ -106,12 +106,14 @@ class TrustV3(controller.V3Controller):
@controller.protected
def create_trust(self, context, trust=None):
- """
- The user creating the trust must be trustor
+ """Create a new trust.
+
+ The user creating the trust must be the trustor.
+
"""
- #TODO instead of raising ValidationError on the first problem,
- #return a collection of all the problems.
+ # TODO(ayoung): instead of raising ValidationError on the first
+ # problem, return a collection of all the problems.
if not trust:
raise exception.ValidationError(attribute='trust',
target='request')
diff --git a/openstack-common.conf b/openstack-common.conf
index 37b5661b..0d6d1cf9 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
-modules=importutils,install_venv_common,jsonutils,setup,timeutils,flakes,version
+modules=importutils,install_venv_common,jsonutils,timeutils
# The base module to hold the copy of openstack.common
base=keystone
diff --git a/setup.cfg b/setup.cfg
index b43ee455..d217ee7c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,34 @@
+[metadata]
+name = keystone
+summary = OpenStack Identity
+description-file =
+ README.rst
+author = OpenStack
+author-email = openstack-dev@lists.openstack.org
+home-page = http://www.openstack.org/
+classifier =
+ Environment :: OpenStack
+ Intended Audience :: Information Technology
+ Intended Audience :: System Administrators
+ License :: OSI Approved :: Apache Software License
+ Operating System :: POSIX :: Linux
+ Programming Language :: Python
+ Programming Language :: Python :: 2
+ Programming Language :: Python :: 2.7
+ Programming Language :: Python :: 2.6
+
+[files]
+packages =
+ keystone
+scripts =
+ bin/keystone-all
+ bin/keystone-manage
+
+[global]
+setup-hooks =
+ pbr.hooks.setup_hook
+
+
[egg_info]
tag_build =
tag_date = 0
diff --git a/setup.py b/setup.py
index 245ebf79..3144d17a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,53 +1,21 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack LLC
+#!/usr/bin/env python
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
-# 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
+# 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
+# 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.
+# 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 setuptools
-from keystone.openstack.common import setup
-
-
-requires = setup.parse_requirements()
-depend_links = setup.parse_dependency_links()
-project = 'keystone'
-
-
setuptools.setup(
- name=project,
- version=setup.get_version(project, '2013.2'),
- description="Authentication service for OpenStack",
- license='Apache License (2.0)',
- author='OpenStack, LLC.',
- author_email='openstack@lists.launchpad.net',
- url='http://www.openstack.org',
- cmdclass=setup.get_cmdclass(),
- packages=setuptools.find_packages(exclude=['test', 'bin']),
- include_package_data=True,
- scripts=['bin/keystone-all', 'bin/keystone-manage'],
- zip_safe=False,
- install_requires=requires,
- dependency_links=depend_links,
- test_suite='nose.collector',
- classifiers=[
- 'Environment :: OpenStack',
- 'Intended Audience :: Information Technology',
- 'Intended Audience :: System Administrators',
- 'License :: OSI Approved :: Apache Software License',
- 'Operating System :: POSIX :: Linux',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- ],
-)
+ setup_requires=['d2to1', 'pbr>=0.5,<0.6'],
+ d2to1=True)
diff --git a/tests/backend_pam.conf b/tests/backend_pam.conf
index b3f922e6..41f868c7 100644
--- a/tests/backend_pam.conf
+++ b/tests/backend_pam.conf
@@ -1,5 +1,4 @@
[pam]
-url = fake://memory
userid = fakeuser
password = fakepass
diff --git a/tests/test_auth.py b/tests/test_auth.py
index faf117ca..e7c9704a 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -95,22 +95,21 @@ class AuthBadRequests(AuthTest):
super(AuthBadRequests, self).setUp()
def test_no_external_auth(self):
- """Verify that _authenticate_external() raises exception if
- not applicable"""
+ """Verify that _authenticate_external() raises exception if N/A."""
self.assertRaises(
token.controllers.ExternalAuthNotApplicable,
self.controller._authenticate_external,
{}, {})
def test_no_token_in_auth(self):
- """Verity that _authenticate_token() raises exception if no token"""
+ """Verify that _authenticate_token() raises exception if no token."""
self.assertRaises(
exception.ValidationError,
self.controller._authenticate_token,
None, {})
def test_no_credentials_in_auth(self):
- """Verity that _authenticate_local() raises exception if no creds"""
+ """Verify that _authenticate_local() raises exception if no creds."""
self.assertRaises(
exception.ValidationError,
self.controller._authenticate_local,
@@ -186,7 +185,7 @@ class AuthWithToken(AuthTest):
super(AuthWithToken, self).setUp()
def test_unscoped_token(self):
- """Verify getting an unscoped token with password creds"""
+ """Verify getting an unscoped token with password creds."""
body_dict = _build_user_auth(username='FOO',
password='foo2')
unscoped_token = self.controller.authenticate({}, body_dict)
@@ -194,7 +193,7 @@ class AuthWithToken(AuthTest):
self.assertEqual(tenant, None)
def test_auth_invalid_token(self):
- """Verify exception is raised if invalid token"""
+ """Verify exception is raised if invalid token."""
body_dict = _build_user_auth(token={"id": uuid.uuid4().hex})
self.assertRaises(
exception.Unauthorized,
@@ -202,7 +201,7 @@ class AuthWithToken(AuthTest):
{}, body_dict)
def test_auth_bad_formatted_token(self):
- """Verify exception is raised if invalid token"""
+ """Verify exception is raised if invalid token."""
body_dict = _build_user_auth(token={})
self.assertRaises(
exception.ValidationError,
@@ -210,7 +209,7 @@ class AuthWithToken(AuthTest):
{}, body_dict)
def test_auth_unscoped_token_no_project(self):
- """Verify getting an unscoped token with an unscoped token"""
+ """Verify getting an unscoped token with an unscoped token."""
body_dict = _build_user_auth(
username='FOO',
password='foo2')
@@ -223,7 +222,7 @@ class AuthWithToken(AuthTest):
self.assertEqualTokens(unscoped_token, unscoped_token_2)
def test_auth_unscoped_token_project(self):
- """Verify getting a token in a tenant with an unscoped token"""
+ """Verify getting a token in a tenant with an unscoped token."""
# Add a role in so we can check we get this back
self.identity_api.add_role_to_user_and_project(
self.user_foo['id'],
@@ -246,7 +245,7 @@ class AuthWithToken(AuthTest):
self.assertEquals(roles[0], self.role_member['id'])
def test_auth_token_project_group_role(self):
- """Verify getting a token in a tenant with group roles"""
+ """Verify getting a token in a tenant with group roles."""
# Add a v2 style role in so we can check we get this back
self.identity_api.add_role_to_user_and_project(
self.user_foo['id'],
@@ -278,7 +277,7 @@ class AuthWithToken(AuthTest):
self.assertIn(self.role_admin['id'], roles)
def test_auth_token_cross_domain_group_and_project(self):
- """Verify getting a token in cross domain group/project roles"""
+ """Verify getting a token in cross domain group/project roles."""
# create domain, project and group and grant roles to user
domain1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.identity_api.create_domain(domain1['id'], domain1)
@@ -375,7 +374,7 @@ class AuthWithPasswordCredentials(AuthTest):
super(AuthWithPasswordCredentials, self).setUp()
def test_auth_invalid_user(self):
- """Verify exception is raised if invalid user"""
+ """Verify exception is raised if invalid user."""
body_dict = _build_user_auth(
username=uuid.uuid4().hex,
password=uuid.uuid4().hex)
@@ -385,7 +384,7 @@ class AuthWithPasswordCredentials(AuthTest):
{}, body_dict)
def test_auth_valid_user_invalid_password(self):
- """Verify exception is raised if invalid password"""
+ """Verify exception is raised if invalid password."""
body_dict = _build_user_auth(
username="FOO",
password=uuid.uuid4().hex)
@@ -395,7 +394,7 @@ class AuthWithPasswordCredentials(AuthTest):
{}, body_dict)
def test_auth_empty_password(self):
- """Verify exception is raised if empty password"""
+ """Verify exception is raised if empty password."""
body_dict = _build_user_auth(
username="FOO",
password="")
@@ -405,7 +404,7 @@ class AuthWithPasswordCredentials(AuthTest):
{}, body_dict)
def test_auth_no_password(self):
- """Verify exception is raised if empty password"""
+ """Verify exception is raised if empty password."""
body_dict = _build_user_auth(username="FOO")
self.assertRaises(
exception.ValidationError,
@@ -413,8 +412,7 @@ class AuthWithPasswordCredentials(AuthTest):
{}, body_dict)
def test_authenticate_blank_password_credentials(self):
- """Verify sending empty json dict as passwordCredentials raises the
- right exception."""
+ """Sending empty dict as passwordCredentials raises a 400 error."""
body_dict = {'passwordCredentials': {}, 'tenantName': 'demo'}
self.assertRaises(exception.ValidationError,
self.controller.authenticate,
@@ -434,7 +432,7 @@ class AuthWithRemoteUser(AuthTest):
super(AuthWithRemoteUser, self).setUp()
def test_unscoped_remote_authn(self):
- """Verify getting an unscoped token with external authn"""
+ """Verify getting an unscoped token with external authn."""
body_dict = _build_user_auth(
username='FOO',
password='foo2')
@@ -448,7 +446,7 @@ class AuthWithRemoteUser(AuthTest):
self.assertEqualTokens(local_token, remote_token)
def test_unscoped_remote_authn_jsonless(self):
- """Verify that external auth with invalid request fails"""
+ """Verify that external auth with invalid request fails."""
self.assertRaises(
exception.ValidationError,
self.controller.authenticate,
@@ -456,7 +454,7 @@ class AuthWithRemoteUser(AuthTest):
None)
def test_scoped_remote_authn(self):
- """Verify getting a token with external authn"""
+ """Verify getting a token with external authn."""
body_dict = _build_user_auth(
username='FOO',
password='foo2',
@@ -472,7 +470,7 @@ class AuthWithRemoteUser(AuthTest):
self.assertEqualTokens(local_token, remote_token)
def test_scoped_nometa_remote_authn(self):
- """Verify getting a token with external authn and no metadata"""
+ """Verify getting a token with external authn and no metadata."""
body_dict = _build_user_auth(
username='TWO',
password='two2',
@@ -487,7 +485,7 @@ class AuthWithRemoteUser(AuthTest):
self.assertEqualTokens(local_token, remote_token)
def test_scoped_remote_authn_invalid_user(self):
- """Verify that external auth with invalid user fails"""
+ """Verify that external auth with invalid user fails."""
body_dict = _build_user_auth(tenant_name="BAR")
self.assertRaises(
exception.Unauthorized,
@@ -596,10 +594,9 @@ class AuthWithTrust(AuthTest):
self.assertEquals(token_user['id'],
self.new_trust['trustee_user_id'])
- #TODO Endpoints
+ # TODO(ayoung): Endpoints
def test_token_from_trust_wrong_user_fails(self):
- new_trust = self.create_trust()
request_body = self.build_v2_token_request('FOO', 'foo2')
self.assertRaises(
exception.Forbidden,
@@ -681,7 +678,7 @@ class AuthWithTrust(AuthTest):
def test_delete_tokens_for_user_invalidates_tokens_from_trust(self):
self.assert_token_count_for_trust(0)
- auth_response = self.fetch_v2_token_from_trust()
+ self.fetch_v2_token_from_trust()
self.assert_token_count_for_trust(1)
self.trust_controller._delete_tokens_for_user(
{},
@@ -699,9 +696,8 @@ class AuthWithTrust(AuthTest):
def test_delete_trust_revokes_token(self):
context = {'token_id': self.unscoped_token['access']['token']['id']}
- auth_response = self.fetch_v2_token_from_trust()
+ self.fetch_v2_token_from_trust()
trust_id = self.new_trust['id']
- trust_token_id = auth_response['access']['token']['id']
tokens = self.token_api.list_tokens(self.trustor['id'],
trust_id=trust_id)
self.assertEquals(len(tokens), 1)
diff --git a/tests/test_backend.py b/tests/test_backend.py
index 9ce47d54..717535c7 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -28,9 +28,15 @@ from keystone import test
CONF = config.CONF
DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
+NULL_OBJECT = object()
class IdentityTests(object):
+ def _get_domain_fixture(self):
+ domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
+ self.identity_api.create_domain(domain['id'], domain)
+ return domain
+
def test_project_add_and_remove_user_role(self):
user_refs = self.identity_api.get_project_users(self.tenant_bar['id'])
self.assertNotIn(self.user_two['id'], [x['id'] for x in user_refs])
@@ -128,7 +134,7 @@ class IdentityTests(object):
user.pop('password')
self.assertEquals(metadata_ref, {"roles":
[CONF.member_role_id]})
- self.assertDictContainsSubset(user_ref, user)
+ self.assertDictContainsSubset(user, user_ref)
self.assertDictEqual(tenant_ref, self.tenant_baz)
def test_password_hashed(self):
@@ -505,7 +511,7 @@ class IdentityTests(object):
self.assertIn('member', roles_ref)
def test_get_roles_for_user_and_domain(self):
- """ Test for getting roles for user on a domain.
+ """Test for getting roles for user on a domain.
Test Plan:
- Create a domain, with 2 users
@@ -561,15 +567,14 @@ class IdentityTests(object):
self.assertEquals(len(roles_ref), 0)
def test_get_roles_for_user_and_domain_404(self):
- """ Test errors raised when getting roles for user on a domain.
+ """Test errors raised when getting roles for user on a domain.
Test Plan:
- Check non-existing user gives UserNotFound
- Check non-existing domain gives DomainNotFound
"""
- new_domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(new_domain['id'], new_domain)
+ new_domain = self._get_domain_fixture()
new_user1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
'password': uuid.uuid4().hex, 'enabled': True,
'domain_id': new_domain['id']}
@@ -711,7 +716,7 @@ class IdentityTests(object):
def test_get_and_remove_role_grant_by_group_and_project(self):
new_domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.identity_api.create_domain(new_domain['id'], new_domain)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': new_domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
@@ -1525,6 +1530,18 @@ class IdentityTests(object):
'fake1',
user)
+ def test_create_user_invalid_enabled_type(self):
+ user = {'id': uuid.uuid4().hex,
+ 'name': uuid.uuid4().hex,
+ 'domain_id': DEFAULT_DOMAIN_ID,
+ 'password': uuid.uuid4().hex,
+ # invalid string value
+ 'enabled': "true"}
+ self.assertRaises(exception.ValidationError,
+ self.identity_man.create_user, {},
+ user['id'],
+ user)
+
def test_update_user_long_name_fails(self):
user = {'id': 'fake1', 'name': 'fake1',
'domain_id': DEFAULT_DOMAIN_ID}
@@ -1568,10 +1585,14 @@ class IdentityTests(object):
self.assertTrue(x for x in users if x['id'] == test_user['id'])
def test_list_groups(self):
- group1 = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
- 'name': uuid.uuid4().hex}
- group2 = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
- 'name': uuid.uuid4().hex}
+ group1 = {
+ 'id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'name': uuid.uuid4().hex}
+ group2 = {
+ 'id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'name': uuid.uuid4().hex}
self.identity_man.create_group({}, group1['id'], group1)
self.identity_man.create_group({}, group2['id'], group2)
groups = self.identity_api.list_groups()
@@ -1663,11 +1684,46 @@ class IdentityTests(object):
user_ref = self.identity_api.get_user('fake1')
self.assertEqual(user_ref['enabled'], user['enabled'])
+ # If not present, enabled field should not be updated
+ del user['enabled']
+ self.identity_api.update_user('fake1', user)
+ user_ref = self.identity_api.get_user('fake1')
+ self.assertEqual(user_ref['enabled'], False)
+
user['enabled'] = True
self.identity_api.update_user('fake1', user)
user_ref = self.identity_api.get_user('fake1')
self.assertEqual(user_ref['enabled'], user['enabled'])
+ del user['enabled']
+ self.identity_api.update_user('fake1', user)
+ user_ref = self.identity_api.get_user('fake1')
+ self.assertEqual(user_ref['enabled'], True)
+
+ # Integers are valid Python's booleans. Explicitly test it.
+ user['enabled'] = 0
+ self.identity_api.update_user('fake1', user)
+ user_ref = self.identity_api.get_user('fake1')
+ self.assertEqual(user_ref['enabled'], False)
+
+ # Any integers other than 0 are interpreted as True
+ user['enabled'] = -42
+ self.identity_api.update_user('fake1', user)
+ user_ref = self.identity_api.get_user('fake1')
+ self.assertEqual(user_ref['enabled'], True)
+
+ def test_update_user_enable_fails(self):
+ user = {'id': 'fake1', 'name': 'fake1', 'enabled': True,
+ 'domain_id': DEFAULT_DOMAIN_ID}
+ self.identity_api.create_user('fake1', user)
+ user_ref = self.identity_api.get_user('fake1')
+ self.assertEqual(user_ref['enabled'], True)
+
+ # Strings are not valid boolean values
+ user['enabled'] = "false"
+ self.assertRaises(exception.ValidationError,
+ self.identity_api.update_user, 'fake1', user)
+
def test_update_project_enable(self):
tenant = {'id': 'fake1', 'name': 'fake1', 'enabled': True,
'domain_id': DEFAULT_DOMAIN_ID}
@@ -1686,9 +1742,8 @@ class IdentityTests(object):
self.assertEqual(tenant_ref['enabled'], tenant['enabled'])
def test_add_user_to_group(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ domain = self._get_domain_fixture()
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
@@ -1706,8 +1761,7 @@ class IdentityTests(object):
self.assertTrue(found)
def test_add_user_to_group_404(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
+ domain = self._get_domain_fixture()
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
'password': uuid.uuid4().hex, 'enabled': True,
'domain_id': domain['id']}
@@ -1717,7 +1771,7 @@ class IdentityTests(object):
new_user['id'],
uuid.uuid4().hex)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
self.assertRaises(exception.UserNotFound,
@@ -1726,9 +1780,8 @@ class IdentityTests(object):
new_group['id'])
def test_check_user_in_group(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ domain = self._get_domain_fixture()
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
@@ -1740,8 +1793,10 @@ class IdentityTests(object):
self.identity_api.check_user_in_group(new_user['id'], new_group['id'])
def test_check_user_not_in_group(self):
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
- 'name': uuid.uuid4().hex}
+ new_group = {
+ 'id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
self.assertRaises(exception.UserNotFound,
self.identity_api.check_user_in_group,
@@ -1749,9 +1804,8 @@ class IdentityTests(object):
new_group['id'])
def test_list_users_in_group(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ domain = self._get_domain_fixture()
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
@@ -1768,9 +1822,8 @@ class IdentityTests(object):
self.assertTrue(found)
def test_remove_user_from_group(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ domain = self._get_domain_fixture()
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
@@ -1779,21 +1832,20 @@ class IdentityTests(object):
self.identity_man.create_user({}, new_user['id'], new_user)
self.identity_api.add_user_to_group(new_user['id'],
new_group['id'])
- agroups = self.identity_api.list_groups_for_user(new_user['id'])
+ groups = self.identity_api.list_groups_for_user(new_user['id'])
+ self.assertIn(new_group['id'], [x['id'] for x in groups])
self.identity_api.remove_user_from_group(new_user['id'],
new_group['id'])
groups = self.identity_api.list_groups_for_user(new_user['id'])
- for x in groups:
- self.assertFalse(x['id'] == new_group['id'])
+ self.assertNotIn(new_group['id'], [x['id'] for x in groups])
def test_remove_user_from_group_404(self):
- domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
+ domain = self._get_domain_fixture()
new_user = {'id': uuid.uuid4().hex, 'name': 'new_user',
'password': uuid.uuid4().hex, 'enabled': True,
'domain_id': domain['id']}
self.identity_man.create_user({}, new_user['id'], new_user)
- new_group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ new_group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, new_group['id'], new_group)
self.assertRaises(exception.NotFound,
@@ -1812,16 +1864,18 @@ class IdentityTests(object):
uuid.uuid4().hex)
def test_group_crud(self):
- group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
+ self.identity_api.create_domain(domain['id'], domain)
+ group = {'id': uuid.uuid4().hex, 'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_man.create_group({}, group['id'], group)
group_ref = self.identity_api.get_group(group['id'])
- self.assertDictContainsSubset(group_ref, group)
+ self.assertDictContainsSubset(group, group_ref)
group['name'] = uuid.uuid4().hex
self.identity_api.update_group(group['id'], group)
group_ref = self.identity_api.get_group(group['id'])
- self.assertDictContainsSubset(group_ref, group)
+ self.assertDictContainsSubset(group, group_ref)
self.identity_api.delete_group(group['id'])
self.assertRaises(exception.GroupNotFound,
@@ -1885,16 +1939,19 @@ class IdentityTests(object):
group1)
def test_project_crud(self):
+ domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
+ 'enabled': True}
+ self.identity_api.create_domain(domain['id'], domain)
project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
- 'domain_id': uuid.uuid4().hex}
+ 'domain_id': domain['id']}
self.identity_man.create_project({}, project['id'], project)
project_ref = self.identity_api.get_project(project['id'])
- self.assertDictContainsSubset(project_ref, project)
+ self.assertDictContainsSubset(project, project_ref)
project['name'] = uuid.uuid4().hex
self.identity_api.update_project(project['id'], project)
project_ref = self.identity_api.get_project(project['id'])
- self.assertDictContainsSubset(project_ref, project)
+ self.assertDictContainsSubset(project, project_ref)
self.identity_api.delete_project(project['id'])
self.assertRaises(exception.ProjectNotFound,
@@ -1919,20 +1976,21 @@ class IdentityTests(object):
domain['id'])
def test_user_crud(self):
- user = {'domain_id': uuid.uuid4().hex, 'id': uuid.uuid4().hex,
+ user = {'domain_id': CONF.identity.default_domain_id,
+ 'id': uuid.uuid4().hex,
'name': uuid.uuid4().hex, 'password': 'passw0rd'}
self.identity_api.create_user(user['id'], user)
user_ref = self.identity_api.get_user(user['id'])
del user['password']
user_ref_dict = dict((x, user_ref[x]) for x in user_ref)
- self.assertDictContainsSubset(user_ref_dict, user)
+ self.assertDictContainsSubset(user, user_ref_dict)
user['password'] = uuid.uuid4().hex
self.identity_api.update_user(user['id'], user)
user_ref = self.identity_api.get_user(user['id'])
del user['password']
user_ref_dict = dict((x, user_ref[x]) for x in user_ref)
- self.assertDictContainsSubset(user_ref_dict, user)
+ self.assertDictContainsSubset(user, user_ref_dict)
self.identity_api.delete_user(user['id'])
self.assertRaises(exception.UserNotFound,
@@ -1940,8 +1998,10 @@ class IdentityTests(object):
user['id'])
def test_list_user_projects(self):
+ domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
+ self.identity_api.create_domain(domain['id'], domain)
user1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
- 'password': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
+ 'password': uuid.uuid4().hex, 'domain_id': domain['id'],
'enabled': True}
self.identity_man.create_user({}, user1['id'], user1)
user_projects = self.identity_api.list_user_projects(user1['id'])
@@ -1958,7 +2018,9 @@ class IdentityTests(object):
class TokenTests(object):
def _create_token_id(self):
- token_id = ""
+ # Token must start with MII here otherwise it fails the asn1 test
+ # and is not hashed in a SQL backend.
+ token_id = "MII"
for i in range(1, 20):
token_id += uuid.uuid4().hex
return token_id
@@ -1972,13 +2034,16 @@ class TokenTests(object):
expires = data_ref.pop('expires')
data_ref.pop('user_id')
self.assertTrue(isinstance(expires, datetime.datetime))
+ data_ref.pop('id')
+ data.pop('id')
self.assertDictEqual(data_ref, data)
new_data_ref = self.token_api.get_token(token_id)
expires = new_data_ref.pop('expires')
+ self.assertTrue(isinstance(expires, datetime.datetime))
new_data_ref.pop('user_id')
+ new_data_ref.pop('id')
- self.assertTrue(isinstance(expires, datetime.datetime))
self.assertEquals(new_data_ref, data)
self.token_api.delete_token(token_id)
@@ -1993,10 +2058,12 @@ class TokenTests(object):
'user': {'id': 'testuserid'}}
if tenant_id is not None:
data['tenant'] = {'id': tenant_id, 'name': tenant_id}
+ if tenant_id is NULL_OBJECT:
+ data['tenant'] = None
if trust_id is not None:
data['trust_id'] = trust_id
- self.token_api.create_token(token_id, data)
- return token_id
+ new_token = self.token_api.create_token(token_id, data)
+ return new_token['id']
def test_token_list(self):
tokens = self.token_api.list_tokens('testuserid')
@@ -2024,12 +2091,15 @@ class TokenTests(object):
tenant2 = uuid.uuid4().hex
token_id3 = self.create_token_sample_data(tenant_id=tenant1)
token_id4 = self.create_token_sample_data(tenant_id=tenant2)
+ # test for existing but empty tenant (LP:1078497)
+ token_id5 = self.create_token_sample_data(tenant_id=NULL_OBJECT)
tokens = self.token_api.list_tokens('testuserid')
- self.assertEquals(len(tokens), 2)
+ self.assertEquals(len(tokens), 3)
self.assertNotIn(token_id1, tokens)
self.assertNotIn(token_id2, tokens)
self.assertIn(token_id3, tokens)
self.assertIn(token_id4, tokens)
+ self.assertIn(token_id5, tokens)
tokens = self.token_api.list_tokens('testuserid', tenant2)
self.assertEquals(len(tokens), 1)
self.assertNotIn(token_id1, tokens)
@@ -2077,6 +2147,12 @@ class TokenTests(object):
data_ref = self.token_api.create_token(token_id, data)
self.assertIsNotNone(data_ref['expires'])
new_data_ref = self.token_api.get_token(token_id)
+
+ # MySQL doesn't store microseconds, so discard them before testing
+ data_ref['expires'] = data_ref['expires'].replace(microsecond=0)
+ new_data_ref['expires'] = new_data_ref['expires'].replace(
+ microsecond=0)
+
self.assertEqual(data_ref, new_data_ref)
def check_list_revoked_tokens(self, token_ids):
@@ -2111,6 +2187,32 @@ class TokenTests(object):
self.check_list_revoked_tokens([self.delete_token()
for x in xrange(2)])
+ def test_flush_expired_token(self):
+ token_id = uuid.uuid4().hex
+ expire_time = timeutils.utcnow() - datetime.timedelta(minutes=1)
+ data = {'id_hash': token_id, 'id': token_id, 'a': 'b',
+ 'expires': expire_time,
+ 'trust_id': None,
+ 'user': {'id': 'testuserid'}}
+ data_ref = self.token_api.create_token(token_id, data)
+ data_ref.pop('user_id')
+ self.assertDictEqual(data_ref, data)
+
+ token_id = uuid.uuid4().hex
+ expire_time = timeutils.utcnow() + datetime.timedelta(minutes=1)
+ data = {'id_hash': token_id, 'id': token_id, 'a': 'b',
+ 'expires': expire_time,
+ 'trust_id': None,
+ 'user': {'id': 'testuserid'}}
+ data_ref = self.token_api.create_token(token_id, data)
+ data_ref.pop('user_id')
+ self.assertDictEqual(data_ref, data)
+
+ self.token_api.flush_expired_tokens()
+ tokens = self.token_api.list_tokens('testuserid')
+ self.assertEqual(len(tokens), 1)
+ self.assertIn(token_id, tokens)
+
class TrustTests(object):
def create_sample_trust(self, new_id):
@@ -2163,13 +2265,13 @@ class TrustTests(object):
self.assertTrue(timeutils.normalize_time(trust_data['expires_at']) >
timeutils.utcnow())
- self.assertEquals([{'id':'member'},
+ self.assertEquals([{'id': 'member'},
{'id': 'other'},
{'id': 'browser'}], trust_data['roles'])
def test_list_trust_by_trustee(self):
for i in range(0, 3):
- trust_data = self.create_sample_trust(uuid.uuid4().hex)
+ self.create_sample_trust(uuid.uuid4().hex)
trusts = self.trust_api.list_trusts_for_trustee(self.trustee['id'])
self.assertEqual(len(trusts), 3)
self.assertEqual(trusts[0]["trustee_user_id"], self.trustee['id'])
@@ -2178,7 +2280,7 @@ class TrustTests(object):
def test_list_trust_by_trustor(self):
for i in range(0, 3):
- trust_data = self.create_sample_trust(uuid.uuid4().hex)
+ self.create_sample_trust(uuid.uuid4().hex)
trusts = self.trust_api.list_trusts_for_trustor(self.trustor['id'])
self.assertEqual(len(trusts), 3)
self.assertEqual(trusts[0]["trustor_user_id"], self.trustor['id'])
@@ -2187,7 +2289,7 @@ class TrustTests(object):
def test_list_trusts(self):
for i in range(0, 3):
- trust_data = self.create_sample_trust(uuid.uuid4().hex)
+ self.create_sample_trust(uuid.uuid4().hex)
trusts = self.trust_api.list_trusts()
self.assertEqual(len(trusts), 3)
diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py
index f3a8ece0..3a4e2da8 100644
--- a/tests/test_backend_kvs.py
+++ b/tests/test_backend_kvs.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import uuid
+
import nose.exc
from keystone import catalog
diff --git a/tests/test_backend_ldap.py b/tests/test_backend_ldap.py
index ef409902..61214002 100644
--- a/tests/test_backend_ldap.py
+++ b/tests/test_backend_ldap.py
@@ -16,6 +16,7 @@
# under the License.
import uuid
+
import nose.exc
from keystone.common.ldap import fakeldap
@@ -32,6 +33,9 @@ CONF = config.CONF
class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
+ def _get_domain_fixture(self):
+ """Domains in LDAP are read-only, so just return the static one."""
+ return self.identity_api.get_domain(CONF.identity.default_domain_id)
def clear_database(self):
db = fakeldap.FakeShelve().get_instance()
@@ -334,7 +338,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
self.assertEqual(user_ref['enabled'], True)
def test_user_api_get_connection_no_user_password(self):
- """Don't bind in case the user and password are blank"""
+ """Don't bind in case the user and password are blank."""
self.config([test.etcdir('keystone.conf.sample'),
test.testsdir('test_overrides.conf')])
CONF.ldap.url = "fake://memory"
@@ -383,7 +387,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
expected_dict = {'description': 'name', 'gecos': 'password'}
self.assertDictEqual(expected_dict, mapping)
-# TODO (henry-nash) These need to be removed when the full LDAP implementation
+# TODO(henry-nash): These need to be removed when the full LDAP implementation
# is submitted - see Bugs 1092187, 1101287, 1101276, 1101289
# (spzala)The group and domain crud tests below override the standard ones
@@ -392,8 +396,11 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
# In the tests below, the update is demonstrated by updating description.
# Refer to bug 1136403 for more detail.
def test_group_crud(self):
- group = {'id': uuid.uuid4().hex, 'domain_id': uuid.uuid4().hex,
- 'name': uuid.uuid4().hex, 'description': uuid.uuid4().hex}
+ group = {
+ 'id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'name': uuid.uuid4().hex,
+ 'description': uuid.uuid4().hex}
self.identity_api.create_group(group['id'], group)
group_ref = self.identity_api.get_group(group['id'])
self.assertDictEqual(group_ref, group)
@@ -410,15 +417,25 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
def test_domain_crud(self):
domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
'enabled': True, 'description': uuid.uuid4().hex}
- self.identity_api.create_domain(domain['id'], domain)
- domain_ref = self.identity_api.get_domain(domain['id'])
- self.assertDictEqual(domain_ref, domain)
- domain['description'] = uuid.uuid4().hex
- self.identity_api.update_domain(domain['id'], domain)
- domain_ref = self.identity_api.get_domain(domain['id'])
- self.assertDictEqual(domain_ref, domain)
-
- self.identity_api.delete_domain(domain['id'])
+ with self.assertRaises(exception.Forbidden):
+ self.identity_api.create_domain(domain['id'], domain)
+ with self.assertRaises(exception.Conflict):
+ self.identity_api.create_domain(
+ CONF.identity.default_domain_id, domain)
+ with self.assertRaises(exception.DomainNotFound):
+ self.identity_api.get_domain(domain['id'])
+ with self.assertRaises(exception.DomainNotFound):
+ domain['description'] = uuid.uuid4().hex
+ self.identity_api.update_domain(domain['id'], domain)
+ with self.assertRaises(exception.Forbidden):
+ self.identity_api.update_domain(
+ CONF.identity.default_domain_id, domain)
+ with self.assertRaises(exception.DomainNotFound):
+ self.identity_api.get_domain(domain['id'])
+ with self.assertRaises(exception.DomainNotFound):
+ self.identity_api.delete_domain(domain['id'])
+ with self.assertRaises(exception.Forbidden):
+ self.identity_api.delete_domain(CONF.identity.default_domain_id)
self.assertRaises(exception.DomainNotFound,
self.identity_api.get_domain,
domain['id'])
@@ -439,10 +456,10 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
raise nose.exc.SkipTest('Blocked by bug 1101287')
def test_get_and_remove_role_grant_by_group_and_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_get_and_remove_role_grant_by_user_and_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_get_and_remove_correct_role_grant_from_a_mix(self):
raise nose.exc.SkipTest('Blocked by bug 1101287')
@@ -452,7 +469,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
# updating of a project name so this method override
# provides a different update test
project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
- 'domain_id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
'description': uuid.uuid4().hex
}
self.identity_api.create_project(project['id'], project)
@@ -475,34 +492,34 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
project['id'])
def test_get_and_remove_role_grant_by_group_and_cross_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_get_and_remove_role_grant_by_user_and_cross_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_role_grant_by_group_and_cross_domain_project(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_role_grant_by_user_and_cross_domain_project(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_multi_role_grant_by_user_group_on_project_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_delete_role_with_user_and_group_grants(self):
raise nose.exc.SkipTest('Blocked by bug 1101287')
def test_delete_user_with_group_project_domain_links(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_delete_group_with_user_project_domain_links(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_list_user_projects(self):
raise nose.exc.SkipTest('Blocked by bug 1101287')
def test_get_project_users(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_create_duplicate_user_name_in_different_domains(self):
raise nose.exc.SkipTest('Blocked by bug 1101276')
@@ -511,7 +528,8 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
raise nose.exc.SkipTest('Blocked by bug 1101276')
def test_create_duplicate_group_name_in_different_domains(self):
- raise nose.exc.SkipTest('Blocked by bug 1101276')
+ raise nose.exc.SkipTest(
+ 'N/A: LDAP does not support multiple domains')
def test_move_user_between_domains(self):
raise nose.exc.SkipTest('Blocked by bug 1101276')
@@ -520,7 +538,8 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
raise nose.exc.SkipTest('Blocked by bug 1101276')
def test_move_group_between_domains(self):
- raise nose.exc.SkipTest('Blocked by bug 1101276')
+ raise nose.exc.SkipTest(
+ 'N/A: LDAP does not support multiple domains')
def test_move_group_between_domains_with_clashing_names_fails(self):
raise nose.exc.SkipTest('Blocked by bug 1101276')
@@ -532,7 +551,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
raise nose.exc.SkipTest('Blocked by bug 1101276')
def test_get_roles_for_user_and_domain(self):
- raise nose.exc.SkipTest('Blocked by bug 1101287')
+ raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains')
def test_list_group_members_missing_entry(self):
"""List group members with deleted user.
@@ -569,6 +588,14 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
self.assertEqual(len(res), 1, "Expected 1 entry (user_1)")
self.assertEqual(res[0]['id'], user_1_id, "Expected user 1 id")
+ def test_list_domains(self):
+ domains = self.identity_api.list_domains()
+ self.assertEquals(
+ domains,
+ [{'id': CONF.identity.default_domain_id,
+ 'name': 'Default',
+ 'enabled': True}])
+
class LDAPIdentityEnabledEmulation(LDAPIdentity):
def setUp(self):
@@ -614,10 +641,11 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
# NOTE(topol): LDAPIdentityEnabledEmulation will create an
# enabled key in the project dictionary so this
# method override handles this side-effect
- project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
- 'domain_id': uuid.uuid4().hex,
- 'description': uuid.uuid4().hex
- }
+ project = {
+ 'id': uuid.uuid4().hex,
+ 'name': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'description': uuid.uuid4().hex}
self.identity_api.create_project(project['id'], project)
project_ref = self.identity_api.get_project(project['id'])
@@ -639,8 +667,11 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
project['id'])
def test_user_crud(self):
- user = {'domain_id': uuid.uuid4().hex, 'id': uuid.uuid4().hex,
- 'name': uuid.uuid4().hex, 'password': 'passw0rd'}
+ user = {
+ 'id': uuid.uuid4().hex,
+ 'domain_id': CONF.identity.default_domain_id,
+ 'name': uuid.uuid4().hex,
+ 'password': uuid.uuid4().hex}
self.identity_man.create_user({}, user['id'], user)
user['enabled'] = True
user_ref = self.identity_api.get_user(user['id'])
diff --git a/tests/test_backend_memcache.py b/tests/test_backend_memcache.py
index 9fbaeb90..f5999002 100644
--- a/tests/test_backend_memcache.py
+++ b/tests/test_backend_memcache.py
@@ -19,6 +19,7 @@ import uuid
import memcache
from keystone.common import utils
+from keystone import exception
from keystone.openstack.common import timeutils
from keystone import test
from keystone.token.backends import memcache as token_memcache
@@ -96,3 +97,7 @@ class MemcacheToken(test.TestCase, test_backend.TokenTests):
def test_list_tokens_unicode_user_id(self):
user_id = unicode(uuid.uuid4().hex)
self.token_api.list_tokens(user_id)
+
+ def test_flush_expired_token(self):
+ with self.assertRaises(exception.NotImplemented):
+ self.token_api.flush_expired_tokens()
diff --git a/tests/test_backend_sql.py b/tests/test_backend_sql.py
index 1e7867bd..e4a19ef9 100644
--- a/tests/test_backend_sql.py
+++ b/tests/test_backend_sql.py
@@ -26,7 +26,6 @@ from keystone import test
from keystone import token
from keystone import trust
-
import default_fixtures
import test_backend
@@ -34,7 +33,8 @@ CONF = config.CONF
DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
-class SqlTests(test.TestCase):
+class SqlTests(test.TestCase, sql.Base):
+
def setUp(self):
super(SqlTests, self).setUp()
self.config([test.etcdir('keystone.conf.sample'),
@@ -48,6 +48,11 @@ class SqlTests(test.TestCase):
self.trust_man = trust.Manager()
self.policy_man = policy.Manager()
+ # create tables and keep an engine reference for cleanup.
+ # this must be done after the models are loaded by the managers.
+ self.engine = self.get_engine()
+ sql.ModelBase.metadata.create_all(bind=self.engine)
+
# create shortcut references to each driver
self.catalog_api = self.catalog_man.driver
self.identity_api = self.identity_man.driver
@@ -61,11 +66,18 @@ class SqlTests(test.TestCase):
self.user_foo['enabled'] = True
def tearDown(self):
+ sql.ModelBase.metadata.drop_all(bind=self.engine)
+ self.engine.dispose()
sql.set_global_engine(None)
super(SqlTests, self).tearDown()
class SqlIdentity(SqlTests, test_backend.IdentityTests):
+ def test_password_hashed(self):
+ session = self.identity_api.get_session()
+ user_ref = self.identity_api._get_user(session, self.user_foo['id'])
+ self.assertNotEqual(user_ref['password'], self.user_foo['password'])
+
def test_delete_user_with_project_association(self):
user = {'id': uuid.uuid4().hex,
'name': uuid.uuid4().hex,
diff --git a/tests/test_catalog.py b/tests/test_catalog.py
index 8a46be80..3c00b1e8 100644
--- a/tests/test_catalog.py
+++ b/tests/test_catalog.py
@@ -31,10 +31,10 @@ class V2CatalogTestCase(test_content_types.RestfulTestCase):
def _get_token_id(self, r):
"""Applicable only to JSON."""
- return r.body['access']['token']['id']
+ return r.result['access']['token']['id']
def assertValidErrorResponse(self, response):
- self.assertEqual(response.status, 400)
+ self.assertEqual(response.status_code, 400)
def _endpoint_create(self, expected_status=200, missing_param=None):
path = '/v2.0/endpoints'
@@ -56,20 +56,20 @@ class V2CatalogTestCase(test_content_types.RestfulTestCase):
def test_endpoint_create(self):
req_body, response = self._endpoint_create(expected_status=200)
- self.assertTrue('endpoint' in response.body)
- self.assertTrue('id' in response.body['endpoint'])
+ self.assertTrue('endpoint' in response.result)
+ self.assertTrue('id' in response.result['endpoint'])
for field, value in req_body['endpoint'].iteritems():
- self.assertEqual(response.body['endpoint'][field], value)
+ self.assertEqual(response.result['endpoint'][field], value)
def test_endpoint_create_with_missing_adminurl(self):
req_body, response = self._endpoint_create(expected_status=200,
missing_param='adminurl')
- self.assertEqual(response.status, 200)
+ self.assertEqual(response.status_code, 200)
def test_endpoint_create_with_missing_internalurl(self):
req_body, response = self._endpoint_create(expected_status=200,
missing_param='internalurl')
- self.assertEqual(response.status, 200)
+ self.assertEqual(response.status_code, 200)
def test_endpoint_create_with_missing_publicurl(self):
req_body, response = self._endpoint_create(expected_status=400,
diff --git a/tests/test_config.py b/tests/test_config.py
new file mode 100644
index 00000000..ae134b68
--- /dev/null
+++ b/tests/test_config.py
@@ -0,0 +1,18 @@
+from keystone import config
+from keystone import exception
+from keystone import test
+
+
+CONF = config.CONF
+
+
+class ConfigTestCase(test.TestCase):
+ def test_paste_config(self):
+ self.assertEqual(config.find_paste_config(),
+ test.etcdir('keystone-paste.ini'))
+ self.opt_in_group('paste_deploy', config_file='XYZ')
+ self.assertRaises(exception.PasteConfigNotFound,
+ config.find_paste_config)
+ self.opt_in_group('paste_deploy', config_file='')
+ self.assertEqual(config.find_paste_config(),
+ test.etcdir('keystone.conf.sample'))
diff --git a/tests/test_content_types.py b/tests/test_content_types.py
index 8f7e9c61..e5bdc56a 100644
--- a/tests/test_content_types.py
+++ b/tests/test_content_types.py
@@ -14,11 +14,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib
+import io
import uuid
from lxml import etree
import nose.exc
+import webtest
from keystone.common import serializer
from keystone.openstack.common import jsonutils
@@ -63,8 +64,10 @@ class RestfulTestCase(test.TestCase):
self.load_backends()
self.load_fixtures(default_fixtures)
- self.public_server = self.serveapp('keystone', name='main')
- self.admin_server = self.serveapp('keystone', name='admin')
+ self.public_app = webtest.TestApp(
+ self.loadapp('keystone', name='main'))
+ self.admin_app = webtest.TestApp(
+ self.loadapp('keystone', name='admin'))
# TODO(termie): is_admin is being deprecated once the policy stuff
# is all working
@@ -77,40 +80,31 @@ class RestfulTestCase(test.TestCase):
def tearDown(self):
"""Kill running servers and release references to avoid leaks."""
- self.public_server.kill()
- self.admin_server.kill()
- self.public_server = None
- self.admin_server = None
+ self.public_app = None
+ self.admin_app = None
super(RestfulTestCase, self).tearDown()
- def request(self, host='0.0.0.0', port=80, method='GET', path='/',
- headers=None, body=None, expected_status=None):
- """Perform request and fetch httplib.HTTPResponse from the server."""
-
- # Initialize headers dictionary
- headers = {} if not headers else headers
-
- connection = httplib.HTTPConnection(host, port, timeout=100000)
+ def request(self, app, path, body=None, headers=None, token=None,
+ expected_status=None, **kwargs):
+ if headers:
+ headers = dict([(str(k), str(v)) for k, v in headers.iteritems()])
+ else:
+ headers = {}
- # Perform the request
- connection.request(method, path, body, headers)
+ if token:
+ headers['X-Auth-Token'] = str(token)
- # Retrieve the response so we can close the connection
- response = connection.getresponse()
+ # setting body this way because of:
+ # https://github.com/Pylons/webtest/issues/71
+ if body:
+ kwargs['body_file'] = io.BytesIO(body)
- response.body = response.read()
+ # sets environ['REMOTE_ADDR']
+ kwargs.setdefault('remote_addr', 'localhost')
- # Close the connection
- connection.close()
+ response = app.request(path, headers=headers,
+ status=expected_status, **kwargs)
- # Automatically assert HTTP status code
- if expected_status:
- self.assertResponseStatus(response, expected_status)
- else:
- self.assertResponseSuccessful(response)
- self.assertValidResponseHeaders(response)
-
- # Contains the response headers, body, etc
return response
def assertResponseSuccessful(self, response):
@@ -121,10 +115,10 @@ class RestfulTestCase(test.TestCase):
example::
- >>> self.assertResponseSuccessful(response, 203)
+ self.assertResponseSuccessful(response, 203)
"""
self.assertTrue(
- response.status >= 200 and response.status <= 299,
+ response.status_code >= 200 and response.status_code <= 299,
'Status code %d is outside of the expected range (2xx)\n\n%s' %
(response.status, response.body))
@@ -136,17 +130,17 @@ class RestfulTestCase(test.TestCase):
example::
- >>> self.assertResponseStatus(response, 203)
+ self.assertResponseStatus(response, 203)
"""
self.assertEqual(
- response.status,
+ response.status_code,
expected_status,
'Status code %s is not %s, as expected)\n\n%s' %
- (response.status, expected_status, response.body))
+ (response.status_code, expected_status, response.body))
def assertValidResponseHeaders(self, response):
"""Ensures that response headers appear as expected."""
- self.assertIn('X-Auth-Token', response.getheader('Vary'))
+ self.assertIn('X-Auth-Token', response.headers.get('Vary'))
def _to_content_type(self, body, headers, content_type=None):
"""Attempt to encode JSON and XML automatically."""
@@ -167,21 +161,18 @@ class RestfulTestCase(test.TestCase):
"""Attempt to decode JSON and XML automatically, if detected."""
content_type = content_type or self.content_type
- # make the original response body available, for convenience
- response.raw = response.body
-
if response.body is not None and response.body.strip():
# if a body is provided, a Content-Type is also expected
- header = response.getheader('Content-Type', None)
+ header = response.headers.get('Content-Type', None)
self.assertIn(content_type, header)
if content_type == 'json':
- response.body = jsonutils.loads(response.body)
+ response.result = jsonutils.loads(response.body)
elif content_type == 'xml':
- response.body = etree.fromstring(response.body)
+ response.result = etree.fromstring(response.body)
def restful_request(self, method='GET', headers=None, body=None,
- token=None, content_type=None, **kwargs):
+ content_type=None, **kwargs):
"""Serializes/deserializes json/xml as request/response body.
.. WARNING::
@@ -193,9 +184,6 @@ class RestfulTestCase(test.TestCase):
# Initialize headers dictionary
headers = {} if not headers else headers
- if token is not None:
- headers['X-Auth-Token'] = token
-
body = self._to_content_type(body, headers, content_type)
# Perform the HTTP request/response
@@ -205,32 +193,26 @@ class RestfulTestCase(test.TestCase):
self._from_content_type(response, content_type)
# we can save some code & improve coverage by always doing this
- if method != 'HEAD' and response.status >= 400:
+ if method != 'HEAD' and response.status_code >= 400:
self.assertValidErrorResponse(response)
# Contains the decoded response.body
return response
- def _get_port(self, server):
- return server.socket_info['socket'][1]
-
- def _public_port(self):
- return self._get_port(self.public_server)
-
- def _admin_port(self):
- return self._get_port(self.admin_server)
+ def _request(self, convert=True, **kwargs):
+ if convert:
+ response = self.restful_request(**kwargs)
+ else:
+ response = self.request(**kwargs)
- def public_request(self, port=None, **kwargs):
- kwargs['port'] = port or self._public_port()
- response = self.restful_request(**kwargs)
self.assertValidResponseHeaders(response)
return response
- def admin_request(self, port=None, **kwargs):
- kwargs['port'] = port or self._admin_port()
- response = self.restful_request(**kwargs)
- self.assertValidResponseHeaders(response)
- return response
+ def public_request(self, **kwargs):
+ return self._request(app=self.public_app, **kwargs)
+
+ def admin_request(self, **kwargs):
+ return self._request(app=self.admin_app, **kwargs)
def get_scoped_token(self, tenant_id=None):
"""Convenience method so that we can test authenticated requests."""
@@ -514,6 +496,72 @@ class CoreApiTests(object):
token=token)
self.assertValidUserResponse(r)
+ def test_create_update_user_invalid_enabled_type(self):
+ # Enforce usage of boolean for 'enabled' field in JSON and XML
+ token = self.get_scoped_token()
+
+ # Test CREATE request
+ r = self.admin_request(
+ method='POST',
+ path='/v2.0/users',
+ body={
+ 'user': {
+ 'name': uuid.uuid4().hex,
+ 'password': uuid.uuid4().hex,
+ # In XML, only "true|false" are converted to boolean.
+ 'enabled': "False",
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
+
+ r = self.admin_request(
+ method='POST',
+ path='/v2.0/users',
+ body={
+ 'user': {
+ 'name': uuid.uuid4().hex,
+ 'password': uuid.uuid4().hex,
+ # In JSON, 0|1 are not booleans
+ 'enabled': 0,
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
+
+ # Test UPDATE request
+ path = '/v2.0/users/%(user_id)s' % {
+ 'user_id': self.user_foo['id'],
+ }
+
+ r = self.admin_request(
+ method='PUT',
+ path=path,
+ body={
+ 'user': {
+ # In XML, only "true|false" are converted to boolean.
+ 'enabled': "False",
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
+
+ r = self.admin_request(
+ method='PUT',
+ path=path,
+ body={
+ 'user': {
+ # In JSON, 0|1 are not booleans
+ 'enabled': 1,
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
+
def test_error_response(self):
"""This triggers assertValidErrorResponse by convention."""
self.public_request(path='/v2.0/tenants', expected_status=401)
@@ -545,129 +593,129 @@ class JsonTestCase(RestfulTestCase, CoreApiTests):
def _get_token_id(self, r):
"""Applicable only to JSON."""
- return r.body['access']['token']['id']
+ return r.result['access']['token']['id']
def assertValidErrorResponse(self, r):
- self.assertIsNotNone(r.body.get('error'))
- self.assertValidError(r.body['error'])
- self.assertEqual(r.body['error']['code'], r.status)
+ self.assertIsNotNone(r.result.get('error'))
+ self.assertValidError(r.result['error'])
+ self.assertEqual(r.result['error']['code'], r.status_code)
def assertValidExtension(self, extension):
super(JsonTestCase, self).assertValidExtension(extension)
self.assertIsNotNone(extension.get('description'))
self.assertIsNotNone(extension.get('links'))
- self.assertTrue(len(extension.get('links')))
+ self.assertNotEmpty(extension.get('links'))
for link in extension.get('links'):
self.assertValidExtensionLink(link)
def assertValidExtensionListResponse(self, r):
- self.assertIsNotNone(r.body.get('extensions'))
- self.assertIsNotNone(r.body['extensions'].get('values'))
- self.assertTrue(len(r.body['extensions'].get('values')))
- for extension in r.body['extensions']['values']:
+ self.assertIsNotNone(r.result.get('extensions'))
+ self.assertIsNotNone(r.result['extensions'].get('values'))
+ self.assertNotEmpty(r.result['extensions'].get('values'))
+ for extension in r.result['extensions']['values']:
self.assertValidExtension(extension)
def assertValidExtensionResponse(self, r):
- self.assertValidExtension(r.body.get('extension'))
+ self.assertValidExtension(r.result.get('extension'))
def assertValidAuthenticationResponse(self, r,
require_service_catalog=False):
- self.assertIsNotNone(r.body.get('access'))
- self.assertIsNotNone(r.body['access'].get('token'))
- self.assertIsNotNone(r.body['access'].get('user'))
+ self.assertIsNotNone(r.result.get('access'))
+ self.assertIsNotNone(r.result['access'].get('token'))
+ self.assertIsNotNone(r.result['access'].get('user'))
# validate token
- self.assertIsNotNone(r.body['access']['token'].get('id'))
- self.assertIsNotNone(r.body['access']['token'].get('expires'))
- tenant = r.body['access']['token'].get('tenant')
+ self.assertIsNotNone(r.result['access']['token'].get('id'))
+ self.assertIsNotNone(r.result['access']['token'].get('expires'))
+ tenant = r.result['access']['token'].get('tenant')
if tenant is not None:
# validate tenant
self.assertIsNotNone(tenant.get('id'))
self.assertIsNotNone(tenant.get('name'))
# validate user
- self.assertIsNotNone(r.body['access']['user'].get('id'))
- self.assertIsNotNone(r.body['access']['user'].get('name'))
+ self.assertIsNotNone(r.result['access']['user'].get('id'))
+ self.assertIsNotNone(r.result['access']['user'].get('name'))
if require_service_catalog:
# roles are only provided with a service catalog
- roles = r.body['access']['user'].get('roles')
- self.assertTrue(len(roles))
+ roles = r.result['access']['user'].get('roles')
+ self.assertNotEmpty(roles)
for role in roles:
self.assertIsNotNone(role.get('name'))
- serviceCatalog = r.body['access'].get('serviceCatalog')
+ serviceCatalog = r.result['access'].get('serviceCatalog')
# validate service catalog
if require_service_catalog:
self.assertIsNotNone(serviceCatalog)
if serviceCatalog is not None:
self.assertTrue(isinstance(serviceCatalog, list))
if require_service_catalog:
- self.assertTrue(len(serviceCatalog))
- for service in r.body['access']['serviceCatalog']:
+ self.assertNotEmpty(serviceCatalog)
+ for service in r.result['access']['serviceCatalog']:
# validate service
self.assertIsNotNone(service.get('name'))
self.assertIsNotNone(service.get('type'))
# services contain at least one endpoint
self.assertIsNotNone(service.get('endpoints'))
- self.assertTrue(len(service['endpoints']))
+ self.assertNotEmpty(service['endpoints'])
for endpoint in service['endpoints']:
# validate service endpoint
self.assertIsNotNone(endpoint.get('publicURL'))
def assertValidTenantListResponse(self, r):
- self.assertIsNotNone(r.body.get('tenants'))
- self.assertTrue(len(r.body['tenants']))
- for tenant in r.body['tenants']:
+ self.assertIsNotNone(r.result.get('tenants'))
+ self.assertNotEmpty(r.result['tenants'])
+ for tenant in r.result['tenants']:
self.assertValidTenant(tenant)
self.assertIsNotNone(tenant.get('enabled'))
self.assertIn(tenant.get('enabled'), [True, False])
def assertValidUserResponse(self, r):
- self.assertIsNotNone(r.body.get('user'))
- self.assertValidUser(r.body['user'])
+ self.assertIsNotNone(r.result.get('user'))
+ self.assertValidUser(r.result['user'])
def assertValidTenantResponse(self, r):
- self.assertIsNotNone(r.body.get('tenant'))
- self.assertValidTenant(r.body['tenant'])
+ self.assertIsNotNone(r.result.get('tenant'))
+ self.assertValidTenant(r.result['tenant'])
def assertValidRoleListResponse(self, r):
- self.assertIsNotNone(r.body.get('roles'))
- self.assertTrue(len(r.body['roles']))
- for role in r.body['roles']:
+ self.assertIsNotNone(r.result.get('roles'))
+ self.assertNotEmpty(r.result['roles'])
+ for role in r.result['roles']:
self.assertValidRole(role)
def assertValidVersion(self, version):
super(JsonTestCase, self).assertValidVersion(version)
self.assertIsNotNone(version.get('links'))
- self.assertTrue(len(version.get('links')))
+ self.assertNotEmpty(version.get('links'))
for link in version.get('links'):
self.assertIsNotNone(link.get('rel'))
self.assertIsNotNone(link.get('href'))
self.assertIsNotNone(version.get('media-types'))
- self.assertTrue(len(version.get('media-types')))
+ self.assertNotEmpty(version.get('media-types'))
for media in version.get('media-types'):
self.assertIsNotNone(media.get('base'))
self.assertIsNotNone(media.get('type'))
def assertValidMultipleChoiceResponse(self, r):
- self.assertIsNotNone(r.body.get('versions'))
- self.assertIsNotNone(r.body['versions'].get('values'))
- self.assertTrue(len(r.body['versions']['values']))
- for version in r.body['versions']['values']:
+ self.assertIsNotNone(r.result.get('versions'))
+ self.assertIsNotNone(r.result['versions'].get('values'))
+ self.assertNotEmpty(r.result['versions']['values'])
+ for version in r.result['versions']['values']:
self.assertValidVersion(version)
def assertValidVersionResponse(self, r):
- self.assertValidVersion(r.body.get('version'))
+ self.assertValidVersion(r.result.get('version'))
def assertValidEndpointListResponse(self, r):
- self.assertIsNotNone(r.body.get('endpoints'))
- self.assertTrue(len(r.body['endpoints']))
- for endpoint in r.body['endpoints']:
+ self.assertIsNotNone(r.result.get('endpoints'))
+ self.assertNotEmpty(r.result['endpoints'])
+ for endpoint in r.result['endpoints']:
self.assertIsNotNone(endpoint.get('id'))
self.assertIsNotNone(endpoint.get('name'))
self.assertIsNotNone(endpoint.get('type'))
@@ -726,16 +774,51 @@ class JsonTestCase(RestfulTestCase, CoreApiTests):
def test_fetch_revocation_list_admin_200(self):
token = self.get_scoped_token()
- r = self.restful_request(
+ r = self.admin_request(
method='GET',
path='/v2.0/tokens/revoked',
token=token,
- expected_status=200,
- port=self._admin_port())
+ expected_status=200)
self.assertValidRevocationListResponse(r)
def assertValidRevocationListResponse(self, response):
- self.assertIsNotNone(response.body['signed'])
+ self.assertIsNotNone(response.result['signed'])
+
+ def test_create_update_user_json_invalid_enabled_type(self):
+ # Enforce usage of boolean for 'enabled' field in JSON
+ token = self.get_scoped_token()
+
+ # Test CREATE request
+ r = self.admin_request(
+ method='POST',
+ path='/v2.0/users',
+ body={
+ 'user': {
+ 'name': uuid.uuid4().hex,
+ 'password': uuid.uuid4().hex,
+ # In JSON, "true|false" are not boolean
+ 'enabled': "true",
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
+
+ # Test UPDATE request
+ r = self.admin_request(
+ method='PUT',
+ path='/v2.0/users/%(user_id)s' % {
+ 'user_id': self.user_foo['id'],
+ },
+ body={
+ 'user': {
+ # In JSON, "true|false" are not boolean
+ 'enabled': "true",
+ },
+ },
+ token=token,
+ expected_status=400)
+ self.assertValidErrorResponse(r)
class XmlTestCase(RestfulTestCase, CoreApiTests):
@@ -743,18 +826,18 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
content_type = 'xml'
def _get_token_id(self, r):
- return r.body.find(self._tag('token')).get('id')
+ return r.result.find(self._tag('token')).get('id')
def _tag(self, tag_name, xmlns=None):
"""Helper method to build an namespaced element name."""
return '{%(ns)s}%(tag)s' % {'ns': xmlns or self.xmlns, 'tag': tag_name}
def assertValidErrorResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('error'))
self.assertValidError(xml)
- self.assertEqual(xml.get('code'), str(r.status))
+ self.assertEqual(xml.get('code'), str(r.status_code))
def assertValidExtension(self, extension):
super(XmlTestCase, self).assertValidExtension(extension)
@@ -762,20 +845,20 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
self.assertIsNotNone(extension.find(self._tag('description')))
self.assertTrue(extension.find(self._tag('description')).text)
links = extension.find(self._tag('links'))
- self.assertTrue(len(links.findall(self._tag('link'))))
+ self.assertNotEmpty(links.findall(self._tag('link')))
for link in links.findall(self._tag('link')):
self.assertValidExtensionLink(link)
def assertValidExtensionListResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('extensions'))
- self.assertTrue(len(xml.findall(self._tag('extension'))))
+ self.assertNotEmpty(xml.findall(self._tag('extension')))
for extension in xml.findall(self._tag('extension')):
self.assertValidExtension(extension)
def assertValidExtensionResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('extension'))
self.assertValidExtension(xml)
@@ -785,37 +868,37 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
links = version.find(self._tag('links'))
self.assertIsNotNone(links)
- self.assertTrue(len(links.findall(self._tag('link'))))
+ self.assertNotEmpty(links.findall(self._tag('link')))
for link in links.findall(self._tag('link')):
self.assertIsNotNone(link.get('rel'))
self.assertIsNotNone(link.get('href'))
media_types = version.find(self._tag('media-types'))
self.assertIsNotNone(media_types)
- self.assertTrue(len(media_types.findall(self._tag('media-type'))))
+ self.assertNotEmpty(media_types.findall(self._tag('media-type')))
for media in media_types.findall(self._tag('media-type')):
self.assertIsNotNone(media.get('base'))
self.assertIsNotNone(media.get('type'))
def assertValidMultipleChoiceResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('versions'))
- self.assertTrue(len(xml.findall(self._tag('version'))))
+ self.assertNotEmpty(xml.findall(self._tag('version')))
for version in xml.findall(self._tag('version')):
self.assertValidVersion(version)
def assertValidVersionResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('version'))
self.assertValidVersion(xml)
def assertValidEndpointListResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('endpoints'))
- self.assertTrue(len(xml.findall(self._tag('endpoint'))))
+ self.assertNotEmpty(xml.findall(self._tag('endpoint')))
for endpoint in xml.findall(self._tag('endpoint')):
self.assertIsNotNone(endpoint.get('id'))
self.assertIsNotNone(endpoint.get('name'))
@@ -825,28 +908,28 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
self.assertIsNotNone(endpoint.get('adminURL'))
def assertValidTenantResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('tenant'))
self.assertValidTenant(xml)
def assertValidUserResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('user'))
self.assertValidUser(xml)
def assertValidRoleListResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('roles'))
- self.assertTrue(len(r.body.findall(self._tag('role'))))
- for role in r.body.findall(self._tag('role')):
+ self.assertNotEmpty(r.result.findall(self._tag('role')))
+ for role in r.result.findall(self._tag('role')):
self.assertValidRole(role)
def assertValidAuthenticationResponse(self, r,
require_service_catalog=False):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('access'))
# validate token
@@ -868,7 +951,7 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
if require_service_catalog:
# roles are only provided with a service catalog
roles = user.findall(self._tag('role'))
- self.assertTrue(len(roles))
+ self.assertNotEmpty(roles)
for role in roles:
self.assertIsNotNone(role.get('name'))
@@ -879,7 +962,7 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
if serviceCatalog is not None:
services = serviceCatalog.findall(self._tag('service'))
if require_service_catalog:
- self.assertTrue(len(services))
+ self.assertNotEmpty(services)
for service in services:
# validate service
self.assertIsNotNone(service.get('name'))
@@ -887,24 +970,23 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
# services contain at least one endpoint
endpoints = service.findall(self._tag('endpoint'))
- self.assertTrue(len(endpoints))
+ self.assertNotEmpty(endpoints)
for endpoint in endpoints:
# validate service endpoint
self.assertIsNotNone(endpoint.get('publicURL'))
def assertValidTenantListResponse(self, r):
- xml = r.body
+ xml = r.result
self.assertEqual(xml.tag, self._tag('tenants'))
- self.assertTrue(len(r.body))
- for tenant in r.body.findall(self._tag('tenant')):
+ self.assertNotEmpty(r.result)
+ for tenant in r.result.findall(self._tag('tenant')):
self.assertValidTenant(tenant)
self.assertIn(tenant.get('enabled'), ['true', 'false'])
def test_authenticate_with_invalid_xml_in_password(self):
# public_request would auto escape the ampersand
- self.request(
- port=self._public_port(),
+ self.public_request(
method='POST',
path='/v2.0/tokens',
headers={
@@ -917,15 +999,13 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
<passwordCredentials username="FOO" password="&"/>
</auth>
""",
- expected_status=400)
+ expected_status=400,
+ convert=False)
def test_add_tenant_xml(self):
- """
- verify create a tenant without providing description field
- """
+ """Create a tenant without providing description field."""
token = self.get_scoped_token()
- r = self.request(
- port=self._admin_port(),
+ r = self.admin_request(
method='POST',
path='/v2.0/tenants',
headers={
@@ -938,19 +1018,17 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
enabled="true" name="ACME Corp">
<description></description>
</tenant>
- """)
+ """,
+ convert=False)
self._from_content_type(r, 'json')
- self.assertIsNotNone(r.body.get('tenant'))
- self.assertValidTenant(r.body['tenant'])
- self.assertEqual(r.body['tenant'].get('description'), "")
+ self.assertIsNotNone(r.result.get('tenant'))
+ self.assertValidTenant(r.result['tenant'])
+ self.assertEqual(r.result['tenant'].get('description'), "")
def test_add_tenant_json(self):
- """
- verify create a tenant without providing description field
- """
+ """Create a tenant without providing description field."""
token = self.get_scoped_token()
- r = self.request(
- port=self._admin_port(),
+ r = self.admin_request(
method='POST',
path='/v2.0/tenants',
headers={
@@ -963,8 +1041,9 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
"description":"",
"enabled":"true"}
}
- """)
+ """,
+ convert=False)
self._from_content_type(r, 'json')
- self.assertIsNotNone(r.body.get('tenant'))
- self.assertValidTenant(r.body['tenant'])
- self.assertEqual(r.body['tenant'].get('description'), "")
+ self.assertIsNotNone(r.result.get('tenant'))
+ self.assertValidTenant(r.result['tenant'])
+ self.assertEqual(r.result['tenant'].get('description'), "")
diff --git a/tests/test_contrib_s3_core.py b/tests/test_contrib_s3_core.py
index 0dceefbf..c76bc898 100644
--- a/tests/test_contrib_s3_core.py
+++ b/tests/test_contrib_s3_core.py
@@ -16,8 +16,8 @@
import uuid
-from keystone.contrib.s3.core import S3Controller
from keystone.contrib import ec2
+from keystone.contrib import s3
from keystone import exception
from keystone import test
@@ -30,7 +30,7 @@ class S3ContribCore(test.TestCase):
self.load_backends()
self.ec2_api = ec2.Manager()
- self.controller = S3Controller()
+ self.controller = s3.S3Controller()
def test_good_signature(self):
creds_ref = {'secret':
diff --git a/tests/test_exception.py b/tests/test_exception.py
index dffa14ed..4be470eb 100644
--- a/tests/test_exception.py
+++ b/tests/test_exception.py
@@ -136,3 +136,12 @@ class SecurityErrorTestCase(ExceptionTestCase):
e = exception.ForbiddenAction(action=risky_info)
self.assertValidJsonRendering(e)
self.assertIn(risky_info, str(e))
+
+ def test_unicode_message(self):
+ message = u'Comment \xe7a va'
+ e = exception.Error(message)
+ self.assertEqual(e.message, message)
+ try:
+ unicode(e)
+ except UnicodeEncodeError:
+ self.fail("unicode error message not supported")
diff --git a/tests/test_ipv6.py b/tests/test_ipv6.py
index 752a1cba..0973648f 100644
--- a/tests/test_ipv6.py
+++ b/tests/test_ipv6.py
@@ -34,9 +34,7 @@ class IPv6TestCase(test.TestCase):
self.load_backends()
def test_ipv6_ok(self):
- """
- Make sure both public and admin API work with ipv6.
- """
+ """Make sure both public and admin API work with ipv6."""
self.public_server = self.serveapp('keystone', name='main',
host="::1", port=0)
self.admin_server = self.serveapp('keystone', name='admin',
diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py
index acca5a0a..49e3bfc9 100644
--- a/tests/test_keystoneclient.py
+++ b/tests/test_keystoneclient.py
@@ -19,9 +19,9 @@ import webob
import nose.exc
+from keystone import config
from keystone.openstack.common import jsonutils
from keystone.openstack.common import timeutils
-from keystone import config
from keystone import test
import default_fixtures
@@ -395,6 +395,30 @@ class KeystoneClientTests(object):
self.get_client,
self.user_foo)
+ def test_delete_user_invalidates_token(self):
+ from keystoneclient import exceptions as client_exceptions
+
+ admin_client = self.get_client(admin=True)
+ client = self.get_client(admin=False)
+
+ username = uuid.uuid4().hex
+ password = uuid.uuid4().hex
+ user_id = admin_client.users.create(
+ name=username, password=password, email=uuid.uuid4().hex).id
+
+ token_id = client.tokens.authenticate(
+ username=username, password=password).id
+
+ # token should be usable before the user is deleted
+ client.tokens.authenticate(token=token_id)
+
+ admin_client.users.delete(user=user_id)
+
+ # authenticate with a token should not work after the user is deleted
+ self.assertRaises(client_exceptions.Unauthorized,
+ client.tokens.authenticate,
+ token=token_id)
+
def test_token_expiry_maintained(self):
timeutils.set_time_override()
foo_client = self.get_client(self.user_foo)
@@ -929,7 +953,7 @@ class KcMasterTestCase(CompatTestCase, KeystoneClientTests):
token_id = client.auth_token
new_password = uuid.uuid4().hex
- # TODO(derekh) : Update to use keystoneclient when available
+ # TODO(derekh): Update to use keystoneclient when available
class FakeResponse(object):
def start_fake_response(self, status, headers):
self.response_status = int(status.split(' ', 1)[0])
@@ -956,7 +980,7 @@ class KcMasterTestCase(CompatTestCase, KeystoneClientTests):
token_id = client.auth_token
new_password = uuid.uuid4().hex
- # TODO(derekh) : Update to use keystoneclient when available
+ # TODO(derekh): Update to use keystoneclient when available
class FakeResponse(object):
def start_fake_response(self, status, headers):
self.response_status = int(status.split(' ', 1)[0])
@@ -985,7 +1009,7 @@ class KcMasterTestCase(CompatTestCase, KeystoneClientTests):
token_id = client.auth_token
new_password = uuid.uuid4().hex
- # TODO(derekh) : Update to use keystoneclient when available
+ # TODO(derekh): Update to use keystoneclient when available
class FakeResponse(object):
def start_fake_response(self, status, headers):
self.response_status = int(status.split(' ', 1)[0])
@@ -1104,8 +1128,7 @@ class KcEssex3TestCase(CompatTestCase, KeystoneClientTests):
raise nose.exc.SkipTest('N/A')
def test_policy_crud(self):
- """Due to lack of endpoint CRUD"""
- raise nose.exc.SkipTest('N/A')
+ raise nose.exc.SkipTest('N/A due to lack of endpoint CRUD')
class Kc11TestCase(CompatTestCase, KeystoneClientTests):
diff --git a/tests/test_keystoneclient_sql.py b/tests/test_keystoneclient_sql.py
index 69c667ed..30f59022 100644
--- a/tests/test_keystoneclient_sql.py
+++ b/tests/test_keystoneclient_sql.py
@@ -28,14 +28,20 @@ import test_keystoneclient
CONF = config.CONF
-class KcMasterSqlTestCase(test_keystoneclient.KcMasterTestCase):
+class KcMasterSqlTestCase(test_keystoneclient.KcMasterTestCase, sql.Base):
def config(self, config_files):
super(KcMasterSqlTestCase, self).config([
test.etcdir('keystone.conf.sample'),
test.testsdir('test_overrides.conf'),
test.testsdir('backend_sql.conf')])
+ self.load_backends()
+ self.engine = self.get_engine()
+ sql.ModelBase.metadata.create_all(bind=self.engine)
+
def tearDown(self):
+ sql.ModelBase.metadata.drop_all(bind=self.engine)
+ self.engine.dispose()
sql.set_global_engine(None)
super(KcMasterSqlTestCase, self).tearDown()
diff --git a/tests/test_policy.py b/tests/test_policy.py
index a43c9d3e..465fd69a 100644
--- a/tests/test_policy.py
+++ b/tests/test_policy.py
@@ -21,9 +21,9 @@ import urllib2
from keystone import config
from keystone import exception
-from keystone import test
from keystone.openstack.common import policy as common_policy
from keystone.policy.backends import rules
+from keystone import test
CONF = config.CONF
diff --git a/tests/test_setup.py b/tests/test_setup.py
deleted file mode 100644
index 56c5af88..00000000
--- a/tests/test_setup.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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 os
-import unittest
-
-from keystone.openstack.common.setup import canonicalize_emails
-from keystone.openstack.common.setup import parse_mailmap
-
-
-class SetupTest(unittest.TestCase):
-
- def setUp(self):
- self.mailmap = '.mailmap.test'
-
- def test_str_dict_replace(self):
- string = 'Johnnie T. Hozer'
- mapping = {'T.': 'The'}
- self.assertEqual('Johnnie The Hozer',
- canonicalize_emails(string, mapping))
-
- def test_mailmap_with_fullname(self):
- with open(self.mailmap, 'w') as mm_fh:
- mm_fh.write("Foo Bar <email@foo.com> Foo Bar <email@bar.com>\n")
- self.assertEqual({'<email@bar.com>': '<email@foo.com>'},
- parse_mailmap(self.mailmap))
-
- def test_mailmap_with_firstname(self):
- with open(self.mailmap, 'w') as mm_fh:
- mm_fh.write("Foo <email@foo.com> Foo <email@bar.com>\n")
- self.assertEqual({'<email@bar.com>': '<email@foo.com>'},
- parse_mailmap(self.mailmap))
-
- def test_mailmap_with_noname(self):
- with open(self.mailmap, 'w') as mm_fh:
- mm_fh.write("<email@foo.com> <email@bar.com>\n")
- self.assertEqual({'<email@bar.com>': '<email@foo.com>'},
- parse_mailmap(self.mailmap))
-
- def tearDown(self):
- if os.path.exists(self.mailmap):
- os.remove(self.mailmap)
diff --git a/tests/test_singular_plural.py b/tests/test_singular_plural.py
index 40745b94..ea3ad27c 100644
--- a/tests/test_singular_plural.py
+++ b/tests/test_singular_plural.py
@@ -27,7 +27,7 @@ from keystone import service
class TestSingularPlural(object):
def test_keyword_arg_condition_or_methods(self):
- """Raise if we see a keyword arg called 'condition' or 'methods'"""
+ """Raise if we see a keyword arg called 'condition' or 'methods'."""
modules = [admin_crud_core, ec2_core, s3_core, stats_core,
user_crud_core, identity_core, service]
for module in modules:
diff --git a/tests/test_sql_upgrade.py b/tests/test_sql_upgrade.py
index 36d452af..51ac2b7d 100644
--- a/tests/test_sql_upgrade.py
+++ b/tests/test_sql_upgrade.py
@@ -703,12 +703,11 @@ class SqlUpgradeTests(test.TestCase):
user['domain_id'] = domain2['id']
cmd = this_table.insert().values(user)
self.engine.execute(cmd)
- # TODO(henry-nash). For now, as part of clean-up we
- # delete one of these users. Although not part of this test,
- # unless we do so the downgrade(16->15) that is part of
- # teardown with fail due to having two uses with clashing
- # name as we try to revert to a single global name space. This
- # limitation is raised as Bug #1125046 and the delete
+ # TODO(henry-nash): For now, as part of clean-up we delete one of these
+ # users. Although not part of this test, unless we do so the
+ # downgrade(16->15) that is part of teardown with fail due to having
+ # two uses with clashing name as we try to revert to a single global
+ # name space. This limitation is raised as Bug #1125046 and the delete
# could be removed depending on how that bug is resolved.
cmd = this_table.delete(id=user['id'])
self.engine.execute(cmd)
@@ -731,9 +730,9 @@ class SqlUpgradeTests(test.TestCase):
project['domain_id'] = domain2['id']
cmd = this_table.insert().values(project)
self.engine.execute(cmd)
- # TODO(henry-nash) For now, we delete one of the projects for
- # the same reason as we delete one of the users (Bug #1125046).
- # This delete could be removed depending on that bug resolution.
+ # TODO(henry-nash): For now, we delete one of the projects for the same
+ # reason as we delete one of the users (Bug #1125046). This delete
+ # could be removed depending on that bug resolution.
cmd = this_table.delete(id=project['id'])
self.engine.execute(cmd)
diff --git a/tests/test_ssl.py b/tests/test_ssl.py
index 1fd66091..da9e4c06 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -39,9 +39,7 @@ class SSLTestCase(test.TestCase):
self.load_backends()
def test_1way_ssl_ok(self):
- """
- Make sure both public and admin API work with 1-way SSL.
- """
+ """Make sure both public and admin API work with 1-way SSL."""
self.public_server = self.serveapp('keystone', name='main',
cert=CERT, key=KEY, ca=CA)
self.admin_server = self.serveapp('keystone', name='admin',
@@ -58,9 +56,9 @@ class SSLTestCase(test.TestCase):
self.assertEqual(resp.status, 300)
def test_2way_ssl_ok(self):
- """
- Make sure both public and admin API work with 2-way SSL. Requires
- client certificate.
+ """Make sure both public and admin API work with 2-way SSL.
+
+ Requires client certificate.
"""
self.public_server = self.serveapp(
'keystone', name='main', cert=CERT,
@@ -82,9 +80,7 @@ class SSLTestCase(test.TestCase):
self.assertEqual(resp.status, 300)
def test_1way_ssl_with_ipv6_ok(self):
- """
- Make sure both public and admin API work with 1-way ipv6 & SSL.
- """
+ """Make sure both public and admin API work with 1-way ipv6 & SSL."""
self.skip_if_no_ipv6()
self.public_server = self.serveapp('keystone', name='main',
cert=CERT, key=KEY, ca=CA,
@@ -104,8 +100,8 @@ class SSLTestCase(test.TestCase):
self.assertEqual(resp.status, 300)
def test_2way_ssl_with_ipv6_ok(self):
- """
- Make sure both public and admin API work with 2-way ipv6 & SSL.
+ """Make sure both public and admin API work with 2-way ipv6 & SSL.
+
Requires client certificate.
"""
self.skip_if_no_ipv6()
@@ -131,9 +127,7 @@ class SSLTestCase(test.TestCase):
self.assertEqual(resp.status, 300)
def test_2way_ssl_fail(self):
- """
- Expect to fail when client does not present proper certificate.
- """
+ """Expect to fail when client does not present proper certificate."""
self.public_server = self.serveapp(
'keystone', name='main', cert=CERT,
key=KEY, ca=CA, cert_required=True)
diff --git a/tests/test_v3.py b/tests/test_v3.py
index 38e88079..8373ae98 100644
--- a/tests/test_v3.py
+++ b/tests/test_v3.py
@@ -2,6 +2,7 @@ import datetime
import uuid
from lxml import etree
+import webtest
from keystone import auth
from keystone.common import serializer
@@ -38,6 +39,11 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
sql_util.setup_test_database()
self.load_backends()
+ self.public_app = webtest.TestApp(
+ self.loadapp('keystone', name='main'))
+ self.admin_app = webtest.TestApp(
+ self.loadapp('keystone', name='admin'))
+
if load_sample_data:
self.domain_id = uuid.uuid4().hex
self.domain = self.new_domain_ref()
@@ -204,8 +210,8 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
"""
r = super(RestfulTestCase, self).admin_request(*args, **kwargs)
- if r.getheader('Content-Type') == 'application/xml':
- r.body = serializer.from_xml(etree.tostring(r.body))
+ if r.headers.get('Content-Type') == 'application/xml':
+ r.result = serializer.from_xml(etree.tostring(r.result))
return r
def get_scoped_token(self):
@@ -234,7 +240,7 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
}
}
})
- return r.getheader('X-Subject-Token')
+ return r.headers.get('X-Subject-Token')
def get_requested_token(self, auth):
"""Request the specific token we want."""
@@ -243,7 +249,7 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
method='POST',
path='/v3/auth/tokens',
body=auth)
- return r.getheader('X-Subject-Token')
+ return r.headers.get('X-Subject-Token')
def v3_request(self, path, **kwargs):
# Check if the caller has passed in auth details for
@@ -296,15 +302,15 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
return r
def assertValidErrorResponse(self, r):
- if r.getheader('Content-Type') == 'application/xml':
- resp = serializer.from_xml(etree.tostring(r.body))
+ if r.headers.get('Content-Type') == 'application/xml':
+ resp = serializer.from_xml(etree.tostring(r.result))
else:
- resp = r.body
+ resp = r.result
self.assertIsNotNone(resp.get('error'))
self.assertIsNotNone(resp['error'].get('code'))
self.assertIsNotNone(resp['error'].get('title'))
self.assertIsNotNone(resp['error'].get('message'))
- self.assertEqual(int(resp['error']['code']), r.status)
+ self.assertEqual(int(resp['error']['code']), r.status_code)
def assertValidListLinks(self, links):
self.assertIsNotNone(links)
@@ -331,17 +337,17 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
response, and asserted to be equal.
"""
- entities = resp.body.get(key)
+ entities = resp.result.get(key)
self.assertIsNotNone(entities)
if expected_length is not None:
self.assertEqual(len(entities), expected_length)
elif ref is not None:
# we're at least expecting the ref
- self.assertTrue(len(entities))
+ self.assertNotEmpty(entities)
# collections should have relational links
- self.assertValidListLinks(resp.body.get('links'))
+ self.assertValidListLinks(resp.result.get('links'))
for entity in entities:
self.assertIsNotNone(entity)
@@ -356,7 +362,7 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
def assertValidResponse(self, resp, key, entity_validator, *args,
**kwargs):
"""Make assertions common to all API responses."""
- entity = resp.body.get(key)
+ entity = resp.result.get(key)
self.assertIsNotNone(entity)
self.assertValidEntity(entity, *args, **kwargs)
entity_validator(entity, *args, **kwargs)
@@ -397,8 +403,8 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
self.assertTrue(isinstance(dt, datetime.datetime))
def assertValidTokenResponse(self, r, user=None):
- self.assertTrue(r.getheader('X-Subject-Token'))
- token = r.body['token']
+ self.assertTrue(r.headers.get('X-Subject-Token'))
+ token = r.result['token']
self.assertIsNotNone(token.get('expires_at'))
expires_at = self.assertValidISO8601ExtendedFormatDatetime(
diff --git a/tests/test_v3_auth.py b/tests/test_v3_auth.py
index 997165ec..1ee3719d 100644
--- a/tests/test_v3_auth.py
+++ b/tests/test_v3_auth.py
@@ -17,8 +17,8 @@ import uuid
import nose.exc
-from keystone.common import cms
from keystone import auth
+from keystone.common import cms
from keystone import config
from keystone import exception
@@ -104,9 +104,9 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
user_domain_id=self.domain_id,
password=self.user['password'])
resp = self.post('/auth/tokens', body=auth_data)
- self.token_data = resp.body
- self.token = resp.getheader('X-Subject-Token')
- self.headers = {'X-Subject-Token': resp.getheader('X-Subject-Token')}
+ self.token_data = resp.result
+ self.token = resp.headers.get('X-Subject-Token')
+ self.headers = {'X-Subject-Token': resp.headers.get('X-Subject-Token')}
def test_default_fixture_scope_token(self):
self.assertIsNotNone(self.get_scoped_token())
@@ -117,8 +117,8 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
user_id=self.user['id'],
password=self.user['password'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token_id = resp.getheader('X-Subject-Token')
+ token_data = resp.result
+ token_id = resp.headers.get('X-Subject-Token')
self.assertIn('expires_at', token_data['token'])
token_signed = cms.cms_sign_token(json.dumps(token_data),
CONF.signing.certfile,
@@ -131,8 +131,7 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
user_id=self.user['id'],
password=self.user['password'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
@@ -152,8 +151,7 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
password=self.user['password'],
domain_id=self.domain['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
@@ -168,8 +166,7 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
password=self.default_domain_user['password'],
project_id=self.project['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
@@ -184,15 +181,15 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
user_id=self.default_domain_user['id'],
password=self.default_domain_user['password'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token_data = resp.result
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
resp = self.admin_request(path=path,
token='ADMIN',
method='GET')
- v2_token = resp.body
+ v2_token = resp.result
self.assertEqual(v2_token['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -206,15 +203,15 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
user_id=self.default_domain_user['id'],
password=self.default_domain_user['password'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token_data = resp.result
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
resp = self.admin_request(path=path,
token='ADMIN',
method='GET')
- v2_token = resp.body
+ v2_token = resp.result
self.assertEqual(v2_token['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -231,15 +228,15 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
password=self.default_domain_user['password'],
project_id=self.default_domain_project['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token_data = resp.result
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
resp = self.admin_request(path=path,
token='ADMIN',
method='GET')
- v2_token = resp.body
+ v2_token = resp.result
self.assertEqual(v2_token['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -258,15 +255,15 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
password=self.default_domain_user['password'],
project_id=self.default_domain_project['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token_data = resp.body
- token = resp.getheader('X-Subject-Token')
+ token_data = resp.result
+ token = resp.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
resp = self.admin_request(path=path,
token='ADMIN',
method='GET')
- v2_token = resp.body
+ v2_token = resp.result
self.assertEqual(v2_token['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -288,11 +285,11 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
resp = self.admin_request(path='/v2.0/tokens',
method='POST',
body=body)
- v2_token_data = resp.body
+ v2_token_data = resp.result
v2_token = v2_token_data['access']['token']['id']
headers = {'X-Subject-Token': v2_token}
resp = self.get('/auth/tokens', headers=headers)
- token_data = resp.body
+ token_data = resp.result
self.assertEqual(v2_token_data['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -312,11 +309,11 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
resp = self.admin_request(path='/v2.0/tokens',
method='POST',
body=body)
- v2_token_data = resp.body
+ v2_token_data = resp.result
v2_token = v2_token_data['access']['token']['id']
headers = {'X-Subject-Token': v2_token}
resp = self.get('/auth/tokens', headers=headers)
- token_data = resp.body
+ token_data = resp.result
self.assertEqual(v2_token_data['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -337,11 +334,11 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
resp = self.admin_request(path='/v2.0/tokens',
method='POST',
body=body)
- v2_token_data = resp.body
+ v2_token_data = resp.result
v2_token = v2_token_data['access']['token']['id']
headers = {'X-Subject-Token': v2_token}
resp = self.get('/auth/tokens', headers=headers)
- token_data = resp.body
+ token_data = resp.result
self.assertEqual(v2_token_data['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -364,11 +361,11 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
resp = self.admin_request(path='/v2.0/tokens',
method='POST',
body=body)
- v2_token_data = resp.body
+ v2_token_data = resp.result
v2_token = v2_token_data['access']['token']['id']
headers = {'X-Subject-Token': v2_token}
resp = self.get('/auth/tokens', headers=headers)
- token_data = resp.body
+ token_data = resp.result
self.assertEqual(v2_token_data['access']['user']['id'],
token_data['token']['user']['id'])
# v2 token time has not fraction of second precision so
@@ -386,7 +383,7 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectScopedTokenResponse(r)
# make sure expires stayed the same
- self.assertEqual(expires, r.body['token']['expires_at'])
+ self.assertEqual(expires, r.result['token']['expires_at'])
def test_check_token(self):
self.head('/auth/tokens', headers=self.headers, expected_status=204)
@@ -402,11 +399,11 @@ class TestTokenAPIs(test_v3.RestfulTestCase):
# make sure we have a CRL
r = self.get('/auth/tokens/OS-PKI/revoked')
- self.assertIn('signed', r.body)
+ self.assertIn('signed', r.result)
class TestTokenRevoking(test_v3.RestfulTestCase):
- """Test token revoking for relevant v3 identity apis"""
+ """Test token revocation on the v3 Identity API."""
def setUp(self):
"""Setup for Token Revoking Test Cases.
@@ -429,51 +426,41 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
# Start by creating a couple of domains and projects
self.domainA = self.new_domain_ref()
- domainA_ref = self.identity_api.create_domain(self.domainA['id'],
- self.domainA)
+ self.identity_api.create_domain(self.domainA['id'], self.domainA)
self.domainB = self.new_domain_ref()
- domainB_ref = self.identity_api.create_domain(self.domainB['id'],
- self.domainB)
+ self.identity_api.create_domain(self.domainB['id'], self.domainB)
self.projectA = self.new_project_ref(domain_id=self.domainA['id'])
- projectA_ref = self.identity_api.create_project(self.projectA['id'],
- self.projectA)
+ self.identity_api.create_project(self.projectA['id'], self.projectA)
self.projectB = self.new_project_ref(domain_id=self.domainA['id'])
- projectB_ref = self.identity_api.create_project(self.projectB['id'],
- self.projectB)
+ self.identity_api.create_project(self.projectB['id'], self.projectB)
# Now create some users, one in domainA and two of them in domainB
self.user1 = self.new_user_ref(
domain_id=self.domainA['id'])
self.user1['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user1['id'],
- self.user1)
+ self.identity_api.create_user(self.user1['id'], self.user1)
self.user2 = self.new_user_ref(
domain_id=self.domainB['id'])
self.user2['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user2['id'],
- self.user2)
+ self.identity_api.create_user(self.user2['id'], self.user2)
self.user3 = self.new_user_ref(
domain_id=self.domainB['id'])
self.user3['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user3['id'],
- self.user3)
+ self.identity_api.create_user(self.user3['id'], self.user3)
self.group1 = self.new_group_ref(
domain_id=self.domainA['id'])
- user_ref = self.identity_api.create_group(self.group1['id'],
- self.group1)
+ self.identity_api.create_group(self.group1['id'], self.group1)
self.group2 = self.new_group_ref(
domain_id=self.domainA['id'])
- user_ref = self.identity_api.create_group(self.group2['id'],
- self.group2)
+ self.identity_api.create_group(self.group2['id'], self.group2)
self.group3 = self.new_group_ref(
domain_id=self.domainB['id'])
- user_ref = self.identity_api.create_group(self.group3['id'],
- self.group3)
+ self.identity_api.create_group(self.group3['id'], self.group3)
self.identity_api.add_user_to_group(self.user1['id'],
self.group1['id'])
@@ -517,7 +504,7 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
password=self.user1['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# Confirm token is valid
self.head('/auth/tokens',
headers={'X-Subject-Token': token},
@@ -548,7 +535,7 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
password=self.user1['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# Confirm token is valid
self.head('/auth/tokens',
headers={'X-Subject-Token': token},
@@ -583,19 +570,19 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
password=self.user1['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token1 = resp.getheader('X-Subject-Token')
+ token1 = resp.headers.get('X-Subject-Token')
auth_data = self.build_authentication_request(
user_id=self.user2['id'],
password=self.user2['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token2 = resp.getheader('X-Subject-Token')
+ token2 = resp.headers.get('X-Subject-Token')
auth_data = self.build_authentication_request(
user_id=self.user3['id'],
password=self.user3['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token3 = resp.getheader('X-Subject-Token')
+ token3 = resp.headers.get('X-Subject-Token')
# Confirm tokens are valid
self.head('/auth/tokens',
headers={'X-Subject-Token': token1},
@@ -640,7 +627,7 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
password=self.user1['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# Confirm token is valid
self.head('/auth/tokens',
headers={'X-Subject-Token': token},
@@ -676,13 +663,13 @@ class TestTokenRevoking(test_v3.RestfulTestCase):
password=self.user1['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token1 = resp.getheader('X-Subject-Token')
+ token1 = resp.headers.get('X-Subject-Token')
auth_data = self.build_authentication_request(
user_id=self.user2['id'],
password=self.user2['password'],
project_id=self.projectA['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token2 = resp.getheader('X-Subject-Token')
+ token2 = resp.headers.get('X-Subject-Token')
# Confirm tokens are valid
self.head('/auth/tokens',
headers={'X-Subject-Token': token1},
@@ -771,7 +758,7 @@ class TestAuthJSON(test_v3.RestfulTestCase):
password=self.user['password'])
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectScopedTokenResponse(r)
- self.assertEqual(r.body['token']['project']['id'], project['id'])
+ self.assertEqual(r.result['token']['project']['id'], project['id'])
def test_default_project_id_scoped_token_with_user_id_401(self):
# create a second project to work with
@@ -950,8 +937,7 @@ class TestAuthJSON(test_v3.RestfulTestCase):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidUnscopedTokenResponse(r)
- token = r.getheader('X-Subject-Token')
- headers = {'X-Subject-Token': r.getheader('X-Subject-Token')}
+ token = r.headers.get('X-Subject-Token')
# test token auth
auth_data = self.build_authentication_request(token=token)
@@ -1195,14 +1181,12 @@ class TestTrustAuth(TestAuthInfo):
self.assertValidProjectTrustScopedTokenResponse(
r, self.default_domain_user)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
- resp = self.admin_request(path=path,
- token='ADMIN',
- method='GET',
- expected_status=401)
+ self.admin_request(
+ path=path, token='ADMIN', method='GET', expected_status=401)
def test_v3_v2_intermix_trustor_not_in_default_domaini_failed(self):
ref = self.new_trust_ref(
@@ -1219,7 +1203,7 @@ class TestTrustAuth(TestAuthInfo):
password=self.default_domain_user['password'],
project_id=self.default_domain_project_id)
r = self.post('/auth/tokens', body=auth_data)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
r = self.post('/OS-TRUST/trusts', body={'trust': ref}, token=token)
trust = self.assertValidTrustResponse(r)
@@ -1231,14 +1215,12 @@ class TestTrustAuth(TestAuthInfo):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(
r, self.trustee_user)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
- resp = self.admin_request(path=path,
- token='ADMIN',
- method='GET',
- expected_status=401)
+ self.admin_request(
+ path=path, token='ADMIN', method='GET', expected_status=401)
def test_v3_v2_intermix_project_not_in_default_domaini_failed(self):
# create a trustee in default domain to delegate stuff to
@@ -1261,7 +1243,7 @@ class TestTrustAuth(TestAuthInfo):
password=self.default_domain_user['password'],
project_id=self.default_domain_project_id)
r = self.post('/auth/tokens', body=auth_data)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
r = self.post('/OS-TRUST/trusts', body={'trust': ref}, token=token)
trust = self.assertValidTrustResponse(r)
@@ -1273,14 +1255,12 @@ class TestTrustAuth(TestAuthInfo):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(
r, trustee_user)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
- resp = self.admin_request(path=path,
- token='ADMIN',
- method='GET',
- expected_status=401)
+ self.admin_request(
+ path=path, token='ADMIN', method='GET', expected_status=401)
def test_v3_v2_intermix(self):
# create a trustee in default domain to delegate stuff to
@@ -1302,7 +1282,7 @@ class TestTrustAuth(TestAuthInfo):
password=self.default_domain_user['password'],
project_id=self.default_domain_project_id)
r = self.post('/auth/tokens', body=auth_data)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
r = self.post('/OS-TRUST/trusts', body={'trust': ref}, token=token)
trust = self.assertValidTrustResponse(r)
@@ -1314,14 +1294,12 @@ class TestTrustAuth(TestAuthInfo):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(
r, trustee_user)
- token = r.getheader('X-Subject-Token')
+ token = r.headers.get('X-Subject-Token')
# now validate the v3 token with v2 API
path = '/v2.0/tokens/%s' % (token)
- resp = self.admin_request(path=path,
- token='ADMIN',
- method='GET',
- expected_status=200)
+ self.admin_request(
+ path=path, token='ADMIN', method='GET', expected_status=200)
def test_exercise_trust_scoped_token_without_impersonation(self):
ref = self.new_trust_ref(
@@ -1342,16 +1320,17 @@ class TestTrustAuth(TestAuthInfo):
trust_id=trust['id'])
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(r, self.trustee_user)
- self.assertEqual(r.body['token']['user']['id'],
+ self.assertEqual(r.result['token']['user']['id'],
self.trustee_user['id'])
- self.assertEqual(r.body['token']['user']['name'],
+ self.assertEqual(r.result['token']['user']['name'],
self.trustee_user['name'])
- self.assertEqual(r.body['token']['user']['domain']['id'],
+ self.assertEqual(r.result['token']['user']['domain']['id'],
self.domain['id'])
- self.assertEqual(r.body['token']['user']['domain']['name'],
+ self.assertEqual(r.result['token']['user']['domain']['name'],
self.domain['name'])
- self.assertEqual(r.body['token']['project']['id'], self.project['id'])
- self.assertEqual(r.body['token']['project']['name'],
+ self.assertEqual(r.result['token']['project']['id'],
+ self.project['id'])
+ self.assertEqual(r.result['token']['project']['name'],
self.project['name'])
def test_exercise_trust_scoped_token_with_impersonation(self):
@@ -1373,14 +1352,15 @@ class TestTrustAuth(TestAuthInfo):
trust_id=trust['id'])
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(r, self.user)
- self.assertEqual(r.body['token']['user']['id'], self.user['id'])
- self.assertEqual(r.body['token']['user']['name'], self.user['name'])
- self.assertEqual(r.body['token']['user']['domain']['id'],
+ self.assertEqual(r.result['token']['user']['id'], self.user['id'])
+ self.assertEqual(r.result['token']['user']['name'], self.user['name'])
+ self.assertEqual(r.result['token']['user']['domain']['id'],
self.domain['id'])
- self.assertEqual(r.body['token']['user']['domain']['name'],
+ self.assertEqual(r.result['token']['user']['domain']['name'],
self.domain['name'])
- self.assertEqual(r.body['token']['project']['id'], self.project['id'])
- self.assertEqual(r.body['token']['project']['name'],
+ self.assertEqual(r.result['token']['project']['id'],
+ self.project['id'])
+ self.assertEqual(r.result['token']['project']['name'],
self.project['name'])
def test_delete_trust(self):
@@ -1427,16 +1407,16 @@ class TestTrustAuth(TestAuthInfo):
for i in range(0, 3):
r = self.post('/OS-TRUST/trusts', body={'trust': ref})
- trust = self.assertValidTrustResponse(r, ref)
+ self.assertValidTrustResponse(r, ref)
r = self.get('/OS-TRUST/trusts?trustor_user_id=%s' %
self.user_id, expected_status=200)
- trusts = r.body['trusts']
+ trusts = r.result['trusts']
self.assertEqual(len(trusts), 3)
r = self.get('/OS-TRUST/trusts?trustee_user_id=%s' %
self.user_id, expected_status=200)
- trusts = r.body['trusts']
+ trusts = r.result['trusts']
self.assertEqual(len(trusts), 0)
def test_change_password_invalidates_trust_tokens(self):
@@ -1459,7 +1439,7 @@ class TestTrustAuth(TestAuthInfo):
r = self.post('/auth/tokens', body=auth_data)
self.assertValidProjectTrustScopedTokenResponse(r, self.user)
- trust_token = r.getheader('X-Subject-Token')
+ trust_token = r.headers.get('X-Subject-Token')
self.get('/OS-TRUST/trusts?trustor_user_id=%s' %
self.user_id, expected_status=200,
diff --git a/tests/test_v3_catalog.py b/tests/test_v3_catalog.py
index e81308fc..408670ec 100644
--- a/tests/test_v3_catalog.py
+++ b/tests/test_v3_catalog.py
@@ -4,7 +4,7 @@ import test_v3
class CatalogTestCase(test_v3.RestfulTestCase):
- """Test service & endpoint CRUD"""
+ """Test service & endpoint CRUD."""
def setUp(self):
super(CatalogTestCase, self).setUp()
@@ -26,7 +26,7 @@ class CatalogTestCase(test_v3.RestfulTestCase):
# service crud tests
def test_create_service(self):
- """POST /services"""
+ """Call ``POST /services``."""
ref = self.new_service_ref()
r = self.post(
'/services',
@@ -34,23 +34,23 @@ class CatalogTestCase(test_v3.RestfulTestCase):
return self.assertValidServiceResponse(r, ref)
def test_list_services(self):
- """GET /services"""
+ """Call ``GET /services``."""
r = self.get('/services')
self.assertValidServiceListResponse(r, ref=self.service)
def test_list_services_xml(self):
- """GET /services (xml data)"""
+ """Call ``GET /services (xml data)``."""
r = self.get('/services', content_type='xml')
self.assertValidServiceListResponse(r, ref=self.service)
def test_get_service(self):
- """GET /services/{service_id}"""
+ """Call ``GET /services/{service_id}``."""
r = self.get('/services/%(service_id)s' % {
'service_id': self.service_id})
self.assertValidServiceResponse(r, self.service)
def test_update_service(self):
- """PATCH /services/{service_id}"""
+ """Call ``PATCH /services/{service_id}``."""
service = self.new_service_ref()
del service['id']
r = self.patch('/services/%(service_id)s' % {
@@ -59,24 +59,24 @@ class CatalogTestCase(test_v3.RestfulTestCase):
self.assertValidServiceResponse(r, service)
def test_delete_service(self):
- """DELETE /services/{service_id}"""
+ """Call ``DELETE /services/{service_id}``."""
self.delete('/services/%(service_id)s' % {
'service_id': self.service_id})
# endpoint crud tests
def test_list_endpoints(self):
- """GET /endpoints"""
+ """Call ``GET /endpoints``."""
r = self.get('/endpoints')
self.assertValidEndpointListResponse(r, ref=self.endpoint)
def test_list_endpoints_xml(self):
- """GET /endpoints (xml data)"""
+ """Call ``GET /endpoints`` (xml data)."""
r = self.get('/endpoints', content_type='xml')
self.assertValidEndpointListResponse(r, ref=self.endpoint)
def test_create_endpoint(self):
- """POST /endpoints"""
+ """Call ``POST /endpoints``."""
ref = self.new_endpoint_ref(service_id=self.service_id)
r = self.post(
'/endpoints',
@@ -84,23 +84,23 @@ class CatalogTestCase(test_v3.RestfulTestCase):
self.assertValidEndpointResponse(r, ref)
def assertValidErrorResponse(self, response):
- self.assertTrue(response.status in [400])
+ self.assertTrue(response.status_code in [400])
def test_create_endpoint_400(self):
- """POST /endpoints"""
+ """Call ``POST /endpoints``."""
ref = self.new_endpoint_ref(service_id=self.service_id)
ref["region"] = "0" * 256
self.post('/endpoints', body={'endpoint': ref}, expected_status=400)
def test_get_endpoint(self):
- """GET /endpoints/{endpoint_id}"""
+ """Call ``GET /endpoints/{endpoint_id}``."""
r = self.get(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id})
self.assertValidEndpointResponse(r, self.endpoint)
def test_update_endpoint(self):
- """PATCH /endpoints/{endpoint_id}"""
+ """Call ``PATCH /endpoints/{endpoint_id}``."""
ref = self.new_endpoint_ref(service_id=self.service_id)
del ref['id']
r = self.patch(
@@ -110,7 +110,7 @@ class CatalogTestCase(test_v3.RestfulTestCase):
self.assertValidEndpointResponse(r, ref)
def test_delete_endpoint(self):
- """DELETE /endpoints/{endpoint_id}"""
+ """Call ``DELETE /endpoints/{endpoint_id}``."""
self.delete(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id})
@@ -135,7 +135,7 @@ class CatalogTestCase(test_v3.RestfulTestCase):
path='/v2.0/endpoints',
token=self.get_scoped_token(),
body={'endpoint': ref})
- endpoint_v2 = r.body['endpoint']
+ endpoint_v2 = r.result['endpoint']
# test the endpoint on v3
r = self.get('/endpoints')
diff --git a/tests/test_v3_credential.py b/tests/test_v3_credential.py
new file mode 100644
index 00000000..6040cca3
--- /dev/null
+++ b/tests/test_v3_credential.py
@@ -0,0 +1,78 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+import test_v3
+
+
+class CredentialTestCase(test_v3.RestfulTestCase):
+ """Test credential CRUD."""
+ def setUp(self):
+
+ super(CredentialTestCase, self).setUp()
+
+ self.credential_id = uuid.uuid4().hex
+ self.credential = self.new_credential_ref(
+ user_id=self.user['id'],
+ project_id=self.project_id)
+ self.credential['id'] = self.credential_id
+ self.credential_api.create_credential(
+ self.credential_id,
+ self.credential)
+
+ def test_list_credentials(self):
+ """Call ``GET /credentials``."""
+ r = self.get('/credentials')
+ self.assertValidCredentialListResponse(r, ref=self.credential)
+
+ def test_list_credentials_xml(self):
+ """Call ``GET /credentials`` (xml data)."""
+ r = self.get('/credentials', content_type='xml')
+ self.assertValidCredentialListResponse(r, ref=self.credential)
+
+ def test_create_credential(self):
+ """Call ``POST /credentials``."""
+ ref = self.new_credential_ref(user_id=self.user['id'])
+ r = self.post(
+ '/credentials',
+ body={'credential': ref})
+ self.assertValidCredentialResponse(r, ref)
+
+ def test_get_credential(self):
+ """Call ``GET /credentials/{credential_id}``."""
+ r = self.get(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id})
+ self.assertValidCredentialResponse(r, self.credential)
+
+ def test_update_credential(self):
+ """Call ``PATCH /credentials/{credential_id}``."""
+ ref = self.new_credential_ref(
+ user_id=self.user['id'],
+ project_id=self.project_id)
+ del ref['id']
+ r = self.patch(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id},
+ body={'credential': ref})
+ self.assertValidCredentialResponse(r, ref)
+
+ def test_delete_credential(self):
+ """Call ``DELETE /credentials/{credential_id}``."""
+ self.delete(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id})
diff --git a/tests/test_v3_identity.py b/tests/test_v3_identity.py
index d212857b..1e10070e 100644
--- a/tests/test_v3_identity.py
+++ b/tests/test_v3_identity.py
@@ -22,7 +22,7 @@ import test_v3
class IdentityTestCase(test_v3.RestfulTestCase):
- """Test domains, projects, users, groups, credential & role CRUD"""
+ """Test domains, projects, users, groups, & role CRUD."""
def setUp(self):
super(IdentityTestCase, self).setUp()
@@ -38,14 +38,14 @@ class IdentityTestCase(test_v3.RestfulTestCase):
user_id=self.user['id'],
project_id=self.project_id)
self.credential['id'] = self.credential_id
- self.identity_api.create_credential(
+ self.credential_api.create_credential(
self.credential_id,
self.credential)
# domain crud tests
def test_create_domain(self):
- """POST /domains"""
+ """Call ``POST /domains``."""
ref = self.new_domain_ref()
r = self.post(
'/domains',
@@ -53,23 +53,23 @@ class IdentityTestCase(test_v3.RestfulTestCase):
return self.assertValidDomainResponse(r, ref)
def test_list_domains(self):
- """GET /domains"""
+ """Call ``GET /domains``."""
r = self.get('/domains')
self.assertValidDomainListResponse(r, ref=self.domain)
def test_list_domains_xml(self):
- """GET /domains (xml data)"""
+ """Call ``GET /domains (xml data)``."""
r = self.get('/domains', content_type='xml')
self.assertValidDomainListResponse(r, ref=self.domain)
def test_get_domain(self):
- """GET /domains/{domain_id}"""
+ """Call ``GET /domains/{domain_id}``."""
r = self.get('/domains/%(domain_id)s' % {
'domain_id': self.domain_id})
self.assertValidDomainResponse(r, self.domain)
def test_update_domain(self):
- """PATCH /domains/{domain_id}"""
+ """Call ``PATCH /domains/{domain_id}``."""
ref = self.new_domain_ref()
del ref['id']
r = self.patch('/domains/%(domain_id)s' % {
@@ -78,7 +78,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidDomainResponse(r, ref)
def test_disable_domain(self):
- """PATCH /domains/{domain_id} (set enabled=False)"""
+ """Call ``PATCH /domains/{domain_id}`` (set enabled=False)."""
# Create a 2nd set of entities in a 2nd domain
self.domain2 = self.new_domain_ref()
self.identity_api.create_domain(self.domain2['id'], self.domain2)
@@ -106,15 +106,13 @@ class IdentityTestCase(test_v3.RestfulTestCase):
'tenantId': self.project2['id']
}
}
- resp = self.admin_request(path='/v2.0/tokens',
- method='POST',
- body=body)
+ self.admin_request(path='/v2.0/tokens', method='POST', body=body)
auth_data = self.build_authentication_request(
user_id=self.user2['id'],
password=self.user2['password'],
project_id=self.project2['id'])
- resp = self.post('/auth/tokens', body=auth_data)
+ self.post('/auth/tokens', body=auth_data)
# Now disable the domain
self.domain2['enabled'] = False
@@ -134,29 +132,25 @@ class IdentityTestCase(test_v3.RestfulTestCase):
'tenantId': self.project2['id']
}
}
- resp = self.admin_request(path='/v2.0/tokens',
- method='POST',
- body=body,
- expected_status=401)
+ self.admin_request(
+ path='/v2.0/tokens', method='POST', body=body, expected_status=401)
# Try looking up in v3 by name and id
auth_data = self.build_authentication_request(
user_id=self.user2['id'],
password=self.user2['password'],
project_id=self.project2['id'])
- resp = self.post('/auth/tokens', body=auth_data,
- expected_status=401)
+ self.post('/auth/tokens', body=auth_data, expected_status=401)
auth_data = self.build_authentication_request(
username=self.user2['name'],
user_domain_id=self.domain2['id'],
password=self.user2['password'],
project_id=self.project2['id'])
- resp = self.post('/auth/tokens', body=auth_data,
- expected_status=401)
+ self.post('/auth/tokens', body=auth_data, expected_status=401)
def test_delete_enabled_domain_fails(self):
- """DELETE /domains/{domain_id}...(when domain enabled)"""
+ """Call ``DELETE /domains/{domain_id}`` (when domain enabled)."""
# Try deleting an enabled domain, which should fail
self.delete('/domains/%(domain_id)s' % {
@@ -164,7 +158,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
expected_status=exception.ForbiddenAction.code)
def test_delete_domain(self):
- """DELETE /domains/{domain_id}
+ """Call ``DELETE /domains/{domain_id}``.
The sample data set up already has a user, group, project
and credential that is part of self.domain. Since the user
@@ -182,6 +176,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
- Check entities in self.domain are unaffected
"""
+
# Create a 2nd set of entities in a 2nd domain
self.domain2 = self.new_domain_ref()
self.identity_api.create_domain(self.domain2['id'], self.domain2)
@@ -202,7 +197,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.credential2 = self.new_credential_ref(
user_id=self.user2['id'],
project_id=self.project2['id'])
- self.identity_api.create_credential(
+ self.credential_api.create_credential(
self.credential2['id'],
self.credential2)
@@ -229,7 +224,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.identity_api.get_user,
user_id=self.user2['id'])
self.assertRaises(exception.CredentialNotFound,
- self.identity_api.get_credential,
+ self.credential_api.get_credential,
credential_id=self.credential2['id'])
# ...and that all self.domain entities are still here
@@ -242,23 +237,23 @@ class IdentityTestCase(test_v3.RestfulTestCase):
r = self.identity_api.get_user(self.user['id'])
self.user.pop('password')
self.assertDictEqual(r, self.user)
- r = self.identity_api.get_credential(self.credential['id'])
+ r = self.credential_api.get_credential(self.credential['id'])
self.assertDictEqual(r, self.credential)
# project crud tests
def test_list_projects(self):
- """GET /projects"""
+ """Call ``GET /projects``."""
r = self.get('/projects')
self.assertValidProjectListResponse(r, ref=self.project)
def test_list_projects_xml(self):
- """GET /projects (xml data)"""
+ """Call ``GET /projects`` (xml data)."""
r = self.get('/projects', content_type='xml')
self.assertValidProjectListResponse(r, ref=self.project)
def test_create_project(self):
- """POST /projects"""
+ """Call ``POST /projects``."""
ref = self.new_project_ref(domain_id=self.domain_id)
r = self.post(
'/projects',
@@ -266,14 +261,14 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidProjectResponse(r, ref)
def test_get_project(self):
- """GET /projects/{project_id}"""
+ """Call ``GET /projects/{project_id}``."""
r = self.get(
'/projects/%(project_id)s' % {
'project_id': self.project_id})
self.assertValidProjectResponse(r, self.project)
def test_update_project(self):
- """PATCH /projects/{project_id}"""
+ """Call ``PATCH /projects/{project_id}``."""
ref = self.new_project_ref(domain_id=self.domain_id)
del ref['id']
r = self.patch(
@@ -283,7 +278,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidProjectResponse(r, ref)
def test_delete_project(self):
- """DELETE /projects/{project_id}
+ """Call ``DELETE /projects/{project_id}
As well as making sure the delete succeeds, we ensure
that any credentials that reference this projects are
@@ -291,7 +286,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
"""
# First check the credential for this project is present
- r = self.identity_api.get_credential(self.credential['id'])
+ r = self.credential_api.get_credential(self.credential['id'])
self.assertDictEqual(r, self.credential)
# Create a second credential with a different project
self.project2 = self.new_project_ref(
@@ -300,7 +295,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.credential2 = self.new_credential_ref(
user_id=self.user['id'],
project_id=self.project2['id'])
- self.identity_api.create_credential(
+ self.credential_api.create_credential(
self.credential2['id'],
self.credential2)
@@ -312,16 +307,16 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# Deleting the project should have deleted any credentials
# that reference this project
self.assertRaises(exception.CredentialNotFound,
- self.identity_api.get_credential,
+ self.credential_api.get_credential,
credential_id=self.credential['id'])
# But the credential for project2 is unaffected
- r = self.identity_api.get_credential(self.credential2['id'])
+ r = self.credential_api.get_credential(self.credential2['id'])
self.assertDictEqual(r, self.credential2)
# user crud tests
def test_create_user(self):
- """POST /users"""
+ """Call ``POST /users``."""
ref = self.new_user_ref(domain_id=self.domain_id)
r = self.post(
'/users',
@@ -329,28 +324,28 @@ class IdentityTestCase(test_v3.RestfulTestCase):
return self.assertValidUserResponse(r, ref)
def test_list_users(self):
- """GET /users"""
+ """Call ``GET /users``."""
r = self.get('/users')
self.assertValidUserListResponse(r, ref=self.user)
def test_list_users_xml(self):
- """GET /users (xml data)"""
+ """Call ``GET /users`` (xml data)."""
r = self.get('/users', content_type='xml')
self.assertValidUserListResponse(r, ref=self.user)
def test_get_user(self):
- """GET /users/{user_id}"""
+ """Call ``GET /users/{user_id}``."""
r = self.get('/users/%(user_id)s' % {
'user_id': self.user['id']})
self.assertValidUserResponse(r, self.user)
def test_add_user_to_group(self):
- """PUT /groups/{group_id}/users/{user_id}"""
+ """Call ``PUT /groups/{group_id}/users/{user_id}``."""
self.put('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
def test_list_groups_for_user(self):
- """GET /users/{user_id}/groups"""
+ """Call ``GET /users/{user_id}/groups``."""
self.user1 = self.new_user_ref(
domain_id=self.domain['id'])
@@ -387,31 +382,31 @@ class IdentityTestCase(test_v3.RestfulTestCase):
expected_status=exception.ForbiddenAction.code)
def test_check_user_in_group(self):
- """HEAD /groups/{group_id}/users/{user_id}"""
+ """Call ``HEAD /groups/{group_id}/users/{user_id}``."""
self.put('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
self.head('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
def test_list_users_in_group(self):
- """GET /groups/{group_id}/users"""
+ """Call ``GET /groups/{group_id}/users``."""
r = self.put('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
r = self.get('/groups/%(group_id)s/users' % {
'group_id': self.group_id})
self.assertValidUserListResponse(r, ref=self.user)
self.assertIn('/groups/%(group_id)s/users' % {
- 'group_id': self.group_id}, r.body['links']['self'])
+ 'group_id': self.group_id}, r.result['links']['self'])
def test_remove_user_from_group(self):
- """DELETE /groups/{group_id}/users/{user_id}"""
+ """Call ``DELETE /groups/{group_id}/users/{user_id}``."""
self.put('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
self.delete('/groups/%(group_id)s/users/%(user_id)s' % {
'group_id': self.group_id, 'user_id': self.user['id']})
def test_update_user(self):
- """PATCH /users/{user_id}"""
+ """Call ``PATCH /users/{user_id}``."""
user = self.new_user_ref(domain_id=self.domain_id)
del user['id']
r = self.patch('/users/%(user_id)s' % {
@@ -420,7 +415,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidUserResponse(r, user)
def test_delete_user(self):
- """DELETE /users/{user_id}
+ """Call ``DELETE /users/{user_id}``.
As well as making sure the delete succeeds, we ensure
that any credentials that reference this user are
@@ -429,7 +424,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
"""
# First check the credential for this user is present
- r = self.identity_api.get_credential(self.credential['id'])
+ r = self.credential_api.get_credential(self.credential['id'])
self.assertDictEqual(r, self.credential)
# Create a second credential with a different user
self.user2 = self.new_user_ref(
@@ -439,7 +434,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.credential2 = self.new_credential_ref(
user_id=self.user2['id'],
project_id=self.project['id'])
- self.identity_api.create_credential(
+ self.credential_api.create_credential(
self.credential2['id'],
self.credential2)
# Create a token for this user which we can check later
@@ -449,7 +444,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
password=self.user['password'],
project_id=self.project['id'])
resp = self.post('/auth/tokens', body=auth_data)
- token = resp.getheader('X-Subject-Token')
+ token = resp.headers.get('X-Subject-Token')
# Confirm token is valid for now
self.head('/auth/tokens',
headers={'X-Subject-Token': token},
@@ -462,19 +457,19 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# Deleting the user should have deleted any credentials
# that reference this project
self.assertRaises(exception.CredentialNotFound,
- self.identity_api.get_credential,
+ self.credential_api.get_credential,
credential_id=self.credential['id'])
# And the no tokens we remain valid
tokens = self.token_api.list_tokens(self.user['id'])
self.assertEquals(len(tokens), 0)
# But the credential for user2 is unaffected
- r = self.identity_api.get_credential(self.credential2['id'])
+ r = self.credential_api.get_credential(self.credential2['id'])
self.assertDictEqual(r, self.credential2)
# group crud tests
def test_create_group(self):
- """POST /groups"""
+ """Call ``POST /groups``."""
ref = self.new_group_ref(domain_id=self.domain_id)
r = self.post(
'/groups',
@@ -482,23 +477,23 @@ class IdentityTestCase(test_v3.RestfulTestCase):
return self.assertValidGroupResponse(r, ref)
def test_list_groups(self):
- """GET /groups"""
+ """Call ``GET /groups``."""
r = self.get('/groups')
self.assertValidGroupListResponse(r, ref=self.group)
def test_list_groups_xml(self):
- """GET /groups (xml data)"""
+ """Call ``GET /groups`` (xml data)."""
r = self.get('/groups', content_type='xml')
self.assertValidGroupListResponse(r, ref=self.group)
def test_get_group(self):
- """GET /groups/{group_id}"""
+ """Call ``GET /groups/{group_id}``."""
r = self.get('/groups/%(group_id)s' % {
'group_id': self.group_id})
self.assertValidGroupResponse(r, self.group)
def test_update_group(self):
- """PATCH /groups/{group_id}"""
+ """Call ``PATCH /groups/{group_id}``."""
group = self.new_group_ref(domain_id=self.domain_id)
del group['id']
r = self.patch('/groups/%(group_id)s' % {
@@ -507,59 +502,14 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidGroupResponse(r, group)
def test_delete_group(self):
- """DELETE /groups/{group_id}"""
+ """Call ``DELETE /groups/{group_id}``."""
self.delete('/groups/%(group_id)s' % {
'group_id': self.group_id})
- # credential crud tests
-
- def test_list_credentials(self):
- """GET /credentials"""
- r = self.get('/credentials')
- self.assertValidCredentialListResponse(r, ref=self.credential)
-
- def test_list_credentials_xml(self):
- """GET /credentials (xml data)"""
- r = self.get('/credentials', content_type='xml')
- self.assertValidCredentialListResponse(r, ref=self.credential)
-
- def test_create_credential(self):
- """POST /credentials"""
- ref = self.new_credential_ref(user_id=self.user['id'])
- r = self.post(
- '/credentials',
- body={'credential': ref})
- self.assertValidCredentialResponse(r, ref)
-
- def test_get_credential(self):
- """GET /credentials/{credential_id}"""
- r = self.get(
- '/credentials/%(credential_id)s' % {
- 'credential_id': self.credential_id})
- self.assertValidCredentialResponse(r, self.credential)
-
- def test_update_credential(self):
- """PATCH /credentials/{credential_id}"""
- ref = self.new_credential_ref(
- user_id=self.user['id'],
- project_id=self.project_id)
- del ref['id']
- r = self.patch(
- '/credentials/%(credential_id)s' % {
- 'credential_id': self.credential_id},
- body={'credential': ref})
- self.assertValidCredentialResponse(r, ref)
-
- def test_delete_credential(self):
- """DELETE /credentials/{credential_id}"""
- self.delete(
- '/credentials/%(credential_id)s' % {
- 'credential_id': self.credential_id})
-
# role crud tests
def test_create_role(self):
- """POST /roles"""
+ """Call ``POST /roles``."""
ref = self.new_role_ref()
r = self.post(
'/roles',
@@ -567,23 +517,23 @@ class IdentityTestCase(test_v3.RestfulTestCase):
return self.assertValidRoleResponse(r, ref)
def test_list_roles(self):
- """GET /roles"""
+ """Call ``GET /roles``."""
r = self.get('/roles')
self.assertValidRoleListResponse(r, ref=self.role)
def test_list_roles_xml(self):
- """GET /roles (xml data)"""
+ """Call ``GET /roles`` (xml data)."""
r = self.get('/roles', content_type='xml')
self.assertValidRoleListResponse(r, ref=self.role)
def test_get_role(self):
- """GET /roles/{role_id}"""
+ """Call ``GET /roles/{role_id}``."""
r = self.get('/roles/%(role_id)s' % {
'role_id': self.role_id})
self.assertValidRoleResponse(r, self.role)
def test_update_role(self):
- """PATCH /roles/{role_id}"""
+ """Call ``PATCH /roles/{role_id}``."""
ref = self.new_role_ref()
del ref['id']
r = self.patch('/roles/%(role_id)s' % {
@@ -592,7 +542,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertValidRoleResponse(r, ref)
def test_delete_role(self):
- """DELETE /roles/{role_id}"""
+ """Call ``DELETE /roles/{role_id}``."""
self.delete('/roles/%(role_id)s' % {
'role_id': self.role_id})
@@ -609,14 +559,14 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
# FIXME(gyee): this test is no longer valid as user
# have no role in the project. Can't get a scoped token
#self.delete(member_url)
#r = self.get(collection_url)
#self.assertValidRoleListResponse(r, expected_length=0)
- #self.assertIn(collection_url, r.body['links']['self'])
+ #self.assertIn(collection_url, r.result['links']['self'])
def test_crud_user_domain_role_grants(self):
collection_url = (
@@ -631,12 +581,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
self.delete(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
def test_crud_group_project_role_grants(self):
collection_url = (
@@ -651,12 +601,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
self.delete(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
def test_crud_group_domain_role_grants(self):
collection_url = (
@@ -671,9 +621,9 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
self.delete(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0)
- self.assertIn(collection_url, r.body['links']['self'])
+ self.assertIn(collection_url, r.result['links']['self'])
diff --git a/tests/test_v3_policy.py b/tests/test_v3_policy.py
index e8855ea3..d988efd2 100644
--- a/tests/test_v3_policy.py
+++ b/tests/test_v3_policy.py
@@ -4,7 +4,7 @@ import test_v3
class PolicyTestCase(test_v3.RestfulTestCase):
- """Test policy CRUD"""
+ """Test policy CRUD."""
def setUp(self):
super(PolicyTestCase, self).setUp()
@@ -18,7 +18,7 @@ class PolicyTestCase(test_v3.RestfulTestCase):
# policy crud tests
def test_create_policy(self):
- """POST /policies"""
+ """Call ``POST /policies``."""
ref = self.new_policy_ref()
r = self.post(
'/policies',
@@ -26,24 +26,24 @@ class PolicyTestCase(test_v3.RestfulTestCase):
return self.assertValidPolicyResponse(r, ref)
def test_list_policies(self):
- """GET /policies"""
+ """Call ``GET /policies``."""
r = self.get('/policies')
self.assertValidPolicyListResponse(r, ref=self.policy)
def test_list_policies_xml(self):
- """GET /policies (xml data)"""
+ """Call ``GET /policies (xml data)``."""
r = self.get('/policies', content_type='xml')
self.assertValidPolicyListResponse(r, ref=self.policy)
def test_get_policy(self):
- """GET /policies/{policy_id}"""
+ """Call ``GET /policies/{policy_id}``."""
r = self.get(
'/policies/%(policy_id)s' % {
'policy_id': self.policy_id})
self.assertValidPolicyResponse(r, self.policy)
def test_update_policy(self):
- """PATCH /policies/{policy_id}"""
+ """Call ``PATCH /policies/{policy_id}``."""
policy = self.new_policy_ref()
policy['id'] = self.policy_id
r = self.patch(
@@ -53,7 +53,7 @@ class PolicyTestCase(test_v3.RestfulTestCase):
self.assertValidPolicyResponse(r, policy)
def test_delete_policy(self):
- """DELETE /policies/{policy_id}"""
+ """Call ``DELETE /policies/{policy_id}``."""
self.delete(
'/policies/%(policy_id)s' % {
'policy_id': self.policy_id})
diff --git a/tests/test_v3_protection.py b/tests/test_v3_protection.py
index 0bf26eb8..38e32813 100644
--- a/tests/test_v3_protection.py
+++ b/tests/test_v3_protection.py
@@ -31,7 +31,7 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
class IdentityTestProtectedCase(test_v3.RestfulTestCase):
- """Test policy protection of a sample of v3 identity apis"""
+ """Test policy enforcement on the v3 Identity API."""
def setUp(self):
"""Setup for Identity Protection Test Cases.
@@ -54,49 +54,34 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
super(IdentityTestProtectedCase, self).setUp(load_sample_data=False)
# Start by creating a couple of domains
self.domainA = self.new_domain_ref()
- domainA_ref = self.identity_api.create_domain(self.domainA['id'],
- self.domainA)
+ self.identity_api.create_domain(self.domainA['id'], self.domainA)
self.domainB = self.new_domain_ref()
- domainB_ref = self.identity_api.create_domain(self.domainB['id'],
- self.domainB)
+ self.identity_api.create_domain(self.domainB['id'], self.domainB)
self.domainC = self.new_domain_ref()
self.domainC['enabled'] = False
- domainC_ref = self.identity_api.create_domain(self.domainC['id'],
- self.domainC)
+ self.identity_api.create_domain(self.domainC['id'], self.domainC)
# Now create some users, one in domainA and two of them in domainB
- self.user1 = self.new_user_ref(
- domain_id=self.domainA['id'])
+ self.user1 = self.new_user_ref(domain_id=self.domainA['id'])
self.user1['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user1['id'],
- self.user1)
+ self.identity_api.create_user(self.user1['id'], self.user1)
- self.user2 = self.new_user_ref(
- domain_id=self.domainB['id'])
+ self.user2 = self.new_user_ref(domain_id=self.domainB['id'])
self.user2['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user2['id'],
- self.user2)
+ self.identity_api.create_user(self.user2['id'], self.user2)
- self.user3 = self.new_user_ref(
- domain_id=self.domainB['id'])
+ self.user3 = self.new_user_ref(domain_id=self.domainB['id'])
self.user3['password'] = uuid.uuid4().hex
- user_ref = self.identity_api.create_user(self.user3['id'],
- self.user3)
+ self.identity_api.create_user(self.user3['id'], self.user3)
- self.group1 = self.new_group_ref(
- domain_id=self.domainA['id'])
- user_ref = self.identity_api.create_group(self.group1['id'],
- self.group1)
+ self.group1 = self.new_group_ref(domain_id=self.domainA['id'])
+ self.identity_api.create_group(self.group1['id'], self.group1)
- self.group2 = self.new_group_ref(
- domain_id=self.domainA['id'])
- user_ref = self.identity_api.create_group(self.group2['id'],
- self.group2)
+ self.group2 = self.new_group_ref(domain_id=self.domainA['id'])
+ self.identity_api.create_group(self.group2['id'], self.group2)
- self.group3 = self.new_group_ref(
- domain_id=self.domainB['id'])
- user_ref = self.identity_api.create_group(self.group3['id'],
- self.group3)
+ self.group3 = self.new_group_ref(domain_id=self.domainB['id'])
+ self.identity_api.create_group(self.group3['id'], self.group3)
self.role = self.new_role_ref()
self.identity_api.create_role(self.role['id'], self.role)
@@ -142,7 +127,7 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
"""
self._set_policy({"identity:list_users": []})
r = self.get('/users', auth=self.auth)
- id_list = self._get_id_list_from_ref_list(r.body.get('users'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('users'))
self.assertIn(self.user1['id'], id_list)
self.assertIn(self.user2['id'], id_list)
self.assertIn(self.user3['id'], id_list)
@@ -160,7 +145,7 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
url_by_name = '/users?domain_id=%s' % self.domainB['id']
r = self.get(url_by_name, auth=self.auth)
# We should get back two users, those in DomainB
- id_list = self._get_id_list_from_ref_list(r.body.get('users'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('users'))
self.assertIn(self.user2['id'], id_list)
self.assertIn(self.user3['id'], id_list)
@@ -174,15 +159,14 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
payload
"""
- # TODO (henry-nash, ayoung): It would be good to expand this
+ # TODO(henry-nash, ayoung): It would be good to expand this
# test for further test flattening, e.g. protect on, say, an
# attribute of an object being created
new_policy = {"identity:get_user": [["user_id:%(user_id)s"]]}
self._set_policy(new_policy)
url_by_name = '/users/%s' % self.user1['id']
r = self.get(url_by_name, auth=self.auth)
- body = r.body
- self.assertEquals(self.user1['id'], body['user']['id'])
+ self.assertEquals(self.user1['id'], r.result['user']['id'])
def test_list_users_protected_by_domain(self):
"""GET /users?domain_id=mydomain (protected)
@@ -205,7 +189,7 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
url_by_name = '/users?domain_id=%s' % self.domainA['id']
r = self.get(url_by_name, auth=self.auth)
# We should only get back one user, the one in DomainA
- id_list = self._get_id_list_from_ref_list(r.body.get('users'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('users'))
self.assertEqual(len(id_list), 1)
self.assertIn(self.user1['id'], id_list)
@@ -234,7 +218,7 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
url_by_name = '/groups?domain_id=%s' % self.domainA['id']
r = self.get(url_by_name, auth=self.auth)
# We should only get back two groups, the ones in DomainA
- id_list = self._get_id_list_from_ref_list(r.body.get('groups'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('groups'))
self.assertEqual(len(id_list), 2)
self.assertIn(self.group1['id'], id_list)
self.assertIn(self.group2['id'], id_list)
@@ -266,7 +250,7 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
r = self.get(url_by_name, auth=self.auth)
# We should only get back one user, the one in DomainA that matches
# the name supplied
- id_list = self._get_id_list_from_ref_list(r.body.get('groups'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('groups'))
self.assertEqual(len(id_list), 1)
self.assertIn(self.group2['id'], id_list)
@@ -285,21 +269,21 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
new_policy = {"identity:list_domains": []}
self._set_policy(new_policy)
r = self.get('/domains?enabled=0', auth=self.auth)
- id_list = self._get_id_list_from_ref_list(r.body.get('domains'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('domains'))
self.assertEqual(len(id_list), 1)
self.assertIn(self.domainC['id'], id_list)
# Now try a few ways of specifying 'true' when we should get back
# the other two domains, plus the default domain
r = self.get('/domains?enabled=1', auth=self.auth)
- id_list = self._get_id_list_from_ref_list(r.body.get('domains'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('domains'))
self.assertEqual(len(id_list), 3)
self.assertIn(self.domainA['id'], id_list)
self.assertIn(self.domainB['id'], id_list)
self.assertIn(DEFAULT_DOMAIN_ID, id_list)
r = self.get('/domains?enabled', auth=self.auth)
- id_list = self._get_id_list_from_ref_list(r.body.get('domains'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('domains'))
self.assertEqual(len(id_list), 3)
self.assertIn(self.domainA['id'], id_list)
self.assertIn(self.domainB['id'], id_list)
@@ -319,6 +303,6 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
my_url = '/domains?enableds&name=%s' % self.domainA['name']
r = self.get(my_url, auth=self.auth)
- id_list = self._get_id_list_from_ref_list(r.body.get('domains'))
+ id_list = self._get_id_list_from_ref_list(r.result.get('domains'))
self.assertEqual(len(id_list), 1)
self.assertIn(self.domainA['id'], id_list)
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index 9bc26017..2d81ba86 100644
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -17,9 +17,9 @@
import webob
from keystone.common import wsgi
+from keystone import exception
from keystone.openstack.common import jsonutils
from keystone import test
-from keystone import exception
class FakeApp(wsgi.Application):
@@ -38,6 +38,37 @@ class BaseWSGITest(test.TestCase):
req.environ['wsgiorg.routing_args'] = [None, args]
return req
+ def test_mask_password(self):
+ message = ("test = 'password': 'aaaaaa', 'param1': 'value1', "
+ "\"new_password\": 'bbbbbb'")
+ self.assertEqual(wsgi.mask_password(message, True),
+ u"test = 'password': '***', 'param1': 'value1', "
+ "\"new_password\": '***'")
+
+ message = "test = 'password' : 'aaaaaa'"
+ self.assertEqual(wsgi.mask_password(message, False, '111'),
+ "test = 'password' : '111'")
+
+ message = u"test = u'password' : u'aaaaaa'"
+ self.assertEqual(wsgi.mask_password(message, True),
+ u"test = u'password' : u'***'")
+
+ message = 'test = "password" : "aaaaaaaaa"'
+ self.assertEqual(wsgi.mask_password(message),
+ 'test = "password" : "***"')
+
+ message = 'test = "original_password" : "aaaaaaaaa"'
+ self.assertEqual(wsgi.mask_password(message),
+ 'test = "original_password" : "***"')
+
+ message = 'test = "original_password" : ""'
+ self.assertEqual(wsgi.mask_password(message),
+ 'test = "original_password" : "***"')
+
+ message = 'test = "param1" : "value"'
+ self.assertEqual(wsgi.mask_password(message),
+ 'test = "param1" : "value"')
+
class ApplicationTest(BaseWSGITest):
def test_response_content_type(self):
diff --git a/tools/flakes.py b/tools/flakes.py
deleted file mode 100644
index 191bd6ea..00000000
--- a/tools/flakes.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
- wrapper for pyflakes to ignore gettext based warning:
- "undefined name '_'"
-
- Synced in from openstack-common
-"""
-
-__all__ = ['main']
-
-import __builtin__ as builtins
-import sys
-
-import pyflakes.api
-from pyflakes import checker
-
-
-def main():
- checker.Checker.builtIns = (set(dir(builtins)) |
- set(['_']) |
- set(checker._MAGIC_GLOBALS))
- sys.exit(pyflakes.api.main())
-
-if __name__ == "__main__":
- main()
diff --git a/tools/pip-requires b/tools/pip-requires
index 40ef5fee..af5d2831 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -1,4 +1,6 @@
# keystone dependencies
+d2to1>=0.2.10,<0.3
+pbr>=0.5,<0.6
pam>=0.1.4
WebOb==1.2.3
eventlet
diff --git a/tools/sample_data.sh b/tools/sample_data.sh
index 08a1dba5..3ad819b3 100755
--- a/tools/sample_data.sh
+++ b/tools/sample_data.sh
@@ -37,6 +37,17 @@
# service ec2 admin
# service swift admin
+# By default, passwords used are those in the OpenStack Install and Deploy Manual.
+# One can override these (publicly known, and hence, insecure) passwords by setting the appropriate
+# environment variables. A common default password for all the services can be used by
+# setting the "SERVICE_PASSWORD" environment variable.
+
+ADMIN_PASSWORD=${ADMIN_PASSWORD:-secrete}
+NOVA_PASSWORD=${NOVA_PASSWORD:-${SERVICE_PASSWORD:-nova}}
+GLANCE_PASSWORD=${GLANCE_PASSWORD:-${SERVICE_PASSWORD:-glance}}
+EC2_PASSWORD=${EC2_PASSWORD:-${SERVICE_PASSWORD:-ec2}}
+SWIFT_PASSWORD=${SWIFT_PASSWORD:-${SERVICE_PASSWORD:-swiftpass}}
+
CONTROLLER_PUBLIC_ADDRESS=${CONTROLLER_PUBLIC_ADDRESS:-localhost}
CONTROLLER_ADMIN_ADDRESS=${CONTROLLER_ADMIN_ADDRESS:-localhost}
CONTROLLER_INTERNAL_ADDRESS=${CONTROLLER_INTERNAL_ADDRESS:-localhost}
@@ -80,7 +91,7 @@ DEMO_TENANT=$(get_id keystone tenant-create --name=demo \
--description "Default Tenant")
ADMIN_USER=$(get_id keystone user-create --name=admin \
- --pass=secrete)
+ --pass="${ADMIN_PASSWORD}")
ADMIN_ROLE=$(get_id keystone role-create --name=admin)
@@ -95,14 +106,14 @@ SERVICE_TENANT=$(get_id keystone tenant-create --name=service \
--description "Service Tenant")
GLANCE_USER=$(get_id keystone user-create --name=glance \
- --pass=glance)
+ --pass="${GLANCE_PASSWORD}")
keystone user-role-add --user-id $GLANCE_USER \
--role-id $ADMIN_ROLE \
--tenant-id $SERVICE_TENANT
NOVA_USER=$(get_id keystone user-create --name=nova \
- --pass=nova \
+ --pass="${NOVA_PASSWORD}" \
--tenant-id $SERVICE_TENANT)
keystone user-role-add --user-id $NOVA_USER \
@@ -110,7 +121,7 @@ keystone user-role-add --user-id $NOVA_USER \
--tenant-id $SERVICE_TENANT
EC2_USER=$(get_id keystone user-create --name=ec2 \
- --pass=ec2 \
+ --pass="${EC2_PASSWORD}" \
--tenant-id $SERVICE_TENANT)
keystone user-role-add --user-id $EC2_USER \
@@ -118,7 +129,7 @@ keystone user-role-add --user-id $EC2_USER \
--tenant-id $SERVICE_TENANT
SWIFT_USER=$(get_id keystone user-create --name=swift \
- --pass=swiftpass \
+ --pass="${SWIFT_PASSWORD}" \
--tenant-id $SERVICE_TENANT)
keystone user-role-add --user-id $SWIFT_USER \
diff --git a/tools/test-requires b/tools/test-requires
index 52a181b8..936eb90c 100644
--- a/tools/test-requires
+++ b/tools/test-requires
@@ -1,3 +1,9 @@
+# Install bounded pep8/pyflakes first, then let flake8 install
+pep8==1.4.5
+pyflakes==0.7.2
+flake8==2.0
+hacking>=0.5.3,<0.6
+
# Optional backend: SQL
pysqlite
@@ -14,8 +20,6 @@ nose # for test discovery and console feedback
nosexcover
openstack.nose_plugin
nosehtmloutput
-pylint # static code analysis
-pep8==1.3.3 # checks for PEP8 code style compliance
Sphinx>=1.1.2 # required to build documentation
unittest2 # backport of unittest lib in python 2.7
webtest # test wsgi apps without starting an http server
diff --git a/tox.ini b/tox.ini
index 102a556c..401ec901 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,6 +3,7 @@ envlist = py26,py27,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
+ EVENTLET_NO_GREENDNS=yes
NOSE_WITH_OPENSTACK=1
NOSE_OPENSTACK_COLOR=1
NOSE_OPENSTACK_RED=0.05
@@ -15,8 +16,8 @@ commands = nosetests {posargs}
[testenv:pep8]
commands =
- pep8 --exclude=.venv,.tox,dist,doc,openstack,vendor,*egg --repeat --show-source .
- pep8 --repeat --show-source --filename=keystone* bin
+ flake8
+ flake8 --filename=keystone* bin
[tox:jenkins]
downloadcache = ~/cache/pip
@@ -27,6 +28,11 @@ setenv = NOSE_WITH_COVERAGE=1
[testenv:venv]
commands = {posargs}
-[testenv:pyflakes]
-deps = pyflakes
-commands = python tools/flakes.py keystone
+[flake8]
+show-source = true
+
+# H304: no relative imports.
+ignore = H304
+
+builtins = _
+exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools,vendor