Merge branch 'stable-2.16' into stable-2.17
[ganeti-github.git] / lib / errors.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """Ganeti exception handling.
32
33 """
34
35 from ganeti import constants
36
37
38 ECODE_RESOLVER = constants.ERRORS_ECODE_RESOLVER
39 ECODE_NORES = constants.ERRORS_ECODE_NORES
40 ECODE_TEMP_NORES = constants.ERRORS_ECODE_TEMP_NORES
41 ECODE_INVAL = constants.ERRORS_ECODE_INVAL
42 ECODE_STATE = constants.ERRORS_ECODE_STATE
43 ECODE_NOENT = constants.ERRORS_ECODE_NOENT
44 ECODE_EXISTS = constants.ERRORS_ECODE_EXISTS
45 ECODE_NOTUNIQUE = constants.ERRORS_ECODE_NOTUNIQUE
46 ECODE_FAULT = constants.ERRORS_ECODE_FAULT
47 ECODE_ENVIRON = constants.ERRORS_ECODE_ENVIRON
48 ECODE_ALL = constants.ERRORS_ECODE_ALL
49
50
51 class GenericError(Exception):
52 """Base exception for Ganeti.
53
54 """
55
56
57 class LockError(GenericError):
58 """Lock error exception.
59
60 This signifies problems in the locking subsystem.
61
62 """
63
64
65 class PidFileLockError(LockError):
66 """PID file is already locked by another process.
67
68 """
69
70
71 class HypervisorError(GenericError):
72 """Hypervisor-related exception.
73
74 This is raised in case we can't communicate with the hypervisor
75 properly.
76
77 """
78
79
80 class HotplugError(HypervisorError):
81 """Hotplug-related exception.
82
83 This is raised in case a hotplug action fails or is not supported.
84 It is currently used only by KVM hypervisor.
85
86 """
87
88
89 class ProgrammerError(GenericError):
90 """Programming-related error.
91
92 This is raised in cases we determine that the calling conventions
93 have been violated, meaning we got some desynchronisation between
94 parts of our code. It signifies a real programming bug.
95
96 """
97
98
99 class BlockDeviceError(GenericError):
100 """Block-device related exception.
101
102 This is raised in case we can't setup the instance's block devices
103 properly.
104
105 """
106
107
108 class ConfigurationError(GenericError):
109 """Configuration related exception.
110
111 Things like having an instance with a primary node that doesn't
112 exist in the config or such raise this exception.
113
114 """
115
116
117 class ConfigVersionMismatch(ConfigurationError):
118 """Version mismatch in the configuration file.
119
120 The error has two arguments: the expected and the actual found
121 version.
122
123 """
124
125
126 class ConfigVerifyError(ConfigurationError):
127 """Error reported by configuration verification
128
129 The error has two arguments: the main error message and a list of errors
130 found.
131
132 """
133
134
135 class AddressPoolError(GenericError):
136 """Errors related to IP address pools.
137
138 """
139
140
141 class ReservationError(GenericError):
142 """Errors reserving a resource.
143
144 """
145
146
147 class RemoteError(GenericError):
148 """Programming-related error on remote call.
149
150 This is raised when an unhandled error occurs in a call to a
151 remote node. It usually signifies a real programming bug.
152
153 """
154
155
156 class SignatureError(GenericError):
157 """Error authenticating a remote message.
158
159 This is raised when the hmac signature on a message doesn't verify correctly
160 to the message itself. It can happen because of network unreliability or
161 because of spurious traffic.
162
163 """
164
165
166 class ParameterError(GenericError):
167 """A passed parameter to a command is invalid.
168
169 This is raised when the parameter passed to a request function is
170 invalid. Correct code should have verified this before passing the
171 request structure.
172
173 The argument to this exception should be the parameter name.
174
175 """
176
177
178 class ResultValidationError(GenericError):
179 """The iallocation results fails validation.
180
181 """
182
183
184 class OpPrereqError(GenericError):
185 """Prerequisites for the OpCode are not fulfilled.
186
187 This exception has two arguments: an error message, and one of the
188 ECODE_* codes.
189
190 """
191
192
193 class OpExecError(GenericError):
194 """Error during OpCode execution.
195
196 """
197
198
199 class OpResultError(GenericError):
200 """Issue with OpCode result.
201
202 """
203
204
205 class OpRetryNotSupportedError(GenericError):
206 """This opcode does not support retries
207
208 """
209
210
211 class DeviceCreationError(GenericError):
212 """Error during the creation of a device.
213
214 This exception should contain the list of the devices actually created
215 up to now, in the form of pairs (node, device)
216
217 """
218 def __init__(self, message, created_devices):
219 GenericError.__init__(self)
220 self.message = message
221 self.created_devices = created_devices
222
223 def __str__(self):
224 return self.message
225
226
227 class OpCodeUnknown(GenericError):
228 """Unknown opcode submitted.
229
230 This signifies a mismatch between the definitions on the client and
231 server side.
232
233 """
234
235
236 class JobLost(GenericError):
237 """Submitted job lost.
238
239 The job was submitted but it cannot be found in the current job
240 list.
241
242 """
243
244
245 class JobCanceled(GenericError):
246 """Submitted job was canceled.
247
248 The job that was submitted has transitioned to a canceling or canceled
249 state.
250
251 """
252
253
254 class JobFileCorrupted(GenericError):
255 """Job file could not be properly decoded/restored.
256
257 """
258
259
260 class ResolverError(GenericError):
261 """Host name cannot be resolved.
262
263 This is not a normal situation for Ganeti, as we rely on having a
264 working resolver.
265
266 The non-resolvable hostname is available as the first element of the
267 args tuple; the other two elements of the tuple are the first two
268 args of the socket.gaierror exception (error code and description).
269
270 """
271
272
273 class HooksFailure(GenericError):
274 """A generic hook failure.
275
276 This signifies usually a setup misconfiguration.
277
278 """
279
280
281 class HooksAbort(HooksFailure):
282 """A required hook has failed.
283
284 This caused an abort of the operation in the initial phase. This
285 exception always has an attribute args which is a list of tuples of:
286 - node: the source node on which this hooks has failed
287 - script: the name of the script which aborted the run
288
289 """
290
291
292 class UnitParseError(GenericError):
293 """Unable to parse size unit.
294
295 """
296
297
298 class ParseError(GenericError):
299 """Generic parse error.
300
301 Raised when unable to parse user input.
302
303 """
304
305
306 class TypeEnforcementError(GenericError):
307 """Unable to enforce data type.
308
309 """
310
311
312 class X509CertError(GenericError):
313 """Invalid X509 certificate.
314
315 This error has two arguments: the certificate filename and the error cause.
316
317 """
318
319
320 class TagError(GenericError):
321 """Generic tag error.
322
323 The argument to this exception will show the exact error.
324
325 """
326
327
328 class CommandError(GenericError):
329 """External command error.
330
331 """
332
333
334 class StorageError(GenericError):
335 """Storage-related exception.
336
337 """
338
339
340 class InotifyError(GenericError):
341 """Error raised when there is a failure setting up an inotify watcher.
342
343 """
344
345
346 class QuitGanetiException(Exception):
347 """Signal Ganeti that it must quit.
348
349 This is not necessarily an error (and thus not a subclass of
350 GenericError), but it's an exceptional circumstance and it is thus
351 treated. This exception should be instantiated with two values. The
352 first one will specify the return code to the caller, and the second
353 one will be the returned result (either as an error or as a normal
354 result). Usually only the leave cluster rpc call should return
355 status True (as there it's expected we quit), every other call will
356 return status False (as a critical error was encountered).
357
358 Examples::
359
360 # Return a result of "True" to the caller, but quit ganeti afterwards
361 raise QuitGanetiException(True, None)
362 # Send an error to the caller, and quit ganeti
363 raise QuitGanetiException(False, "Fatal safety violation, shutting down")
364
365 """
366
367
368 class JobQueueError(GenericError):
369 """Job queue error.
370
371 """
372
373
374 class JobQueueDrainError(JobQueueError):
375 """Job queue is marked for drain error.
376
377 This is raised when a job submission attempt is made but the queue
378 is marked for drain.
379
380 """
381
382
383 class JobQueueFull(JobQueueError):
384 """Job queue full error.
385
386 Raised when job queue size reached its hard limit.
387
388 """
389
390
391 class ConfdMagicError(GenericError):
392 """A magic fourcc error in Ganeti confd.
393
394 Errors processing the fourcc in ganeti confd datagrams.
395
396 """
397
398
399 class ConfdClientError(GenericError):
400 """A magic fourcc error in Ganeti confd.
401
402 Errors in the confd client library.
403
404 """
405
406
407 class UdpDataSizeError(GenericError):
408 """UDP payload too big.
409
410 """
411
412
413 class NoCtypesError(GenericError):
414 """python ctypes module is not found in the system.
415
416 """
417
418
419 class IPAddressError(GenericError):
420 """Generic IP address error.
421
422 """
423
424
425 class LuxiError(GenericError):
426 """LUXI error.
427
428 """
429
430
431 class QueryFilterParseError(ParseError):
432 """Error while parsing query filter.
433
434 This exception must be instantiated with two values. The first one is a
435 string with an error description, the second one is an instance of a subclass
436 of C{pyparsing.ParseBaseException} (used to display the exact error
437 location).
438
439 """
440 def GetDetails(self):
441 """Returns a list of strings with details about the error.
442
443 """
444 try:
445 (_, inner) = self.args
446 except IndexError:
447 return None
448
449 return [str(inner.line),
450 (" " * (inner.column - 1)) + "^",
451 str(inner)]
452
453
454 class RapiTestResult(GenericError):
455 """Exception containing results from RAPI test utilities.
456
457 """
458
459
460 class FileStoragePathError(GenericError):
461 """Error from file storage path validation.
462
463 """
464
465
466 class SshUpdateError(GenericError):
467 """Error from updating the SSH setup.
468
469 """
470
471
472 class JobSubmittedException(Exception):
473 """Job was submitted, client should exit.
474
475 This exception has one argument, the ID of the job that was
476 submitted. The handler should print this ID.
477
478 This is not an error, just a structured way to exit from clients.
479
480 """
481
482
483 # errors should be added above
484
485
486 def GetErrorClass(name):
487 """Return the class of an exception.
488
489 Given the class name, return the class itself.
490
491 @type name: str
492 @param name: the exception name
493 @rtype: class
494 @return: the actual class, or None if not found
495
496 """
497 item = globals().get(name, None)
498 if item is not None:
499 if not (isinstance(item, type(Exception)) and
500 issubclass(item, GenericError)):
501 item = None
502 return item
503
504
505 def EncodeException(err):
506 """Encodes an exception into a format that L{MaybeRaise} will recognise.
507
508 The passed L{err} argument will be formatted as a tuple (exception
509 name, arguments) that the MaybeRaise function will recognise.
510
511 @type err: GenericError child
512 @param err: usually a child of GenericError (but any exception
513 will be accepted)
514 @rtype: tuple
515 @return: tuple of (exception name, exception arguments)
516
517 """
518 return (err.__class__.__name__, err.args)
519
520
521 def GetEncodedError(result):
522 """If this looks like an encoded Ganeti exception, return it.
523
524 This function tries to parse the passed argument and if it looks
525 like an encoding done by EncodeException, it will return the class
526 object and arguments.
527
528 """
529 tlt = (tuple, list)
530 if (isinstance(result, tlt) and len(result) == 2 and
531 isinstance(result[1], tlt)):
532 # custom ganeti errors
533 errcls = GetErrorClass(result[0])
534 if errcls:
535 return (errcls, tuple(result[1]))
536
537 return None
538
539
540 def MaybeRaise(result):
541 """If this looks like an encoded Ganeti exception, raise it.
542
543 This function tries to parse the passed argument and if it looks
544 like an encoding done by EncodeException, it will re-raise it.
545
546 """
547 error = GetEncodedError(result)
548 if error:
549 (errcls, args) = error
550 raise errcls(*args)