Merge "Add rootwrap daemon mode support"
This commit is contained in:
commit
84243b2f08
@ -31,6 +31,12 @@ ROOT_HELPER_OPTS = [
|
||||
default=True,
|
||||
help=_('Use the root helper to read the namespaces from '
|
||||
'the operating system.')),
|
||||
# We can't just use root_helper=sudo neutron-rootwrap-daemon $cfg because
|
||||
# it isn't appropriate for long-lived processes spawned with create_process
|
||||
# Having a bool use_rootwrap_daemon option precludes specifying the
|
||||
# rootwrap daemon command, which may be necessary for Xen?
|
||||
cfg.StrOpt('root_helper_daemon',
|
||||
help=_('Root helper daemon application to use when possible.')),
|
||||
]
|
||||
|
||||
AGENT_STATE_OPTS = [
|
||||
|
@ -20,12 +20,14 @@ import shlex
|
||||
import socket
|
||||
import struct
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
import eventlet
|
||||
from eventlet.green import subprocess
|
||||
from eventlet import greenthread
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_rootwrap import client
|
||||
from oslo_utils import excutils
|
||||
|
||||
from neutron.agent.common import config
|
||||
@ -38,56 +40,95 @@ LOG = logging.getLogger(__name__)
|
||||
config.register_root_helper(cfg.CONF)
|
||||
|
||||
|
||||
class RootwrapDaemonHelper(object):
|
||||
__client = None
|
||||
__lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
"""There is no reason to instantiate this class"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def get_client(cls):
|
||||
with cls.__lock:
|
||||
if cls.__client is None:
|
||||
cls.__client = client.Client(
|
||||
shlex.split(cfg.CONF.AGENT.root_helper_daemon))
|
||||
return cls.__client
|
||||
|
||||
|
||||
def addl_env_args(addl_env):
|
||||
"""Build arugments for adding additional environment vars with env"""
|
||||
|
||||
# NOTE (twilson) If using rootwrap, an EnvFilter should be set up for the
|
||||
# command instead of a CommandFilter.
|
||||
if addl_env is None:
|
||||
return []
|
||||
return ['env'] + ['%s=%s' % pair for pair in addl_env.items()]
|
||||
|
||||
|
||||
def create_process(cmd, run_as_root=False, addl_env=None):
|
||||
"""Create a process object for the given command.
|
||||
|
||||
The return value will be a tuple of the process object and the
|
||||
list of command arguments used to create it.
|
||||
"""
|
||||
cmd = map(str, addl_env_args(addl_env) + cmd)
|
||||
if run_as_root:
|
||||
cmd = shlex.split(config.get_root_helper(cfg.CONF)) + cmd
|
||||
cmd = map(str, cmd)
|
||||
|
||||
LOG.debug("Running command: %s", cmd)
|
||||
env = os.environ.copy()
|
||||
if addl_env:
|
||||
env.update(addl_env)
|
||||
|
||||
obj = utils.subprocess_popen(cmd, shell=False,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env)
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
return obj, cmd
|
||||
|
||||
|
||||
def execute_rootwrap_daemon(cmd, process_input, addl_env):
|
||||
cmd = map(str, addl_env_args(addl_env) + cmd)
|
||||
# NOTE(twilson) oslo_rootwrap.daemon will raise on filter match
|
||||
# errors, whereas oslo_rootwrap.cmd converts them to return codes.
|
||||
# In practice, no neutron code should be trying to execute something that
|
||||
# would throw those errors, and if it does it should be fixed as opposed to
|
||||
# just logging the execution error.
|
||||
LOG.debug("Running command (rootwrap daemon): %s", cmd)
|
||||
client = RootwrapDaemonHelper.get_client()
|
||||
return client.execute(cmd, process_input)
|
||||
|
||||
|
||||
def execute(cmd, process_input=None, addl_env=None,
|
||||
check_exit_code=True, return_stderr=False, log_fail_as_error=True,
|
||||
extra_ok_codes=None, run_as_root=False):
|
||||
try:
|
||||
obj, cmd = create_process(cmd, run_as_root=run_as_root,
|
||||
addl_env=addl_env)
|
||||
_stdout, _stderr = obj.communicate(process_input)
|
||||
obj.stdin.close()
|
||||
m = _("\nCommand: %(cmd)s\nExit code: %(code)s\nStdin: %(stdin)s\n"
|
||||
"Stdout: %(stdout)s\nStderr: %(stderr)s") % \
|
||||
{'cmd': cmd,
|
||||
'code': obj.returncode,
|
||||
'stdin': process_input or '',
|
||||
'stdout': _stdout,
|
||||
'stderr': _stderr}
|
||||
if run_as_root and cfg.CONF.AGENT.root_helper_daemon:
|
||||
returncode, _stdout, _stderr = (
|
||||
execute_rootwrap_daemon(cmd, process_input, addl_env))
|
||||
else:
|
||||
obj, cmd = create_process(cmd, run_as_root=run_as_root,
|
||||
addl_env=addl_env)
|
||||
_stdout, _stderr = obj.communicate(process_input)
|
||||
returncode = obj.returncode
|
||||
obj.stdin.close()
|
||||
|
||||
m = _("\nCommand: {cmd}\nExit code: {code}\nStdin: {stdin}\n"
|
||||
"Stdout: {stdout}\nStderr: {stderr}").format(
|
||||
cmd=cmd,
|
||||
code=returncode,
|
||||
stdin=process_input or '',
|
||||
stdout=_stdout,
|
||||
stderr=_stderr)
|
||||
|
||||
extra_ok_codes = extra_ok_codes or []
|
||||
if obj.returncode and obj.returncode in extra_ok_codes:
|
||||
obj.returncode = None
|
||||
if returncode and returncode in extra_ok_codes:
|
||||
returncode = None
|
||||
|
||||
if obj.returncode and log_fail_as_error:
|
||||
if returncode and log_fail_as_error:
|
||||
LOG.error(m)
|
||||
else:
|
||||
LOG.debug(m)
|
||||
|
||||
if obj.returncode and check_exit_code:
|
||||
if returncode and check_exit_code:
|
||||
raise RuntimeError(m)
|
||||
finally:
|
||||
# NOTE(termie): this appears to be necessary to let the subprocess
|
||||
|
@ -92,7 +92,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
|
||||
get_temp_file_path('external/pids'))
|
||||
conf.set_override('host', host)
|
||||
agent = neutron_l3_agent.L3NATAgentWithStateReport(host, conf)
|
||||
mock.patch.object(ip_lib, 'send_gratuitous_arp').start()
|
||||
mock.patch.object(ip_lib, '_arping').start()
|
||||
|
||||
return agent
|
||||
|
||||
|
@ -56,3 +56,6 @@ class BaseSudoTestCase(base.BaseTestCase):
|
||||
config.register_root_helper(cfg.CONF)
|
||||
self.config(group='AGENT',
|
||||
root_helper=os.environ.get('OS_ROOTWRAP_CMD', SUDO_CMD))
|
||||
self.config(group='AGENT',
|
||||
root_helper_daemon=os.environ.get(
|
||||
'OS_ROOTWRAP_DAEMON_CMD'))
|
||||
|
@ -34,3 +34,10 @@ class TestRootHelper(base.BaseTestCase):
|
||||
conf = config.setup_conf()
|
||||
config.register_root_helper(conf)
|
||||
self.assertEqual(config.get_root_helper(conf), 'sudo')
|
||||
|
||||
def test_agent_root_helper_daemon(self):
|
||||
conf = config.setup_conf()
|
||||
config.register_root_helper(conf)
|
||||
rhd = 'my_root_helper_daemon'
|
||||
conf.set_override('root_helper_daemon', rhd, 'AGENT')
|
||||
self.assertEqual(rhd, conf.AGENT.root_helper_daemon)
|
||||
|
@ -106,6 +106,7 @@ console_scripts =
|
||||
neutron-restproxy-agent = neutron.plugins.bigswitch.agent.restproxy_agent:main
|
||||
neutron-server = neutron.cmd.eventlet.server:main
|
||||
neutron-rootwrap = oslo_rootwrap.cmd:main
|
||||
neutron-rootwrap-daemon = oslo_rootwrap.cmd:daemon
|
||||
neutron-usage-audit = neutron.cmd.usage_audit:main
|
||||
neutron-metering-agent = neutron.cmd.eventlet.services.metering_agent:main
|
||||
neutron-sriov-nic-agent = neutron.plugins.sriovnicagent.sriov_nic_agent:main
|
||||
|
@ -178,6 +178,7 @@ function _install_rootwrap_sudoers {
|
||||
VENV_NAME=${venv:-dsvm-functional}
|
||||
VENV_PATH=$NEUTRON_PATH/.tox/$VENV_NAME
|
||||
ROOTWRAP_SUDOER_CMD="$VENV_PATH/bin/neutron-rootwrap $VENV_PATH/etc/neutron/rootwrap.conf *"
|
||||
ROOTWRAP_DAEMON_SUDOER_CMD="$VENV_PATH/bin/neutron-rootwrap-daemon $VENV_PATH/etc/neutron/rootwrap.conf"
|
||||
TEMPFILE=$(mktemp)
|
||||
cat << EOF > $TEMPFILE
|
||||
# A bug in oslo.rootwrap [1] prevents commands executed with 'ip netns
|
||||
@ -194,6 +195,7 @@ function _install_rootwrap_sudoers {
|
||||
#
|
||||
Defaults:$STACK_USER secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$VENV_PATH/bin"
|
||||
$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_SUDOER_CMD
|
||||
$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_DAEMON_SUDOER_CMD
|
||||
EOF
|
||||
chmod 0440 $TEMPFILE
|
||||
sudo chown root:root $TEMPFILE
|
||||
|
1
tox.ini
1
tox.ini
@ -40,6 +40,7 @@ deps =
|
||||
setenv = OS_TEST_PATH=./neutron/tests/functional
|
||||
OS_SUDO_TESTING=1
|
||||
OS_ROOTWRAP_CMD=sudo {envbindir}/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf
|
||||
OS_ROOTWRAP_DAEMON_CMD=sudo {envbindir}/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf
|
||||
OS_FAIL_ON_MISSING_DEPS=1
|
||||
OS_TEST_TIMEOUT=90
|
||||
sitepackages=True
|
||||
|
Loading…
Reference in New Issue
Block a user