Merge "Adds tcp_keepalive and tcp_keepidle config options"

This commit is contained in:
Jenkins 2014-02-07 04:08:07 +00:00 committed by Gerrit Code Review
commit afb0374705
5 changed files with 97 additions and 3 deletions

View File

@ -56,7 +56,9 @@ CONF = config.CONF
def create_server(conf, name, host, port): def create_server(conf, name, host, port):
app = deploy.loadapp('config:%s' % conf, name=name) app = deploy.loadapp('config:%s' % conf, name=name)
server = environment.Server(app, host=host, port=port) server = environment.Server(app, host=host, port=port,
keepalive=CONF.tcp_keepalive,
keepidle=CONF.tcp_keepidle)
if CONF.ssl.enable: if CONF.ssl.enable:
server.set_ssl(CONF.ssl.certfile, CONF.ssl.keyfile, server.set_ssl(CONF.ssl.certfile, CONF.ssl.keyfile,
CONF.ssl.ca_certs, CONF.ssl.cert_required) CONF.ssl.ca_certs, CONF.ssl.cert_required)

View File

@ -15,6 +15,14 @@
# The port number which the public admin listens on # The port number which the public admin listens on
# admin_port = 35357 # admin_port = 35357
# 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.
# tcp_keepalive = False
# Sets the value of TCP_KEEPIDLE in seconds for each server socket. Only
# applies if tcp_keepalive is True. Not supported on OS X.
# tcp_keepidle = 600
# The base endpoint URLs for keystone that are advertised to clients # The base endpoint URLs for keystone that are advertised to clients
# (NOTE: this does NOT affect how keystone listens for connections) # (NOTE: this does NOT affect how keystone listens for connections)
# public_endpoint = http://localhost:%(public_port)s/ # public_endpoint = http://localhost:%(public_port)s/

View File

@ -47,7 +47,17 @@ FILE_OPTIONS = {
cfg.StrOpt('member_role_id', cfg.StrOpt('member_role_id',
default='9fe2ff9ee4384b1894a90878d3e92bab'), default='9fe2ff9ee4384b1894a90878d3e92bab'),
cfg.StrOpt('member_role_name', default='_member_'), cfg.StrOpt('member_role_name', default='_member_'),
cfg.IntOpt('crypt_strength', default=40000)], cfg.IntOpt('crypt_strength', default=40000),
cfg.BoolOpt('tcp_keepalive', default=False,
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,
help=("Sets the value of TCP_KEEPIDLE in seconds for each "
"server socket. Only applies if tcp_keepalive is "
"True. Not supported on OS X."))],
'identity': [ 'identity': [
cfg.StrOpt('default_domain_id', default='default'), cfg.StrOpt('default_domain_id', default='default'),
cfg.BoolOpt('domain_specific_drivers_enabled', cfg.BoolOpt('domain_specific_drivers_enabled',

View File

@ -35,7 +35,8 @@ LOG = log.getLogger(__name__)
class Server(object): class Server(object):
"""Server class to manage multiple WSGI sockets and applications.""" """Server class to manage multiple WSGI sockets and applications."""
def __init__(self, application, host=None, port=None, threads=1000): def __init__(self, application, host=None, port=None, threads=1000,
keepalive=False, keepidle=None):
self.application = application self.application = application
self.host = host or '0.0.0.0' self.host = host or '0.0.0.0'
self.port = port or 0 self.port = port or 0
@ -44,6 +45,8 @@ class Server(object):
self.greenthread = None self.greenthread = None
self.do_ssl = False self.do_ssl = False
self.cert_required = False self.cert_required = False
self.keepalive = keepalive
self.keepidle = keepidle
def start(self, key=None, backlog=128): def start(self, key=None, backlog=128):
"""Run a WSGI server with the given application.""" """Run a WSGI server with the given application."""
@ -77,6 +80,15 @@ class Server(object):
ca_certs=self.ca_certs) ca_certs=self.ca_certs)
_socket = sslsocket _socket = sslsocket
# Optionally enable keepalive on the wsgi socket.
if self.keepalive:
_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# This option isn't available in the OS X version of eventlet
if hasattr(socket, 'TCP_KEEPIDLE') and self.keepidle is not None:
_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE,
self.keepidle)
self.greenthread = self.pool.spawn(self._run, self.greenthread = self.pool.spawn(self._run,
self.application, self.application,
_socket) _socket)

View File

@ -16,7 +16,10 @@
from babel import localedata from babel import localedata
import gettext import gettext
import mock
import socket
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.openstack.common.fixture import moxstubout from keystone.openstack.common.fixture import moxstubout
@ -249,3 +252,62 @@ class LocalizedResponseTest(tests.TestCase):
# are lazy-translated. # are lazy-translated.
self.assertIsInstance(_('The resource could not be found.'), self.assertIsInstance(_('The resource could not be found.'),
gettextutils.Message) gettextutils.Message)
class ServerTest(tests.TestCase):
def setUp(self):
super(ServerTest, self).setUp()
environment.use_eventlet()
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 = mock.Mock()
mock_sock.setsockopt = mock.Mock()
mock_listen.return_value = mock_sock
server = environment.Server(mock.MagicMock(), host=self.host,
port=self.port)
server.start()
self.assertTrue(mock_listen.called)
self.assertFalse(mock_sock.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 = mock.Mock()
mock_sock.setsockopt = mock.Mock()
mock_listen.return_value = mock_sock
server = environment.Server(mock.MagicMock(), host=self.host,
port=self.port, keepalive=True)
server.start()
mock_sock.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 = mock.Mock()
mock_sock.setsockopt = mock.Mock()
mock_listen.return_value = mock_sock
server = environment.Server(mock.MagicMock(), host=self.host,
port=self.port, keepalive=True,
keepidle=1)
server.start()
self.assertEqual(mock_sock.setsockopt.call_count, 2)
# Test the last set of call args i.e. for the keepidle
mock_sock.setsockopt.assert_called_with(socket.IPPROTO_TCP,
socket.TCP_KEEPIDLE,
1)
self.assertTrue(mock_listen.called)