Remove WSGIService and WSGIServer classes

The previous patch[1] removed the Eventlet based WSGI entry points, and
that code was the only real user of the in tree WSGIService and
WSGIServer classes, we can remove those too. This removes a good chunk
of eventlet dependency from our tree.

There is a catch though. The functional test env used these to start the
nova-metadata-api service. We re-implemented the fixture to use
load the wsgi app and use the wsgi intercept instead. This also showed
that while the Eventlet based API service could be reset via the
oslo.service interface the wsgi APP based API service cannot. So the
related cell caches reset testing is removed.

[1] Ie758550c0b8fb02aeb398396961467d9f845fcc9

Change-Id: I79b725f3b3569e9c1460a93ac40ca92269e7d003
This commit is contained in:
Balazs Gibizer
2025-04-15 15:12:56 +02:00
parent 05b219746f
commit 51eb60063f
4 changed files with 20 additions and 707 deletions

View File

@@ -20,21 +20,13 @@
import os
import os.path
import random
import socket
import ssl
import sys
import eventlet
import eventlet.wsgi
import greenlet
from oslo_concurrency import processutils
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_service import service
from oslo_utils import excutils
from oslo_utils import importutils
from nova.api import wsgi as api_wsgi
from nova import baserpc
from nova import conductor
import nova.conf
@@ -327,342 +319,6 @@ class Service(service.Service):
context.CELL_CACHE = {}
class WSGIServer(service.ServiceBase):
"""Server class to manage a WSGI server, serving a WSGI application."""
default_pool_size = CONF.wsgi.default_pool_size
def __init__(self, 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):
"""Initialize, but do not start, a WSGI server.
:param name: Pretty name for logging.
:param app: The WSGI application to serve.
:param host: IP address to serve the application.
:param port: Port number to server the application.
:param pool_size: Maximum number of eventlets to spawn concurrently.
:param backlog: Maximum number of queued connections.
:param max_url_len: Maximum length of permitted URLs.
:returns: None
:raises: nova.exception.InvalidInput
"""
# Allow operators to customize http requests max header line size.
eventlet.wsgi.MAX_HEADER_LINE = CONF.wsgi.max_header_line
self.name = name
self.app = app
self._server = None
self._protocol = protocol
self.pool_size = pool_size or self.default_pool_size
self._pool = eventlet.GreenPool(self.pool_size)
self._logger = logging.getLogger("nova.%s.wsgi.server" % self.name)
self._use_ssl = use_ssl
self._max_url_len = max_url_len
self.client_socket_timeout = CONF.wsgi.client_socket_timeout or None
if backlog < 1:
raise exception.InvalidInput(
reason=_('The backlog must be more than 0'))
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
# future or monitor upstream for a fix
try:
info = socket.getaddrinfo(bind_addr[0],
bind_addr[1],
socket.AF_UNSPEC,
socket.SOCK_STREAM)[0]
family = info[0]
bind_addr = info[-1]
except Exception:
family = socket.AF_INET
try:
self._socket = eventlet.listen(bind_addr, family, backlog=backlog)
except EnvironmentError:
LOG.error("Could not bind to %(host)s:%(port)s",
{'host': host, 'port': port})
raise
(self.host, self.port) = self._socket.getsockname()[0:2]
LOG.info("%(name)s listening on %(host)s:%(port)s",
{'name': self.name, 'host': self.host, 'port': self.port})
def start(self):
"""Start serving a WSGI application.
:returns: None
"""
# The server socket object will be closed after server exits,
# but the underlying file descriptor will remain open, and will
# give bad file descriptor error. So duplicating the socket object,
# to keep file descriptor usable.
dup_socket = self._socket.dup()
dup_socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
# sockets can hang around forever without keepalive
dup_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'):
dup_socket.setsockopt(socket.IPPROTO_TCP,
socket.TCP_KEEPIDLE,
CONF.wsgi.tcp_keepidle)
if self._use_ssl:
try:
ca_file = CONF.wsgi.ssl_ca_file
cert_file = CONF.wsgi.ssl_cert_file
key_file = CONF.wsgi.ssl_key_file
if cert_file and not os.path.exists(cert_file):
raise RuntimeError(
_("Unable to find cert_file : %s") % cert_file)
if ca_file and not os.path.exists(ca_file):
raise RuntimeError(
_("Unable to find ca_file : %s") % ca_file)
if key_file and not os.path.exists(key_file):
raise RuntimeError(
_("Unable to find key_file : %s") % key_file)
if self._use_ssl and (not cert_file or not key_file):
raise RuntimeError(
_("When running server in SSL mode, you must "
"specify both a cert_file and key_file "
"option value in your configuration file"))
ssl_kwargs = {
'server_side': True,
'certfile': cert_file,
'keyfile': key_file,
'cert_reqs': ssl.CERT_NONE,
}
if CONF.wsgi.ssl_ca_file:
ssl_kwargs['ca_certs'] = ca_file
ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED
dup_socket = eventlet.wrap_ssl(dup_socket,
**ssl_kwargs)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(
"Failed to start %(name)s on %(host)s:%(port)s with "
"SSL support",
{'name': self.name, 'host': self.host,
'port': self.port})
wsgi_kwargs = {
'func': eventlet.wsgi.server,
'sock': dup_socket,
'site': self.app,
'protocol': self._protocol,
'custom_pool': self._pool,
'log': self._logger,
'log_format': CONF.wsgi.wsgi_log_format,
'debug': False,
'keepalive': CONF.wsgi.keep_alive,
'socket_timeout': self.client_socket_timeout
}
if self._max_url_len:
wsgi_kwargs['url_length_limit'] = self._max_url_len
self._server = utils.spawn(**wsgi_kwargs)
def reset(self):
"""Reset server greenpool size to default.
:returns: None
"""
self._pool.resize(self.pool_size)
def stop(self):
"""Stop this server.
This is not a very nice action, as currently the method by which a
server is stopped is by killing its eventlet.
:returns: None
"""
LOG.info("Stopping WSGI server.")
if self._server is not None:
# Resize pool to stop new requests from being processed
self._pool.resize(0)
self._server.kill()
def wait(self):
"""Block, until the server has stopped.
Waits on the server's eventlet to finish, then returns.
:returns: None
"""
try:
if self._server is not None:
self._pool.waitall()
self._server.wait()
except greenlet.GreenletExit:
LOG.info("WSGI server has stopped.")
class WSGIService(service.Service):
"""Provides ability to launch API from a 'paste' configuration."""
def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
"""Initialize, but do not start the WSGI server.
:param name: The name of the WSGI server given to the loader.
:param loader: Loads the WSGI application using the given name.
:returns: None
"""
self.name = name
# NOTE(danms): Name can be metadata, osapi_compute, per
# nova.service's enabled_apis
self.binary = 'nova-%s' % name
LOG.warning('Running %s using eventlet is deprecated. Deploy with '
'a WSGI server such as uwsgi or mod_wsgi.', self.binary)
self.topic = None
self.manager = self._get_manager()
self.loader = loader or api_wsgi.Loader()
self.app = self.loader.load_app(name)
# inherit all compute_api worker counts from osapi_compute
if name.startswith('openstack_compute_api'):
wname = 'osapi_compute'
else:
wname = name
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
self.port = getattr(CONF, '%s_listen_port' % name, 0)
self.workers = (getattr(CONF, '%s_workers' % wname, None) or
processutils.get_worker_count())
if self.workers and self.workers < 1:
worker_name = '%s_workers' % name
msg = (_("%(worker_name)s value of %(workers)s is invalid, "
"must be greater than 0") %
{'worker_name': worker_name,
'workers': str(self.workers)})
raise exception.InvalidInput(msg)
self.use_ssl = use_ssl
self.server = WSGIServer(
name,
self.app,
host=self.host,
port=self.port,
use_ssl=self.use_ssl,
max_url_len=max_url_len,
)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None
setup_profiler(name, self.host)
def reset(self):
"""Reset the following:
* server greenpool size to default
* service version cache
* cell cache holding database transaction context managers
:returns: None
"""
self.server.reset()
service_obj.Service.clear_min_version_cache()
context.CELL_CACHE = {}
def _get_manager(self):
"""Initialize a Manager object appropriate for this service.
Use the service name to look up a Manager subclass from the
configuration and initialize an instance. If no class name
is configured, just return None.
:returns: a Manager instance, or None.
"""
manager = SERVICE_MANAGERS.get(self.binary)
if manager is None:
return None
manager_class = importutils.import_class(manager)
return manager_class()
def start(self):
"""Start serving this service using loaded configuration.
Also, retrieve updated port number in case '0' was passed in, which
indicates a random port should be used.
:returns: None
"""
# NOTE(melwitt): Clear the cell cache holding database transaction
# context manager objects. We do this to ensure we create new internal
# oslo.db locks to avoid a situation where a child process receives an
# already locked oslo.db lock when it is forked. When a child process
# inherits a locked oslo.db lock, database accesses through that
# transaction context manager will never be able to acquire the lock
# and requests will fail with CellTimeout errors.
# See https://bugs.python.org/issue6721 for more information.
# With python 3.7, it would be possible for oslo.db to make use of the
# os.register_at_fork() method to reinitialize its lock. Until we
# require python 3.7 as a minimum version, we must handle the situation
# outside of oslo.db.
context.CELL_CACHE = {}
ctxt = context.get_admin_context()
service_ref = objects.Service.get_by_host_and_binary(ctxt, self.host,
self.binary)
if service_ref:
_update_service_ref(service_ref)
else:
try:
service_ref = _create_service_ref(self, ctxt)
except (exception.ServiceTopicExists,
exception.ServiceBinaryExists):
# NOTE(danms): If we race to create a record with a sibling,
# don't fail here.
service_ref = objects.Service.get_by_host_and_binary(
ctxt, self.host, self.binary)
self.service_ref = service_ref
if self.manager:
self.manager.init_host()
self.manager.pre_start_hook(self.service_ref)
if self.backdoor_port is not None:
self.manager.backdoor_port = self.backdoor_port
self.server.start()
if self.manager:
self.manager.post_start_hook()
def stop(self):
"""Stop serving this API.
:returns: None
"""
self.server.stop()
def wait(self):
"""Wait for the service to stop serving this API.
:returns: None
"""
self.server.wait()
def process_launcher():
return service.ProcessLauncher(CONF, restart_method='mutate')

