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
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