xref: /dpdk/dts/framework/exception.py (revision cfa443351ef581b7189467842ca102ab710cb7d2)
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright(c) 2010-2014 Intel Corporation
3# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
4# Copyright(c) 2022-2023 University of New Hampshire
5# Copyright(c) 2024 Arm Limited
6
7"""DTS exceptions.
8
9The exceptions all have different severities expressed as an integer.
10The highest severity of all raised exceptions is used as the exit code of DTS.
11"""
12
13from enum import IntEnum, unique
14from typing import ClassVar
15
16
17@unique
18class ErrorSeverity(IntEnum):
19    """The severity of errors that occur during DTS execution.
20
21    All exceptions are caught and the most severe error is used as return code.
22    """
23
24    #:
25    NO_ERR = 0
26    #:
27    GENERIC_ERR = 1
28    #:
29    CONFIG_ERR = 2
30    #:
31    REMOTE_CMD_EXEC_ERR = 3
32    #:
33    SSH_ERR = 4
34    #:
35    INTERNAL_ERR = 5
36    #:
37    DPDK_BUILD_ERR = 10
38    #:
39    TESTCASE_VERIFY_ERR = 20
40    #:
41    BLOCKING_TESTSUITE_ERR = 25
42
43
44class DTSError(Exception):
45    """The base exception from which all DTS exceptions are subclassed.
46
47    Do not use this exception, only use subclassed exceptions.
48    """
49
50    #:
51    severity: ClassVar[ErrorSeverity] = ErrorSeverity.GENERIC_ERR
52
53
54class SSHTimeoutError(DTSError):
55    """The SSH execution of a command timed out."""
56
57    #:
58    severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
59    _command: str
60
61    def __init__(self, command: str):
62        """Define the meaning of the first argument.
63
64        Args:
65            command: The executed command.
66        """
67        self._command = command
68
69    def __str__(self) -> str:
70        """Add some context to the string representation."""
71        return f"{self._command} execution timed out."
72
73
74class SSHConnectionError(DTSError):
75    """An unsuccessful SSH connection."""
76
77    #:
78    severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
79    _host: str
80    _errors: list[str]
81
82    def __init__(self, host: str, errors: list[str] | None = None):
83        """Define the meaning of the first two arguments.
84
85        Args:
86            host: The hostname to which we're trying to connect.
87            errors: Any errors that occurred during the connection attempt.
88        """
89        self._host = host
90        self._errors = [] if errors is None else errors
91
92    def __str__(self) -> str:
93        """Include the errors in the string representation."""
94        message = f"Error trying to connect with {self._host}."
95        if self._errors:
96            message += f" Errors encountered while retrying: {', '.join(self._errors)}"
97
98        return message
99
100
101class SSHSessionDeadError(DTSError):
102    """The SSH session is no longer alive."""
103
104    #:
105    severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
106    _host: str
107
108    def __init__(self, host: str):
109        """Define the meaning of the first argument.
110
111        Args:
112            host: The hostname of the disconnected node.
113        """
114        self._host = host
115
116    def __str__(self) -> str:
117        """Add some context to the string representation."""
118        return f"SSH session with {self._host} has died."
119
120
121class ConfigurationError(DTSError):
122    """An invalid configuration."""
123
124    #:
125    severity: ClassVar[ErrorSeverity] = ErrorSeverity.CONFIG_ERR
126
127
128class RemoteCommandExecutionError(DTSError):
129    """An unsuccessful execution of a remote command."""
130
131    #:
132    severity: ClassVar[ErrorSeverity] = ErrorSeverity.REMOTE_CMD_EXEC_ERR
133    #: The executed command.
134    command: str
135    _command_stderr: str
136    _command_return_code: int
137
138    def __init__(self, command: str, command_stderr: str, command_return_code: int):
139        """Define the meaning of the first two arguments.
140
141        Args:
142            command: The executed command.
143            command_stderr: The stderr of the executed command.
144            command_return_code: The return code of the executed command.
145        """
146        self.command = command
147        self._command_stderr = command_stderr
148        self._command_return_code = command_return_code
149
150    def __str__(self) -> str:
151        """Include the command, its return code and stderr in the string representation."""
152        return (
153            f"Command '{self.command}' returned a non-zero exit code: "
154            f"{self._command_return_code}\nStderr: {self._command_stderr}"
155        )
156
157
158class InteractiveCommandExecutionError(DTSError):
159    """An unsuccessful execution of a remote command in an interactive environment."""
160
161    #:
162    severity: ClassVar[ErrorSeverity] = ErrorSeverity.REMOTE_CMD_EXEC_ERR
163
164
165class RemoteDirectoryExistsError(DTSError):
166    """A directory that exists on a remote node."""
167
168    #:
169    severity: ClassVar[ErrorSeverity] = ErrorSeverity.REMOTE_CMD_EXEC_ERR
170
171
172class DPDKBuildError(DTSError):
173    """A DPDK build failure."""
174
175    #:
176    severity: ClassVar[ErrorSeverity] = ErrorSeverity.DPDK_BUILD_ERR
177
178
179class TestCaseVerifyError(DTSError):
180    """A test case failure."""
181
182    #:
183    severity: ClassVar[ErrorSeverity] = ErrorSeverity.TESTCASE_VERIFY_ERR
184
185
186class BlockingTestSuiteError(DTSError):
187    """A failure in a blocking test suite."""
188
189    #:
190    severity: ClassVar[ErrorSeverity] = ErrorSeverity.BLOCKING_TESTSUITE_ERR
191    _suite_name: str
192
193    def __init__(self, suite_name: str) -> None:
194        """Define the meaning of the first argument.
195
196        Args:
197            suite_name: The blocking test suite.
198        """
199        self._suite_name = suite_name
200
201    def __str__(self) -> str:
202        """Add some context to the string representation."""
203        return f"Blocking suite {self._suite_name} failed."
204
205
206class InternalError(DTSError):
207    """An internal error or bug has occurred in DTS."""
208
209    #:
210    severity: ClassVar[ErrorSeverity] = ErrorSeverity.INTERNAL_ERR
211