2 Internal global error types
5 import sys
, traceback
, threading
, logging
6 from traceback
import format_exception
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', 'context_aware', 'context', 'get_context',
16 t
, o
, tb
= sys
.exc_info()
17 trace
= format_exception(t
, o
, tb
)
18 # Clear the backtrace to prevent a circular reference
19 # in the heap -- as per tutorial
25 # Exception context information:
26 # ------------------------------
27 # Every function can have some context string associated with it.
28 # The context string can be changed by calling context(str) and cleared by
29 # calling context() with no parameters.
30 # get_context() joins the current context strings of all functions in the
31 # provided traceback. The result is a brief description of what the test was
32 # doing in the provided traceback (which should be the traceback of a caught
35 # For example: assume a() calls b() and b() calls c().
37 # @error.context_aware
39 # error.context("hello")
41 # error.context("world")
42 # error.get_context() ----> 'world'
44 # @error.context_aware
46 # error.context("foo")
49 # @error.context_aware
51 # error.context("bar")
52 # error.get_context() ----> 'hello --> foo --> bar'
54 # The current context is automatically inserted into exceptions raised in
55 # context_aware functions, so usually test code doesn't need to call
56 # error.get_context().
58 ctx
= threading
.local()
61 def _new_context(s
=""):
62 if not hasattr(ctx
, "contexts"):
64 ctx
.contexts
.append(s
)
71 def context(s
="", log
=None):
73 Set the context for the currently executing function and optionally log it.
75 @param s: A string. If not provided, the context for the current function
77 @param log: A logging function to pass the context message to. If None, no
78 function will be called.
82 log("Context: %s" % get_context())
85 def base_context(s
="", log
=None):
87 Set the base context for the currently executing function and optionally
88 log it. The base context is just another context level that is hidden by
89 default. Functions that require a single context level should not use
92 @param s: A string. If not provided, the base context for the current
93 function will be cleared.
94 @param log: A logging function to pass the context message to. If None, no
95 function will be called.
100 log("Context: %s" % get_context())
104 """Return the current context (or None if none is defined)."""
105 if hasattr(ctx
, "contexts"):
106 return " --> ".join([s
for s
in ctx
.contexts
if s
])
109 def exception_context(e
):
110 """Return the context of a given exception (or None if none is defined)."""
111 if hasattr(e
, "_context"):
115 def set_exception_context(e
, s
):
116 """Set the context of a given exception."""
120 def join_contexts(s1
, s2
):
121 """Join two context strings."""
124 return "%s --> %s" % (s1
, s2
)
131 def context_aware(fn
):
132 """A decorator that must be applied to functions that call context()."""
133 def new_fn(*args
, **kwargs
):
135 _new_context("(%s)" % fn
.__name
__)
138 return fn(*args
, **kwargs
)
140 if not exception_context(e
):
141 set_exception_context(e
, get_context())
146 new_fn
.__name
__ = fn
.__name
__
147 new_fn
.__doc
__ = fn
.__doc
__
148 new_fn
.__dict
__.update(fn
.__dict
__)
152 def _context_message(e
):
153 s
= exception_context(e
)
155 return " [context: %s]" % s
160 class JobContinue(SystemExit):
161 """Allow us to bail out requesting continuance."""
165 class JobComplete(SystemExit):
166 """Allow us to bail out indicating continuation not required."""
170 class AutotestError(Exception):
171 """The parent of all errors deliberatly thrown within the client code."""
173 return Exception.__str
__(self
) + _context_message(self
)
176 class JobError(AutotestError
):
177 """Indicates an error which terminates and fails the whole job (ABORT)."""
181 class UnhandledJobError(JobError
):
182 """Indicates an unhandled error in a job."""
183 def __init__(self
, unhandled_exception
):
184 if isinstance(unhandled_exception
, JobError
):
185 JobError
.__init
__(self
, *unhandled_exception
.args
)
186 elif isinstance(unhandled_exception
, str):
187 JobError
.__init
__(self
, unhandled_exception
)
189 msg
= "Unhandled %s: %s"
190 msg
%= (unhandled_exception
.__class
__.__name
__,
192 if not isinstance(unhandled_exception
, AutotestError
):
193 msg
+= _context_message(unhandled_exception
)
194 msg
+= "\n" + traceback
.format_exc()
195 JobError
.__init
__(self
, msg
)
198 class TestBaseException(AutotestError
):
199 """The parent of all test exceptions."""
200 # Children are required to override this. Never instantiate directly.
201 exit_status
="NEVER_RAISE_THIS"
204 class TestError(TestBaseException
):
205 """Indicates that something went wrong with the test harness itself."""
209 class TestNAError(TestBaseException
):
210 """Indictates that the test is Not Applicable. Should be thrown
211 when various conditions are such that the test is inappropriate."""
212 exit_status
="TEST_NA"
215 class TestFail(TestBaseException
):
216 """Indicates that the test failed, but the job will not continue."""
220 class TestWarn(TestBaseException
):
221 """Indicates that bad things (may) have happened, but not an explicit
226 class UnhandledTestError(TestError
):
227 """Indicates an unhandled error in a test."""
228 def __init__(self
, unhandled_exception
):
229 if isinstance(unhandled_exception
, TestError
):
230 TestError
.__init
__(self
, *unhandled_exception
.args
)
231 elif isinstance(unhandled_exception
, str):
232 TestError
.__init
__(self
, unhandled_exception
)
234 msg
= "Unhandled %s: %s"
235 msg
%= (unhandled_exception
.__class
__.__name
__,
237 if not isinstance(unhandled_exception
, AutotestError
):
238 msg
+= _context_message(unhandled_exception
)
239 msg
+= "\n" + traceback
.format_exc()
240 TestError
.__init
__(self
, msg
)
243 class UnhandledTestFail(TestFail
):
244 """Indicates an unhandled fail in a test."""
245 def __init__(self
, unhandled_exception
):
246 if isinstance(unhandled_exception
, TestFail
):
247 TestFail
.__init
__(self
, *unhandled_exception
.args
)
248 elif isinstance(unhandled_exception
, str):
249 TestFail
.__init
__(self
, unhandled_exception
)
251 msg
= "Unhandled %s: %s"
252 msg
%= (unhandled_exception
.__class
__.__name
__,
254 if not isinstance(unhandled_exception
, AutotestError
):
255 msg
+= _context_message(unhandled_exception
)
256 msg
+= "\n" + traceback
.format_exc()
257 TestFail
.__init
__(self
, msg
)
260 class CmdError(TestError
):
262 Indicates that a command failed, is fatal to the test unless caught.
264 def __init__(self
, command
, result_obj
, additional_text
=None):
265 TestError
.__init
__(self
, command
, result_obj
, additional_text
)
266 self
.command
= command
267 self
.result_obj
= result_obj
268 self
.additional_text
= additional_text
271 if self
.result_obj
.exit_status
is None:
272 msg
= "Command <%s> failed and is not responding to signals"
275 msg
= "Command <%s> failed, rc=%d"
276 msg
%= (self
.command
, self
.result_obj
.exit_status
)
278 if self
.additional_text
:
279 msg
+= ", " + self
.additional_text
280 msg
+= _context_message(self
)
281 msg
+= '\n' + repr(self
.result_obj
)
285 class PackageError(TestError
):
286 """Indicates an error trying to perform a package operation."""
290 class BarrierError(JobError
):
291 """Indicates an error happened during a barrier operation."""
295 class BarrierAbortError(BarrierError
):
296 """Indicate that the barrier was explicitly aborted by a member."""
300 class InstallError(JobError
):
301 """Indicates an installation error which Terminates and fails the job."""
305 class AutotestRunError(AutotestError
):
306 """Indicates a problem running server side control files."""
310 class AutotestTimeoutError(AutotestError
):
311 """This exception is raised when an autotest test exceeds the timeout
312 parameter passed to run_timed_test and is killed.
316 class HostRunErrorMixIn(Exception):
318 Indicates a problem in the host run() function raised from client code.
319 Should always be constructed with a tuple of two args (error description
320 (str), run result object). This is a common class mixed in to create the
321 client and server side versions of it.
323 def __init__(self
, description
, result_obj
):
324 self
.description
= description
325 self
.result_obj
= result_obj
326 Exception.__init
__(self
, description
, result_obj
)
329 return self
.description
+ '\n' + repr(self
.result_obj
)
332 class AutotestHostRunError(HostRunErrorMixIn
, AutotestError
):
336 # server-specific errors
338 class AutoservError(Exception):
342 class AutoservSSHTimeout(AutoservError
):
343 """SSH experienced a connection timeout"""
347 class AutoservRunError(HostRunErrorMixIn
, AutoservError
):
351 class AutoservSshPermissionDeniedError(AutoservRunError
):
352 """Indicates that a SSH permission denied error was encountered."""
356 class AutoservVirtError(AutoservError
):
357 """Vitualization related error"""
361 class AutoservUnsupportedError(AutoservError
):
362 """Error raised when you try to use an unsupported optional feature"""
366 class AutoservHostError(AutoservError
):
367 """Error reaching a host"""
371 class AutoservHostIsShuttingDownError(AutoservHostError
):
372 """Host is shutting down"""
376 class AutoservNotMountedHostError(AutoservHostError
):
377 """Found unmounted partitions that should be mounted"""
381 class AutoservSshPingHostError(AutoservHostError
):
382 """SSH ping failed"""
386 class AutoservDiskFullHostError(AutoservHostError
):
387 """Not enough free disk space on host"""
388 def __init__(self
, path
, want_gb
, free_space_gb
):
389 AutoservHostError
.__init
__(self
,
390 'Not enough free space on %s - %.3fGB free, want %.3fGB' %
391 (path
, free_space_gb
, want_gb
))
394 self
.want_gb
= want_gb
395 self
.free_space_gb
= free_space_gb
398 class AutoservHardwareHostError(AutoservHostError
):
399 """Found hardware problems with the host"""
403 class AutoservRebootError(AutoservError
):
404 """Error occured while rebooting a machine"""
408 class AutoservShutdownError(AutoservRebootError
):
409 """Error occured during shutdown of machine"""
413 class AutoservSubcommandError(AutoservError
):
414 """Indicates an error while executing a (forked) subcommand"""
415 def __init__(self
, func
, exit_code
):
416 AutoservError
.__init
__(self
, func
, exit_code
)
418 self
.exit_code
= exit_code
421 return ("Subcommand %s failed with exit code %d" %
422 (self
.func
, self
.exit_code
))
425 class AutoservHardwareRepairRequestedError(AutoservError
):
427 Exception class raised from Host.repair_full() (or overrides) when software
428 repair fails but it successfully managed to request a hardware repair (by
429 notifying the staff, sending mail, etc)
434 class AutoservHardwareRepairRequiredError(AutoservError
):
436 Exception class raised during repairs to indicate that a hardware repair
437 is going to be necessary.
442 class AutoservInstallError(AutoservError
):
443 """Error occured while installing autotest on a host"""
447 # packaging system errors
449 class PackagingError(AutotestError
):
450 'Abstract error class for all packaging related errors.'
453 class PackageUploadError(PackagingError
):
454 'Raised when there is an error uploading the package'
457 class PackageFetchError(PackagingError
):
458 'Raised when there is an error fetching the package'
461 class PackageRemoveError(PackagingError
):
462 'Raised when there is an error removing the package'
465 class PackageInstallError(PackagingError
):
466 'Raised when there is an error installing the package'
469 class RepoDiskFullError(PackagingError
):
470 'Raised when the destination for packages is full'
473 class RepoWriteError(PackagingError
):
474 "Raised when packager cannot write to a repo's desitnation"
477 class RepoUnknownError(PackagingError
):
478 "Raised when packager cannot write to a repo's desitnation"
481 class RepoError(PackagingError
):
482 "Raised when a repo isn't working in some way"
485 # This MUST remain at the end of the file.
486 # Limit 'from error import *' to only import the exception instances.
487 for _name
, _thing
in locals().items():
489 if issubclass(_thing
, Exception):
490 __all__
.append(_name
)
492 pass # _thing not a class
493 __all__
= tuple(__all__
)