From b0e56a31eabe79a8363101253795c2be6960e1de Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 19 Sep 2023 10:58:36 +0200 Subject: [PATCH] Display stderr and stdout when run_command fails Throwing a `CalledProcessError` would hide the messages when converted into a `Xenapi.Failure` use a generic `Exception` with a properly formed detail instead. Signed-off-by: BenjiReis --- SOURCES/etc/xapi.d/plugins/raid.py | 4 ++-- .../etc/xapi.d/plugins/xcpngutils/__init__.py | 16 +++++++++++++++- tests/test_raid.py | 3 ++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/SOURCES/etc/xapi.d/plugins/raid.py b/SOURCES/etc/xapi.d/plugins/raid.py index ed91973..36b4a91 100755 --- a/SOURCES/etc/xapi.d/plugins/raid.py +++ b/SOURCES/etc/xapi.d/plugins/raid.py @@ -8,7 +8,7 @@ import XenAPIPlugin sys.path.append('.') -from xcpngutils import configure_logging, run_command, error_wrapped +from xcpngutils import configure_logging, run_command, error_wrapped, ProcessException from xcpngutils.operationlocker import OperationLocker _LOGGER = configure_logging('raid') @@ -21,7 +21,7 @@ def check_raid_pool(session, args): with OperationLocker(): try: result = run_command(['mdadm', '--detail', device]) - except subprocess.CalledProcessError: + except ProcessException: # No RAID return json.dumps({}) diff --git a/SOURCES/etc/xapi.d/plugins/xcpngutils/__init__.py b/SOURCES/etc/xapi.d/plugins/xcpngutils/__init__.py index 2bed512..fb062d8 100644 --- a/SOURCES/etc/xapi.d/plugins/xcpngutils/__init__.py +++ b/SOURCES/etc/xapi.d/plugins/xcpngutils/__init__.py @@ -63,12 +63,24 @@ def log_unhandled_exception(origin, exception_type, exception_value, return logger +class ProcessException(Exception): + def __init__(self, code, command, stderr, stdout) -> None: + super().__init__(code, command, stderr, stdout) + self.returncode = code + self.command = command + self.stderr = stderr + self.stdout = stdout + + def __str__(self) -> str: + return f"Command {self.command} failed with code: {self.returncode} and stderr: {self.stderr}," + \ + f" stdout: {self.stdout}" + def run_command(command, check=True): process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() code = process.returncode if check and code != 0: - raise subprocess.CalledProcessError(code, command, None) + raise ProcessException(code, command, stderr, stdout) result = {'stdout': stdout, 'stderr': stderr, 'command': command, 'returncode': code} return result @@ -123,6 +135,8 @@ def wrapper(*args, **kwds): except EnvironmentError as e: message = e.strerror if e.strerror is not None else str(e.args) raise XenAPIPlugin.Failure(str(e.errno), [message, str(e.filename), traceback.format_exc()]) + except ProcessException as e: + raise_plugin_error(e.returncode, str(e), backtrace=traceback.format_exc()) except Exception as e: raise_plugin_error('-1', str(e), backtrace=traceback.format_exc()) diff --git a/tests/test_raid.py b/tests/test_raid.py index 73c3d2d..e125cf9 100644 --- a/tests/test_raid.py +++ b/tests/test_raid.py @@ -5,6 +5,7 @@ import XenAPIPlugin from raid import check_raid_pool +from xcpngutils import ProcessException MDADM_DETAIL_CMD = ['mdadm', '--detail', '/dev/md127'] @@ -61,7 +62,7 @@ def test_raid_error(self, run_command, fs): assert e.value.params[1] == 'Error!' def test_raid_missing(self, run_command, fs): - run_command.side_effect = subprocess.CalledProcessError(1, MDADM_DETAIL_CMD, None) + run_command.side_effect = ProcessException(1, MDADM_DETAIL_CMD, "", "") res = check_raid_pool(None, None) assert json.loads(res) == json.loads("{}") run_command.assert_called_once_with(MDADM_DETAIL_CMD)