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