shell_command: raise a ShellCommandError in run_with_response

This error contains the return code and anything received from stdout and stderr when "run_with_response" encounters an error.  This allows the caller to receive the output and easily distinguish between a successfully run command and an erronous command.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Arksine 2021-03-17 11:42:42 -04:00
parent 3f48dd5510
commit 06c35fa0b2

View File

@ -9,6 +9,14 @@ import logging
import signal
import asyncio
from tornado import gen
from utils import ServerError
class ShellCommandError(ServerError):
def __init__(self, message, return_code, err_output=(b"", b""),
status_code=400):
super().__init__(message, status_code=status_code)
self.stdout, self.stderr = err_output
self.return_code = return_code
class SCProcess(asyncio.subprocess.Process):
def initialize(self, program_name, std_out_cb, std_err_cb, log_stderr):
@ -16,7 +24,6 @@ class SCProcess(asyncio.subprocess.Process):
self.std_out_cb = std_out_cb
self.std_err_cb = std_err_cb
self.log_stderr = log_stderr
self.partial_data = b""
self.cancel_requested = False
async def _read_stream_with_cb(self, fd):
@ -103,9 +110,12 @@ class ShellCommand:
def get_return_code(self):
return self.return_code
async def run(self, timeout=2., verbose=True, log_complete=True):
def _reset_command_data(self):
self.return_code = self.proc = None
self.cancelled = False
async def run(self, timeout=2., verbose=True, log_complete=True):
self._reset_command_data()
if not timeout:
# Never timeout
timeout = 9999999999999999.
@ -130,10 +140,10 @@ class ShellCommand:
async def run_with_response(self, timeout=2., retries=1,
log_complete=True):
self.return_code = self.proc = None
self.cancelled = False
self._reset_command_data()
retries = max(1, retries)
while retries > 0:
stdout = stderr = None
stdout = stderr = b""
if await self._create_subprocess():
try:
ret = self.proc.communicate()
@ -154,7 +164,9 @@ class ShellCommand:
f"\n{stdout.decode()}")
retries -= 1
await gen.sleep(.5)
return None
raise ShellCommandError(
f"Error running shell command: '{self.command}'",
self.return_code, err_output=(stdout, stderr))
async def _create_subprocess(self):
loop = asyncio.get_event_loop()
@ -197,6 +209,7 @@ class ShellCommand:
return success
class ShellCommandFactory:
error = ShellCommandError
def build_shell_command(self, cmd, callback=None, std_err_callback=None,
env=None, log_stderr=False):
return ShellCommand(cmd, callback, std_err_callback, env, log_stderr)