1# Lint as: python2, python3
2#pylint: disable-msg=C0111
3
4"""
5Internal global error types
6"""
7
8from __future__ import absolute_import
9from __future__ import division
10from __future__ import print_function
11
12import six
13
14import sys, traceback
15from traceback import format_exception
16
17# Add names you want to be imported by 'from errors import *' to this list.
18# This must be list not a tuple as we modify it to include all of our
19# the Exception classes we define below at the end of this file.
20__all__ = ['format_error']
21
22
23def format_error():
24    t, o, tb = sys.exc_info()
25    trace = format_exception(t, o, tb)
26    # Clear the backtrace to prevent a circular reference
27    # in the heap -- as per tutorial
28    tb = ''
29
30    return ''.join(trace)
31
32
33class TimeoutException(Exception):
34    """Generic exception raised on retry timeouts."""
35
36
37class JobContinue(SystemExit):
38    """Allow us to bail out requesting continuance."""
39
40
41class JobComplete(SystemExit):
42    """Allow us to bail out indicating continuation not required."""
43
44
45class AutotestError(Exception):
46    """The parent of all errors deliberatly thrown within the client code."""
47
48
49class JobError(AutotestError):
50    """Indicates an error which terminates and fails the whole job (ABORT)."""
51
52
53class UnhandledJobError(JobError):
54    """Indicates an unhandled error in a job."""
55    def __init__(self, unhandled_exception):
56        if isinstance(unhandled_exception, JobError):
57            JobError.__init__(self, *unhandled_exception.args)
58        elif isinstance(unhandled_exception, six.string_types):
59            JobError.__init__(self, unhandled_exception)
60        else:
61            msg = "Unhandled %s: %s"
62            msg %= (unhandled_exception.__class__.__name__,
63                    unhandled_exception)
64            msg += "\n" + traceback.format_exc()
65            JobError.__init__(self, msg)
66
67
68class TestBaseException(AutotestError):
69    """The parent of all test exceptions."""
70    # Children are required to override this.  Never instantiate directly.
71    exit_status = "NEVER_RAISE_THIS"
72
73
74class TestError(TestBaseException):
75    """Indicates that something went wrong with the test harness itself."""
76    exit_status = "ERROR"
77
78
79class TestNAError(TestBaseException):
80    """Indictates that the test is Not Applicable.  Should be thrown
81    when various conditions are such that the test is inappropriate."""
82    exit_status = "TEST_NA"
83
84
85class TestFail(TestBaseException):
86    """Indicates that the test failed, but the job will not continue."""
87    exit_status = "FAIL"
88
89
90class TestWarn(TestBaseException):
91    """Indicates that bad things (may) have happened, but not an explicit
92    failure."""
93    exit_status = "WARN"
94
95
96class UnhandledTestError(TestError):
97    """Indicates an unhandled error in a test."""
98    def __init__(self, unhandled_exception):
99        if isinstance(unhandled_exception, TestError):
100            TestError.__init__(self, *unhandled_exception.args)
101        elif isinstance(unhandled_exception, six.string_types):
102            TestError.__init__(self, unhandled_exception)
103        else:
104            msg = "Unhandled %s: %s"
105            msg %= (unhandled_exception.__class__.__name__,
106                    unhandled_exception)
107            msg += "\n" + traceback.format_exc()
108            TestError.__init__(self, msg)
109
110
111class UnhandledTestFail(TestFail):
112    """Indicates an unhandled fail in a test."""
113    def __init__(self, unhandled_exception):
114        if isinstance(unhandled_exception, TestFail):
115            TestFail.__init__(self, *unhandled_exception.args)
116        elif isinstance(unhandled_exception, six.string_types):
117            TestFail.__init__(self, unhandled_exception)
118        else:
119            msg = "Unhandled %s: %s"
120            msg %= (unhandled_exception.__class__.__name__,
121                    unhandled_exception)
122            msg += "\n" + traceback.format_exc()
123            TestFail.__init__(self, msg)
124
125
126class CmdError(TestError):
127    """Indicates that a command failed, is fatal to the test unless caught.
128
129    @type command: str
130    @type result_obj: autotest_lib.client.common_lib.utils.CmdResult
131    @type additional_text: str | None
132    """
133    def __init__(self, command, result_obj, additional_text=None):
134        TestError.__init__(self, command, result_obj, additional_text)
135        self.command = command
136        self.result_obj = result_obj
137        self.additional_text = additional_text
138
139    def __str__(self):
140        if self.result_obj.exit_status is None:
141            msg = "Command <%s> failed and is not responding to signals"
142            msg %= self.command
143        else:
144            msg = "Command <%s> failed, rc=%d"
145            msg %= (self.command, self.result_obj.exit_status)
146
147        if self.additional_text:
148            msg += ", " + self.additional_text
149        msg += '\n' + repr(self.result_obj)
150        return msg
151
152    def __eq__(self, other):
153        if type(self) == type(other):
154            return (self.command == other.command
155                    and self.result_obj == other.result_obj
156                    and self.additional_text == other.additional_text)
157        else:
158            return NotImplemented
159
160
161class CmdTimeoutError(CmdError):
162    """Indicates that a command timed out."""
163
164
165class PackageError(TestError):
166    """Indicates an error trying to perform a package operation."""
167
168
169class BarrierError(JobError):
170    """Indicates an error happened during a barrier operation."""
171
172
173class BarrierAbortError(BarrierError):
174    """Indicate that the barrier was explicitly aborted by a member."""
175
176
177class InstallError(JobError):
178    """Indicates an installation error which Terminates and fails the job."""
179
180
181class AutotestRunError(AutotestError):
182    """Indicates a problem running server side control files."""
183
184
185class AutotestTimeoutError(AutotestError):
186    """This exception is raised when an autotest test exceeds the timeout
187    parameter passed to run_timed_test and is killed.
188    """
189
190
191class GenericHostRunError(Exception):
192    """Indicates a problem in the host run() function running in either client
193    or server code.
194
195    Should always be constructed with a tuple of two args (error description
196    (str), run result object). This is a common class used to create the client
197    and server side versions of it when the distinction is useful.
198    """
199    def __init__(self, description, result_obj):
200        self.description = description
201        self.result_obj = result_obj
202        Exception.__init__(self, description, result_obj)
203
204    def __str__(self):
205        return self.description + '\n' + repr(self.result_obj)
206
207
208class HostInstallTimeoutError(JobError):
209    """
210    Indicates the machine failed to be installed after the predetermined
211    timeout.
212    """
213
214
215class AutotestHostRunError(GenericHostRunError, AutotestError):
216    pass
217
218
219class AutotestHostRunCmdError(AutotestHostRunError):
220    """Indicates that the command run via Host.run failed.
221
222    This is equivalent to CmdError when raised from a Host object instead of
223    directly on the DUT using utils.run
224    """
225
226    def __init__(self, command, result_obj, additional_text=''):
227        description = command
228        if additional_text:
229            description += ' (%s)' % additional_text
230        super(AutotestHostRunCmdError, self).__init__(description, result_obj)
231        self.command = command
232        self.additional_text = additional_text
233
234
235class AutotestHostRunTimeoutError(AutotestHostRunCmdError):
236    """Indicates that a command run via Host.run timed out.
237
238    This is equivalent to CmdTimeoutError when raised from a Host object instead
239    of directly on the DUT using utils.run
240    """
241
242
243# server-specific errors
244
245class AutoservError(Exception):
246    pass
247
248
249class AutoservSSHTimeout(AutoservError):
250    """SSH experienced a connection timeout"""
251
252
253class AutoservRunError(GenericHostRunError, AutoservError):
254    pass
255
256
257class AutoservSshPermissionDeniedError(AutoservRunError):
258    """Indicates that a SSH permission denied error was encountered."""
259
260
261class AutoservUnsupportedError(AutoservError):
262    """Error raised when you try to use an unsupported optional feature"""
263
264
265class AutoservHostError(AutoservError):
266    """Error reaching a host"""
267
268
269class AutoservHostIsShuttingDownError(AutoservHostError):
270    """Host is shutting down"""
271
272
273class AutoservNotMountedHostError(AutoservHostError):
274    """Found unmounted partitions that should be mounted"""
275
276
277class AutoservSshPingHostError(AutoservHostError):
278    """SSH ping failed"""
279
280
281class AutoservDiskFullHostError(AutoservHostError):
282    """Not enough free disk space on host"""
283
284    def __init__(self, path, want_gb, free_space_gb):
285        super(AutoservDiskFullHostError, self).__init__(
286            'Not enough free space on %s - %.3fGB free, want %.3fGB' %
287                    (path, free_space_gb, want_gb))
288        self.path = path
289        self.want_gb = want_gb
290        self.free_space_gb = free_space_gb
291
292
293class AutoservNoFreeInodesError(AutoservHostError):
294    """Not enough free i-nodes on host"""
295
296    def __init__(self, path, want_inodes, free_inodes):
297        super(AutoservNoFreeInodesError, self).__init__(
298            'Not enough free inodes on %s - %d free, want %d' %
299                    (path, free_inodes, want_inodes))
300        self.path = path
301        self.want_inodes = want_inodes
302        self.free_inodes = free_inodes
303
304
305class AutoservHardwareHostError(AutoservHostError):
306    """Found hardware problems with the host"""
307
308
309class AutoservRebootError(AutoservError):
310    """Error occured while rebooting a machine"""
311
312
313class AutoservShutdownError(AutoservRebootError):
314    """Error occured during shutdown of machine"""
315
316
317class AutoservSuspendError(AutoservRebootError):
318    """Error occured while suspending a machine"""
319
320
321class AutoservSubcommandError(AutoservError):
322    """Indicates an error while executing a (forked) subcommand"""
323    def __init__(self, func, exit_code):
324        AutoservError.__init__(self, func, exit_code)
325        self.func = func
326        self.exit_code = exit_code
327
328    def __str__(self):
329        return ("Subcommand %s failed with exit code %d" %
330                (self.func, self.exit_code))
331
332
333class AutoservRepairTotalFailure(AutoservError):
334    """Raised if all attempts to repair the DUT failed."""
335
336
337class AutoservInstallError(AutoservError):
338    """Error occured while installing autotest on a host"""
339
340
341class AutoservPidAlreadyDeadError(AutoservError):
342    """Error occured by trying to kill a nonexistant PID"""
343
344
345# packaging system errors
346
347class PackagingError(AutotestError):
348    'Abstract error class for all packaging related errors.'
349
350
351class PackageUploadError(PackagingError):
352    'Raised when there is an error uploading the package'
353
354
355class PackageFetchError(PackagingError):
356    'Raised when there is an error fetching the package'
357
358
359class PackageRemoveError(PackagingError):
360    'Raised when there is an error removing the package'
361
362
363class PackageInstallError(PackagingError):
364    'Raised when there is an error installing the package'
365
366
367class RepoDiskFullError(PackagingError):
368    'Raised when the destination for packages is full'
369
370
371class RepoWriteError(PackagingError):
372    "Raised when packager cannot write to a repo's desitnation"
373
374
375class RepoUnknownError(PackagingError):
376    "Raised when packager cannot write to a repo's desitnation"
377
378
379class RepoError(PackagingError):
380    "Raised when a repo isn't working in some way"
381
382
383class StageControlFileFailure(Exception):
384    """Exceptions encountered staging control files."""
385
386
387class CrosDynamicSuiteException(Exception):
388    """
389    Base class for exceptions coming from dynamic suite code in
390    server/cros/dynamic_suite/*.
391    """
392
393
394class StageBuildFailure(CrosDynamicSuiteException):
395    """Raised when the dev server throws 500 while staging a build."""
396
397
398class ControlFileEmpty(CrosDynamicSuiteException):
399    """Raised when the control file exists on the server, but can't be read."""
400
401
402class ControlFileMalformed(CrosDynamicSuiteException):
403    """Raised when an invalid control file is read."""
404
405
406class AsynchronousBuildFailure(CrosDynamicSuiteException):
407    """Raised when the dev server throws 500 while finishing staging of a build.
408    """
409
410
411class SuiteArgumentException(CrosDynamicSuiteException):
412    """Raised when improper arguments are used to run a suite."""
413
414
415class MalformedDependenciesException(CrosDynamicSuiteException):
416    """Raised when a build has a malformed dependency_info file."""
417
418
419class InadequateHostsException(CrosDynamicSuiteException):
420    """Raised when there are too few hosts to run a suite."""
421
422
423class NoHostsException(CrosDynamicSuiteException):
424    """Raised when there are no healthy hosts to run a suite."""
425
426
427class ControlFileNotFound(CrosDynamicSuiteException):
428    """Raised when a control file cannot be found and/or read."""
429
430
431class NoControlFileList(CrosDynamicSuiteException):
432    """Raised to indicate that a listing can't be done."""
433
434
435class SuiteControlFileException(CrosDynamicSuiteException):
436    """Raised when failing to list the contents of all control file."""
437
438
439class HostLockManagerReuse(CrosDynamicSuiteException):
440    """Raised when a caller tries to re-use a HostLockManager instance."""
441
442
443class ReimageAbortedException(CrosDynamicSuiteException):
444    """Raised when a Reimage job is aborted"""
445
446
447class UnknownReimageType(CrosDynamicSuiteException):
448    """Raised when a suite passes in an invalid reimage type"""
449
450
451class NoUniquePackageFound(Exception):
452    """Raised when an executable cannot be mapped back to a single package."""
453
454
455class RPCException(Exception):
456    """Raised when an RPC encounters an error that a client might wish to
457    handle specially."""
458
459
460class NoEligibleHostException(RPCException):
461    """Raised when no host could satisfy the requirements of a job."""
462
463
464class UnmodifiableLabelException(RPCException):
465    """Raised when an RPC tries to modify static labels."""
466
467
468class UnmodifiableAttributeException(RPCException):
469    """Raised when an RPC tries to modify static attributes."""
470
471
472class InvalidBgJobCall(Exception):
473    """Raised when an invalid call is made to a BgJob object."""
474
475
476class HeartbeatOnlyAllowedInShardModeException(Exception):
477    """Raised when a heartbeat is attempted but not allowed."""
478
479
480class UnallowedRecordsSentToMain(Exception):
481    """Raised when an illegal record was sent from shard to main."""
482
483
484class IgnorableUnallowedRecordsSentToMain(UnallowedRecordsSentToMain):
485    """Raised when non-fatal illegal record was sent from shard.
486
487    This exception may be raised by rpc model logic on main, but will
488    not be returned back to heartbeat client. It indicates that some records
489    may have been illegal, but the main is ignoring those records and
490    proceeding with the rest of the heartbeat handling.
491    """
492
493
494class InvalidDataError(Exception):
495    """Exception raised when invalid data provided for database operation."""
496
497
498class ContainerError(Exception):
499    """Exception raised when program runs into error using container."""
500
501
502class IllegalUser(Exception):
503    """Exception raise when a program runs as an illegal user."""
504
505
506class AutoservDirectoryNotFoundError(AutoservHostError):
507    """Exception raised when an expected directory is not found."""
508
509
510class AutoservDiskSizeUnknownError(AutoservHostError):
511    """Exception raised when the disk space could not be determined."""
512
513
514# This MUST remain at the end of the file.
515# Limit 'from error import *' to only import the exception instances.
516for _name, _thing in list(locals().items()):
517    try:
518        if issubclass(_thing, Exception):
519            __all__.append(_name)
520    except TypeError:
521        pass  # _thing not a class
522__all__ = tuple(__all__)
523