blob: 21a21ac9ab983ab22e73883d5aaa0d1d6240a57c [file] [log] [blame]
##############################################################################
# Copyright 2018 EuropeanSoftwareMarketingLtd.
# ===================================================================
# Licensed under the ApacheLicense, Version2.0 (the"License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under
# the License
##############################################################################
# vnftest comment: this is a modified copy of
# yardstick/common/process.py
import logging
import multiprocessing
import signal
import subprocess
import time
import os
from oslo_utils import encodeutils
from vnftest.common import exceptions
from vnftest.common import utils
LOG = logging.getLogger(__name__)
def check_if_process_failed(proc, timeout=1):
if proc is not None:
proc.join(timeout)
# Only abort if the process aborted
if proc.exitcode is not None and proc.exitcode > 0:
raise RuntimeError("{} exited with status {}".format(proc.name, proc.exitcode))
def terminate_children(timeout=3):
current_proccess = multiprocessing.current_process()
active_children = multiprocessing.active_children()
if not active_children:
LOG.debug("no children to terminate")
return
for child in active_children:
LOG.debug("%s %s %s, child: %s %s", current_proccess.name, current_proccess.pid,
os.getpid(), child, child.pid)
LOG.debug("joining %s", child)
child.join(timeout)
child.terminate()
active_children = multiprocessing.active_children()
if not active_children:
LOG.debug("no children to terminate")
for child in active_children:
LOG.debug("%s %s %s, after terminate child: %s %s", current_proccess.name,
current_proccess.pid, os.getpid(), child, child.pid)
def _additional_env_args(additional_env):
"""Build arguments for adding additional environment vars with env"""
if additional_env is None:
return []
return ['env'] + ['%s=%s' % pair for pair in additional_env.items()]
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 subprocess_popen(args, stdin=None, stdout=None, stderr=None, shell=False,
env=None, preexec_fn=_subprocess_setup, close_fds=True):
return subprocess.Popen(args, shell=shell, stdin=stdin, stdout=stdout,
stderr=stderr, preexec_fn=preexec_fn,
close_fds=close_fds, env=env)
def create_process(cmd, run_as_root=False, additional_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.
"""
if not isinstance(cmd, list):
cmd = [cmd]
cmd = list(map(str, _additional_env_args(additional_env) + cmd))
if run_as_root:
# NOTE(ralonsoh): to handle a command executed as root, using
# a root wrapper, instead of using "sudo".
pass
LOG.debug("Running command: %s", cmd)
obj = subprocess_popen(cmd, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return obj, cmd
def execute(cmd, process_input=None, additional_env=None,
check_exit_code=True, return_stderr=False, log_fail_as_error=True,
extra_ok_codes=None, run_as_root=False):
try:
if process_input is not None:
_process_input = encodeutils.to_utf8(process_input)
else:
_process_input = None
# NOTE(ralonsoh): to handle the execution of a command as root,
# using a root wrapper, instead of using "sudo".
obj, cmd = create_process(cmd, run_as_root=run_as_root,
additional_env=additional_env)
_stdout, _stderr = obj.communicate(_process_input)
returncode = obj.returncode
obj.stdin.close()
_stdout = utils.safe_decode_utf8(_stdout)
_stderr = utils.safe_decode_utf8(_stderr)
extra_ok_codes = extra_ok_codes or []
if returncode and returncode not in extra_ok_codes:
msg = ("Exit code: %(returncode)d; "
"Stdin: %(stdin)s; "
"Stdout: %(stdout)s; "
"Stderr: %(stderr)s") % {'returncode': returncode,
'stdin': process_input or '',
'stdout': _stdout,
'stderr': _stderr}
if log_fail_as_error:
LOG.error(msg)
if check_exit_code:
raise exceptions.ProcessExecutionError(msg,
returncode=returncode)
finally:
# This appears to be necessary in order for the subprocess to clean up
# something between call; without it, the second process hangs when two
# execute calls are made in a row.
time.sleep(0)
return (_stdout, _stderr) if return_stderr else _stdout