View File

@@ -1106,23 +1106,30 @@ class OSMetadataServer(fixtures.Fixture):
"""
def __init__(self):
self.md_url = None
def setUp(self):
super(OSMetadataServer, self).setUp()
# in order to run these in tests we need to bind only to local
# host, and dynamically allocate ports
# A unique hostname for the wsgi-intercept.
hostname = uuidsentinel.metadata_host
service_name = 'metadata'
endpoint = f'http://{hostname}/'
conf_overrides = {
'metadata_listen': '127.0.0.1',
'metadata_listen_port': 0,
'debug': True
'metadata_listen': hostname,
'debug': True,
}
self.useFixture(ConfPatcher(**conf_overrides))
self.metadata = service.WSGIService("metadata")
self.metadata.start()
self.addCleanup(self.metadata.stop)
self.md_url = "http://%s:%s/" % (
conf_overrides['metadata_listen'],
self.metadata.port)
loader = wsgi.Loader().load_app(service_name)
app = lambda: loader
intercept = interceptor.RequestsInterceptor(app, url=endpoint)
intercept.install_intercept()
self.addCleanup(intercept.uninstall_intercept)
self.md_url = endpoint
class PoisonFunctions(fixtures.Fixture):

View File

@@ -44,10 +44,6 @@ class ServiceTestCase(test.TestCase,
api_version='v2.1')).api
self.start_service('conductor')
self.scheduler = self.start_service('scheduler')
# Our OSAPIFixture does not use a WSGIService, so just use the metadata
# server fixture (which uses WSGIService) for testing.
self.metadata = self.useFixture(
nova_fixtures.OSMetadataServer()).metadata
# Start one compute service.
self.start_service('compute')
@@ -55,6 +51,7 @@ class ServiceTestCase(test.TestCase,
"""Tests that the cell cache for database transaction context managers
is cleared after a service reset (example scenario: SIGHUP).
"""
self.assertFalse(nova_context.CELL_CACHE)
server_req = self._build_server()
server = self.api.post_server({'server': server_req})
self._wait_for_state_change(server, 'ACTIVE')
@@ -64,20 +61,12 @@ class ServiceTestCase(test.TestCase,
# Cell cache should be empty after the service reset.
self.assertEqual({}, nova_context.CELL_CACHE)
# Now test the WSGI service.
server = self.api.post_server({'server': server_req})
self._wait_for_state_change(server, 'ACTIVE')
# Cell cache should be populated after creating a server.
self.assertTrue(nova_context.CELL_CACHE)
self.metadata.reset()
# Cell cache should be empty after the service reset.
self.assertEqual({}, nova_context.CELL_CACHE)
def test_service_start_resets_cell_cache(self):
"""Tests that the cell cache for database transaction context managers
is cleared upon a service start (example scenario: service start after
a SIGTERM and the parent process forks child process workers).
"""
self.assertFalse(nova_context.CELL_CACHE)
server_req = self._build_server()
server = self.api.post_server({'server': server_req})
self._wait_for_state_change(server, 'ACTIVE')
@@ -96,20 +85,6 @@ class ServiceTestCase(test.TestCase,
# Cell cache should be empty after the service start.
self.assertEqual({}, nova_context.CELL_CACHE)
# Now test the WSGI service.
server = self.api.post_server({'server': server_req})
self._wait_for_state_change(server, 'ACTIVE')
# Cell cache should be populated after creating a server.
self.assertTrue(nova_context.CELL_CACHE)
# we need to mock nova.utils.raise_if_old_compute() that is run at
# service startup as that will check the global service level which
# populates the cell cache
with mock.patch("nova.utils.raise_if_old_compute"):
self.metadata.stop()
self.metadata.start()
# Cell cache should be empty after the service reset.
self.assertEqual({}, nova_context.CELL_CACHE)
class TestOldComputeCheck(
test.TestCase, integrated_helpers.InstanceHelperMixin):

View File

@@ -19,17 +19,10 @@ Unit Tests for remote procedure calls using queue
"""
import os.path
import socket
from unittest import mock
import eventlet
import eventlet.wsgi
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_service import service as _service
import requests
import testtools
import webob
from nova import exception
from nova import manager
@@ -38,7 +31,6 @@ from nova.objects import base as obj_base
from nova import rpc
from nova import service
from nova import test
from nova.tests.unit import utils
test_service_opts = [
cfg.HostAddressOpt("test_service_listen",
@@ -323,323 +315,6 @@ class ServiceTestCase(test.NoDBTestCase):
mock_check_old.assert_has_calls([mock.call(), mock.call()])
class TestWSGIServer(test.NoDBTestCase):
"""WSGI server tests."""
def test_no_app(self):
server = service.WSGIServer("test_app", None)
self.assertEqual("test_app", server.name)
def test_custom_max_header_line(self):
self.flags(max_header_line=4096, group='wsgi') # Default is 16384
service.WSGIServer("test_custom_max_header_line", None)
self.assertEqual(CONF.wsgi.max_header_line,
eventlet.wsgi.MAX_HEADER_LINE)
def test_start_random_port(self):
server = service.WSGIServer(
"test_random_port", None, host="127.0.0.1", port=0)
server.start()
self.assertNotEqual(0, server.port)
server.stop()
server.wait()
@testtools.skipIf(not utils.is_ipv6_supported(), "no ipv6 support")
def test_start_random_port_with_ipv6(self):
server = service.WSGIServer(
"test_random_port", None,
host="::1", port=0)
server.start()
self.assertEqual("::1", server.host)
self.assertNotEqual(0, server.port)
server.stop()
server.wait()
@testtools.skipIf(not utils.is_linux(), 'SO_REUSEADDR behaves differently '
'on OSX and BSD, see bugs '
'1436895 and 1467145')
def test_socket_options_for_simple_server(self):
# test normal socket options has set properly
self.flags(tcp_keepidle=500, group='wsgi')
server = service.WSGIServer(
"test_socket_options", None, host="127.0.0.1", port=0)
server.start()
sock = server._socket
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_KEEPALIVE))
if hasattr(socket, 'TCP_KEEPIDLE'):
self.assertEqual(CONF.wsgi.tcp_keepidle,
sock.getsockopt(socket.IPPROTO_TCP,
socket.TCP_KEEPIDLE))
server.stop()
server.wait()
def test_server_pool_waitall(self):
# test pools waitall method gets called while stopping server
server = service.WSGIServer(
"test_server", None, host="127.0.0.1")
server.start()
with mock.patch.object(server._pool,
'waitall') as mock_waitall:
server.stop()
server.wait()
mock_waitall.assert_called_once_with()
def test_uri_length_limit(self):
server = service.WSGIServer(
"test_uri_length_limit", None,
host="127.0.0.1", max_url_len=16384)
server.start()
uri = "http://127.0.0.1:%d/%s" % (server.port, 10000 * 'x')
resp = requests.get(uri, proxies={"http": ""})
eventlet.sleep(0)
self.assertNotEqual(resp.status_code,
requests.codes.REQUEST_URI_TOO_LARGE)
uri = "http://127.0.0.1:%d/%s" % (server.port, 20000 * 'x')
resp = requests.get(uri, proxies={"http": ""})
eventlet.sleep(0)
self.assertEqual(resp.status_code,
requests.codes.REQUEST_URI_TOO_LARGE)
server.stop()
server.wait()
def test_reset_pool_size_to_default(self):
server = service.WSGIServer(
"test_resize", None,
host="127.0.0.1", max_url_len=16384)
server.start()
# Stopping the server, which in turn sets pool size to 0
server.stop()
self.assertEqual(server._pool.size, 0)
# Resetting pool size to default
server.reset()
server.start()
self.addCleanup(server.stop)
self.assertEqual(server._pool.size, CONF.wsgi.default_pool_size)
def test_client_socket_timeout(self):
self.flags(client_socket_timeout=5, group='wsgi')
# mocking eventlet spawn method to check it is called with
# configured 'client_socket_timeout' value.
with mock.patch('nova.utils.spawn') as mock_spawn:
server = service.WSGIServer(
"test_app", None,
host="127.0.0.1", port=0)
server.start()
self.addCleanup(server.stop)
_, kwargs = mock_spawn.call_args
self.assertEqual(CONF.wsgi.client_socket_timeout,
kwargs['socket_timeout'])
def test_keep_alive(self):
self.flags(keep_alive=False, group='wsgi')
# mocking eventlet spawn method to check it is called with
# configured 'keep_alive' value.
with mock.patch('nova.utils.spawn') as mock_spawn:
server = service.WSGIServer(
"test_app", None,
host="127.0.0.1", port=0)
server.start()
self.addCleanup(server.stop)
_, kwargs = mock_spawn.call_args
self.assertEqual(CONF.wsgi.keep_alive,
kwargs['keepalive'])
@testtools.skip("bug/1482633: test hangs on Python 3")
class TestWSGIServerWithSSL(test.NoDBTestCase):
"""WSGI server with SSL tests."""
def setUp(self):
super(TestWSGIServerWithSSL, self).setUp()
self.flags(enabled_ssl_apis=['fake_ssl'])
self.flags(
ssl_cert_file=os.path.join(SSL_CERT_DIR, 'certificate.crt'),
ssl_key_file=os.path.join(SSL_CERT_DIR, 'privatekey.key'),
group='wsgi')
def test_ssl_server(self):
def test_app(env, start_response):
start_response('200 OK', {})
return ['PONG']
fake_ssl_server = service.WSGIServer(
"fake_ssl", test_app,
host="127.0.0.1", port=0,
use_ssl=True)
fake_ssl_server.start()
self.assertNotEqual(0, fake_ssl_server.port)
response = requests.post(
'https://127.0.0.1:%s/' % fake_ssl_server.port,
verify=os.path.join(SSL_CERT_DIR, 'ca.crt'), data='PING')
self.assertEqual(response.text, 'PONG')
fake_ssl_server.stop()
fake_ssl_server.wait()
def test_two_servers(self):
def test_app(env, start_response):
start_response('200 OK', {})
return ['PONG']
fake_ssl_server = service.WSGIServer(
"fake_ssl", test_app,
host="127.0.0.1", port=0, use_ssl=True)
fake_ssl_server.start()
self.assertNotEqual(0, fake_ssl_server.port)
fake_server = service.WSGIServer(
"fake", test_app,
host="127.0.0.1", port=0)
fake_server.start()
self.assertNotEqual(0, fake_server.port)
response = requests.post(
'https://127.0.0.1:%s/' % fake_ssl_server.port,
verify=os.path.join(SSL_CERT_DIR, 'ca.crt'), data='PING')
self.assertEqual(response.text, 'PONG')
response = requests.post('http://127.0.0.1:%s/' % fake_server.port,
data='PING')
self.assertEqual(response.text, 'PONG')
fake_ssl_server.stop()
fake_ssl_server.wait()
fake_server.stop()
fake_server.wait()
@testtools.skipIf(not utils.is_linux(), 'SO_REUSEADDR behaves differently '
'on OSX and BSD, see bugs '
'1436895 and 1467145')
def test_socket_options_for_ssl_server(self):
# test normal socket options has set properly
self.flags(tcp_keepidle=500, group='wsgi')
server = service.WSGIServer(
"test_socket_options", None,
host="127.0.0.1", port=0,
use_ssl=True)
server.start()
sock = server._socket
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
self.assertEqual(1, sock.getsockopt(socket.SOL_SOCKET,
socket.SO_KEEPALIVE))
if hasattr(socket, 'TCP_KEEPIDLE'):
self.assertEqual(CONF.wsgi.tcp_keepidle,
sock.getsockopt(socket.IPPROTO_TCP,
socket.TCP_KEEPIDLE))
server.stop()
server.wait()
@testtools.skipIf(not utils.is_ipv6_supported(), "no ipv6 support")
def test_app_using_ipv6_and_ssl(self):
greetings = 'Hello, World!!!'
@webob.dec.wsgify
def hello_world(req):
return greetings
server = service.WSGIServer(
"fake_ssl", hello_world,
host="::1", port=0, use_ssl=True)
server.start()
response = requests.get('https://[::1]:%d/' % server.port,
verify=os.path.join(SSL_CERT_DIR, 'ca.crt'))
self.assertEqual(greetings, response.text)
server.stop()
server.wait()
class TestWSGIService(test.NoDBTestCase):
def setUp(self):
super(TestWSGIService, self).setUp()
self.stub_out('nova.api.wsgi.Loader.load_app',
lambda *a, **kw: mock.MagicMock())
@mock.patch('nova.objects.Service.get_by_host_and_binary')
@mock.patch('nova.objects.Service.create')
def test_service_start_creates_record(self, mock_create, mock_get):
mock_get.return_value = None
test_service = service.WSGIService("test_service")
test_service.start()
self.addCleanup(test_service.stop)
self.assertTrue(mock_create.called)
@mock.patch('nova.objects.Service.get_by_host_and_binary')
@mock.patch('nova.objects.Service.create')
def test_service_start_does_not_create_record(self, mock_create, mock_get):
test_service = service.WSGIService("test_service")
test_service.start()
self.addCleanup(test_service.stop)
self.assertFalse(mock_create.called)
@mock.patch('nova.objects.Service.get_by_host_and_binary')
def test_service_random_port(self, mock_get):
test_service = service.WSGIService("test_service")
test_service.start()
self.addCleanup(test_service.stop)
self.assertNotEqual(0, test_service.port)
def test_workers_set_default(self):
test_service = service.WSGIService("osapi_compute")
self.assertEqual(test_service.workers, processutils.get_worker_count())
def test_workers_set_good_user_setting(self):
CONF.set_override('osapi_compute_workers', 8)
test_service = service.WSGIService("osapi_compute")
self.assertEqual(test_service.workers, 8)
def test_openstack_compute_api_workers_set_default(self):
test_service = service.WSGIService("openstack_compute_api_v2")
self.assertEqual(test_service.workers, processutils.get_worker_count())
def test_openstack_compute_api_workers_set_good_user_setting(self):
CONF.set_override('osapi_compute_workers', 8)
test_service = service.WSGIService("openstack_compute_api_v2")
self.assertEqual(test_service.workers, 8)
@testtools.skipIf(not utils.is_ipv6_supported(), "no ipv6 support")
@mock.patch('nova.objects.Service.get_by_host_and_binary')
def test_service_random_port_with_ipv6(self, mock_get):
CONF.set_default("test_service_listen", "::1")
test_service = service.WSGIService("test_service")
test_service.start()
self.addCleanup(test_service.stop)
self.assertEqual("::1", test_service.host)
self.assertNotEqual(0, test_service.port)
@mock.patch('nova.objects.Service.get_by_host_and_binary')
def test_reset_pool_size_to_default(self, mock_get):
test_service = service.WSGIService("test_service")
test_service.start()
# Stopping the service, which in turn sets pool size to 0
test_service.stop()
self.assertEqual(test_service.server._pool.size, 0)
# Resetting pool size to default
test_service.reset()
test_service.start()
self.addCleanup(test_service.stop)
self.assertEqual(test_service.server._pool.size,
CONF.wsgi.default_pool_size)
class TestLauncher(test.NoDBTestCase):
@mock.patch.object(_service, 'launch')