blob: d9047c80d3bbd56be8959b94cfab34f331cb7fd3 [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 """
Dale Curtis8adf7892011-09-08 16:13:36 -0700314 pass
mbligh6f015c42008-02-12 20:55:03 +0000315
316
mblighce955fc2009-08-24 21:59:02 +0000317class HostRunErrorMixIn(Exception):
318 """
319 Indicates a problem in the host run() function raised from client code.
320 Should always be constructed with a tuple of two args (error description
321 (str), run result object). This is a common class mixed in to create the
322 client and server side versions of it.
323 """
324 def __init__(self, description, result_obj):
325 self.description = description
326 self.result_obj = result_obj
327 Exception.__init__(self, description, result_obj)
328
329 def __str__(self):
330 return self.description + '\n' + repr(self.result_obj)
331
332
Dale Curtis8adf7892011-09-08 16:13:36 -0700333class HostInstallTimeoutError(JobError):
334 """
335 Indicates the machine failed to be installed after the predetermined
336 timeout.
337 """
338 pass
339
340
mblighce955fc2009-08-24 21:59:02 +0000341class AutotestHostRunError(HostRunErrorMixIn, AutotestError):
342 pass
343
344
mbligh03f4fc72007-11-29 20:56:14 +0000345# server-specific errors
346
347class AutoservError(Exception):
jadmanski0afbb632008-06-06 21:10:57 +0000348 pass
mbligh03f4fc72007-11-29 20:56:14 +0000349
350
mbligh34faa282008-01-16 17:44:49 +0000351class AutoservSSHTimeout(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000352 """SSH experienced a connection timeout"""
353 pass
mbligh34faa282008-01-16 17:44:49 +0000354
355
mblighce955fc2009-08-24 21:59:02 +0000356class AutoservRunError(HostRunErrorMixIn, AutoservError):
357 pass
showard6d7e94f2008-08-20 20:53:34 +0000358
mbligh03f4fc72007-11-29 20:56:14 +0000359
mbligh9d738d62009-03-09 21:17:10 +0000360class AutoservSshPermissionDeniedError(AutoservRunError):
361 """Indicates that a SSH permission denied error was encountered."""
362 pass
363
364
mbligh03f4fc72007-11-29 20:56:14 +0000365class AutoservVirtError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000366 """Vitualization related error"""
367 pass
mbligh03f4fc72007-11-29 20:56:14 +0000368
369
370class AutoservUnsupportedError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000371 """Error raised when you try to use an unsupported optional feature"""
372 pass
mbligh03f4fc72007-11-29 20:56:14 +0000373
mbligh7e1b1502008-06-06 15:05:41 +0000374
mbligh03f4fc72007-11-29 20:56:14 +0000375class AutoservHostError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000376 """Error reaching a host"""
377 pass
mbligh03f4fc72007-11-29 20:56:14 +0000378
mbligh7e1b1502008-06-06 15:05:41 +0000379
mblighc971c5f2009-06-08 16:48:54 +0000380class AutoservHostIsShuttingDownError(AutoservHostError):
381 """Host is shutting down"""
382 pass
383
384
385class AutoservNotMountedHostError(AutoservHostError):
386 """Found unmounted partitions that should be mounted"""
387 pass
388
389
390class AutoservSshPingHostError(AutoservHostError):
391 """SSH ping failed"""
392 pass
393
394
395class AutoservDiskFullHostError(AutoservHostError):
396 """Not enough free disk space on host"""
397 def __init__(self, path, want_gb, free_space_gb):
398 AutoservHostError.__init__(self,
399 'Not enough free space on %s - %.3fGB free, want %.3fGB' %
400 (path, free_space_gb, want_gb))
401
402 self.path = path
403 self.want_gb = want_gb
404 self.free_space_gb = free_space_gb
405
406
407class AutoservHardwareHostError(AutoservHostError):
408 """Found hardware problems with the host"""
409 pass
410
411
mbligh03f4fc72007-11-29 20:56:14 +0000412class AutoservRebootError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000413 """Error occured while rebooting a machine"""
414 pass
mbligh6e2ffec2008-03-05 16:08:34 +0000415
mbligh7e1b1502008-06-06 15:05:41 +0000416
jadmanski65eb8f52009-07-24 18:34:43 +0000417class AutoservShutdownError(AutoservRebootError):
418 """Error occured during shutdown of machine"""
419 pass
420
421
mbligh6e2ffec2008-03-05 16:08:34 +0000422class AutoservSubcommandError(AutoservError):
jadmanski0afbb632008-06-06 21:10:57 +0000423 """Indicates an error while executing a (forked) subcommand"""
424 def __init__(self, func, exit_code):
425 AutoservError.__init__(self, func, exit_code)
426 self.func = func
427 self.exit_code = exit_code
mbligh7e1b1502008-06-06 15:05:41 +0000428
jadmanski0afbb632008-06-06 21:10:57 +0000429 def __str__(self):
430 return ("Subcommand %s failed with exit code %d" %
431 (self.func, self.exit_code))
mbligh91672252008-10-16 22:28:34 +0000432
433
mbligh25c0b8c2009-01-24 01:44:17 +0000434class AutoservHardwareRepairRequestedError(AutoservError):
435 """
436 Exception class raised from Host.repair_full() (or overrides) when software
437 repair fails but it successfully managed to request a hardware repair (by
438 notifying the staff, sending mail, etc)
439 """
440 pass
441
442
jadmanski2615f4a2010-07-19 16:39:56 +0000443class AutoservHardwareRepairRequiredError(AutoservError):
444 """
445 Exception class raised during repairs to indicate that a hardware repair
446 is going to be necessary.
447 """
448 pass
449
450
jadmanskic1dda212009-11-18 19:22:00 +0000451class AutoservInstallError(AutoservError):
452 """Error occured while installing autotest on a host"""
453 pass
454
455
jadmanskic27c2312009-08-05 20:58:51 +0000456# packaging system errors
457
458class PackagingError(AutotestError):
459 'Abstract error class for all packaging related errors.'
460
461
462class PackageUploadError(PackagingError):
463 'Raised when there is an error uploading the package'
464
465
466class PackageFetchError(PackagingError):
467 'Raised when there is an error fetching the package'
468
469
470class PackageRemoveError(PackagingError):
471 'Raised when there is an error removing the package'
472
473
474class PackageInstallError(PackagingError):
475 'Raised when there is an error installing the package'
476
477
478class RepoDiskFullError(PackagingError):
479 'Raised when the destination for packages is full'
480
481
482class RepoWriteError(PackagingError):
483 "Raised when packager cannot write to a repo's desitnation"
484
485
486class RepoUnknownError(PackagingError):
487 "Raised when packager cannot write to a repo's desitnation"
488
489
490class RepoError(PackagingError):
491 "Raised when a repo isn't working in some way"
492
493
Chris Masonef8b53062012-05-08 22:14:18 -0700494class CrosDynamicSuiteException(Exception):
495 """
496 Base class for exceptions coming from dynamic suite code in server/cros/*.
497 """
498 pass
499
500
501class StageBuildFailure(CrosDynamicSuiteException):
502 """Raised when the dev server throws 500 while staging a build."""
503 pass
504
505
506class ControlFileEmpty(CrosDynamicSuiteException):
507 """Raised when the control file exists on the server, but can't be read."""
508 pass
509
510
511class AsynchronousBuildFailure(CrosDynamicSuiteException):
512 """Raised when the dev server throws 500 while finishing staging of a build.
513 """
514 pass
515
516
517class SuiteArgumentException(CrosDynamicSuiteException):
518 """Raised when improper arguments are used to run a suite."""
519 pass
520
521
522class InadequateHostsException(CrosDynamicSuiteException):
523 """Raised when there are too few hosts to run a suite."""
524 pass
525
526
527class NoHostsException(CrosDynamicSuiteException):
528 """Raised when there are no healthy hosts to run a suite."""
529 pass
530
531
532class ControlFileNotFound(CrosDynamicSuiteException):
533 """Raised when a control file cannot be found and/or read."""
534 pass
535
536
537class NoControlFileList(CrosDynamicSuiteException):
Chris Masone9807bd62012-07-11 14:44:17 -0700538 """Raised to indicate that a listing can't be done."""
539 pass
540
541
542class HostLockManagerReuse(CrosDynamicSuiteException):
543 """Raised when a caller tries to re-use a HostLockManager instance."""
Chris Masonef8b53062012-05-08 22:14:18 -0700544 pass
545
546
mbligh91672252008-10-16 22:28:34 +0000547# This MUST remain at the end of the file.
548# Limit 'from error import *' to only import the exception instances.
549for _name, _thing in locals().items():
550 try:
551 if issubclass(_thing, Exception):
552 __all__.append(_name)
553 except TypeError:
554 pass # _thing not a class
555__all__ = tuple(__all__)