KVM test: tests_base.cfg: Introduce parameter 'vm_type'
[autotest-zwu.git] / client / common_lib / error.py
blob0c5641cd8b8cef51696abe655892bf2b22bebef3
1 """
2 Internal global error types
3 """
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',
12 'exception_context']
15 def format_error():
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
20 tb = ''
22 return ''.join(trace)
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
33 # exception).
35 # For example: assume a() calls b() and b() calls c().
37 # @error.context_aware
38 # def a():
39 # error.context("hello")
40 # b()
41 # error.context("world")
42 # error.get_context() ----> 'world'
44 # @error.context_aware
45 # def b():
46 # error.context("foo")
47 # c()
49 # @error.context_aware
50 # def c():
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"):
63 ctx.contexts = []
64 ctx.contexts.append(s)
67 def _pop_context():
68 ctx.contexts.pop()
71 def context(s="", log=None):
72 """
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
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())
85 def 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().
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())
103 def 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"):
112 return e._context
115 def set_exception_context(e, s):
116 """Set the context of a given exception."""
117 e._context = s
120 def 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
131 def 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
152 def _context_message(e):
153 s = exception_context(e)
154 if s:
155 return " [context: %s]" % s
156 else:
157 return ""
160 class JobContinue(SystemExit):
161 """Allow us to bail out requesting continuance."""
162 pass
165 class JobComplete(SystemExit):
166 """Allow us to bail out indicating continuation not required."""
167 pass
170 class AutotestError(Exception):
171 """The parent of all errors deliberatly thrown within the client code."""
172 def __str__(self):
173 return Exception.__str__(self) + _context_message(self)
176 class JobError(AutotestError):
177 """Indicates an error which terminates and fails the whole job (ABORT)."""
178 pass
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)
188 else:
189 msg = "Unhandled %s: %s"
190 msg %= (unhandled_exception.__class__.__name__,
191 unhandled_exception)
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."""
206 exit_status="ERROR"
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."""
217 exit_status="FAIL"
220 class TestWarn(TestBaseException):
221 """Indicates that bad things (may) have happened, but not an explicit
222 failure."""
223 exit_status="WARN"
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)
233 else:
234 msg = "Unhandled %s: %s"
235 msg %= (unhandled_exception.__class__.__name__,
236 unhandled_exception)
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)
250 else:
251 msg = "Unhandled %s: %s"
252 msg %= (unhandled_exception.__class__.__name__,
253 unhandled_exception)
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):
261 """\
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
270 def __str__(self):
271 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)
278 if self.additional_text:
279 msg += ", " + self.additional_text
280 msg += _context_message(self)
281 msg += '\n' + repr(self.result_obj)
282 return msg
285 class PackageError(TestError):
286 """Indicates an error trying to perform a package operation."""
287 pass
290 class BarrierError(JobError):
291 """Indicates an error happened during a barrier operation."""
292 pass
295 class BarrierAbortError(BarrierError):
296 """Indicate that the barrier was explicitly aborted by a member."""
297 pass
300 class InstallError(JobError):
301 """Indicates an installation error which Terminates and fails the job."""
302 pass
305 class AutotestRunError(AutotestError):
306 """Indicates a problem running server side control files."""
307 pass
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)
328 def __str__(self):
329 return self.description + '\n' + repr(self.result_obj)
332 class AutotestHostRunError(HostRunErrorMixIn, AutotestError):
333 pass
336 # server-specific errors
338 class AutoservError(Exception):
339 pass
342 class AutoservSSHTimeout(AutoservError):
343 """SSH experienced a connection timeout"""
344 pass
347 class AutoservRunError(HostRunErrorMixIn, AutoservError):
348 pass
351 class AutoservSshPermissionDeniedError(AutoservRunError):
352 """Indicates that a SSH permission denied error was encountered."""
353 pass
356 class AutoservVirtError(AutoservError):
357 """Vitualization related error"""
358 pass
361 class AutoservUnsupportedError(AutoservError):
362 """Error raised when you try to use an unsupported optional feature"""
363 pass
366 class AutoservHostError(AutoservError):
367 """Error reaching a host"""
368 pass
371 class AutoservHostIsShuttingDownError(AutoservHostError):
372 """Host is shutting down"""
373 pass
376 class AutoservNotMountedHostError(AutoservHostError):
377 """Found unmounted partitions that should be mounted"""
378 pass
381 class AutoservSshPingHostError(AutoservHostError):
382 """SSH ping failed"""
383 pass
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))
393 self.path = path
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"""
400 pass
403 class AutoservRebootError(AutoservError):
404 """Error occured while rebooting a machine"""
405 pass
408 class AutoservShutdownError(AutoservRebootError):
409 """Error occured during shutdown of machine"""
410 pass
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)
417 self.func = func
418 self.exit_code = exit_code
420 def __str__(self):
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)
431 pass
434 class AutoservHardwareRepairRequiredError(AutoservError):
436 Exception class raised during repairs to indicate that a hardware repair
437 is going to be necessary.
439 pass
442 class AutoservInstallError(AutoservError):
443 """Error occured while installing autotest on a host"""
444 pass
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():
488 try:
489 if issubclass(_thing, Exception):
490 __all__.append(_name)
491 except TypeError:
492 pass # _thing not a class
493 __all__ = tuple(__all__)