blob: 0c5641cd8b8cef51696abe655892bf2b22bebef3 [file] [log] [blame]
mbligh906b9f72007-11-29 18:56:17 +00001"""
2Internal global error types
3"""
4
Eric Li861b2d52011-02-04 14:50:35 -08005import sys, traceback, threading, logging
mbligh906b9f72007-11-29 18:56:17 +00006from traceback import format_exception
7
mbligh91672252008-10-16 22:28:34 +00008# 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.
Eric Li861b2d52011-02-04 14:50:35 -080011__all__ = ['format_error', 'context_aware', 'context', 'get_context',
12 'exception_context']
mbligh91672252008-10-16 22:28:34 +000013
14
mbligh906b9f72007-11-29 18:56:17 +000015def format_error():
jadmanski0afbb632008-06-06 21:10:57 +000016 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
20 tb = ''
mbligh906b9f72007-11-29 18:56:17 +000021
jadmanski0afbb632008-06-06 21:10:57 +000022 return ''.join(trace)
mbligh906b9f72007-11-29 18:56:17 +000023
mbligh4f407462008-12-03 15:22:39 +000024
Eric Li861b2d52011-02-04 14:50:35 -080025# 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
33# exception).
34#
35# For example: assume a() calls b() and b() calls c().
36#
37# @error.context_aware
38# def a():
39# error.context("hello")
40# b()
41# error.context("world")
42# error.get_context() ----> 'world'
43#
44# @error.context_aware
45# def b():
46# error.context("foo")
47# c()
48#
49# @error.context_aware
50# def c():
51# error.context("bar")
52# error.get_context() ----> 'hello --> foo --> bar'
53#
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().
57
58ctx = threading.local()
59
60
61def _new_context(s=""):
62 if not hasattr(ctx, "contexts"):
63 ctx.contexts = []
64 ctx.contexts.append(s)
65
66
67def _pop_context():
68 ctx.contexts.pop()
69
70
71def context(s="", log=None):
72 """
73 Set the context for the currently executing function and optionally log it.
74
75 @param s: A string. If not provided, the context for the current function
76 will be cleared.
77 @param log: A logging function to pass the context message to. If None, no
78 function will be called.
79 """
80 ctx.contexts[-1] = s
81 if s and log:
82 log("Context: %s" % get_context())
83
84
85def base_context(s="", log=None):
86 """
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
90 base_context().
91
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.
96 """
97 ctx.contexts[-1] = ""
98 ctx.contexts[-2] = s
99 if s and log:
100 log("Context: %s" % get_context())
101
102
103def 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])
107
108
109def exception_context(e):
110 """Return the context of a given exception (or None if none is defined)."""
111 if hasattr(e, "_context"):
112 return e._context
113
114
115def set_exception_context(e, s):
116 """Set the context of a given exception."""
117 e._context = s
118
119
120def join_contexts(s1, s2):
121 """Join two context strings."""
122 if s1:
123 if s2:
124 return "%s --> %s" % (s1, s2)
125 else:
126 return s1
127 else:
128 return s2
129
130
131def context_aware(fn):
132 """A decorator that must be applied to functions that call context()."""
133 def new_fn(*args, **kwargs):
134 _new_context()
135 _new_context("(%s)" % fn.__name__)
136 try:
137 try:
138 return fn(*args, **kwargs)
139 except Exception, e:
140 if not exception_context(e):
141 set_exception_context(e, get_context())
142 raise
143 finally:
144 _pop_context()
145 _pop_context()
146 new_fn.__name__ = fn.__name__
147 new_fn.__doc__ = fn.__doc__
148 new_fn.__dict__.update(fn.__dict__)
149 return new_fn
150
151
152def _context_message(e):
153 s = exception_context(e)
154 if s:
155 return " [context: %s]" % s
156 else:
157 return ""
158
159
mbligh906b9f72007-11-29 18:56:17 +0000160class JobContinue(SystemExit):
jadmanski0afbb632008-06-06 21:10:57 +0000161 """Allow us to bail out requesting continuance."""
162 pass
mbligh906b9f72007-11-29 18:56:17 +0000163
mbligh7e1b1502008-06-06 15:05:41 +0000164
mbligh906b9f72007-11-29 18:56:17 +0000165class JobComplete(SystemExit):
jadmanski0afbb632008-06-06 21:10:57 +0000166 """Allow us to bail out indicating continuation not required."""
167 pass
mbligh906b9f72007-11-29 18:56:17 +0000168
mbligh7e1b1502008-06-06 15:05:41 +0000169
mbligh906b9f72007-11-29 18:56:17 +0000170class AutotestError(Exception):
jadmanski0afbb632008-06-06 21:10:57 +0000171 """The parent of all errors deliberatly thrown within the client code."""
Eric Li861b2d52011-02-04 14:50:35 -0800172 def __str__(self):
173 return Exception.__str__(self) + _context_message(self)
mbligh906b9f72007-11-29 18:56:17 +0000174
mbligh7e1b1502008-06-06 15:05:41 +0000175
mbligh906b9f72007-11-29 18:56:17 +0000176class JobError(AutotestError):
mbligh4f407462008-12-03 15:22:39 +0000177 """Indicates an error which terminates and fails the whole job (ABORT)."""
jadmanski0afbb632008-06-06 21:10:57 +0000178 pass
mbligh906b9f72007-11-29 18:56:17 +0000179
mbligh7e1b1502008-06-06 15:05:41 +0000180
mbligh4f407462008-12-03 15:22:39 +0000181class UnhandledJobError(JobError):
182 """Indicates an unhandled error in a job."""
183 def __init__(self, unhandled_exception):
184 if isinstance(unhandled_exception, JobError):
mbligh1ca1c2c2008-12-09 23:38:25 +0000185 JobError.__init__(self, *unhandled_exception.args)
Eric Li861b2d52011-02-04 14:50:35 -0800186 elif isinstance(unhandled_exception, str):
187 JobError.__init__(self, unhandled_exception)
mbligh4f407462008-12-03 15:22:39 +0000188 else:
189 msg = "Unhandled %s: %s"
190 msg %= (unhandled_exception.__class__.__name__,
191 unhandled_exception)
Eric Li861b2d52011-02-04 14:50:35 -0800192 if not isinstance(unhandled_exception, AutotestError):
193 msg += _context_message(unhandled_exception)
mbligh4f407462008-12-03 15:22:39 +0000194 msg += "\n" + traceback.format_exc()
195 JobError.__init__(self, msg)
196
197
mblighc2180832008-07-25 03:26:12 +0000198class TestBaseException(AutotestError):
199 """The parent of all test exceptions."""
mbligh021679f2008-11-27 00:43:19 +0000200 # Children are required to override this. Never instantiate directly.
201 exit_status="NEVER_RAISE_THIS"
mblighc2180832008-07-25 03:26:12 +0000202
203
204class TestError(TestBaseException):
mblighb48fa562008-06-23 17:29:40 +0000205 """Indicates that something went wrong with the test harness itself."""
206 exit_status="ERROR"
mblighb48fa562008-06-23 17:29:40 +0000207
jadmanski8d01bfe2008-06-23 18:13:24 +0000208
mblighc2180832008-07-25 03:26:12 +0000209class TestNAError(TestBaseException):
jadmanski0afbb632008-06-06 21:10:57 +0000210 """Indictates that the test is Not Applicable. Should be thrown
mblighb48fa562008-06-23 17:29:40 +0000211 when various conditions are such that the test is inappropriate."""
212 exit_status="TEST_NA"
mblighb48fa562008-06-23 17:29:40 +0000213
jadmanski8d01bfe2008-06-23 18:13:24 +0000214
mblighc2180832008-07-25 03:26:12 +0000215class TestFail(TestBaseException):
mblighb48fa562008-06-23 17:29:40 +0000216 """Indicates that the test failed, but the job will not continue."""
217 exit_status="FAIL"
mblighb48fa562008-06-23 17:29:40 +0000218
jadmanski8d01bfe2008-06-23 18:13:24 +0000219
mblighc2180832008-07-25 03:26:12 +0000220class TestWarn(TestBaseException):
mblighb48fa562008-06-23 17:29:40 +0000221 """Indicates that bad things (may) have happened, but not an explicit
222 failure."""
223 exit_status="WARN"
mbligh6a2a2df2008-01-16 17:41:55 +0000224
mbligh7e1b1502008-06-06 15:05:41 +0000225
mblighc2180832008-07-25 03:26:12 +0000226class 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)
Eric Li861b2d52011-02-04 14:50:35 -0800231 elif isinstance(unhandled_exception, str):
232 TestError.__init__(self, unhandled_exception)
mblighc2180832008-07-25 03:26:12 +0000233 else:
234 msg = "Unhandled %s: %s"
235 msg %= (unhandled_exception.__class__.__name__,
236 unhandled_exception)
Eric Li861b2d52011-02-04 14:50:35 -0800237 if not isinstance(unhandled_exception, AutotestError):
238 msg += _context_message(unhandled_exception)
mblighc2180832008-07-25 03:26:12 +0000239 msg += "\n" + traceback.format_exc()
240 TestError.__init__(self, msg)
241
242
243class 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)
Eric Li861b2d52011-02-04 14:50:35 -0800248 elif isinstance(unhandled_exception, str):
249 TestFail.__init__(self, unhandled_exception)
mblighc2180832008-07-25 03:26:12 +0000250 else:
251 msg = "Unhandled %s: %s"
252 msg %= (unhandled_exception.__class__.__name__,
253 unhandled_exception)
Eric Li861b2d52011-02-04 14:50:35 -0800254 if not isinstance(unhandled_exception, AutotestError):
255 msg += _context_message(unhandled_exception)
mblighc2180832008-07-25 03:26:12 +0000256 msg += "\n" + traceback.format_exc()
257 TestFail.__init__(self, msg)
258
259
mbligh906b9f72007-11-29 18:56:17 +0000260class CmdError(TestError):
jadmanski0afbb632008-06-06 21:10:57 +0000261 """\
262 Indicates that a command failed, is fatal to the test unless caught.
263 """
264 def __init__(self, command, result_obj, additional_text=None):
265 TestError.__init__(self, command, result_obj, additional_text)
mblighc23051c2008-06-27 19:26:46 +0000266 self.command = command
267 self.result_obj = result_obj
268 self.additional_text = additional_text
mbligh6a2a2df2008-01-16 17:41:55 +0000269
jadmanski0afbb632008-06-06 21:10:57 +0000270 def __str__(self):
jadmanski6ef0b672008-09-30 22:50:19 +0000271 if self.result_obj.exit_status is None:
272 msg = "Command <%s> failed and is not responding to signals"
273 msg %= self.command
274 else:
275 msg = "Command <%s> failed, rc=%d"
276 msg %= (self.command, self.result_obj.exit_status)
277
mblighc23051c2008-06-27 19:26:46 +0000278 if self.additional_text:
279 msg += ", " + self.additional_text
Eric Li861b2d52011-02-04 14:50:35 -0800280 msg += _context_message(self)
showard6d7e94f2008-08-20 20:53:34 +0000281 msg += '\n' + repr(self.result_obj)
jadmanski0afbb632008-06-06 21:10:57 +0000282 return msg
mbligh906b9f72007-11-29 18:56:17 +0000283
mbligh7e1b1502008-06-06 15:05:41 +0000284
mbligh906b9f72007-11-29 18:56:17 +0000285class PackageError(TestError):
jadmanski0afbb632008-06-06 21:10:57 +0000286 """Indicates an error trying to perform a package operation."""
287 pass
mbligh906b9f72007-11-29 18:56:17 +0000288
mbligh7e1b1502008-06-06 15:05:41 +0000289
mblighe8673102008-07-16 14:09:03 +0000290class BarrierError(JobError):
291 """Indicates an error happened during a barrier operation."""
292 pass
293
294
mbligh999fb132010-04-23 17:22:03 +0000295class BarrierAbortError(BarrierError):
296 """Indicate that the barrier was explicitly aborted by a member."""
297 pass
298
299
mbligh5deff3d2008-01-04 21:21:28 +0000300class InstallError(JobError):
jadmanski0afbb632008-06-06 21:10:57 +0000301 """Indicates an installation error which Terminates and fails the job."""
302 pass
mbligh03f4fc72007-11-29 20:56:14 +0000303
mbligh7e1b1502008-06-06 15:05:41 +0000304
mbligh6f015c42008-02-12 20:55:03 +0000305class AutotestRunError(AutotestError):
mbligh021679f2008-11-27 00:43:19 +0000306 """Indicates a problem running server side control files."""
jadmanski0afbb632008-06-06 21:10:57 +0000307 pass
mbligh6f015c42008-02-12 20:55:03 +0000308
mbligh7e1b1502008-06-06 15:05:41 +0000309
mbligh6f015c42008-02-12 20:55:03 +0000310class AutotestTimeoutError(AutotestError):
jadmanski0afbb632008-06-06 21:10:57 +0000311 """This exception is raised when an autotest test exceeds the timeout
312 parameter passed to run_timed_test and is killed.
313 """
mbligh6f015c42008-02-12 20:55:03 +0000314
315
mblighce955fc2009-08-24 21:59:02 +0000316class HostRunErrorMixIn(Exception):
317 """
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.
322 """
323 def __init__(self, description, result_obj):
324 self.description = description
325 self.result_obj = result_obj
326 Exception.__init__(self, description, result_obj)
327
328 def __str__(self):
329 return self.description + '\n' + repr(self.result_obj)
330
331
332class AutotestHostRunError(HostRunErrorMixIn, AutotestError):
333 pass
334
335
mbligh03f4fc72007-11-29 20:56:14 +0000336# server-specific errors
337
338class AutoservError(Exception):
jadmanski0afbb632008-06-06 21:10:57 +0000339 pass
mbligh03f4fc72007-11-29 20:56:14 +0000340
341
mbligh34faa282008-01-16 17:44:49 +0000342class AutoservSSHTimeout(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000343 """SSH experienced a connection timeout"""
344 pass
mbligh34faa282008-01-16 17:44:49 +0000345
346
mblighce955fc2009-08-24 21:59:02 +0000347class AutoservRunError(HostRunErrorMixIn, AutoservError):
348 pass
showard6d7e94f2008-08-20 20:53:34 +0000349
mbligh03f4fc72007-11-29 20:56:14 +0000350
mbligh9d738d62009-03-09 21:17:10 +0000351class AutoservSshPermissionDeniedError(AutoservRunError):
352 """Indicates that a SSH permission denied error was encountered."""
353 pass
354
355
mbligh03f4fc72007-11-29 20:56:14 +0000356class AutoservVirtError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000357 """Vitualization related error"""
358 pass
mbligh03f4fc72007-11-29 20:56:14 +0000359
360
361class AutoservUnsupportedError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000362 """Error raised when you try to use an unsupported optional feature"""
363 pass
mbligh03f4fc72007-11-29 20:56:14 +0000364
mbligh7e1b1502008-06-06 15:05:41 +0000365
mbligh03f4fc72007-11-29 20:56:14 +0000366class AutoservHostError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000367 """Error reaching a host"""
368 pass
mbligh03f4fc72007-11-29 20:56:14 +0000369
mbligh7e1b1502008-06-06 15:05:41 +0000370
mblighc971c5f2009-06-08 16:48:54 +0000371class AutoservHostIsShuttingDownError(AutoservHostError):
372 """Host is shutting down"""
373 pass
374
375
376class AutoservNotMountedHostError(AutoservHostError):
377 """Found unmounted partitions that should be mounted"""
378 pass
379
380
381class AutoservSshPingHostError(AutoservHostError):
382 """SSH ping failed"""
383 pass
384
385
386class 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))
392
393 self.path = path
394 self.want_gb = want_gb
395 self.free_space_gb = free_space_gb
396
397
398class AutoservHardwareHostError(AutoservHostError):
399 """Found hardware problems with the host"""
400 pass
401
402
mbligh03f4fc72007-11-29 20:56:14 +0000403class AutoservRebootError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000404 """Error occured while rebooting a machine"""
405 pass
mbligh6e2ffec2008-03-05 16:08:34 +0000406
mbligh7e1b1502008-06-06 15:05:41 +0000407
jadmanski65eb8f52009-07-24 18:34:43 +0000408class AutoservShutdownError(AutoservRebootError):
409 """Error occured during shutdown of machine"""
410 pass
411
412
mbligh6e2ffec2008-03-05 16:08:34 +0000413class AutoservSubcommandError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000414 """Indicates an error while executing a (forked) subcommand"""
415 def __init__(self, func, exit_code):
416 AutoservError.__init__(self, func, exit_code)
417 self.func = func
418 self.exit_code = exit_code
mbligh7e1b1502008-06-06 15:05:41 +0000419
jadmanski0afbb632008-06-06 21:10:57 +0000420 def __str__(self):
421 return ("Subcommand %s failed with exit code %d" %
422 (self.func, self.exit_code))
mbligh91672252008-10-16 22:28:34 +0000423
424
mbligh25c0b8c2009-01-24 01:44:17 +0000425class AutoservHardwareRepairRequestedError(AutoservError):
426 """
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)
430 """
431 pass
432
433
jadmanski2615f4a2010-07-19 16:39:56 +0000434class AutoservHardwareRepairRequiredError(AutoservError):
435 """
436 Exception class raised during repairs to indicate that a hardware repair
437 is going to be necessary.
438 """
439 pass
440
441
jadmanskic1dda212009-11-18 19:22:00 +0000442class AutoservInstallError(AutoservError):
443 """Error occured while installing autotest on a host"""
444 pass
445
446
jadmanskic27c2312009-08-05 20:58:51 +0000447# packaging system errors
448
449class PackagingError(AutotestError):
450 'Abstract error class for all packaging related errors.'
451
452
453class PackageUploadError(PackagingError):
454 'Raised when there is an error uploading the package'
455
456
457class PackageFetchError(PackagingError):
458 'Raised when there is an error fetching the package'
459
460
461class PackageRemoveError(PackagingError):
462 'Raised when there is an error removing the package'
463
464
465class PackageInstallError(PackagingError):
466 'Raised when there is an error installing the package'
467
468
469class RepoDiskFullError(PackagingError):
470 'Raised when the destination for packages is full'
471
472
473class RepoWriteError(PackagingError):
474 "Raised when packager cannot write to a repo's desitnation"
475
476
477class RepoUnknownError(PackagingError):
478 "Raised when packager cannot write to a repo's desitnation"
479
480
481class RepoError(PackagingError):
482 "Raised when a repo isn't working in some way"
483
484
mbligh91672252008-10-16 22:28:34 +0000485# This MUST remain at the end of the file.
486# Limit 'from error import *' to only import the exception instances.
487for _name, _thing in locals().items():
488 try:
489 if issubclass(_thing, Exception):
490 __all__.append(_name)
491 except TypeError:
492 pass # _thing not a class
493__all__ = tuple(__all__)