]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume: process: module for running system commands
authorAlfredo Deza <adeza@redhat.com>
Thu, 22 Jun 2017 19:37:55 +0000 (15:37 -0400)
committerAlfredo Deza <adeza@redhat.com>
Fri, 4 Aug 2017 14:25:57 +0000 (10:25 -0400)
Signed-off-by: Alfredo Deza <adeza@redhat.com>
src/ceph-volume/ceph_volume/process.py [new file with mode: 0644]

diff --git a/src/ceph-volume/ceph_volume/process.py b/src/ceph-volume/ceph_volume/process.py
new file mode 100644 (file)
index 0000000..abd8f9d
--- /dev/null
@@ -0,0 +1,86 @@
+import subprocess
+import sys
+from select import select
+from ceph_volume import terminal
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def log_output(descriptor, message, terminal_logging):
+    """
+    log output to both the logger and the terminal if terminal_logging is
+    enabled
+    """
+    line = '%s %s' % (descriptor, message)
+    if terminal_logging:
+        getattr(terminal, descriptor)(message)
+    logger.info(line)
+
+
+def log_descriptors(reads, process, terminal_logging):
+    """
+    Helper to send output to the terminal while polling the subprocess
+    """
+    err_read = out_read = None
+    while True:
+        for descriptor in reads:
+            if descriptor == process.stdout.fileno():
+                out_read = process.stdout.readline()
+                if out_read:
+                    log_output('stdout', out_read, terminal_logging)
+                    sys.stdout.flush()
+
+            if descriptor == process.stderr.fileno():
+                err_read = process.stderr.readline()
+                if err_read:
+                    log_output('stderr', err_read, terminal_logging)
+                    sys.stderr.flush()
+        if not err_read and not out_read:
+            break
+
+
+def run(command, **kw):
+    """
+    A real-time-logging implementation of a remote subprocess.Popen call where
+    a command is just executed on the remote end and no other handling is done.
+
+    :param command: The command to pass in to the remote subprocess.Popen as a list
+    :param stop_on_error: If a nonzero exit status is return, it raises a ``RuntimeError``
+    """
+    stop_on_error = kw.pop('stop_on_error', True)
+    command_msg = "Running command: %s" % ' '.join(command)
+    logger.info(command_msg)
+    terminal.write(command_msg)
+    terminal_logging = kw.get('terminal_logging', True)
+
+    process = subprocess.Popen(
+        command,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        close_fds=True,
+        **kw
+    )
+
+    while True:
+        reads, _, _ = select(
+            [process.stdout.fileno(), process.stderr.fileno()],
+            [], []
+        )
+        log_descriptors(reads, process, terminal_logging)
+
+        if process.poll() is not None:
+            # ensure we do not have anything pending in stdout or stderr
+            log_descriptors(reads, process, terminal_logging)
+
+            break
+
+    returncode = process.wait()
+    if returncode != 0:
+        msg = "command returned non-zero exit status: %s" % returncode
+        if stop_on_error:
+            raise RuntimeError(msg)
+        else:
+            terminal.warning(msg)
+            logger.warning(msg)