Sync processutils from oslo code
Sync openstack/common/processutils.py: cdcc19c1d78a4a88daabfb64b27abd4924aa442d 2a4d15dda6f2c32cce322d82f1a81de6b50853f0 8a0f5678485076fbe0ea1f318c7fd49bac3f1df8 51778f9eef2dd88a949679f0280f69ee99fd5567 fcf517d72cb81f972fad20caa9ff0341e9b4aa9c af4159266f6e366a3cd7366a672f8f8b38a2f0f2 f773ea298cd9f322023b91247e688726cf674cd5 8b2b0b743e84ceed7841cf470afed6a5da8e1d07 3b71f46628822c74c66d37cae8eca9e7d8679d0d 12bcdb71ffbe9ee1688beed1f0ddb0c198822682 a4dab739a780a38c5263b02a670a1d0d009f829e d6a963e911b8456c06dceb5ee3cc88a70c08bf82 aa5b6588292e804557c7ff7f24a88f0ea97acca8 1a2df89c05d33495d7e057dcdf0714d05beb1fc8 7119e29cb535426c587eaf2cfc2cfcd11a422df0 2f013887c20310c522f9494ed854554e6f8c7aa4 a51469326e84ed977ecc4e57fd3d46cdc21aa08f Change-Id: I50439ced3adec1e6befcb7cb65fe558852f2611a
This commit is contained in:
parent
fb7817a574
commit
8b61c44fe2
@ -1,5 +1,3 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -19,6 +17,8 @@
|
||||
System-level utilities and helper functions.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import logging as stdlib_logging
|
||||
import os
|
||||
import random
|
||||
import shlex
|
||||
@ -26,6 +26,7 @@ import signal
|
||||
|
||||
from eventlet.green import subprocess
|
||||
from eventlet import greenthread
|
||||
import six
|
||||
|
||||
from ironic.openstack.common.gettextutils import _
|
||||
from ironic.openstack.common import log as logging
|
||||
@ -54,11 +55,18 @@ class ProcessExecutionError(Exception):
|
||||
self.description = description
|
||||
|
||||
if description is None:
|
||||
description = "Unexpected error while running command."
|
||||
description = _("Unexpected error while running command.")
|
||||
if exit_code is None:
|
||||
exit_code = '-'
|
||||
message = ("%s\nCommand: %s\nExit code: %s\nStdout: %r\nStderr: %r"
|
||||
% (description, cmd, exit_code, stdout, stderr))
|
||||
message = _('%(description)s\n'
|
||||
'Command: %(cmd)s\n'
|
||||
'Exit code: %(exit_code)s\n'
|
||||
'Stdout: %(stdout)r\n'
|
||||
'Stderr: %(stderr)r') % {'description': description,
|
||||
'cmd': cmd,
|
||||
'exit_code': exit_code,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr}
|
||||
super(ProcessExecutionError, self).__init__(message)
|
||||
|
||||
|
||||
@ -74,14 +82,17 @@ def _subprocess_setup():
|
||||
|
||||
|
||||
def execute(*cmd, **kwargs):
|
||||
"""
|
||||
Helper method to shell out and execute a command through subprocess with
|
||||
optional retry.
|
||||
"""Helper method to shell out and execute a command through subprocess.
|
||||
|
||||
Allows optional retry.
|
||||
|
||||
:param cmd: Passed to subprocess.Popen.
|
||||
:type cmd: string
|
||||
:param process_input: Send to opened process.
|
||||
:type proces_input: string
|
||||
:type process_input: string
|
||||
:param env_variables: Environment variables and their values that
|
||||
will be set for the process.
|
||||
:type env_variables: dict
|
||||
:param check_exit_code: Single bool, int, or list of allowed exit
|
||||
codes. Defaults to [0]. Raise
|
||||
:class:`ProcessExecutionError` unless
|
||||
@ -102,6 +113,9 @@ def execute(*cmd, **kwargs):
|
||||
:param shell: whether or not there should be a shell used to
|
||||
execute this command. Defaults to false.
|
||||
:type shell: boolean
|
||||
:param loglevel: log level for execute commands.
|
||||
:type loglevel: int. (Should be stdlib_logging.DEBUG or
|
||||
stdlib_logging.INFO)
|
||||
:returns: (stdout, stderr) from process execution
|
||||
:raises: :class:`UnknownArgumentError` on
|
||||
receiving unknown arguments
|
||||
@ -109,6 +123,7 @@ def execute(*cmd, **kwargs):
|
||||
"""
|
||||
|
||||
process_input = kwargs.pop('process_input', None)
|
||||
env_variables = kwargs.pop('env_variables', None)
|
||||
check_exit_code = kwargs.pop('check_exit_code', [0])
|
||||
ignore_exit_code = False
|
||||
delay_on_retry = kwargs.pop('delay_on_retry', True)
|
||||
@ -116,6 +131,7 @@ def execute(*cmd, **kwargs):
|
||||
run_as_root = kwargs.pop('run_as_root', False)
|
||||
root_helper = kwargs.pop('root_helper', '')
|
||||
shell = kwargs.pop('shell', False)
|
||||
loglevel = kwargs.pop('loglevel', stdlib_logging.DEBUG)
|
||||
|
||||
if isinstance(check_exit_code, bool):
|
||||
ignore_exit_code = not check_exit_code
|
||||
@ -127,11 +143,11 @@ def execute(*cmd, **kwargs):
|
||||
raise UnknownArgumentError(_('Got unknown keyword args '
|
||||
'to utils.execute: %r') % kwargs)
|
||||
|
||||
if run_as_root and os.geteuid() != 0:
|
||||
if run_as_root and hasattr(os, 'geteuid') and os.geteuid() != 0:
|
||||
if not root_helper:
|
||||
raise NoRootWrapSpecified(
|
||||
message=('Command requested root, but did not specify a root '
|
||||
'helper.'))
|
||||
message=_('Command requested root, but did not '
|
||||
'specify a root helper.'))
|
||||
cmd = shlex.split(root_helper) + list(cmd)
|
||||
|
||||
cmd = map(str, cmd)
|
||||
@ -139,7 +155,8 @@ def execute(*cmd, **kwargs):
|
||||
while attempts > 0:
|
||||
attempts -= 1
|
||||
try:
|
||||
LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
|
||||
LOG.log(loglevel, 'Running cmd (subprocess): %s',
|
||||
' '.join(logging.mask_password(cmd)))
|
||||
_PIPE = subprocess.PIPE # pylint: disable=E1101
|
||||
|
||||
if os.name == 'nt':
|
||||
@ -155,28 +172,37 @@ def execute(*cmd, **kwargs):
|
||||
stderr=_PIPE,
|
||||
close_fds=close_fds,
|
||||
preexec_fn=preexec_fn,
|
||||
shell=shell)
|
||||
shell=shell,
|
||||
env=env_variables)
|
||||
result = None
|
||||
if process_input is not None:
|
||||
result = obj.communicate(process_input)
|
||||
else:
|
||||
result = obj.communicate()
|
||||
for _i in six.moves.range(20):
|
||||
# NOTE(russellb) 20 is an arbitrary number of retries to
|
||||
# prevent any chance of looping forever here.
|
||||
try:
|
||||
if process_input is not None:
|
||||
result = obj.communicate(process_input)
|
||||
else:
|
||||
result = obj.communicate()
|
||||
except OSError as e:
|
||||
if e.errno in (errno.EAGAIN, errno.EINTR):
|
||||
continue
|
||||
raise
|
||||
break
|
||||
obj.stdin.close() # pylint: disable=E1101
|
||||
_returncode = obj.returncode # pylint: disable=E1101
|
||||
if _returncode:
|
||||
LOG.debug(_('Result was %s') % _returncode)
|
||||
if not ignore_exit_code and _returncode not in check_exit_code:
|
||||
(stdout, stderr) = result
|
||||
raise ProcessExecutionError(exit_code=_returncode,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cmd=' '.join(cmd))
|
||||
LOG.log(loglevel, 'Result was %s' % _returncode)
|
||||
if not ignore_exit_code and _returncode not in check_exit_code:
|
||||
(stdout, stderr) = result
|
||||
raise ProcessExecutionError(exit_code=_returncode,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cmd=' '.join(cmd))
|
||||
return result
|
||||
except ProcessExecutionError:
|
||||
if not attempts:
|
||||
raise
|
||||
else:
|
||||
LOG.debug(_('%r failed. Retrying.'), cmd)
|
||||
LOG.log(loglevel, '%r failed. Retrying.', cmd)
|
||||
if delay_on_retry:
|
||||
greenthread.sleep(random.randint(20, 200) / 100.0)
|
||||
finally:
|
||||
@ -187,8 +213,7 @@ def execute(*cmd, **kwargs):
|
||||
|
||||
|
||||
def trycmd(*args, **kwargs):
|
||||
"""
|
||||
A wrapper around execute() to more easily handle warnings and errors.
|
||||
"""A wrapper around execute() to more easily handle warnings and errors.
|
||||
|
||||
Returns an (out, err) tuple of strings containing the output of
|
||||
the command's stdout and stderr. If 'err' is not empty then the
|
||||
@ -203,8 +228,8 @@ def trycmd(*args, **kwargs):
|
||||
try:
|
||||
out, err = execute(*args, **kwargs)
|
||||
failed = False
|
||||
except ProcessExecutionError, exn:
|
||||
out, err = '', str(exn)
|
||||
except ProcessExecutionError as exn:
|
||||
out, err = '', six.text_type(exn)
|
||||
failed = True
|
||||
|
||||
if not failed and discard_warnings and err:
|
||||
@ -216,7 +241,7 @@ def trycmd(*args, **kwargs):
|
||||
|
||||
def ssh_execute(ssh, cmd, process_input=None,
|
||||
addl_env=None, check_exit_code=True):
|
||||
LOG.debug(_('Running cmd (SSH): %s'), cmd)
|
||||
LOG.debug('Running cmd (SSH): %s', cmd)
|
||||
if addl_env:
|
||||
raise InvalidArgumentError(_('Environment not supported over SSH'))
|
||||
|
||||
@ -237,7 +262,7 @@ def ssh_execute(ssh, cmd, process_input=None,
|
||||
|
||||
# exit_status == -1 if no exit code was returned
|
||||
if exit_status != -1:
|
||||
LOG.debug(_('Result was %s') % exit_status)
|
||||
LOG.debug('Result was %s' % exit_status)
|
||||
if check_exit_code and exit_status != 0:
|
||||
raise ProcessExecutionError(exit_code=exit_status,
|
||||
stdout=stdout,
|
||||
|
Loading…
x
Reference in New Issue
Block a user