This patch switches the default concurrency mode to native threading for the services that gained native threading support in Flamingo: nova-scheduler, nova-api, and nova-metadata. The OS_NOVA_DISABLE_EVENTLET_PATCHING env variable still can be used to explicitly switch the concurrency mode to eventlet by OS_NOVA_DISABLE_EVENTLET_PATCHING=false We also ensure that the cover, docs, py3xx and functional tox targets are still running with eventlet while py312-threading kept running with native threading. Change-Id: I86c7f31f19ca3345218171f0abfa8ddd4f8fc7ea Signed-off-by: Balazs Gibizer <gibi@redhat.com>
151 lines
5.8 KiB
Python
151 lines
5.8 KiB
Python
# Copyright 2010 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# Copyright 2011 Justin Santa Barbara
|
|
# Copyright 2019 Red Hat, Inc.
|
|
# 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.
|
|
|
|
"""Enable eventlet monkey patching."""
|
|
|
|
import os
|
|
|
|
MONKEY_PATCHED = False
|
|
|
|
|
|
def is_patched():
|
|
return MONKEY_PATCHED
|
|
|
|
|
|
def _monkey_patch():
|
|
if is_patched():
|
|
return False
|
|
|
|
# NOTE(mdbooth): Anything imported here will not be monkey patched. It is
|
|
# important to take care not to import anything here which requires monkey
|
|
# patching.
|
|
# NOTE(artom) eventlet processes environment variables at import-time.
|
|
# as such any eventlet configuration should happen here if needed.
|
|
import eventlet
|
|
import sys
|
|
|
|
# Note any modules with known monkey-patching issues which have been
|
|
# imported before monkey patching.
|
|
# urllib3: https://bugs.launchpad.net/nova/+bug/1808951
|
|
# oslo_context.context: https://bugs.launchpad.net/nova/+bug/1773102
|
|
problems = (set(['urllib3', 'oslo_context.context']) &
|
|
set(sys.modules.keys()))
|
|
|
|
eventlet.monkey_patch()
|
|
|
|
# NOTE(mdbooth): Log here instead of earlier to avoid loading oslo logging
|
|
# before monkey patching.
|
|
# NOTE(mdbooth): Ideally we would raise an exception here, as this is
|
|
# likely to cause problems when executing nova code. However, some non-nova
|
|
# tools load nova only to extract metadata and do not execute it. Two
|
|
# examples are oslopolicy-policy-generator and sphinx, both of which can
|
|
# fail if we assert here. It is not ideal that these utilities are monkey
|
|
# patching at all, but we should not break them.
|
|
# TODO(mdbooth): If there is any way to reliably determine if we are being
|
|
# loaded in that kind of context without breaking existing callers, we
|
|
# should do it and bypass monkey patching here entirely.
|
|
if problems:
|
|
from oslo_log import log as logging
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
LOG.warning("Modules with known eventlet monkey patching issues were "
|
|
"imported prior to eventlet monkey patching: %s. This "
|
|
"warning can usually be ignored if the caller is only "
|
|
"importing and not executing nova code.",
|
|
', '.join(problems))
|
|
|
|
return True
|
|
|
|
|
|
def patch(backend='eventlet'):
|
|
"""Apply eventlet monkey patching according to environment.
|
|
|
|
:param backend: Defines the default backend if not explicitly set via
|
|
the environment. If 'eventlet', then monkey patch if environment
|
|
variable is not defined. If 'threading', then do not monkey patch if
|
|
environment variable is not defined. Any other value results in a
|
|
ValueError. If the environment variable is defined this parameter
|
|
is ignored.
|
|
"""
|
|
if backend not in ('eventlet', 'threading'):
|
|
raise ValueError(
|
|
"the backend can only be 'eventlet' or 'threading'")
|
|
|
|
env = os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', '').lower()
|
|
if env == '':
|
|
should_patch = (backend == 'eventlet')
|
|
elif env in ('1', 'true', 'yes'):
|
|
should_patch = False
|
|
else:
|
|
should_patch = True
|
|
|
|
if should_patch:
|
|
if _monkey_patch():
|
|
global MONKEY_PATCHED
|
|
MONKEY_PATCHED = True
|
|
|
|
import oslo_service.backend as service
|
|
service.init_backend(service.BackendType.EVENTLET)
|
|
from oslo_log import log as logging
|
|
LOG = logging.getLogger(__name__)
|
|
LOG.info("Service is starting with Eventlet based service backend")
|
|
else:
|
|
# We asked not to monkey patch so we will run in native threading mode
|
|
import oslo_service.backend as service
|
|
# NOTE(gibi): This will raise if the backend is already initialized
|
|
# with Eventlet
|
|
service.init_backend(service.BackendType.THREADING)
|
|
|
|
# NOTE(gibi): We were asked not to monkey patch. Let's enforce it by
|
|
# removing the possibility to monkey_patch accidentally
|
|
poison_eventlet()
|
|
|
|
from oslo_log import log as logging
|
|
LOG = logging.getLogger(__name__)
|
|
LOG.warning(
|
|
"Service is starting with native threading. This is currently "
|
|
"experimental. Do not use it in production without first "
|
|
"testing it in pre-production.")
|
|
|
|
|
|
def _poison(*args, **kwargs):
|
|
raise RuntimeError(
|
|
"The service is started with native threading via "
|
|
"OS_NOVA_DISABLE_EVENTLET_PATCHING set to '%s', but then the "
|
|
"service tried to call eventlet.monkey_patch(). This is a bug."
|
|
% os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', ''))
|
|
|
|
|
|
def poison_eventlet():
|
|
import eventlet
|
|
eventlet.monkey_patch = _poison
|
|
eventlet.patcher.monkey_patch = _poison
|
|
|
|
# We want to have this but cannot have this yet as we still have common
|
|
# code that imports eventlet like nova.utils.tpool
|
|
#
|
|
# class PoisonEventletImport:
|
|
# def find_spec(self, fullname, path, target=None):
|
|
# if fullname.startswith('eventlet'):
|
|
# raise ImportError(
|
|
# "The service started in threading mode so it should "
|
|
# "not import eventlet")
|
|
|
|
# import sys
|
|
# sys.meta_path.insert(0, PoisonEventletImport())
|