Merge "Add functionality for creating Unix domain WSGI servers"

This commit is contained in:
Jenkins 2015-11-26 23:00:54 +00:00 committed by Gerrit Code Review
commit 5c927520e0
4 changed files with 100 additions and 23 deletions

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
from oslo_config import fixture as config
from oslotest import base as test_base
@ -29,6 +30,44 @@ class ServiceBaseTestCase(test_base.BaseTestCase):
self.conf_fixture.register_opts(_options.ssl_opts,
sslutils.config_section)
self.conf_fixture.register_opts(_options.periodic_opts)
self.conf_fixture.register_opts(_options.wsgi_opts)
self.conf = self.conf_fixture.conf
self.config = self.conf_fixture.config
self.conf(args=[], default_config_files=[])
def get_new_temp_dir(self):
"""Create a new temporary directory.
:returns fixtures.TempDir
"""
return self.useFixture(fixtures.TempDir())
def get_default_temp_dir(self):
"""Create a default temporary directory.
Returns the same directory during the whole test case.
:returns fixtures.TempDir
"""
if not hasattr(self, '_temp_dir'):
self._temp_dir = self.get_new_temp_dir()
return self._temp_dir
def get_temp_file_path(self, filename, root=None):
"""Returns an absolute path for a temporary file.
If root is None, the file is created in default temporary directory. It
also creates the directory if it's not initialized yet.
If root is not None, the file is created inside the directory passed as
root= argument.
:param filename: filename
:type filename: string
:param root: temporary directory to create a new file in
:type root: fixtures.TempDir
:returns absolute file path string
"""
root = root or self.get_default_temp_dir()
return root.join(filename)

View File

