blob: 21a21ac9ab983ab22e73883d5aaa0d1d6240a57c [file] [log] [blame]
Moshe0bb532c2018-02-26 13:39:57 +02001##############################################################################
2# Copyright 2018 EuropeanSoftwareMarketingLtd.
3# ===================================================================
4# Licensed under the ApacheLicense, Version2.0 (the"License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# software distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and limitations under
12# the License
13##############################################################################
14# vnftest comment: this is a modified copy of
15# yardstick/common/process.py
16
17import logging
18import multiprocessing
19import signal
20import subprocess
21import time
22
23import os
24from oslo_utils import encodeutils
25
26from vnftest.common import exceptions
27from vnftest.common import utils
28
29
30LOG = logging.getLogger(__name__)
31
32
33def check_if_process_failed(proc, timeout=1):
34 if proc is not None:
35 proc.join(timeout)
36 # Only abort if the process aborted
37 if proc.exitcode is not None and proc.exitcode > 0:
38 raise RuntimeError("{} exited with status {}".format(proc.name, proc.exitcode))
39
40
41def terminate_children(timeout=3):
42 current_proccess = multiprocessing.current_process()
43 active_children = multiprocessing.active_children()
44 if not active_children:
45 LOG.debug("no children to terminate")
46 return
47 for child in active_children:
48 LOG.debug("%s %s %s, child: %s %s", current_proccess.name, current_proccess.pid,
49 os.getpid(), child, child.pid)
50 LOG.debug("joining %s", child)
51 child.join(timeout)
52 child.terminate()
53 active_children = multiprocessing.active_children()
54 if not active_children:
55 LOG.debug("no children to terminate")
56 for child in active_children:
57 LOG.debug("%s %s %s, after terminate child: %s %s", current_proccess.name,
58 current_proccess.pid, os.getpid(), child, child.pid)
59
60
61def _additional_env_args(additional_env):
62 """Build arguments for adding additional environment vars with env"""
63 if additional_env is None:
64 return []
65 return ['env'] + ['%s=%s' % pair for pair in additional_env.items()]
66
67
68def _subprocess_setup():
69 # Python installs a SIGPIPE handler by default. This is usually not what
70 # non-Python subprocesses expect.
71 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
72
73
74def subprocess_popen(args, stdin=None, stdout=None, stderr=None, shell=False,
75 env=None, preexec_fn=_subprocess_setup, close_fds=True):
76 return subprocess.Popen(args, shell=shell, stdin=stdin, stdout=stdout,
77 stderr=stderr, preexec_fn=preexec_fn,
78 close_fds=close_fds, env=env)
79
80
81def create_process(cmd, run_as_root=False, additional_env=None):
82 """Create a process object for the given command.
83
84 The return value will be a tuple of the process object and the
85 list of command arguments used to create it.
86 """
87 if not isinstance(cmd, list):
88 cmd = [cmd]
89 cmd = list(map(str, _additional_env_args(additional_env) + cmd))
90 if run_as_root:
91 # NOTE(ralonsoh): to handle a command executed as root, using
92 # a root wrapper, instead of using "sudo".
93 pass
94 LOG.debug("Running command: %s", cmd)
95 obj = subprocess_popen(cmd, shell=False, stdin=subprocess.PIPE,
96 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97 return obj, cmd
98
99
100def execute(cmd, process_input=None, additional_env=None,
101 check_exit_code=True, return_stderr=False, log_fail_as_error=True,
102 extra_ok_codes=None, run_as_root=False):
103 try:
104 if process_input is not None:
105 _process_input = encodeutils.to_utf8(process_input)
106 else:
107 _process_input = None
108
109 # NOTE(ralonsoh): to handle the execution of a command as root,
110 # using a root wrapper, instead of using "sudo".
111 obj, cmd = create_process(cmd, run_as_root=run_as_root,
112 additional_env=additional_env)
113 _stdout, _stderr = obj.communicate(_process_input)
114 returncode = obj.returncode
115 obj.stdin.close()
116 _stdout = utils.safe_decode_utf8(_stdout)
117 _stderr = utils.safe_decode_utf8(_stderr)
118
119 extra_ok_codes = extra_ok_codes or []
120 if returncode and returncode not in extra_ok_codes:
121 msg = ("Exit code: %(returncode)d; "
122 "Stdin: %(stdin)s; "
123 "Stdout: %(stdout)s; "
124 "Stderr: %(stderr)s") % {'returncode': returncode,
125 'stdin': process_input or '',
126 'stdout': _stdout,
127 'stderr': _stderr}
128 if log_fail_as_error:
129 LOG.error(msg)
130 if check_exit_code:
131 raise exceptions.ProcessExecutionError(msg,
132 returncode=returncode)
133
134 finally:
135 # This appears to be necessary in order for the subprocess to clean up
136 # something between call; without it, the second process hangs when two
137 # execute calls are made in a row.
138 time.sleep(0)
139
140 return (_stdout, _stderr) if return_stderr else _stdout