diff --git a/sonic_installer/common.py b/sonic_installer/common.py index 50b736901f..2551fe8e85 100644 --- a/sonic_installer/common.py +++ b/sonic_installer/common.py @@ -46,10 +46,15 @@ def run_command_or_raise(argv, raise_exception=True, capture=True): stdout = subprocess.PIPE if capture else None proc = subprocess.Popen(argv, text=True, stdout=stdout) - out, _ = proc.communicate() + out, err = proc.communicate() if proc.returncode != 0 and raise_exception: - raise SonicRuntimeException("Failed to run command '{0}'".format(argv)) + sre = SonicRuntimeException("Failed to run command '{0}'".format(argv)) + if out: + sre.add_note("\nSTDOUT:\n{}".format(out.rstrip("\n"))) + if err: + sre.add_note("\nSTDERR:\n{}".format(err.rstrip("\n"))) + raise sre if out is not None: out = out.rstrip("\n") diff --git a/sonic_installer/exception.py b/sonic_installer/exception.py index 08eab7d2ff..c9e15c9bbd 100644 --- a/sonic_installer/exception.py +++ b/sonic_installer/exception.py @@ -5,4 +5,15 @@ class SonicRuntimeException(Exception): """SONiC Runtime Excpetion class used to report SONiC related errors """ - pass + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.notes = [] + + def __str__(self): + msg = super().__str__() + if self.notes: + msg += "\n" + "\n".join(self.notes) + return msg + + def add_note(self, note): + self.notes.append(note) diff --git a/tests/test_sonic_installer.py b/tests/test_sonic_installer.py index e9cb727d37..9e8438a7fc 100644 --- a/tests/test_sonic_installer.py +++ b/tests/test_sonic_installer.py @@ -129,3 +129,18 @@ def test_set_fips(get_bootloader): mock_bootloader.get_fips = Mock(return_value=True) result = runner.invoke(sonic_installer.commands["get-fips"], [next_image]) assert "FIPS is enabled" in result.output + +@patch("sonic_installer.common.subprocess.Popen") +def test_runtime_exception(mock_popen): + """ This test covers the "sonic-installer" exception handling. """ + + mock_popen.return_value.returncode = 1 + mock_popen.return_value.communicate.return_value = ('Running', 'Failed') + + with pytest.raises(sonic_installer_common.SonicRuntimeException) as sre: + sonic_installer_common.run_command_or_raise(["test.sh"]) + + assert '\nSTDOUT:\nRunning' in sre.value.notes, "Invalid STDOUT" + assert '\nSTDERR:\nFailed' in sre.value.notes, "Invalid STDERR" + + assert all(v in str(sre.value) for v in ['test.sh', 'Running', 'Failed']), "Invalid message"