error.py revision 9fa2afc4d2a8c36e3e892365c62f661b9770eca6
1"""
2Internal global error types
3"""
4
5import sys, traceback
6from traceback import format_exception
7
8# Add names you want to be imported by 'from errors import *' to this list.
9# This must be list not a tuple as we modify it to include all of our
10# the Exception classes we define below at the end of this file.
11__all__ = ['format_error']
12
13
14def format_error():
15    t, o, tb = sys.exc_info()
16    trace = format_exception(t, o, tb)
17    # Clear the backtrace to prevent a circular reference
18    # in the heap -- as per tutorial
19    tb = ''
20
21    return ''.join(trace)
22
23
24class JobContinue(SystemExit):
25    """Allow us to bail out requesting continuance."""
26    pass
27
28
29class JobComplete(SystemExit):
30    """Allow us to bail out indicating continuation not required."""
31    pass
32
33
34class AutotestError(Exception):
35    """The parent of all errors deliberatly thrown within the client code."""
36    pass
37
38
39class JobError(AutotestError):
40    """Indicates an error which terminates and fails the whole job (ABORT)."""
41    pass
42
43
44class UnhandledJobError(JobError):
45    """Indicates an unhandled error in a job."""
46    def __init__(self, unhandled_exception):
47        if isinstance(unhandled_exception, JobError):
48            JobError.__init__(self, *unhandled_exception.args)
49        else:
50            msg = "Unhandled %s: %s"
51            msg %= (unhandled_exception.__class__.__name__,
52                    unhandled_exception)
53            msg += "\n" + traceback.format_exc()
54            JobError.__init__(self, msg)
55
56
57class TestBaseException(AutotestError):
58    """The parent of all test exceptions."""
59    # Children are required to override this.  Never instantiate directly.
60    exit_status="NEVER_RAISE_THIS"
61
62
63class TestError(TestBaseException):
64    """Indicates that something went wrong with the test harness itself."""
65    exit_status="ERROR"
66
67
68class TestNAError(TestBaseException):
69    """Indictates that the test is Not Applicable.  Should be thrown
70    when various conditions are such that the test is inappropriate."""
71    exit_status="TEST_NA"
72
73
74class TestFail(TestBaseException):
75    """Indicates that the test failed, but the job will not continue."""
76    exit_status="FAIL"
77
78
79class TestWarn(TestBaseException):
80    """Indicates that bad things (may) have happened, but not an explicit
81    failure."""
82    exit_status="WARN"
83
84
85class UnhandledTestError(TestError):
86    """Indicates an unhandled error in a test."""
87    def __init__(self, unhandled_exception):
88        if isinstance(unhandled_exception, TestError):
89            TestError.__init__(self, *unhandled_exception.args)
90        else:
91            msg = "Unhandled %s: %s"
92            msg %= (unhandled_exception.__class__.__name__,
93                    unhandled_exception)
94            msg += "\n" + traceback.format_exc()
95            TestError.__init__(self, msg)
96
97
98class UnhandledTestFail(TestFail):
99    """Indicates an unhandled fail in a test."""
100    def __init__(self, unhandled_exception):
101        if isinstance(unhandled_exception, TestFail):
102            TestFail.__init__(self, *unhandled_exception.args)
103        else:
104            msg = "Unhandled %s: %s"
105            msg %= (unhandled_exception.__class__.__name__,
106                    unhandled_exception)
107            msg += "\n" + traceback.format_exc()
108            TestFail.__init__(self, msg)
109
110
111class CmdError(TestError):
112    """\
113    Indicates that a command failed, is fatal to the test unless caught.
114    """
115    def __init__(self, command, result_obj, additional_text=None):
116        TestError.__init__(self, command, result_obj, additional_text)
117        self.command = command
118        self.result_obj = result_obj
119        self.additional_text = additional_text
120
121
122    def __str__(self):
123        if self.result_obj.exit_status is None:
124            msg = "Command <%s> failed and is not responding to signals"
125            msg %= self.command
126        else:
127            msg = "Command <%s> failed, rc=%d"
128            msg %= (self.command, self.result_obj.exit_status)
129
130        if self.additional_text:
131            msg += ", " + self.additional_text
132        msg += '\n' + repr(self.result_obj)
133        return msg
134
135
136class PackageError(TestError):
137    """Indicates an error trying to perform a package operation."""
138    pass
139
140
141class BarrierError(JobError):
142    """Indicates an error happened during a barrier operation."""
143    pass
144
145
146class InstallError(JobError):
147    """Indicates an installation error which Terminates and fails the job."""
148    pass
149
150
151class AutotestRunError(AutotestError):
152    """Indicates a problem running server side control files."""
153    pass
154
155
156class AutotestTimeoutError(AutotestError):
157    """This exception is raised when an autotest test exceeds the timeout
158    parameter passed to run_timed_test and is killed.
159    """
160
161
162class HostRunErrorMixIn(Exception):
163    """
164    Indicates a problem in the host run() function raised from client code.
165    Should always be constructed with a tuple of two args (error description
166    (str), run result object). This is a common class mixed in to create the
167    client and server side versions of it.
168    """
169    def __init__(self, description, result_obj):
170        self.description = description
171        self.result_obj = result_obj
172        Exception.__init__(self, description, result_obj)
173
174    def __str__(self):
175        return self.description + '\n' + repr(self.result_obj)
176
177
178class AutotestHostRunError(HostRunErrorMixIn, AutotestError):
179    pass
180
181
182# server-specific errors
183
184class AutoservError(Exception):
185    pass
186
187
188class AutoservInstallError(Exception):
189    """Autoserv failed in installing autotest on a client machine"""
190    pass
191
192
193class AutoservSSHTimeout(AutoservError):
194    """SSH experienced a connection timeout"""
195    pass
196
197
198class AutoservRunError(HostRunErrorMixIn, AutoservError):
199    pass
200
201
202class AutoservSshPermissionDeniedError(AutoservRunError):
203    """Indicates that a SSH permission denied error was encountered."""
204    pass
205
206
207class AutoservVirtError(AutoservError):
208    """Vitualization related error"""
209    pass
210
211
212class AutoservUnsupportedError(AutoservError):
213    """Error raised when you try to use an unsupported optional feature"""
214    pass
215
216
217class AutoservHostError(AutoservError):
218    """Error reaching a host"""
219    pass
220
221
222class AutoservHostIsShuttingDownError(AutoservHostError):
223    """Host is shutting down"""
224    pass
225
226
227class AutoservNotMountedHostError(AutoservHostError):
228    """Found unmounted partitions that should be mounted"""
229    pass
230
231
232class AutoservSshPingHostError(AutoservHostError):
233    """SSH ping failed"""
234    pass
235
236
237class AutoservDiskFullHostError(AutoservHostError):
238    """Not enough free disk space on host"""
239    def __init__(self, path, want_gb, free_space_gb):
240        AutoservHostError.__init__(self,
241            'Not enough free space on %s - %.3fGB free, want %.3fGB' %
242            (path, free_space_gb, want_gb))
243
244        self.path = path
245        self.want_gb = want_gb
246        self.free_space_gb = free_space_gb
247
248
249class AutoservHardwareHostError(AutoservHostError):
250    """Found hardware problems with the host"""
251    pass
252
253
254class AutoservRebootError(AutoservError):
255    """Error occured while rebooting a machine"""
256    pass
257
258
259class AutoservShutdownError(AutoservRebootError):
260    """Error occured during shutdown of machine"""
261    pass
262
263
264class AutoservSubcommandError(AutoservError):
265    """Indicates an error while executing a (forked) subcommand"""
266    def __init__(self, func, exit_code):
267        AutoservError.__init__(self, func, exit_code)
268        self.func = func
269        self.exit_code = exit_code
270
271    def __str__(self):
272        return ("Subcommand %s failed with exit code %d" %
273                (self.func, self.exit_code))
274
275
276class AutoservHardwareRepairRequestedError(AutoservError):
277    """
278    Exception class raised from Host.repair_full() (or overrides) when software
279    repair fails but it successfully managed to request a hardware repair (by
280    notifying the staff, sending mail, etc)
281    """
282    pass
283
284
285# packaging system errors
286
287class PackagingError(AutotestError):
288    'Abstract error class for all packaging related errors.'
289
290
291class PackageUploadError(PackagingError):
292    'Raised when there is an error uploading the package'
293
294
295class PackageFetchError(PackagingError):
296    'Raised when there is an error fetching the package'
297
298
299class PackageRemoveError(PackagingError):
300    'Raised when there is an error removing the package'
301
302
303class PackageInstallError(PackagingError):
304    'Raised when there is an error installing the package'
305
306
307class RepoDiskFullError(PackagingError):
308    'Raised when the destination for packages is full'
309
310
311class RepoWriteError(PackagingError):
312    "Raised when packager cannot write to a repo's desitnation"
313
314
315class RepoUnknownError(PackagingError):
316    "Raised when packager cannot write to a repo's desitnation"
317
318
319class RepoError(PackagingError):
320    "Raised when a repo isn't working in some way"
321
322
323# This MUST remain at the end of the file.
324# Limit 'from error import *' to only import the exception instances.
325for _name, _thing in locals().items():
326    try:
327        if issubclass(_thing, Exception):
328            __all__.append(_name)
329    except TypeError:
330        pass  # _thing not a class
331__all__ = tuple(__all__)
332