Add JS file to NSB report template
[yardstick.git] / yardstick / common / process.py
index 812ddea..ede6cdd 100644 (file)
 # 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.
+
 import logging
 import multiprocessing
+import signal
+import subprocess
+import time
 
 import os
+from oslo_utils import encodeutils
+
+from yardstick.common import exceptions
+from yardstick.common import utils
+
 
 LOG = logging.getLogger(__name__)
 
@@ -45,3 +54,85 @@ def terminate_children(timeout=3):
     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