Undo the eventlet monkey patch for the privileged daemon

Backport Notes:

- We want to avoid requirements bump on stable branch
  so we don't backport requirements and lower-constraints changes.
- Tests are skipped because SSL on Python2 with monkey patch
  work differently than with Python3 (tests are uncompatible).

Change-Id: I422125b137a3beadb0a79f5944a19fce62f093d6
Closes-Bug: #1887506
(cherry picked from commit 1dc378c76f)
(cherry picked from commit 6d41ef9f91)
This commit is contained in:
Rodolfo Alonso Hernandez 2020-07-14 13:51:07 +00:00 committed by Hervé Beraud
parent 6db45ef76d
commit 00e69d4f32
3 changed files with 77 additions and 0 deletions

View File

@ -57,6 +57,7 @@ import tempfile
import threading
import eventlet
from eventlet import patcher
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import encodeutils
@ -75,6 +76,25 @@ if platform.system() == 'Linux':
LOG = logging.getLogger(__name__)
EVENTLET_MODULES = ('os', 'select', 'socket', 'thread', 'time', 'MySQLdb',
'builtins', 'subprocess')
EVENTLET_LIBRARIES = []
def _null():
return []
for module in EVENTLET_MODULES:
if hasattr(patcher, '_green_%s_modules' % module):
method = getattr(patcher, '_green_%s_modules' % module)
elif hasattr(patcher, '_green_%s' % module):
method = getattr(patcher, '_green_%s' % module)
else:
method = _null()
EVENTLET_LIBRARIES.append((module, method))
@enum.unique
class StdioFd(enum.IntEnum):
# NOTE(gus): We can't use sys.std*.fileno() here. sys.std*
@ -258,6 +278,21 @@ def replace_logging(handler, log_root=None):
log_root.addHandler(handler)
def un_monkey_patch():
for eventlet_mod_name, func_modules in EVENTLET_LIBRARIES:
if not eventlet.patcher.is_monkey_patched(eventlet_mod_name):
continue
for name, mod in func_modules():
patched_mod = sys.modules.get(name)
orig_mod = eventlet.patcher.original(name)
for attr_name in mod.__patched__:
patched_attr = getattr(mod, attr_name, None)
unpatched_attr = getattr(orig_mod, attr_name, None)
if patched_attr is not None:
setattr(patched_mod, attr_name, unpatched_attr)
class ForkingClientChannel(_ClientChannel):
def __init__(self, context):
"""Start privsep daemon using fork()
@ -279,6 +314,7 @@ class ForkingClientChannel(_ClientChannel):
if os.fork() == 0:
# child
un_monkey_patch()
channel = comm.ServerChannel(sock_b)
sock_a.close()

View File

@ -13,11 +13,13 @@
# under the License.
import copy
import eventlet
import fixtures
import functools
import logging as pylogging
import mock
import platform
import sys
import time
from oslo_log import formatters
@ -212,3 +214,31 @@ class ClientChannelTestCase(base.BaseTestCase):
with mock.patch.object(daemon.LOG, 'warning') as mock_warning:
self.client_channel.out_of_band([daemon.Message.PING])
mock_warning.assert_called_once()
class UnMonkeyPatch(base.BaseTestCase):
@testtools.skipIf(sys.version_info < (3, 0), 'works only with python 3')
def test_un_monkey_patch(self):
self.assertFalse(any(
eventlet.patcher.is_monkey_patched(eventlet_mod_name)
for eventlet_mod_name in daemon.EVENTLET_MODULES))
eventlet.monkey_patch()
self.assertTrue(any(
eventlet.patcher.is_monkey_patched(eventlet_mod_name)
for eventlet_mod_name in daemon.EVENTLET_MODULES))
daemon.un_monkey_patch()
for eventlet_mod_name, func_modules in daemon.EVENTLET_LIBRARIES:
if not eventlet.patcher.is_monkey_patched(eventlet_mod_name):
continue
for name, green_mod in func_modules():
orig_mod = eventlet.patcher.original(name)
patched_mod = sys.modules.get(name)
for attr_name in green_mod.__patched__:
un_monkey_patched_attr = getattr(patched_mod, attr_name,
None)
original_attr = getattr(orig_mod, attr_name, None)
self.assertEqual(un_monkey_patched_attr, original_attr)

View File

@ -0,0 +1,11 @@
---
other:
- |
The ``oslo.privsep`` client can be called from a program using eventlet.
If ``eventlet.monkey_patch``, some libraries will be patched, for example
``threading`` or ``os``. When the root daemon is forked from the client
process, those libraries remain patched. Now, when the daemon is forked
from the client process, those libraries and methods are restored to the
original values. The goal is to prevent some timeouts when using eventlet
threads (user threads); system threads are preemptive and the code does
not need to care about the executor token.