System-level utilities and helper functions.
"""
-import logging
+import os
import random
import shlex
+import signal
from eventlet.green import subprocess
from eventlet import greenthread
from cinder.openstack.common.gettextutils import _
+from cinder.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class ProcessExecutionError(Exception):
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
description=None):
+ self.exit_code = exit_code
+ self.stderr = stderr
+ self.stdout = stdout
+ self.cmd = cmd
+ self.description = description
+
if description is None:
description = "Unexpected error while running command."
if exit_code is None:
super(ProcessExecutionError, self).__init__(message)
+class NoRootWrapSpecified(Exception):
+ def __init__(self, message=None):
+ super(NoRootWrapSpecified, self).__init__(message)
+
+
+def _subprocess_setup():
+ # Python installs a SIGPIPE handler by default. This is usually not what
+ # non-Python subprocesses expect.
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+
def execute(*cmd, **kwargs):
"""
Helper method to shell out and execute a command through subprocess with
:type cmd: string
:param process_input: Send to opened process.
:type proces_input: string
- :param check_exit_code: Defaults to 0. Will raise
- :class:`ProcessExecutionError`
- if the command exits without returning this value
- as a returncode
- :type check_exit_code: int
+ :param check_exit_code: Single bool, int, or list of allowed exit
+ codes. Defaults to [0]. Raise
+ :class:`ProcessExecutionError` unless
+ program exits with one of these code.
+ :type check_exit_code: boolean, int, or [int]
:param delay_on_retry: True | False. Defaults to True. If set to True,
wait a short amount of time before retrying.
:type delay_on_retry: boolean
the command is prefixed by the command specified
in the root_helper kwarg.
:type run_as_root: boolean
- :param root_helper: command to prefix all cmd's with
+ :param root_helper: command to prefix to commands called with
+ run_as_root=True
:type root_helper: string
+ :param shell: whether or not there should be a shell used to
+ execute this command. Defaults to false.
+ :type shell: boolean
:returns: (stdout, stderr) from process execution
:raises: :class:`UnknownArgumentError` on
receiving unknown arguments
"""
process_input = kwargs.pop('process_input', None)
- check_exit_code = kwargs.pop('check_exit_code', 0)
+ check_exit_code = kwargs.pop('check_exit_code', [0])
+ ignore_exit_code = False
delay_on_retry = kwargs.pop('delay_on_retry', True)
attempts = kwargs.pop('attempts', 1)
run_as_root = kwargs.pop('run_as_root', False)
root_helper = kwargs.pop('root_helper', '')
+ shell = kwargs.pop('shell', False)
+
+ if isinstance(check_exit_code, bool):
+ ignore_exit_code = not check_exit_code
+ check_exit_code = [0]
+ elif isinstance(check_exit_code, int):
+ check_exit_code = [check_exit_code]
+
if len(kwargs):
raise UnknownArgumentError(_('Got unknown keyword args '
'to utils.execute: %r') % kwargs)
- if run_as_root:
+
+ if run_as_root and os.geteuid() != 0:
+ if not root_helper:
+ raise NoRootWrapSpecified(
+ message=('Command requested root, but did not specify a root '
+ 'helper.'))
cmd = shlex.split(root_helper) + list(cmd)
+
cmd = map(str, cmd)
while attempts > 0:
try:
LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
_PIPE = subprocess.PIPE # pylint: disable=E1101
+
+ if os.name == 'nt':
+ preexec_fn = None
+ close_fds = False
+ else:
+ preexec_fn = _subprocess_setup
+ close_fds = True
+
obj = subprocess.Popen(cmd,
stdin=_PIPE,
stdout=_PIPE,
stderr=_PIPE,
- close_fds=True)
+ close_fds=close_fds,
+ preexec_fn=preexec_fn,
+ shell=shell)
result = None
if process_input is not None:
result = obj.communicate(process_input)
_returncode = obj.returncode # pylint: disable=E1101
if _returncode:
LOG.debug(_('Result was %s') % _returncode)
- if (isinstance(check_exit_code, int) and
- not isinstance(check_exit_code, bool) and
- _returncode != check_exit_code):
+ if not ignore_exit_code and _returncode not in check_exit_code:
(stdout, stderr) = result
raise ProcessExecutionError(exit_code=_returncode,
stdout=stdout,