@ -16,7 +16,7 @@
"""Unit tests for `wsgi`."""
import os.path
import os
import platform
import socket
import tempfile
@ -29,12 +29,11 @@ import requests
import webob
from oslo_config import cfg
from oslo_config import fixture as config
from oslo_service import _options
from oslo_service import sslutils
from oslo_service.tests import base
from oslo_service import wsgi
from oslo_utils import netutils
from oslotest import base as test_base
from oslotest import moxstubout
@ -44,15 +43,12 @@ SSL_CERT_DIR = os.path.normpath(os.path.join(
CONF = cfg.CONF
class WsgiTestCase(test_base.BaseTestCase):
class WsgiTestCase(base.ServiceBaseTestCase):
"""Base class for WSGI tests."""
def setUp(self):
super(WsgiTestCase, self).setUp()
self.conf_fixture = self.useFixture(config.Config())
self.conf_fixture.register_opts(_options.wsgi_opts)
self.conf = self.conf_fixture.conf
self.config = self.conf_fixture.config
self.conf(args=[], default_config_files=[])
@ -163,7 +159,7 @@ class TestWSGIServer(WsgiTestCase):
server = wsgi.Server(self.conf, "test_socket_options", None,
host="127.0.0.1", port=0)
server.start()
sock = server._socket
sock = server.socket
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
@ -177,6 +173,24 @@ class TestWSGIServer(WsgiTestCase):
server.wait()
self.assertTrue(server._server.dead)
@testtools.skipIf(not hasattr(socket, "AF_UNIX"),
'UNIX sockets not supported')
def test_server_with_unix_socket(self):
socket_file = self.get_temp_file_path('sock')
socket_mode = 0o644
server = wsgi.Server(self.conf, "test_socket_options", None,
socket_family=socket.AF_UNIX,
socket_mode=socket_mode,
socket_file=socket_file)
self.assertEqual(socket_file, server.socket.getsockname())
self.assertEqual(socket_mode,
os.stat(socket_file).st_mode & 0o777)
server.start()
self.assertFalse(server._server.dead)
server.stop()
server.wait()
self.assertTrue(server._server.dead)
def test_server_pool_waitall(self):
# test pools waitall method gets called while stopping server
server = wsgi.Server(self.conf, "test_server", None, host="127.0.0.1")
@ -333,7 +347,7 @@ class TestWSGIServerWithSSL(WsgiTestCase):
server = wsgi.Server(self.conf, "test_socket_options", None,
host="127.0.0.1", port=0, use_ssl=True)
server.start()
sock = server._socket
sock = server.socket
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,

View File

@ -59,10 +59,11 @@ class InvalidInput(Exception):
class Server(service.ServiceBase):
"""Server class to manage a WSGI server, serving a WSGI application."""
def __init__(self, conf, name, app, host='0.0.0.0', port=0, pool_size=None,
protocol=eventlet.wsgi.HttpProtocol, backlog=128,
use_ssl=False, max_url_len=None,
logger_name='eventlet.wsgi.server'):
def __init__(self, conf, name, app, host='0.0.0.0', port=0,
pool_size=None, protocol=eventlet.wsgi.HttpProtocol,
backlog=128, use_ssl=False, max_url_len=None,
logger_name='eventlet.wsgi.server',
socket_family=None, socket_file=None, socket_mode=None):
"""Initialize, but do not start, a WSGI server.
:param conf: Instance of ConfigOpts.
@ -76,6 +77,9 @@ class Server(service.ServiceBase):
:param use_ssl: Wraps the socket in an SSL context if True.
:param max_url_len: Maximum length of permitted URLs.
:param logger_name: The name for the logger.
:param socket_family: Socket family.
:param socket_file: location of UNIX socket.
:param socket_mode: UNIX socket mode.
:returns: None
:raises: InvalidInput
:raises: EnvironmentError
@ -102,6 +106,21 @@ class Server(service.ServiceBase):
if backlog < 1:
raise InvalidInput(reason=_('The backlog must be more than 0'))
if not socket_family or socket_family in [socket.AF_INET,
socket.AF_INET6]:
self.socket = self._get_socket(host, port, backlog)
elif hasattr(socket, "AF_UNIX") and socket_family == socket.AF_UNIX:
self.socket = self._get_unix_socket(socket_file, socket_mode,
backlog)
else:
raise ValueError(_("Unsupported socket family: %s"), socket_family)
(self.host, self.port) = self.socket.getsockname()[0:2]
if self._use_ssl:
sslutils.is_enabled(conf)
def _get_socket(self, host, port, backlog):
bind_addr = (host, port)
# TODO(dims): eventlet's green dns/socket module does not actually
# support IPv6 in getaddrinfo(). We need to get around this in the
@ -116,19 +135,25 @@ class Server(service.ServiceBase):
except Exception:
family = socket.AF_INET
if self._use_ssl:
sslutils.is_enabled(conf)
try:
self._socket = eventlet.listen(bind_addr, family, backlog=backlog)
sock = eventlet.listen(bind_addr, family, backlog=backlog)
except EnvironmentError:
LOG.error(_LE("Could not bind to %(host)s:%(port)s"),
{'host': host, 'port': port})
raise
(self.host, self.port) = self._socket.getsockname()[0:2]
sock = self._set_socket_opts(sock)
LOG.info(_LI("%(name)s listening on %(host)s:%(port)s"),
{'name': self.name, 'host': self.host, 'port': self.port})
{'name': self.name, 'host': host, 'port': port})
return sock
def _get_unix_socket(self, socket_file, socket_mode, backlog):
sock = eventlet.listen(socket_file, family=socket.AF_UNIX,
backlog=backlog)
if socket_mode is not None:
os.chmod(socket_file, socket_mode)
LOG.info(_LI("%(name)s listening on %(socket_file)s:"),
{'name': self.name, 'socket_file': socket_file})
return sock
def start(self):
"""Start serving a WSGI application.
@ -140,9 +165,7 @@ class Server(service.ServiceBase):
# give bad file descriptor error. So duplicating the socket object,
# to keep file descriptor usable.
self.dup_socket = self._socket.dup()
self.dup_socket = self._set_socket_opts(self.dup_socket)
self.dup_socket = self.socket.dup()
if self._use_ssl:
self.dup_socket = sslutils.wrap(self.conf, self.dup_socket)

View File

@ -2,6 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
fixtures>=1.3.1
hacking<0.11,>=0.10.0
mock>=1.2
oslotest>=1.10.0 # Apache-2.0