Add lock around channel creation

When a neutron agent starts up, it processes different resources
in separate eventlet threads. These can race creating the channel
which results in redundant privsep-helper processes. This patch
fixes that by adding a lock around channel creation.

Change-Id: I5de22b72059133b05d64be47f4c1d3f566b46a6e
Closes-Bug: #1864664
This commit is contained in:
Darragh O'Reilly 2020-02-25 15:28:20 +00:00
parent 7b8c4d4597
commit bbe24aa6e0
2 changed files with 23 additions and 10 deletions

View File

@ -19,6 +19,7 @@ import logging
import multiprocessing
import shlex
import sys
import threading
from oslo_config import cfg
from oslo_config import types
@ -144,6 +145,7 @@ class PrivContext(object):
# The client_mode should be set to False on Windows.
self.client_mode = sys.platform != 'win32'
self.channel = None
self.start_lock = threading.Lock()
cfg.CONF.register_opts(OPTS, group=cfg_section)
cfg.CONF.set_default('capabilities', group=cfg_section,
@ -247,18 +249,19 @@ class PrivContext(object):
return func(*args, **kwargs)
def start(self, method=Method.ROOTWRAP):
if self.channel is not None:
LOG.warning('privsep daemon already running')
return
with self.start_lock:
if self.channel is not None:
LOG.warning('privsep daemon already running')
return
if method is Method.ROOTWRAP:
channel = daemon.RootwrapClientChannel(context=self)
elif method is Method.FORK:
channel = daemon.ForkingClientChannel(context=self)
else:
raise ValueError('Unknown method: %s' % method)
if method is Method.ROOTWRAP:
channel = daemon.RootwrapClientChannel(context=self)
elif method is Method.FORK:
channel = daemon.ForkingClientChannel(context=self)
else:
raise ValueError('Unknown method: %s' % method)
self.channel = channel
self.channel = channel
def stop(self):
if self.channel is not None:

View File

@ -127,6 +127,16 @@ class PrivContextTest(testctx.TestContextTestCase):
self.assertEqual(testctx.context.helper_command('/sock')[:3],
['sudo', 'rootwrap', 'privsep-helper'])
def test_start_acquires_lock(self):
context = priv_context.PrivContext('test', capabilities=[])
context.channel = "something not None"
context.start_lock = mock.Mock()
context.start_lock.__enter__ = mock.Mock()
context.start_lock.__exit__ = mock.Mock()
self.assertFalse(context.start_lock.__enter__.called)
context.start()
self.assertTrue(context.start_lock.__enter__.called)
@testtools.skipIf(platform.system() != 'Linux',
'works only on Linux platform.')