Merge "Remove eventlet support"
This commit is contained in:
commit
23bb657369
@ -9,7 +9,6 @@ namespace = oslo.messaging
|
|||||||
namespace = oslo.policy
|
namespace = oslo.policy
|
||||||
namespace = oslo.db
|
namespace = oslo.db
|
||||||
namespace = oslo.middleware
|
namespace = oslo.middleware
|
||||||
namespace = oslo.service.service
|
|
||||||
namespace = osprofiler
|
namespace = osprofiler
|
||||||
# We don't use oslo.concurrency config options in
|
# We don't use oslo.concurrency config options in
|
||||||
# keystone now, just in case it slips through unnoticed.
|
# keystone now, just in case it slips through unnoticed.
|
||||||
|
@ -111,9 +111,7 @@ modindex_common_prefix = ['keystone.']
|
|||||||
|
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('man/keystone-manage', 'keystone-manage', u'Keystone Management Utility',
|
('man/keystone-manage', 'keystone-manage', u'Keystone Management Utility',
|
||||||
[u'OpenStack'], 1),
|
[u'OpenStack'], 1)
|
||||||
('man/keystone-all', 'keystone-all', u'Keystone Startup Command',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ Configuring Keystone
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
man/keystone-manage
|
man/keystone-manage
|
||||||
man/keystone-all
|
|
||||||
|
|
||||||
Once Keystone is installed, it is configured via a primary configuration file
|
Once Keystone is installed, it is configured via a primary configuration file
|
||||||
(``etc/keystone.conf``), a PasteDeploy configuration file
|
(``etc/keystone.conf``), a PasteDeploy configuration file
|
||||||
@ -45,48 +44,6 @@ To make the above change persistent,
|
|||||||
``net.ipv4.ip_local_reserved_ports = 35357`` should be added to
|
``net.ipv4.ip_local_reserved_ports = 35357`` should be added to
|
||||||
``/etc/sysctl.conf`` or to ``/etc/sysctl.d/keystone.conf``.
|
``/etc/sysctl.conf`` or to ``/etc/sysctl.d/keystone.conf``.
|
||||||
|
|
||||||
Starting and Stopping Keystone under Eventlet
|
|
||||||
=============================================
|
|
||||||
|
|
||||||
.. WARNING::
|
|
||||||
|
|
||||||
Running keystone under eventlet has been deprecated as of the Kilo release.
|
|
||||||
Support for utilizing eventlet will be removed as of the M-release. The
|
|
||||||
recommended deployment is to run keystone in a WSGI server such as Apache
|
|
||||||
httpd with ``mod_wsgi``.
|
|
||||||
|
|
||||||
Keystone can be run using either its built-in eventlet server or it can be run
|
|
||||||
embedded in a web server. While the eventlet server is convenient and easy to
|
|
||||||
use, it's lacking in security features that have been developed into Internet-
|
|
||||||
based web servers over the years. As such, running the eventlet server as
|
|
||||||
described in this section is not recommended.
|
|
||||||
|
|
||||||
Start Keystone services using the command:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ keystone-all
|
|
||||||
|
|
||||||
Invoking this command starts up two ``wsgi.Server`` instances, ``admin`` (the
|
|
||||||
administration API) and ``main`` (the primary/public API interface). Both
|
|
||||||
services are configured to run in a single process.
|
|
||||||
|
|
||||||
.. NOTE::
|
|
||||||
|
|
||||||
The separation into ``admin`` and ``main`` interfaces is a historical
|
|
||||||
anomaly. The new V3 API provides the same interface on both the admin and
|
|
||||||
main interfaces (this can be configured in ``keystone-paste.ini``, but the
|
|
||||||
default is to have both the same). The V2.0 API provides a limited public
|
|
||||||
API (getting and validating tokens) on ``main``, and an administrative API
|
|
||||||
(which can include creating users and such) on the ``admin`` interface.
|
|
||||||
|
|
||||||
Stop the process using ``Control-C``.
|
|
||||||
|
|
||||||
.. NOTE::
|
|
||||||
|
|
||||||
If you have not already configured Keystone, it may not start as expected.
|
|
||||||
|
|
||||||
|
|
||||||
Configuration Files
|
Configuration Files
|
||||||
===================
|
===================
|
||||||
|
|
||||||
@ -115,8 +72,6 @@ The primary configuration file is organized into the following sections:
|
|||||||
* ``[credential]`` - Credential system driver configuration
|
* ``[credential]`` - Credential system driver configuration
|
||||||
* ``[endpoint_filter]`` - Endpoint filtering configuration
|
* ``[endpoint_filter]`` - Endpoint filtering configuration
|
||||||
* ``[endpoint_policy]`` - Endpoint policy configuration
|
* ``[endpoint_policy]`` - Endpoint policy configuration
|
||||||
* ``[eventlet_server]`` - Eventlet server configuration
|
|
||||||
* ``[eventlet_server_ssl]`` - Eventlet server SSL configuration
|
|
||||||
* ``[federation]`` - Federation driver configuration
|
* ``[federation]`` - Federation driver configuration
|
||||||
* ``[identity]`` - Identity system driver configuration
|
* ``[identity]`` - Identity system driver configuration
|
||||||
* ``[identity_mapping]`` - Identity mapping system driver configuration
|
* ``[identity_mapping]`` - Identity mapping system driver configuration
|
||||||
@ -977,32 +932,19 @@ certificates are just provided as an example.
|
|||||||
Configuration
|
Configuration
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
To enable SSL modify the ``etc/keystone.conf`` file under the ``[ssl]`` and
|
To enable SSL a deployment should configure a web server (such as Apache) to
|
||||||
``[eventlet_server_ssl]`` sections. The following is an SSL configuration
|
use SSL. Keystone is able to generate SSL certificates by modifying the
|
||||||
example using the included sample certificates:
|
``[ssl]`` section in the ``etc/keystone.conf`` file. The following is an SSL
|
||||||
|
configuration example using the included sample certificates:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[eventlet_server_ssl]
|
|
||||||
enable = True
|
|
||||||
certfile = <path to keystone.pem>
|
|
||||||
keyfile = <path to keystonekey.pem>
|
|
||||||
ca_certs = <path to ca.pem>
|
|
||||||
cert_required = False
|
|
||||||
|
|
||||||
[ssl]
|
[ssl]
|
||||||
ca_key = <path to cakey.pem>
|
ca_key = <path to cakey.pem>
|
||||||
key_size = 1024
|
key_size = 1024
|
||||||
valid_days=3650
|
valid_days=3650
|
||||||
cert_subject=/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost
|
cert_subject=/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost
|
||||||
|
|
||||||
* ``enable``: True enables SSL. Defaults to False.
|
|
||||||
* ``certfile``: Path to Keystone public certificate file.
|
|
||||||
* ``keyfile``: Path to Keystone private certificate file. If the private key is
|
|
||||||
included in the certfile, the keyfile may be omitted.
|
|
||||||
* ``ca_certs``: Path to CA trust chain.
|
|
||||||
* ``cert_required``: Requires client certificate. Defaults to False.
|
|
||||||
|
|
||||||
When generating SSL certificates the following values are read
|
When generating SSL certificates the following values are read
|
||||||
|
|
||||||
* ``key_size``: Key size to create. Defaults to 1024.
|
* ``key_size``: Key size to create. Defaults to 1024.
|
||||||
|
@ -222,6 +222,5 @@ HTTP/1.1 Chunked Encoding
|
|||||||
|
|
||||||
Running Keystone under HTTPD in the recommended (and tested) configuration does not support
|
Running Keystone under HTTPD in the recommended (and tested) configuration does not support
|
||||||
the use of ``Transfer-Encoding: chunked``. This is due to a limitation with the WSGI spec
|
the use of ``Transfer-Encoding: chunked``. This is due to a limitation with the WSGI spec
|
||||||
and the implementation used by ``mod_wsgi``. Support for chunked encoding under ``eventlet``
|
and the implementation used by ``mod_wsgi``. It is recommended that all
|
||||||
may or may not continue. It is recommended that all clients assume Keystone will not support
|
clients assume Keystone will not support ``Transfer-Encoding: chunked``.
|
||||||
``Transfer-Encoding: chunked``.
|
|
||||||
|
@ -78,7 +78,6 @@ Man Pages
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
man/keystone-all
|
|
||||||
man/keystone-manage
|
man/keystone-manage
|
||||||
|
|
||||||
Developers Documentation
|
Developers Documentation
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
============
|
|
||||||
keystone-all
|
|
||||||
============
|
|
||||||
|
|
||||||
------------------------
|
|
||||||
Keystone Startup Command
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
:Author: openstack@lists.openstack.org
|
|
||||||
:Date: 2015-10-15
|
|
||||||
:Copyright: OpenStack Foundation
|
|
||||||
:Version: 8.0.0
|
|
||||||
:Manual section: 1
|
|
||||||
:Manual group: cloud computing
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
========
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
keystone-all [-h] [--config-dir DIR] [--config-file PATH] [--debug]
|
|
||||||
[--log-config-append PATH] [--log-date-format DATE_FORMAT]
|
|
||||||
[--log-dir LOG_DIR] [--log-file PATH]
|
|
||||||
[--log-format FORMAT] [--nodebug] [--nostandard-threads]
|
|
||||||
[--nouse-syslog] [--nouse-syslog-rfc-format] [--noverbose]
|
|
||||||
[--pydev-debug-host PYDEV_DEBUG_HOST]
|
|
||||||
[--pydev-debug-port PYDEV_DEBUG_PORT] [--standard-threads]
|
|
||||||
[--syslog-log-facility SYSLOG_LOG_FACILITY] [--use-syslog]
|
|
||||||
[--use-syslog-rfc-format] [--verbose] [--version]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
===========
|
|
||||||
|
|
||||||
keystone-all starts both the service and administrative APIs in a single
|
|
||||||
process to provide catalog, authorization, and authentication services for
|
|
||||||
OpenStack.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
=======
|
|
||||||
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
--config-dir DIR Path to a config directory to pull \*.conf files from.
|
|
||||||
This file set is sorted, so as to provide a
|
|
||||||
predictable parse order if individual options are
|
|
||||||
over-ridden. The set is parsed after the file(s)
|
|
||||||
specified via previous --config-file, arguments hence
|
|
||||||
over-ridden options in the directory take precedence.
|
|
||||||
--config-file PATH Path to a config file to use. Multiple config files
|
|
||||||
can be specified, with values in later files taking
|
|
||||||
precedence. The default files used are: None.
|
|
||||||
--debug, -d Print debugging output (set logging level to DEBUG
|
|
||||||
instead of default WARNING level).
|
|
||||||
--log-config-append PATH, --log_config PATH
|
|
||||||
The name of a logging configuration file. This file is
|
|
||||||
appended to any existing logging configuration files.
|
|
||||||
For details about logging configuration files, see the
|
|
||||||
Python logging module documentation.
|
|
||||||
--log-date-format DATE_FORMAT
|
|
||||||
Format string for %(asctime)s in log records. Default:
|
|
||||||
None .
|
|
||||||
--log-dir LOG_DIR, --logdir LOG_DIR
|
|
||||||
(Optional) The base directory used for relative --log-
|
|
||||||
file paths.
|
|
||||||
--log-file PATH, --logfile PATH
|
|
||||||
(Optional) Name of log file to output to. If no
|
|
||||||
default is set, logging will go to stdout.
|
|
||||||
--log-format FORMAT DEPRECATED. A logging.Formatter log message format
|
|
||||||
string which may use any of the available
|
|
||||||
logging.LogRecord attributes. This option is
|
|
||||||
deprecated. Please use logging_context_format_string
|
|
||||||
and logging_default_format_string instead.
|
|
||||||
--nodebug The inverse of --debug
|
|
||||||
--nostandard-threads The inverse of --standard-threads
|
|
||||||
--nouse-syslog The inverse of --use-syslog
|
|
||||||
--nouse-syslog-rfc-format
|
|
||||||
The inverse of --use-syslog-rfc-format
|
|
||||||
--noverbose The inverse of --verbose
|
|
||||||
--pydev-debug-host PYDEV_DEBUG_HOST
|
|
||||||
Host to connect to for remote debugger.
|
|
||||||
--pydev-debug-port PYDEV_DEBUG_PORT
|
|
||||||
Port to connect to for remote debugger.
|
|
||||||
--standard-threads Do not monkey-patch threading system modules.
|
|
||||||
--syslog-log-facility SYSLOG_LOG_FACILITY
|
|
||||||
Syslog facility to receive log lines.
|
|
||||||
--use-syslog Use syslog for logging. Existing syslog format is
|
|
||||||
DEPRECATED during I, and will change in J to honor
|
|
||||||
RFC5424.
|
|
||||||
--use-syslog-rfc-format
|
|
||||||
(Optional) Enables or disables syslog rfc5424 format
|
|
||||||
for logging. If enabled, prefixes the MSG part of the
|
|
||||||
syslog message with APP-NAME (RFC5424). The format
|
|
||||||
without the APP-NAME is deprecated in I, and will be
|
|
||||||
removed in J.
|
|
||||||
--verbose, -v Print more verbose output (set logging level to INFO
|
|
||||||
instead of default WARNING level).
|
|
||||||
--version show program's version number and exit
|
|
||||||
|
|
||||||
FILES
|
|
||||||
=====
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
SEE ALSO
|
|
||||||
========
|
|
||||||
|
|
||||||
* `OpenStack Keystone <http://keystone.openstack.org>`__
|
|
||||||
|
|
||||||
SOURCE
|
|
||||||
======
|
|
||||||
|
|
||||||
* Keystone source is managed in Gerrit git `Keystone <https://git.openstack.org/cgit/openstack/keystone>`__
|
|
||||||
* Keystone bugs are managed at Launchpad `Keystone <https://bugs.launchpad.net/keystone>`__
|
|
@ -1,39 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright 2013 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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 sys
|
|
||||||
|
|
||||||
|
|
||||||
# If ../../keystone/__init__.py exists, add ../../ to Python search path, so
|
|
||||||
# that it will override what happens to be installed in
|
|
||||||
# /usr/(local/)lib/python...
|
|
||||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__),
|
|
||||||
os.pardir,
|
|
||||||
os.pardir,
|
|
||||||
os.pardir))
|
|
||||||
if os.path.exists(os.path.join(possible_topdir,
|
|
||||||
'keystone',
|
|
||||||
'__init__.py')):
|
|
||||||
sys.path.insert(0, possible_topdir)
|
|
||||||
|
|
||||||
|
|
||||||
from keystone.server import eventlet as eventlet_server
|
|
||||||
|
|
||||||
|
|
||||||
# entry point.
|
|
||||||
def main():
|
|
||||||
eventlet_server.run(possible_topdir)
|
|
@ -30,13 +30,10 @@ if os.path.exists(os.path.join(possible_topdir,
|
|||||||
sys.path.insert(0, possible_topdir)
|
sys.path.insert(0, possible_topdir)
|
||||||
|
|
||||||
from keystone.cmd import cli
|
from keystone.cmd import cli
|
||||||
from keystone.common import environment
|
|
||||||
|
|
||||||
|
|
||||||
# entry point.
|
# entry point.
|
||||||
def main():
|
def main():
|
||||||
environment.use_stdlib()
|
|
||||||
|
|
||||||
dev_conf = os.path.join(possible_topdir,
|
dev_conf = os.path.join(possible_topdir,
|
||||||
'etc',
|
'etc',
|
||||||
'keystone.conf')
|
'keystone.conf')
|
||||||
|
@ -49,6 +49,11 @@ _DEPRECATE_DII_MSG = ('The option to set domain_id_immutable to false '
|
|||||||
'has been deprecated in the M release and will '
|
'has been deprecated in the M release and will '
|
||||||
'be removed in the O release.')
|
'be removed in the O release.')
|
||||||
|
|
||||||
|
_DEPRECATE_EVENTLET_MSG = ('Support for running keystone under eventlet has '
|
||||||
|
'been removed in the N release. These options '
|
||||||
|
'remain for backwards compatibility because they '
|
||||||
|
'are used for URL substitutions.')
|
||||||
|
|
||||||
FILE_OPTIONS = {
|
FILE_OPTIONS = {
|
||||||
None: [
|
None: [
|
||||||
cfg.StrOpt('admin_token', secret=True, default=None,
|
cfg.StrOpt('admin_token', secret=True, default=None,
|
||||||
@ -999,20 +1004,6 @@ FILE_OPTIONS = {
|
|||||||
'assertions.'),
|
'assertions.'),
|
||||||
],
|
],
|
||||||
'eventlet_server': [
|
'eventlet_server': [
|
||||||
cfg.IntOpt('public_workers',
|
|
||||||
deprecated_name='public_workers',
|
|
||||||
deprecated_group='DEFAULT',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='The number of worker processes to serve the public '
|
|
||||||
'eventlet application. Defaults to number of CPUs '
|
|
||||||
'(minimum of 2).'),
|
|
||||||
cfg.IntOpt('admin_workers',
|
|
||||||
deprecated_name='admin_workers',
|
|
||||||
deprecated_group='DEFAULT',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='The number of worker processes to serve the admin '
|
|
||||||
'eventlet application. Defaults to number of CPUs '
|
|
||||||
'(minimum of 2).'),
|
|
||||||
cfg.StrOpt('public_bind_host',
|
cfg.StrOpt('public_bind_host',
|
||||||
default='0.0.0.0', # nosec : Bind to all interfaces by
|
default='0.0.0.0', # nosec : Bind to all interfaces by
|
||||||
# default for backwards compatibility.
|
# default for backwards compatibility.
|
||||||
@ -1021,12 +1012,14 @@ FILE_OPTIONS = {
|
|||||||
cfg.DeprecatedOpt('public_bind_host',
|
cfg.DeprecatedOpt('public_bind_host',
|
||||||
group='DEFAULT'), ],
|
group='DEFAULT'), ],
|
||||||
deprecated_for_removal=True,
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=_DEPRECATE_EVENTLET_MSG,
|
||||||
help='The IP address of the network interface for the '
|
help='The IP address of the network interface for the '
|
||||||
'public service to listen on.'),
|
'public service to listen on.'),
|
||||||
cfg.PortOpt('public_port', default=5000,
|
cfg.PortOpt('public_port', default=5000,
|
||||||
deprecated_name='public_port',
|
deprecated_name='public_port',
|
||||||
deprecated_group='DEFAULT',
|
deprecated_group='DEFAULT',
|
||||||
deprecated_for_removal=True,
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=_DEPRECATE_EVENTLET_MSG,
|
||||||
help='The port number which the public service listens '
|
help='The port number which the public service listens '
|
||||||
'on.'),
|
'on.'),
|
||||||
cfg.StrOpt('admin_bind_host',
|
cfg.StrOpt('admin_bind_host',
|
||||||
@ -1037,6 +1030,7 @@ FILE_OPTIONS = {
|
|||||||
cfg.DeprecatedOpt('admin_bind_host',
|
cfg.DeprecatedOpt('admin_bind_host',
|
||||||
group='DEFAULT')],
|
group='DEFAULT')],
|
||||||
deprecated_for_removal=True,
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=_DEPRECATE_EVENTLET_MSG,
|
||||||
help='The IP address of the network interface for the '
|
help='The IP address of the network interface for the '
|
||||||
'admin service to listen on.'),
|
'admin service to listen on.'),
|
||||||
cfg.PortOpt('admin_port', default=35357,
|
cfg.PortOpt('admin_port', default=35357,
|
||||||
@ -1045,60 +1039,6 @@ FILE_OPTIONS = {
|
|||||||
deprecated_for_removal=True,
|
deprecated_for_removal=True,
|
||||||
help='The port number which the admin service listens '
|
help='The port number which the admin service listens '
|
||||||
'on.'),
|
'on.'),
|
||||||
cfg.BoolOpt('wsgi_keep_alive', default=True,
|
|
||||||
help='If set to false, disables keepalives on the server; '
|
|
||||||
'all connections will be closed after serving one '
|
|
||||||
'request.'),
|
|
||||||
cfg.IntOpt('client_socket_timeout', default=900,
|
|
||||||
help='Timeout for socket operations on a client '
|
|
||||||
'connection. If an incoming connection is idle for '
|
|
||||||
'this number of seconds it will be closed. A value '
|
|
||||||
'of "0" means wait forever.'),
|
|
||||||
cfg.BoolOpt('tcp_keepalive', default=False,
|
|
||||||
deprecated_name='tcp_keepalive',
|
|
||||||
deprecated_group='DEFAULT',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Set this to true if you want to enable '
|
|
||||||
'TCP_KEEPALIVE on server sockets, i.e. sockets used '
|
|
||||||
'by the Keystone wsgi server for client '
|
|
||||||
'connections.'),
|
|
||||||
cfg.IntOpt('tcp_keepidle',
|
|
||||||
default=600,
|
|
||||||
deprecated_name='tcp_keepidle',
|
|
||||||
deprecated_group='DEFAULT',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Sets the value of TCP_KEEPIDLE in seconds for each '
|
|
||||||
'server socket. Only applies if tcp_keepalive is '
|
|
||||||
'true. Ignored if system does not support it.'),
|
|
||||||
],
|
|
||||||
'eventlet_server_ssl': [
|
|
||||||
cfg.BoolOpt('enable', default=False, deprecated_name='enable',
|
|
||||||
deprecated_group='ssl',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Toggle for SSL support on the Keystone '
|
|
||||||
'eventlet servers.'),
|
|
||||||
cfg.StrOpt('certfile',
|
|
||||||
default='/etc/keystone/ssl/certs/keystone.pem',
|
|
||||||
deprecated_name='certfile', deprecated_group='ssl',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Path of the certfile for SSL. For non-production '
|
|
||||||
'environments, you may be interested in using '
|
|
||||||
'`keystone-manage ssl_setup` to generate self-signed '
|
|
||||||
'certificates.'),
|
|
||||||
cfg.StrOpt('keyfile',
|
|
||||||
default='/etc/keystone/ssl/private/keystonekey.pem',
|
|
||||||
deprecated_name='keyfile', deprecated_group='ssl',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Path of the keyfile for SSL.'),
|
|
||||||
cfg.StrOpt('ca_certs',
|
|
||||||
default='/etc/keystone/ssl/certs/ca.pem',
|
|
||||||
deprecated_name='ca_certs', deprecated_group='ssl',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Path of the CA cert file for SSL.'),
|
|
||||||
cfg.BoolOpt('cert_required', default=False,
|
|
||||||
deprecated_name='cert_required', deprecated_group='ssl',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Require client certificate.'),
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
# Copyright 2013 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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 functools
|
|
||||||
import os
|
|
||||||
|
|
||||||
from oslo_log import log
|
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('Server', 'httplib', 'subprocess')
|
|
||||||
|
|
||||||
_configured = False
|
|
||||||
|
|
||||||
Server = None
|
|
||||||
httplib = None
|
|
||||||
subprocess = None
|
|
||||||
|
|
||||||
|
|
||||||
def configure_once(name):
|
|
||||||
"""Ensure that environment configuration is only run once.
|
|
||||||
|
|
||||||
If environment is reconfigured in the same way then it is ignored.
|
|
||||||
It is an error to attempt to reconfigure environment in a different way.
|
|
||||||
"""
|
|
||||||
def decorator(func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
global _configured
|
|
||||||
if _configured:
|
|
||||||
if _configured == name:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise SystemError("Environment has already been "
|
|
||||||
"configured as %s" % _configured)
|
|
||||||
|
|
||||||
LOG.debug("Environment configured as: %s", name)
|
|
||||||
_configured = name
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
@configure_once('eventlet')
|
|
||||||
def use_eventlet(monkeypatch_thread=None):
|
|
||||||
global httplib, subprocess, Server
|
|
||||||
|
|
||||||
# This must be set before the initial import of eventlet because if
|
|
||||||
# dnspython is present in your environment then eventlet monkeypatches
|
|
||||||
# socket.getaddrinfo() with an implementation which doesn't work for IPv6.
|
|
||||||
os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
|
|
||||||
|
|
||||||
import eventlet
|
|
||||||
from eventlet.green import httplib as _httplib
|
|
||||||
from eventlet.green import subprocess as _subprocess
|
|
||||||
|
|
||||||
from keystone.common.environment import eventlet_server
|
|
||||||
|
|
||||||
if monkeypatch_thread is None:
|
|
||||||
monkeypatch_thread = not os.getenv('STANDARD_THREADS')
|
|
||||||
|
|
||||||
# Raise the default from 8192 to accommodate large tokens
|
|
||||||
eventlet.wsgi.MAX_HEADER_LINE = 16384
|
|
||||||
|
|
||||||
# NOTE(ldbragst): Explicitly declare what should be monkey patched and
|
|
||||||
# what shouldn't. Doing this allows for more readable code when
|
|
||||||
# understanding Eventlet in Keystone. The following is a complete list
|
|
||||||
# of what is monkey patched instead of passing all=False and then passing
|
|
||||||
# module=True to monkey patch a specific module.
|
|
||||||
eventlet.patcher.monkey_patch(os=False, select=True, socket=True,
|
|
||||||
thread=monkeypatch_thread, time=True,
|
|
||||||
psycopg=False, MySQLdb=False)
|
|
||||||
|
|
||||||
Server = eventlet_server.Server
|
|
||||||
httplib = _httplib
|
|
||||||
subprocess = _subprocess
|
|
||||||
|
|
||||||
|
|
||||||
@configure_once('stdlib')
|
|
||||||
def use_stdlib():
|
|
||||||
global httplib, subprocess
|
|
||||||
|
|
||||||
import six.moves.http_client as _httplib
|
|
||||||
import subprocess as _subprocess # nosec : This is used in .federation.idp
|
|
||||||
# and .common.openssl. See there.
|
|
||||||
|
|
||||||
httplib = _httplib
|
|
||||||
subprocess = _subprocess
|
|
@ -1,212 +0,0 @@
|
|||||||
# Copyright 2012 OpenStack Foundation
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# Copyright 2010 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import errno
|
|
||||||
import re
|
|
||||||
import socket
|
|
||||||
import ssl
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import eventlet.wsgi
|
|
||||||
import greenlet
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log
|
|
||||||
from oslo_service import service
|
|
||||||
|
|
||||||
from keystone.i18n import _LE, _LI
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
# The size of a pool that is used to spawn a single green thread in which
|
|
||||||
# a wsgi server is then started. The size of one is enough, because in case
|
|
||||||
# of several workers the parent process forks and each child gets a copy
|
|
||||||
# of a pool, which does not include any greenthread object as the spawn is
|
|
||||||
# done after the fork.
|
|
||||||
POOL_SIZE = 1
|
|
||||||
|
|
||||||
|
|
||||||
class EventletFilteringLogger(object):
|
|
||||||
# NOTE(morganfainberg): This logger is designed to filter out specific
|
|
||||||
# Tracebacks to limit the amount of data that eventlet can log. In the
|
|
||||||
# case of broken sockets (EPIPE and ECONNRESET), we are seeing a huge
|
|
||||||
# volume of data being written to the logs due to ~14 lines+ per traceback.
|
|
||||||
# The traceback in these cases are, at best, useful for limited debugging
|
|
||||||
# cases.
|
|
||||||
def __init__(self, logger, level=log.INFO):
|
|
||||||
self.logger = logger
|
|
||||||
self.level = level
|
|
||||||
self.regex = re.compile(r'errno (%d|%d)' %
|
|
||||||
(errno.EPIPE, errno.ECONNRESET), re.IGNORECASE)
|
|
||||||
|
|
||||||
def write(self, msg):
|
|
||||||
m = self.regex.search(msg)
|
|
||||||
if m:
|
|
||||||
self.logger.log(log.logging.DEBUG, 'Error(%s) writing to socket.',
|
|
||||||
m.group(1))
|
|
||||||
else:
|
|
||||||
self.logger.log(self.level, msg.rstrip())
|
|
||||||
|
|
||||||
|
|
||||||
class Server(service.ServiceBase):
|
|
||||||
"""Server class to manage multiple WSGI sockets and applications."""
|
|
||||||
|
|
||||||
def __init__(self, application, host=None, port=None, keepalive=False,
|
|
||||||
keepidle=None):
|
|
||||||
self.application = application
|
|
||||||
self.host = host or '0.0.0.0' # nosec : Bind to all interfaces by
|
|
||||||
# default for backwards compatibility.
|
|
||||||
self.port = port or 0
|
|
||||||
# Pool for a green thread in which wsgi server will be running
|
|
||||||
self.pool = eventlet.GreenPool(POOL_SIZE)
|
|
||||||
self.socket_info = {}
|
|
||||||
self.greenthread = None
|
|
||||||
self.do_ssl = False
|
|
||||||
self.cert_required = False
|
|
||||||
self.keepalive = keepalive
|
|
||||||
self.keepidle = keepidle
|
|
||||||
self.socket = None
|
|
||||||
|
|
||||||
def listen(self, key=None, backlog=128):
|
|
||||||
"""Create and start listening on socket.
|
|
||||||
|
|
||||||
Call before forking worker processes.
|
|
||||||
|
|
||||||
Raises Exception if this has already been called.
|
|
||||||
"""
|
|
||||||
# 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.
|
|
||||||
# Please refer below link
|
|
||||||
# (https://bitbucket.org/eventlet/eventlet/
|
|
||||||
# src/e0f578180d7d82d2ed3d8a96d520103503c524ec/eventlet/support/
|
|
||||||
# greendns.py?at=0.12#cl-163)
|
|
||||||
info = socket.getaddrinfo(self.host,
|
|
||||||
self.port,
|
|
||||||
socket.AF_UNSPEC,
|
|
||||||
socket.SOCK_STREAM)[0]
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.socket = eventlet.listen(info[-1], family=info[0],
|
|
||||||
backlog=backlog)
|
|
||||||
except EnvironmentError:
|
|
||||||
LOG.error(_LE("Could not bind to %(host)s:%(port)s"),
|
|
||||||
{'host': self.host, 'port': self.port})
|
|
||||||
raise
|
|
||||||
|
|
||||||
LOG.info(_LI('Starting %(arg0)s on %(host)s:%(port)s'),
|
|
||||||
{'arg0': sys.argv[0],
|
|
||||||
'host': self.host,
|
|
||||||
'port': self.port})
|
|
||||||
|
|
||||||
def start(self, key=None, backlog=128):
|
|
||||||
"""Run a WSGI server with the given application."""
|
|
||||||
if self.socket is None:
|
|
||||||
self.listen(key=key, backlog=backlog)
|
|
||||||
|
|
||||||
dup_socket = self.socket.dup()
|
|
||||||
if key:
|
|
||||||
self.socket_info[key] = self.socket.getsockname()
|
|
||||||
# SSL is enabled
|
|
||||||
if self.do_ssl:
|
|
||||||
if self.cert_required:
|
|
||||||
cert_reqs = ssl.CERT_REQUIRED
|
|
||||||
else:
|
|
||||||
cert_reqs = ssl.CERT_NONE
|
|
||||||
|
|
||||||
dup_socket = eventlet.wrap_ssl(dup_socket, certfile=self.certfile,
|
|
||||||
keyfile=self.keyfile,
|
|
||||||
server_side=True,
|
|
||||||
cert_reqs=cert_reqs,
|
|
||||||
ca_certs=self.ca_certs)
|
|
||||||
|
|
||||||
# Optionally enable keepalive on the wsgi socket.
|
|
||||||
if self.keepalive:
|
|
||||||
dup_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
||||||
|
|
||||||
if self.keepidle is not None:
|
|
||||||
if hasattr(socket, 'TCP_KEEPIDLE'):
|
|
||||||
dup_socket.setsockopt(socket.IPPROTO_TCP,
|
|
||||||
socket.TCP_KEEPIDLE,
|
|
||||||
self.keepidle)
|
|
||||||
else:
|
|
||||||
LOG.warning("System does not support TCP_KEEPIDLE but "
|
|
||||||
"tcp_keepidle has been set. Ignoring.")
|
|
||||||
|
|
||||||
self.greenthread = self.pool.spawn(self._run,
|
|
||||||
self.application,
|
|
||||||
dup_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 stop(self):
|
|
||||||
if self.greenthread is not None:
|
|
||||||
self.greenthread.kill()
|
|
||||||
|
|
||||||
def wait(self):
|
|
||||||
"""Wait until all servers have completed running."""
|
|
||||||
try:
|
|
||||||
self.pool.waitall()
|
|
||||||
except KeyboardInterrupt: # nosec
|
|
||||||
# If CTRL-C, just break out of the loop.
|
|
||||||
pass
|
|
||||||
except greenlet.GreenletExit: # nosec
|
|
||||||
# If exiting, break out of the loop.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""Required by the service interface.
|
|
||||||
|
|
||||||
The service interface is used by the launcher when receiving a
|
|
||||||
SIGHUP. The service interface is defined in
|
|
||||||
oslo_service.service.Service.
|
|
||||||
|
|
||||||
Keystone does not need to do anything here.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _run(self, application, socket):
|
|
||||||
"""Start a WSGI server with a new green thread pool."""
|
|
||||||
logger = log.getLogger('eventlet.wsgi.server')
|
|
||||||
|
|
||||||
# NOTE(dolph): [eventlet_server] client_socket_timeout is required to
|
|
||||||
# be an integer in keystone.conf, but in order to make
|
|
||||||
# eventlet.wsgi.server() wait forever, we pass None instead of 0.
|
|
||||||
socket_timeout = CONF.eventlet_server.client_socket_timeout or None
|
|
||||||
|
|
||||||
try:
|
|
||||||
eventlet.wsgi.server(
|
|
||||||
socket, application, log=EventletFilteringLogger(logger),
|
|
||||||
debug=False, keepalive=CONF.eventlet_server.wsgi_keep_alive,
|
|
||||||
socket_timeout=socket_timeout)
|
|
||||||
except greenlet.GreenletExit: # nosec
|
|
||||||
# Wait until all servers have completed running
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
LOG.exception(_LE('Server error'))
|
|
||||||
raise
|
|
@ -14,11 +14,11 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import subprocess # nosec : see comments in the code below
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone.i18n import _LI, _LE, _LW
|
from keystone.i18n import _LI, _LE, _LW
|
||||||
|
|
||||||
@ -42,22 +42,22 @@ class BaseCertificateConfigure(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, conf_obj, server_conf_obj, keystone_user,
|
def __init__(self, conf_obj, keystone_user,
|
||||||
keystone_group, rebuild, **kwargs):
|
keystone_group, rebuild, **kwargs):
|
||||||
self.conf_dir = os.path.dirname(server_conf_obj.ca_certs)
|
self.conf_dir = os.path.dirname(conf_obj.ca_certs)
|
||||||
self.use_keystone_user = keystone_user
|
self.use_keystone_user = keystone_user
|
||||||
self.use_keystone_group = keystone_group
|
self.use_keystone_group = keystone_group
|
||||||
self.rebuild = rebuild
|
self.rebuild = rebuild
|
||||||
self.ssl_config_file_name = os.path.join(self.conf_dir, "openssl.conf")
|
self.ssl_config_file_name = os.path.join(self.conf_dir, "openssl.conf")
|
||||||
self.request_file_name = os.path.join(self.conf_dir, "req.pem")
|
self.request_file_name = os.path.join(self.conf_dir, "req.pem")
|
||||||
self.ssl_dictionary = {'conf_dir': self.conf_dir,
|
self.ssl_dictionary = {'conf_dir': self.conf_dir,
|
||||||
'ca_cert': server_conf_obj.ca_certs,
|
'ca_cert': conf_obj.ca_certs,
|
||||||
'default_md': 'default',
|
'default_md': 'default',
|
||||||
'ssl_config': self.ssl_config_file_name,
|
'ssl_config': self.ssl_config_file_name,
|
||||||
'ca_private_key': conf_obj.ca_key,
|
'ca_private_key': conf_obj.ca_key,
|
||||||
'request_file': self.request_file_name,
|
'request_file': self.request_file_name,
|
||||||
'signing_key': server_conf_obj.keyfile,
|
'signing_key': conf_obj.keyfile,
|
||||||
'signing_cert': server_conf_obj.certfile,
|
'signing_cert': conf_obj.certfile,
|
||||||
'key_size': int(conf_obj.key_size),
|
'key_size': int(conf_obj.key_size),
|
||||||
'valid_days': int(conf_obj.valid_days),
|
'valid_days': int(conf_obj.valid_days),
|
||||||
'cert_subject': conf_obj.cert_subject}
|
'cert_subject': conf_obj.cert_subject}
|
||||||
@ -65,12 +65,12 @@ class BaseCertificateConfigure(object):
|
|||||||
try:
|
try:
|
||||||
# OpenSSL 1.0 and newer support default_md = default,
|
# OpenSSL 1.0 and newer support default_md = default,
|
||||||
# older versions do not
|
# older versions do not
|
||||||
openssl_ver = environment.subprocess.check_output( # the arguments
|
openssl_ver = subprocess.check_output( # nosec : the arguments
|
||||||
# are hardcoded and just check the openssl version
|
# are hardcoded and just check the openssl version
|
||||||
['openssl', 'version'])
|
['openssl', 'version'])
|
||||||
if b'OpenSSL 0.' in openssl_ver:
|
if b'OpenSSL 0.' in openssl_ver:
|
||||||
self.ssl_dictionary['default_md'] = 'sha1'
|
self.ssl_dictionary['default_md'] = 'sha1'
|
||||||
except environment.subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
LOG.warning(_LW('Failed to invoke ``openssl version``, '
|
LOG.warning(_LW('Failed to invoke ``openssl version``, '
|
||||||
'assuming is v1.0 or newer'))
|
'assuming is v1.0 or newer'))
|
||||||
self.ssl_dictionary.update(kwargs)
|
self.ssl_dictionary.update(kwargs)
|
||||||
@ -81,12 +81,12 @@ class BaseCertificateConfigure(object):
|
|||||||
try:
|
try:
|
||||||
# NOTE(shaleh): use check_output instead of the simpler
|
# NOTE(shaleh): use check_output instead of the simpler
|
||||||
# `check_call()` in order to log any output from an error.
|
# `check_call()` in order to log any output from an error.
|
||||||
environment.subprocess.check_output( # the arguments being passed
|
subprocess.check_output( # nosec : the arguments being passed
|
||||||
# in are defined in this file and trusted to build CAs, keys
|
# in are defined in this file and trusted to build CAs, keys
|
||||||
# and certs
|
# and certs
|
||||||
to_exec,
|
to_exec,
|
||||||
stderr=environment.subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
except environment.subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
LOG.error(_LE('Command %(to_exec)s exited with %(retcode)s '
|
LOG.error(_LE('Command %(to_exec)s exited with %(retcode)s '
|
||||||
'- %(output)s'),
|
'- %(output)s'),
|
||||||
{'to_exec': to_exec,
|
{'to_exec': to_exec,
|
||||||
@ -249,9 +249,8 @@ class ConfigurePKI(BaseCertificateConfigure):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, keystone_user, keystone_group, rebuild=False):
|
def __init__(self, keystone_user, keystone_group, rebuild=False):
|
||||||
super(ConfigurePKI, self).__init__(CONF.signing, CONF.signing,
|
super(ConfigurePKI, self).__init__(CONF.signing, keystone_user,
|
||||||
keystone_user, keystone_group,
|
keystone_group, rebuild=rebuild)
|
||||||
rebuild=rebuild)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigureSSL(BaseCertificateConfigure):
|
class ConfigureSSL(BaseCertificateConfigure):
|
||||||
@ -262,9 +261,8 @@ class ConfigureSSL(BaseCertificateConfigure):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, keystone_user, keystone_group, rebuild=False):
|
def __init__(self, keystone_user, keystone_group, rebuild=False):
|
||||||
super(ConfigureSSL, self).__init__(CONF.ssl, CONF.eventlet_server_ssl,
|
super(ConfigureSSL, self).__init__(CONF.ssl, keystone_user,
|
||||||
keystone_user, keystone_group,
|
keystone_group, rebuild=rebuild)
|
||||||
rebuild=rebuild)
|
|
||||||
|
|
||||||
|
|
||||||
BaseCertificateConfigure.sslconfig = """
|
BaseCertificateConfigure.sslconfig = """
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
import subprocess # nosec : see comments in the code below
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -31,7 +32,6 @@ xmldsig = importutils.try_import("saml2.xmldsig")
|
|||||||
if not xmldsig:
|
if not xmldsig:
|
||||||
xmldsig = importutils.try_import("xmldsig")
|
xmldsig = importutils.try_import("xmldsig")
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _, _LE
|
from keystone.i18n import _, _LE
|
||||||
@ -422,7 +422,6 @@ def _sign_assertion(assertion):
|
|||||||
nspair={'saml': saml2.NAMESPACE,
|
nspair={'saml': saml2.NAMESPACE,
|
||||||
'xmldsig': xmldsig.NAMESPACE}))
|
'xmldsig': xmldsig.NAMESPACE}))
|
||||||
command_list.append(file_path)
|
command_list.append(file_path)
|
||||||
subprocess = environment.subprocess
|
|
||||||
stdout = subprocess.check_output(command_list, # nosec : The contents
|
stdout = subprocess.check_output(command_list, # nosec : The contents
|
||||||
# of the command list are coming from
|
# of the command list are coming from
|
||||||
# a trusted source because the
|
# a trusted source because the
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
|
|
||||||
# Copyright 2013 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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 logging
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils
|
|
||||||
from oslo_config import cfg
|
|
||||||
import oslo_i18n
|
|
||||||
from oslo_service import service
|
|
||||||
from oslo_service import systemd
|
|
||||||
import pbr.version
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE(dstanek): i18n.enable_lazy() must be called before
|
|
||||||
# keystone.i18n._() is called to ensure it has the desired lazy lookup
|
|
||||||
# behavior. This includes cases, like keystone.exceptions, where
|
|
||||||
# keystone.i18n._() is called at import time.
|
|
||||||
oslo_i18n.enable_lazy()
|
|
||||||
|
|
||||||
|
|
||||||
from keystone.common import config
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import profiler
|
|
||||||
from keystone.common import utils
|
|
||||||
from keystone.i18n import _
|
|
||||||
from keystone.server import common
|
|
||||||
from keystone.version import service as keystone_service
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class ServerWrapper(object):
|
|
||||||
"""Wrap a Server with some launching info & capabilities."""
|
|
||||||
|
|
||||||
def __init__(self, server, workers):
|
|
||||||
self.server = server
|
|
||||||
self.workers = workers
|
|
||||||
|
|
||||||
def launch_with(self, launcher):
|
|
||||||
self.server.listen()
|
|
||||||
if self.workers > 1:
|
|
||||||
# Use multi-process launcher
|
|
||||||
launcher.launch_service(self.server, self.workers)
|
|
||||||
else:
|
|
||||||
# Use single process launcher
|
|
||||||
launcher.launch_service(self.server)
|
|
||||||
|
|
||||||
|
|
||||||
def create_server(conf, name, host, port, workers):
|
|
||||||
app = keystone_service.loadapp('config:%s' % conf, name)
|
|
||||||
server = environment.Server(app, host=host, port=port,
|
|
||||||
keepalive=CONF.eventlet_server.tcp_keepalive,
|
|
||||||
keepidle=CONF.eventlet_server.tcp_keepidle)
|
|
||||||
profiler.setup(name, host)
|
|
||||||
if CONF.eventlet_server_ssl.enable:
|
|
||||||
server.set_ssl(CONF.eventlet_server_ssl.certfile,
|
|
||||||
CONF.eventlet_server_ssl.keyfile,
|
|
||||||
CONF.eventlet_server_ssl.ca_certs,
|
|
||||||
CONF.eventlet_server_ssl.cert_required)
|
|
||||||
return name, ServerWrapper(server, workers)
|
|
||||||
|
|
||||||
|
|
||||||
def serve(*servers):
|
|
||||||
logging.warning(_('Running keystone via eventlet is deprecated as of Kilo '
|
|
||||||
'in favor of running in a WSGI server (e.g. mod_wsgi). '
|
|
||||||
'Support for keystone under eventlet will be removed in '
|
|
||||||
'the "M"-Release.'))
|
|
||||||
if max([server[1].workers for server in servers]) > 1:
|
|
||||||
launcher = service.ProcessLauncher(CONF)
|
|
||||||
else:
|
|
||||||
launcher = service.ServiceLauncher(CONF)
|
|
||||||
|
|
||||||
for name, server in servers:
|
|
||||||
try:
|
|
||||||
server.launch_with(launcher)
|
|
||||||
except socket.error:
|
|
||||||
logging.exception(_('Failed to start the %(name)s server') % {
|
|
||||||
'name': name})
|
|
||||||
raise
|
|
||||||
|
|
||||||
# notify calling process we are ready to serve
|
|
||||||
systemd.notify_once()
|
|
||||||
|
|
||||||
for name, server in servers:
|
|
||||||
launcher.wait()
|
|
||||||
|
|
||||||
|
|
||||||
def _get_workers(worker_type_config_opt):
|
|
||||||
# Get the value from config, if the config value is None (not set), return
|
|
||||||
# the number of cpus with a minimum of 2.
|
|
||||||
worker_count = CONF.eventlet_server.get(worker_type_config_opt)
|
|
||||||
if not worker_count:
|
|
||||||
worker_count = max(2, processutils.get_worker_count())
|
|
||||||
return worker_count
|
|
||||||
|
|
||||||
|
|
||||||
def configure_threading():
|
|
||||||
monkeypatch_thread = not CONF.standard_threads
|
|
||||||
pydev_debug_url = utils.setup_remote_pydev_debug()
|
|
||||||
if pydev_debug_url:
|
|
||||||
# in order to work around errors caused by monkey patching we have to
|
|
||||||
# set the thread to False. An explanation is here:
|
|
||||||
# http://lists.openstack.org/pipermail/openstack-dev/2012-August/
|
|
||||||
# 000794.html
|
|
||||||
monkeypatch_thread = False
|
|
||||||
environment.use_eventlet(monkeypatch_thread)
|
|
||||||
|
|
||||||
|
|
||||||
def run(possible_topdir):
|
|
||||||
dev_conf = os.path.join(possible_topdir,
|
|
||||||
'etc',
|
|
||||||
'keystone.conf')
|
|
||||||
config_files = None
|
|
||||||
if os.path.exists(dev_conf):
|
|
||||||
config_files = [dev_conf]
|
|
||||||
|
|
||||||
common.configure(
|
|
||||||
version=pbr.version.VersionInfo('keystone').version_string(),
|
|
||||||
config_files=config_files,
|
|
||||||
pre_setup_logging_fn=configure_threading)
|
|
||||||
|
|
||||||
paste_config = config.find_paste_config()
|
|
||||||
|
|
||||||
def create_servers():
|
|
||||||
admin_worker_count = _get_workers('admin_workers')
|
|
||||||
public_worker_count = _get_workers('public_workers')
|
|
||||||
|
|
||||||
servers = []
|
|
||||||
servers.append(create_server(paste_config,
|
|
||||||
'admin',
|
|
||||||
CONF.eventlet_server.admin_bind_host,
|
|
||||||
CONF.eventlet_server.admin_port,
|
|
||||||
admin_worker_count))
|
|
||||||
servers.append(create_server(paste_config,
|
|
||||||
'main',
|
|
||||||
CONF.eventlet_server.public_bind_host,
|
|
||||||
CONF.eventlet_server.public_port,
|
|
||||||
public_worker_count))
|
|
||||||
return servers
|
|
||||||
|
|
||||||
_unused, servers = common.setup_backends(
|
|
||||||
startup_application_fn=create_servers)
|
|
||||||
serve(*servers)
|
|
@ -28,7 +28,6 @@ oslo_i18n.enable_lazy()
|
|||||||
|
|
||||||
|
|
||||||
from keystone.common import config
|
from keystone.common import config
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.server import common
|
from keystone.server import common
|
||||||
from keystone.version import service as keystone_service
|
from keystone.version import service as keystone_service
|
||||||
|
|
||||||
@ -43,8 +42,6 @@ def initialize_application(name, post_log_configured_function=lambda: None):
|
|||||||
if CONF.debug:
|
if CONF.debug:
|
||||||
CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
|
CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
|
||||||
|
|
||||||
environment.use_stdlib()
|
|
||||||
|
|
||||||
post_log_configured_function()
|
post_log_configured_function()
|
||||||
|
|
||||||
def loadapp():
|
def loadapp():
|
||||||
|
@ -42,12 +42,6 @@ from sqlalchemy import exc
|
|||||||
import testtools
|
import testtools
|
||||||
from testtools import testcase
|
from testtools import testcase
|
||||||
|
|
||||||
# NOTE(ayoung)
|
|
||||||
# environment.use_eventlet must run before any of the code that will
|
|
||||||
# call the eventlet monkeypatching.
|
|
||||||
from keystone.common import environment # noqa
|
|
||||||
environment.use_eventlet()
|
|
||||||
|
|
||||||
from keystone import auth
|
from keystone import auth
|
||||||
from keystone.common import config
|
from keystone.common import config
|
||||||
from keystone.common import dependency
|
from keystone.common import dependency
|
||||||
@ -648,7 +642,7 @@ class TestCase(BaseTestCase):
|
|||||||
# log module. This is not in a function or otherwise available to use
|
# log module. This is not in a function or otherwise available to use
|
||||||
# without having a CONF object to setup logging. This should help to
|
# without having a CONF object to setup logging. This should help to
|
||||||
# reduce the log size by limiting what we log (similar to how Keystone
|
# reduce the log size by limiting what we log (similar to how Keystone
|
||||||
# would run under mod_wsgi or eventlet).
|
# would run under mod_wsgi).
|
||||||
for pair in CONF.default_log_levels:
|
for pair in CONF.default_log_levels:
|
||||||
mod, _sep, level_name = pair.partition('=')
|
mod, _sep, level_name = pair.partition('=')
|
||||||
logger = logging.getLogger(mod)
|
logger = logging.getLogger(mod)
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
# Copyright 2013 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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 __future__ import absolute_import
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
from oslo_config import cfg
|
|
||||||
from paste import deploy
|
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
MAIN = 'main'
|
|
||||||
ADMIN = 'admin'
|
|
||||||
|
|
||||||
|
|
||||||
class AppServer(fixtures.Fixture):
|
|
||||||
"""A fixture for managing an application server instance."""
|
|
||||||
|
|
||||||
def __init__(self, config, name, cert=None, key=None, ca=None,
|
|
||||||
cert_required=False, host='127.0.0.1', port=0):
|
|
||||||
super(AppServer, self).__init__()
|
|
||||||
self.config = config
|
|
||||||
self.name = name
|
|
||||||
self.cert = cert
|
|
||||||
self.key = key
|
|
||||||
self.ca = ca
|
|
||||||
self.cert_required = cert_required
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(AppServer, self).setUp()
|
|
||||||
|
|
||||||
app = deploy.loadapp(self.config, name=self.name)
|
|
||||||
self.server = environment.Server(app, self.host, self.port)
|
|
||||||
self._setup_SSL_if_requested()
|
|
||||||
self.server.start(key='socket')
|
|
||||||
|
|
||||||
# some tests need to know the port we ran on.
|
|
||||||
self.port = self.server.socket_info['socket'][1]
|
|
||||||
self._update_config_opt()
|
|
||||||
|
|
||||||
self.addCleanup(self.server.stop)
|
|
||||||
|
|
||||||
def _setup_SSL_if_requested(self):
|
|
||||||
# TODO(dstanek): fix environment.Server to take a SSLOpts instance
|
|
||||||
# so that the params are either always set or not
|
|
||||||
if (self.cert is not None and
|
|
||||||
self.ca is not None and
|
|
||||||
self.key is not None):
|
|
||||||
self.server.set_ssl(certfile=self.cert,
|
|
||||||
keyfile=self.key,
|
|
||||||
ca_certs=self.ca,
|
|
||||||
cert_required=self.cert_required)
|
|
||||||
|
|
||||||
def _update_config_opt(self):
|
|
||||||
"""Update the config with the actual port used."""
|
|
||||||
opt_name = self._get_config_option_for_section_name()
|
|
||||||
CONF.set_override(opt_name, self.port, group='eventlet_server',
|
|
||||||
enforce_type=True)
|
|
||||||
|
|
||||||
def _get_config_option_for_section_name(self):
|
|
||||||
"""Map Paster config section names to port option names."""
|
|
||||||
return {'admin': 'admin_port', 'main': 'public_port'}[self.name]
|
|
@ -15,12 +15,12 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import openssl
|
from keystone.common import openssl
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.tests import unit
|
from keystone.tests import unit
|
||||||
@ -60,14 +60,6 @@ class CertSetupTestCase(rest.RestfulTestCase):
|
|||||||
ca_certs=ca_certs,
|
ca_certs=ca_certs,
|
||||||
ca_key=ca_key,
|
ca_key=ca_key,
|
||||||
keyfile=os.path.join(KEYDIR, 'signing_key.pem'))
|
keyfile=os.path.join(KEYDIR, 'signing_key.pem'))
|
||||||
self.config_fixture.config(
|
|
||||||
group='ssl',
|
|
||||||
ca_key=ca_key)
|
|
||||||
self.config_fixture.config(
|
|
||||||
group='eventlet_server_ssl',
|
|
||||||
ca_certs=ca_certs,
|
|
||||||
certfile=os.path.join(CERTDIR, 'keystone.pem'),
|
|
||||||
keyfile=os.path.join(KEYDIR, 'keystonekey.pem'))
|
|
||||||
self.config_fixture.config(group='token', provider='pkiz')
|
self.config_fixture.config(group='token', provider='pkiz')
|
||||||
|
|
||||||
def test_can_handle_missing_certs(self):
|
def test_can_handle_missing_certs(self):
|
||||||
@ -93,13 +85,6 @@ class CertSetupTestCase(rest.RestfulTestCase):
|
|||||||
self.assertTrue(os.path.exists(CONF.signing.ca_certs))
|
self.assertTrue(os.path.exists(CONF.signing.ca_certs))
|
||||||
self.assertTrue(os.path.exists(CONF.signing.keyfile))
|
self.assertTrue(os.path.exists(CONF.signing.keyfile))
|
||||||
|
|
||||||
def test_create_ssl_certs(self, rebuild=False):
|
|
||||||
ssl = openssl.ConfigureSSL(None, None, rebuild=rebuild)
|
|
||||||
ssl.run()
|
|
||||||
self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.ca_certs))
|
|
||||||
self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.certfile))
|
|
||||||
self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.keyfile))
|
|
||||||
|
|
||||||
def test_fetch_signing_cert(self, rebuild=False):
|
def test_fetch_signing_cert(self, rebuild=False):
|
||||||
pki = openssl.ConfigurePKI(None, None, rebuild=rebuild)
|
pki = openssl.ConfigurePKI(None, None, rebuild=rebuild)
|
||||||
pki.run()
|
pki.run()
|
||||||
@ -156,17 +141,6 @@ class CertSetupTestCase(rest.RestfulTestCase):
|
|||||||
|
|
||||||
self.assertNotEqual(cert_file1, cert_file2)
|
self.assertNotEqual(cert_file1, cert_file2)
|
||||||
|
|
||||||
def test_ssl_certs_rebuild(self):
|
|
||||||
self.test_create_ssl_certs()
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file1 = f.read()
|
|
||||||
|
|
||||||
self.test_create_ssl_certs(rebuild=True)
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file2 = f.read()
|
|
||||||
|
|
||||||
self.assertNotEqual(cert_file1, cert_file2)
|
|
||||||
|
|
||||||
@mock.patch.object(os, 'remove')
|
@mock.patch.object(os, 'remove')
|
||||||
def test_rebuild_pki_certs_remove_error(self, mock_remove):
|
def test_rebuild_pki_certs_remove_error(self, mock_remove):
|
||||||
self.test_create_pki_certs()
|
self.test_create_pki_certs()
|
||||||
@ -180,19 +154,6 @@ class CertSetupTestCase(rest.RestfulTestCase):
|
|||||||
|
|
||||||
self.assertEqual(cert_file1, cert_file2)
|
self.assertEqual(cert_file1, cert_file2)
|
||||||
|
|
||||||
@mock.patch.object(os, 'remove')
|
|
||||||
def test_rebuild_ssl_certs_remove_error(self, mock_remove):
|
|
||||||
self.test_create_ssl_certs()
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file1 = f.read()
|
|
||||||
|
|
||||||
mock_remove.side_effect = OSError()
|
|
||||||
self.test_create_ssl_certs(rebuild=True)
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file2 = f.read()
|
|
||||||
|
|
||||||
self.assertEqual(cert_file1, cert_file2)
|
|
||||||
|
|
||||||
def test_create_pki_certs_twice_without_rebuild(self):
|
def test_create_pki_certs_twice_without_rebuild(self):
|
||||||
self.test_create_pki_certs()
|
self.test_create_pki_certs()
|
||||||
with open(CONF.signing.certfile) as f:
|
with open(CONF.signing.certfile) as f:
|
||||||
@ -204,40 +165,29 @@ class CertSetupTestCase(rest.RestfulTestCase):
|
|||||||
|
|
||||||
self.assertEqual(cert_file1, cert_file2)
|
self.assertEqual(cert_file1, cert_file2)
|
||||||
|
|
||||||
def test_create_ssl_certs_twice_without_rebuild(self):
|
|
||||||
self.test_create_ssl_certs()
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file1 = f.read()
|
|
||||||
|
|
||||||
self.test_create_ssl_certs()
|
|
||||||
with open(CONF.eventlet_server_ssl.certfile) as f:
|
|
||||||
cert_file2 = f.read()
|
|
||||||
|
|
||||||
self.assertEqual(cert_file1, cert_file2)
|
|
||||||
|
|
||||||
|
|
||||||
class TestExecCommand(unit.TestCase):
|
class TestExecCommand(unit.TestCase):
|
||||||
|
|
||||||
@mock.patch.object(environment.subprocess.Popen, 'poll')
|
@mock.patch.object(subprocess.Popen, 'poll')
|
||||||
def test_running_a_successful_command(self, mock_poll):
|
def test_running_a_successful_command(self, mock_poll):
|
||||||
mock_poll.return_value = 0
|
mock_poll.return_value = 0
|
||||||
|
|
||||||
ssl = openssl.ConfigureSSL('keystone_user', 'keystone_group')
|
ssl = openssl.ConfigurePKI('keystone_user', 'keystone_group')
|
||||||
ssl.exec_command(['ls'])
|
ssl.exec_command(['ls'])
|
||||||
|
|
||||||
@mock.patch.object(environment.subprocess, 'check_output')
|
@mock.patch.object(subprocess, 'check_output')
|
||||||
def test_running_an_invalid_command(self, mock_check_output):
|
def test_running_an_invalid_command(self, mock_check_output):
|
||||||
cmd = ['ls']
|
cmd = ['ls']
|
||||||
|
|
||||||
output = 'this is the output string'
|
output = 'this is the output string'
|
||||||
|
|
||||||
error = environment.subprocess.CalledProcessError(returncode=1,
|
error = subprocess.CalledProcessError(returncode=1,
|
||||||
cmd=cmd,
|
cmd=cmd,
|
||||||
output=output)
|
output=output)
|
||||||
mock_check_output.side_effect = error
|
mock_check_output.side_effect = error
|
||||||
|
|
||||||
ssl = openssl.ConfigureSSL('keystone_user', 'keystone_group')
|
ssl = openssl.ConfigurePKI('keystone_user', 'keystone_group')
|
||||||
e = self.assertRaises(environment.subprocess.CalledProcessError,
|
e = self.assertRaises(subprocess.CalledProcessError,
|
||||||
ssl.exec_command,
|
ssl.exec_command,
|
||||||
cmd)
|
cmd)
|
||||||
self.assertThat(e.output, matchers.Equals(output))
|
self.assertThat(e.output, matchers.Equals(output))
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
# Copyright 2012 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.tests import unit
|
|
||||||
from keystone.tests.unit.ksfixtures import appserver
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class IPv6TestCase(unit.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.skip_if_no_ipv6()
|
|
||||||
super(IPv6TestCase, self).setUp()
|
|
||||||
self.load_backends()
|
|
||||||
|
|
||||||
def test_ipv6_ok(self):
|
|
||||||
"""Make sure both public and admin API work with ipv6."""
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, host="::1"):
|
|
||||||
conn = environment.httplib.HTTPConnection(
|
|
||||||
'::1', CONF.eventlet_server.admin_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, host="::1"):
|
|
||||||
conn = environment.httplib.HTTPConnection(
|
|
||||||
'::1', CONF.eventlet_server.public_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
@ -1,186 +0,0 @@
|
|||||||
# Copyright 2012 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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 ssl
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.tests import unit
|
|
||||||
from keystone.tests.unit.ksfixtures import appserver
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
CERTDIR = unit.dirs.root('examples', 'pki', 'certs')
|
|
||||||
KEYDIR = unit.dirs.root('examples', 'pki', 'private')
|
|
||||||
CERT = os.path.join(CERTDIR, 'ssl_cert.pem')
|
|
||||||
KEY = os.path.join(KEYDIR, 'ssl_key.pem')
|
|
||||||
CA = os.path.join(CERTDIR, 'cacert.pem')
|
|
||||||
CLIENT = os.path.join(CERTDIR, 'middleware.pem')
|
|
||||||
|
|
||||||
|
|
||||||
class SSLTestCase(unit.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(SSLTestCase, self).setUp()
|
|
||||||
raise self.skipTest('SSL Version and Ciphers cannot be configured '
|
|
||||||
'with eventlet, some platforms have disabled '
|
|
||||||
'SSLv3. See bug 1381365.')
|
|
||||||
# NOTE(morganfainberg): It has been determined that this
|
|
||||||
# will not be fixed. These tests should be re-enabled for the full
|
|
||||||
# functional test suite when run against an SSL terminated
|
|
||||||
# endpoint. Some distributions/environments have patched OpenSSL to
|
|
||||||
# not have SSLv3 at all due to POODLE and this causes differing
|
|
||||||
# behavior depending on platform. See bug 1381365 for more information.
|
|
||||||
|
|
||||||
# NOTE(jamespage):
|
|
||||||
# Deal with more secure certificate chain verification
|
|
||||||
# introduced in python 2.7.9 under PEP-0476
|
|
||||||
# https://github.com/python/peps/blob/master/pep-0476.txt
|
|
||||||
self.context = None
|
|
||||||
if hasattr(ssl, '_create_unverified_context'):
|
|
||||||
self.context = ssl._create_unverified_context()
|
|
||||||
self.load_backends()
|
|
||||||
|
|
||||||
def get_HTTPSConnection(self, *args):
|
|
||||||
"""Simple helper to configure HTTPSConnection objects."""
|
|
||||||
if self.context:
|
|
||||||
return environment.httplib.HTTPSConnection(
|
|
||||||
*args,
|
|
||||||
context=self.context
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return environment.httplib.HTTPSConnection(*args)
|
|
||||||
|
|
||||||
def test_1way_ssl_ok(self):
|
|
||||||
"""Make sure both public and admin API work with 1-way SSL."""
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA)
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.admin_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.public_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
def test_2way_ssl_ok(self):
|
|
||||||
"""Make sure both public and admin API work with 2-way SSL.
|
|
||||||
|
|
||||||
Requires client certificate.
|
|
||||||
"""
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, cert_required=True)
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.admin_port, CLIENT, CLIENT)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.public_port, CLIENT, CLIENT)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
def test_1way_ssl_with_ipv6_ok(self):
|
|
||||||
"""Make sure both public and admin API work with 1-way ipv6 & SSL."""
|
|
||||||
self.skip_if_no_ipv6()
|
|
||||||
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, host="::1")
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'::1', CONF.eventlet_server.admin_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'::1', CONF.eventlet_server.public_port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
def test_2way_ssl_with_ipv6_ok(self):
|
|
||||||
"""Make sure both public and admin API work with 2-way ipv6 & SSL.
|
|
||||||
|
|
||||||
Requires client certificate.
|
|
||||||
"""
|
|
||||||
self.skip_if_no_ipv6()
|
|
||||||
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA,
|
|
||||||
cert_required=True, host="::1")
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'::1', CONF.eventlet_server.admin_port, CLIENT, CLIENT)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'::1', CONF.eventlet_server.public_port, CLIENT, CLIENT)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
resp = conn.getresponse()
|
|
||||||
self.assertEqual(300, resp.status)
|
|
||||||
|
|
||||||
def test_2way_ssl_fail(self):
|
|
||||||
"""Expect to fail when client does not present proper certificate."""
|
|
||||||
paste_conf = self._paste_config('keystone')
|
|
||||||
ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, cert_required=True)
|
|
||||||
|
|
||||||
# Verify Admin
|
|
||||||
with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.admin_port)
|
|
||||||
try:
|
|
||||||
conn.request('GET', '/')
|
|
||||||
self.fail('Admin API shoulda failed with SSL handshake!')
|
|
||||||
except ssl.SSLError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Verify Public
|
|
||||||
with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs):
|
|
||||||
conn = self.get_HTTPSConnection(
|
|
||||||
'127.0.0.1', CONF.eventlet_server.public_port)
|
|
||||||
try:
|
|
||||||
conn.request('GET', '/')
|
|
||||||
self.fail('Public API shoulda failed with SSL handshake!')
|
|
||||||
except ssl.SSLError:
|
|
||||||
pass
|
|
@ -798,17 +798,10 @@ class PKIProviderTests(object):
|
|||||||
from keystoneclient.common import cms
|
from keystoneclient.common import cms
|
||||||
self.cms = cms
|
self.cms = cms
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
self.environment = environment
|
|
||||||
|
|
||||||
old_cms_subprocess = cms.subprocess
|
old_cms_subprocess = cms.subprocess
|
||||||
self.addCleanup(setattr, cms, 'subprocess', old_cms_subprocess)
|
self.addCleanup(setattr, cms, 'subprocess', old_cms_subprocess)
|
||||||
|
|
||||||
old_env_subprocess = environment.subprocess
|
|
||||||
self.addCleanup(setattr, environment, 'subprocess', old_env_subprocess)
|
|
||||||
|
|
||||||
self.cms.subprocess = self.target_subprocess
|
self.cms.subprocess = self.target_subprocess
|
||||||
self.environment.subprocess = self.target_subprocess
|
|
||||||
|
|
||||||
# force module reload so the imports get re-evaluated
|
# force module reload so the imports get re-evaluated
|
||||||
reload_module(pki)
|
reload_module(pki)
|
||||||
@ -825,16 +818,6 @@ class PKIProviderTests(object):
|
|||||||
token_data)
|
token_data)
|
||||||
|
|
||||||
|
|
||||||
class TestPKIProviderWithEventlet(PKIProviderTests, unit.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
# force keystoneclient.common.cms to use eventlet's subprocess
|
|
||||||
from eventlet.green import subprocess
|
|
||||||
self.target_subprocess = subprocess
|
|
||||||
|
|
||||||
super(TestPKIProviderWithEventlet, self).setUp()
|
|
||||||
|
|
||||||
|
|
||||||
class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase):
|
class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import subprocess
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -33,7 +34,6 @@ if not xmldsig:
|
|||||||
xmldsig = importutils.try_import("xmldsig")
|
xmldsig = importutils.try_import("xmldsig")
|
||||||
|
|
||||||
from keystone.auth import controllers as auth_controllers
|
from keystone.auth import controllers as auth_controllers
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.contrib.federation import routers
|
from keystone.contrib.federation import routers
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.federation import controllers as federation_controllers
|
from keystone.federation import controllers as federation_controllers
|
||||||
@ -49,8 +49,6 @@ from keystone.tests.unit import utils
|
|||||||
from keystone.token.providers import common as token_common
|
from keystone.token.providers import common as token_common
|
||||||
|
|
||||||
|
|
||||||
subprocess = environment.subprocess
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
ROOTDIR = os.path.dirname(os.path.abspath(__file__))
|
ROOTDIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
XMLDIR = os.path.join(ROOTDIR, 'saml2/')
|
XMLDIR = os.path.join(ROOTDIR, 'saml2/')
|
||||||
|
@ -672,17 +672,15 @@ class VersionTestCase(unit.TestCase):
|
|||||||
self.public_app = self.loadapp('keystone', 'main')
|
self.public_app = self.loadapp('keystone', 'main')
|
||||||
self.admin_app = self.loadapp('keystone', 'admin')
|
self.admin_app = self.loadapp('keystone', 'admin')
|
||||||
|
|
||||||
|
self.admin_port = random.randint(10000, 30000)
|
||||||
|
self.public_port = random.randint(40000, 60000)
|
||||||
|
|
||||||
self.config_fixture.config(
|
self.config_fixture.config(
|
||||||
public_endpoint='http://localhost:%(public_port)d',
|
public_endpoint='http://localhost:%d' % self.public_port,
|
||||||
admin_endpoint='http://localhost:%(admin_port)d')
|
admin_endpoint='http://localhost:%d' % self.admin_port)
|
||||||
|
|
||||||
def config_overrides(self):
|
def config_overrides(self):
|
||||||
super(VersionTestCase, self).config_overrides()
|
super(VersionTestCase, self).config_overrides()
|
||||||
admin_port = random.randint(10000, 30000)
|
|
||||||
public_port = random.randint(40000, 60000)
|
|
||||||
self.config_fixture.config(group='eventlet_server',
|
|
||||||
public_port=public_port,
|
|
||||||
admin_port=admin_port)
|
|
||||||
|
|
||||||
def _paste_in_port(self, response, port):
|
def _paste_in_port(self, response, port):
|
||||||
for link in response['links']:
|
for link in response['links']:
|
||||||
@ -698,12 +696,10 @@ class VersionTestCase(unit.TestCase):
|
|||||||
for version in expected['versions']['values']:
|
for version in expected['versions']['values']:
|
||||||
if version['id'].startswith('v3'):
|
if version['id'].startswith('v3'):
|
||||||
self._paste_in_port(
|
self._paste_in_port(
|
||||||
version, 'http://localhost:%s/v3/' %
|
version, 'http://localhost:%s/v3/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
elif version['id'] == 'v2.0':
|
elif version['id'] == 'v2.0':
|
||||||
self._paste_in_port(
|
self._paste_in_port(
|
||||||
version, 'http://localhost:%s/v2.0/' %
|
version, 'http://localhost:%s/v2.0/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
self.assertThat(data, _VersionsEqual(expected))
|
self.assertThat(data, _VersionsEqual(expected))
|
||||||
|
|
||||||
def test_admin_versions(self):
|
def test_admin_versions(self):
|
||||||
@ -715,12 +711,10 @@ class VersionTestCase(unit.TestCase):
|
|||||||
for version in expected['versions']['values']:
|
for version in expected['versions']['values']:
|
||||||
if version['id'].startswith('v3'):
|
if version['id'].startswith('v3'):
|
||||||
self._paste_in_port(
|
self._paste_in_port(
|
||||||
version, 'http://localhost:%s/v3/' %
|
version, 'http://localhost:%s/v3/' % self.admin_port)
|
||||||
CONF.eventlet_server.admin_port)
|
|
||||||
elif version['id'] == 'v2.0':
|
elif version['id'] == 'v2.0':
|
||||||
self._paste_in_port(
|
self._paste_in_port(
|
||||||
version, 'http://localhost:%s/v2.0/' %
|
version, 'http://localhost:%s/v2.0/' % self.admin_port)
|
||||||
CONF.eventlet_server.admin_port)
|
|
||||||
self.assertThat(data, _VersionsEqual(expected))
|
self.assertThat(data, _VersionsEqual(expected))
|
||||||
|
|
||||||
def test_use_site_url_if_endpoint_unset(self):
|
def test_use_site_url_if_endpoint_unset(self):
|
||||||
@ -749,8 +743,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v2_VERSION_RESPONSE
|
expected = v2_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v2.0/' %
|
'http://localhost:%s/v2.0/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
def test_admin_version_v2(self):
|
def test_admin_version_v2(self):
|
||||||
@ -760,8 +753,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v2_VERSION_RESPONSE
|
expected = v2_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v2.0/' %
|
'http://localhost:%s/v2.0/' % self.admin_port)
|
||||||
CONF.eventlet_server.admin_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
def test_use_site_url_if_endpoint_unset_v2(self):
|
def test_use_site_url_if_endpoint_unset_v2(self):
|
||||||
@ -782,8 +774,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v3_VERSION_RESPONSE
|
expected = v3_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v3/' %
|
'http://localhost:%s/v3/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
@utils.wip('waiting on bug #1381961')
|
@utils.wip('waiting on bug #1381961')
|
||||||
@ -794,8 +785,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v3_VERSION_RESPONSE
|
expected = v3_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v3/' %
|
'http://localhost:%s/v3/' % self.admin_port)
|
||||||
CONF.eventlet_server.admin_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
def test_use_site_url_if_endpoint_unset_v3(self):
|
def test_use_site_url_if_endpoint_unset_v3(self):
|
||||||
@ -822,8 +812,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v3_VERSION_RESPONSE
|
expected = v3_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v3/' %
|
'http://localhost:%s/v3/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
# only v3 information should be displayed by requests to /
|
# only v3 information should be displayed by requests to /
|
||||||
@ -835,8 +824,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self._paste_in_port(v3_only_response['versions']['values'][0],
|
self._paste_in_port(v3_only_response['versions']['values'][0],
|
||||||
'http://localhost:%s/v3/' %
|
'http://localhost:%s/v3/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
resp = client.get('/')
|
resp = client.get('/')
|
||||||
self.assertEqual(300, resp.status_int)
|
self.assertEqual(300, resp.status_int)
|
||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
@ -855,8 +843,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
expected = v2_VERSION_RESPONSE
|
expected = v2_VERSION_RESPONSE
|
||||||
self._paste_in_port(expected['version'],
|
self._paste_in_port(expected['version'],
|
||||||
'http://localhost:%s/v2.0/' %
|
'http://localhost:%s/v2.0/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
self.assertEqual(expected, data)
|
self.assertEqual(expected, data)
|
||||||
|
|
||||||
# only v2 information should be displayed by requests to /
|
# only v2 information should be displayed by requests to /
|
||||||
@ -868,8 +855,7 @@ class VersionTestCase(unit.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self._paste_in_port(v2_only_response['versions']['values'][0],
|
self._paste_in_port(v2_only_response['versions']['values'][0],
|
||||||
'http://localhost:%s/v2.0/' %
|
'http://localhost:%s/v2.0/' % self.public_port)
|
||||||
CONF.eventlet_server.public_port)
|
|
||||||
resp = client.get('/')
|
resp = client.get('/')
|
||||||
self.assertEqual(300, resp.status_int)
|
self.assertEqual(300, resp.status_int)
|
||||||
data = jsonutils.loads(resp.body)
|
data = jsonutils.loads(resp.body)
|
||||||
@ -971,17 +957,15 @@ class VersionSingleAppTestCase(unit.TestCase):
|
|||||||
super(VersionSingleAppTestCase, self).setUp()
|
super(VersionSingleAppTestCase, self).setUp()
|
||||||
self.load_backends()
|
self.load_backends()
|
||||||
|
|
||||||
|
self.admin_port = random.randint(10000, 30000)
|
||||||
|
self.public_port = random.randint(40000, 60000)
|
||||||
|
|
||||||
self.config_fixture.config(
|
self.config_fixture.config(
|
||||||
public_endpoint='http://localhost:%(public_port)d',
|
public_endpoint='http://localhost:%d' % self.public_port,
|
||||||
admin_endpoint='http://localhost:%(admin_port)d')
|
admin_endpoint='http://localhost:%d' % self.admin_port)
|
||||||
|
|
||||||
def config_overrides(self):
|
def config_overrides(self):
|
||||||
super(VersionSingleAppTestCase, self).config_overrides()
|
super(VersionSingleAppTestCase, self).config_overrides()
|
||||||
admin_port = random.randint(10000, 30000)
|
|
||||||
public_port = random.randint(40000, 60000)
|
|
||||||
self.config_fixture.config(group='eventlet_server',
|
|
||||||
public_port=public_port,
|
|
||||||
admin_port=admin_port)
|
|
||||||
|
|
||||||
def _paste_in_port(self, response, port):
|
def _paste_in_port(self, response, port):
|
||||||
for link in response['links']:
|
for link in response['links']:
|
||||||
@ -991,9 +975,9 @@ class VersionSingleAppTestCase(unit.TestCase):
|
|||||||
def _test_version(self, app_name):
|
def _test_version(self, app_name):
|
||||||
def app_port():
|
def app_port():
|
||||||
if app_name == 'admin':
|
if app_name == 'admin':
|
||||||
return CONF.eventlet_server.admin_port
|
return self.admin_port
|
||||||
else:
|
else:
|
||||||
return CONF.eventlet_server.public_port
|
return self.public_port
|
||||||
app = self.loadapp('keystone', app_name)
|
app = self.loadapp('keystone', app_name)
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
resp = client.get('/')
|
resp = client.get('/')
|
||||||
|
@ -15,10 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import socket
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import mock
|
import mock
|
||||||
import oslo_i18n
|
import oslo_i18n
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
@ -27,7 +25,6 @@ from six.moves import http_client
|
|||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import wsgi
|
from keystone.common import wsgi
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.tests import unit
|
from keystone.tests import unit
|
||||||
@ -485,102 +482,3 @@ class LocalizedResponseTest(unit.TestCase):
|
|||||||
self.assertThat(resp.json['error']['message'],
|
self.assertThat(resp.json['error']['message'],
|
||||||
matchers.Equals(exp_msg))
|
matchers.Equals(exp_msg))
|
||||||
self.assertThat(xlation_mock.called, matchers.Equals(True))
|
self.assertThat(xlation_mock.called, matchers.Equals(True))
|
||||||
|
|
||||||
|
|
||||||
class ServerTest(unit.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(ServerTest, self).setUp()
|
|
||||||
self.host = '127.0.0.1'
|
|
||||||
self.port = '1234'
|
|
||||||
|
|
||||||
@mock.patch('eventlet.listen')
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
|
||||||
def test_keepalive_unset(self, mock_getaddrinfo, mock_listen):
|
|
||||||
mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)]
|
|
||||||
mock_sock_dup = mock_listen.return_value.dup.return_value
|
|
||||||
|
|
||||||
server = environment.Server(mock.MagicMock(), host=self.host,
|
|
||||||
port=self.port)
|
|
||||||
server.start()
|
|
||||||
self.addCleanup(server.stop)
|
|
||||||
self.assertTrue(mock_listen.called)
|
|
||||||
self.assertFalse(mock_sock_dup.setsockopt.called)
|
|
||||||
|
|
||||||
@mock.patch('eventlet.listen')
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
|
||||||
def test_keepalive_set(self, mock_getaddrinfo, mock_listen):
|
|
||||||
mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)]
|
|
||||||
mock_sock_dup = mock_listen.return_value.dup.return_value
|
|
||||||
|
|
||||||
server = environment.Server(mock.MagicMock(), host=self.host,
|
|
||||||
port=self.port, keepalive=True)
|
|
||||||
server.start()
|
|
||||||
self.addCleanup(server.stop)
|
|
||||||
mock_sock_dup.setsockopt.assert_called_once_with(socket.SOL_SOCKET,
|
|
||||||
socket.SO_KEEPALIVE,
|
|
||||||
1)
|
|
||||||
self.assertTrue(mock_listen.called)
|
|
||||||
|
|
||||||
@mock.patch('eventlet.listen')
|
|
||||||
@mock.patch('socket.getaddrinfo')
|
|
||||||
def test_keepalive_and_keepidle_set(self, mock_getaddrinfo, mock_listen):
|
|
||||||
mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)]
|
|
||||||
mock_sock_dup = mock_listen.return_value.dup.return_value
|
|
||||||
|
|
||||||
server = environment.Server(mock.MagicMock(), host=self.host,
|
|
||||||
port=self.port, keepalive=True,
|
|
||||||
keepidle=1)
|
|
||||||
server.start()
|
|
||||||
self.addCleanup(server.stop)
|
|
||||||
|
|
||||||
if hasattr(socket, 'TCP_KEEPIDLE'):
|
|
||||||
self.assertEqual(2, mock_sock_dup.setsockopt.call_count)
|
|
||||||
# Test the last set of call args i.e. for the keepidle
|
|
||||||
mock_sock_dup.setsockopt.assert_called_with(socket.IPPROTO_TCP,
|
|
||||||
socket.TCP_KEEPIDLE,
|
|
||||||
1)
|
|
||||||
else:
|
|
||||||
self.assertEqual(1, mock_sock_dup.setsockopt.call_count)
|
|
||||||
|
|
||||||
self.assertTrue(mock_listen.called)
|
|
||||||
|
|
||||||
def test_client_socket_timeout(self):
|
|
||||||
# mocking server method of eventlet.wsgi to check it is called with
|
|
||||||
# configured 'client_socket_timeout' value.
|
|
||||||
for socket_timeout in range(1, 10):
|
|
||||||
self.config_fixture.config(group='eventlet_server',
|
|
||||||
client_socket_timeout=socket_timeout)
|
|
||||||
server = environment.Server(mock.MagicMock(), host=self.host,
|
|
||||||
port=self.port)
|
|
||||||
with mock.patch.object(eventlet.wsgi, 'server') as mock_server:
|
|
||||||
fake_application = uuid.uuid4().hex
|
|
||||||
fake_socket = uuid.uuid4().hex
|
|
||||||
server._run(fake_application, fake_socket)
|
|
||||||
mock_server.assert_called_once_with(
|
|
||||||
fake_socket,
|
|
||||||
fake_application,
|
|
||||||
debug=mock.ANY,
|
|
||||||
socket_timeout=socket_timeout,
|
|
||||||
log=mock.ANY,
|
|
||||||
keepalive=mock.ANY)
|
|
||||||
|
|
||||||
def test_wsgi_keep_alive(self):
|
|
||||||
# mocking server method of eventlet.wsgi to check it is called with
|
|
||||||
# configured 'wsgi_keep_alive' value.
|
|
||||||
wsgi_keepalive = False
|
|
||||||
self.config_fixture.config(group='eventlet_server',
|
|
||||||
wsgi_keep_alive=wsgi_keepalive)
|
|
||||||
|
|
||||||
server = environment.Server(mock.MagicMock(), host=self.host,
|
|
||||||
port=self.port)
|
|
||||||
with mock.patch.object(eventlet.wsgi, 'server') as mock_server:
|
|
||||||
fake_application = uuid.uuid4().hex
|
|
||||||
fake_socket = uuid.uuid4().hex
|
|
||||||
server._run(fake_application, fake_socket)
|
|
||||||
mock_server.assert_called_once_with(fake_socket,
|
|
||||||
fake_application,
|
|
||||||
debug=mock.ANY,
|
|
||||||
socket_timeout=mock.ANY,
|
|
||||||
log=mock.ANY,
|
|
||||||
keepalive=wsgi_keepalive)
|
|
||||||
|
@ -14,13 +14,14 @@
|
|||||||
|
|
||||||
"""Keystone PKI Token Provider"""
|
"""Keystone PKI Token Provider"""
|
||||||
|
|
||||||
|
import subprocess # nosec : used to catch subprocess exceptions
|
||||||
|
|
||||||
from keystoneclient.common import cms
|
from keystoneclient.common import cms
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_log import versionutils
|
from oslo_log import versionutils
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _, _LE
|
from keystone.i18n import _, _LE
|
||||||
@ -48,7 +49,7 @@ class Provider(common.BaseProvider):
|
|||||||
CONF.signing.certfile,
|
CONF.signing.certfile,
|
||||||
CONF.signing.keyfile))
|
CONF.signing.keyfile))
|
||||||
return token_id
|
return token_id
|
||||||
except environment.subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
LOG.exception(_LE('Unable to sign token'))
|
LOG.exception(_LE('Unable to sign token'))
|
||||||
raise exception.UnexpectedError(_(
|
raise exception.UnexpectedError(_(
|
||||||
'Unable to sign token.'))
|
'Unable to sign token.'))
|
||||||
|
@ -12,13 +12,14 @@
|
|||||||
|
|
||||||
"""Keystone Compressed PKI Token Provider"""
|
"""Keystone Compressed PKI Token Provider"""
|
||||||
|
|
||||||
|
import subprocess # nosec : used to catch subprocess exceptions
|
||||||
|
|
||||||
from keystoneclient.common import cms
|
from keystoneclient.common import cms
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_log import versionutils
|
from oslo_log import versionutils
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from keystone.common import environment
|
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _
|
from keystone.i18n import _
|
||||||
@ -47,7 +48,7 @@ class Provider(common.BaseProvider):
|
|||||||
CONF.signing.certfile,
|
CONF.signing.certfile,
|
||||||
CONF.signing.keyfile))
|
CONF.signing.keyfile))
|
||||||
return token_id
|
return token_id
|
||||||
except environment.subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
LOG.exception(ERROR_MESSAGE)
|
LOG.exception(ERROR_MESSAGE)
|
||||||
raise exception.UnexpectedError(ERROR_MESSAGE)
|
raise exception.UnexpectedError(ERROR_MESSAGE)
|
||||||
|
|
||||||
|
@ -4,4 +4,12 @@ other:
|
|||||||
[`blueprint removed-as-of-newton <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-newton>`_]
|
[`blueprint removed-as-of-newton <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-newton>`_]
|
||||||
Removed the backend and route from ``keystone.contrib.endpoint_policy``.
|
Removed the backend and route from ``keystone.contrib.endpoint_policy``.
|
||||||
The package has been moved to ``keystone.endpoint_policy``. This was
|
The package has been moved to ``keystone.endpoint_policy``. This was
|
||||||
deprecated in the Liberty release.
|
deprecated in the Liberty release.
|
||||||
|
- >
|
||||||
|
[`blueprint removed-as-of-newton <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-newton>`_]
|
||||||
|
Removed ``[eventlet_server]`` and ``[eventlet_server_ssl]`` sections from
|
||||||
|
the `keystone.conf`.
|
||||||
|
- >
|
||||||
|
[`blueprint removed-as-of-newton <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-newton>`_]
|
||||||
|
Removed support for running keystone under eventlet. It is recommended to
|
||||||
|
run keystone in an HTTP server.
|
||||||
|
@ -8,8 +8,6 @@ Babel>=1.3,!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3 # BSD
|
|||||||
|
|
||||||
pbr>=1.6 # Apache-2.0
|
pbr>=1.6 # Apache-2.0
|
||||||
WebOb>=1.2.3 # MIT
|
WebOb>=1.2.3 # MIT
|
||||||
eventlet!=0.18.3,>=0.18.2 # MIT
|
|
||||||
greenlet>=0.3.2 # MIT
|
|
||||||
PasteDeploy>=1.5.0 # MIT
|
PasteDeploy>=1.5.0 # MIT
|
||||||
Paste # MIT
|
Paste # MIT
|
||||||
Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT
|
Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT
|
||||||
@ -33,7 +31,6 @@ oslo.log>=1.14.0 # Apache-2.0
|
|||||||
oslo.middleware>=3.0.0 # Apache-2.0
|
oslo.middleware>=3.0.0 # Apache-2.0
|
||||||
oslo.policy>=0.5.0 # Apache-2.0
|
oslo.policy>=0.5.0 # Apache-2.0
|
||||||
oslo.serialization>=1.10.0 # Apache-2.0
|
oslo.serialization>=1.10.0 # Apache-2.0
|
||||||
oslo.service>=1.0.0 # Apache-2.0
|
|
||||||
oslo.utils>=3.5.0 # Apache-2.0
|
oslo.utils>=3.5.0 # Apache-2.0
|
||||||
oauthlib>=0.6 # BSD
|
oauthlib>=0.6 # BSD
|
||||||
pysaml2<4.0.3,>=2.4.0 # Apache-2.0
|
pysaml2<4.0.3,>=2.4.0 # Apache-2.0
|
||||||
|
@ -69,7 +69,6 @@ autodoc_tree_index_modules = True
|
|||||||
|
|
||||||
[entry_points]
|
[entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
keystone-all = keystone.cmd.all:main
|
|
||||||
keystone-manage = keystone.cmd.manage:main
|
keystone-manage = keystone.cmd.manage:main
|
||||||
|
|
||||||
wsgi_scripts =
|
wsgi_scripts =
|
||||||
|
2
tox.ini
2
tox.ini
@ -122,7 +122,7 @@ passenv =
|
|||||||
KSTEST_PROJECT_ID
|
KSTEST_PROJECT_ID
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
filename= *.py,keystone-all,keystone-manage
|
filename= *.py,keystone-manage
|
||||||
show-source = true
|
show-source = true
|
||||||
|
|
||||||
# D100: Missing docstring in public module
|
# D100: Missing docstring in public module
|
||||||
|
Loading…
Reference in New Issue
Block a user