#
#
-# Copyright (C) 2006, 2007 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
"""Ganeti exception handling"""
+# OpPrereqError failure types
+
+# resolver errors
+ECODE_RESOLVER = "resolver_error"
+# not enough resources (iallocator failure, disk space, memory, etc.)
+ECODE_NORES = "insufficient_resources"
+# wrong arguments (at syntax level)
+ECODE_INVAL = "wrong_input"
+# wrong entity state
+ECODE_STATE = "wrong_state"
+# entity not found
+ECODE_NOENT = "unknown_entity"
+# entity already exists
+ECODE_EXISTS = "already_exists"
+# resource not unique (e.g. MAC or IP duplication)
+ECODE_NOTUNIQUE = "resource_not_unique"
+# internal cluster error
+ECODE_FAULT = "internal_error"
+# environment error (e.g. node disk error)
+ECODE_ENVIRON = "environment_error"
+
+#: List of all failure types
+ECODE_ALL = frozenset([
+ ECODE_RESOLVER,
+ ECODE_NORES,
+ ECODE_INVAL,
+ ECODE_STATE,
+ ECODE_NOENT,
+ ECODE_EXISTS,
+ ECODE_NOTUNIQUE,
+ ECODE_FAULT,
+ ECODE_ENVIRON,
+ ])
+
+
class GenericError(Exception):
"""Base exception for Ganeti.
pass
+class ConfigVersionMismatch(ConfigurationError):
+ """Version mismatch in the configuration file.
+
+ The error has two arguments: the expected and the actual found
+ version.
+
+ """
+ pass
+
+
+class ReservationError(GenericError):
+ """Errors reserving a resource.
+
+ """
+
+
class RemoteError(GenericError):
"""Programming-related error on remote call.
pass
-class InvalidOS(GenericError):
- """Missing OS on node.
+class SignatureError(GenericError):
+ """Error authenticating a remote message.
- This is raised when an OS exists on the master (or is otherwise
- requested to the code) but not on the target node.
-
- This exception has three arguments:
- - the name of the os
- - the source directory, if any
- - the reason why we consider this an invalid OS (text of error message)
+ This is raised when the hmac signature on a message doesn't verify correctly
+ to the message itself. It can happen because of network unreliability or
+ because of spurious traffic.
"""
+ pass
class ParameterError(GenericError):
class OpPrereqError(GenericError):
"""Prerequisites for the OpCode are not fulfilled.
+ This exception will have either one or two arguments. For the
+ two-argument construction, the second argument should be one of the
+ ECODE_* codes.
+
"""
"""
-class OpRetryError(OpExecError):
- """Error during OpCode execution, action can be retried.
+class OpResultError(GenericError):
+ """Issue with OpCode result.
"""
"""
+class JobFileCorrupted(GenericError):
+ """Job file could not be properly decoded/restored.
+
+ """
+
+
class ResolverError(GenericError):
"""Host name cannot be resolved.
"""
+class ParseError(GenericError):
+ """Generic parse error.
+
+ Raised when unable to parse user input.
+
+ """
+
+
+class TypeEnforcementError(GenericError):
+ """Unable to enforce data type.
+
+ """
+
+
class SshKeyError(GenericError):
"""Invalid SSH key.
"""
+class X509CertError(GenericError):
+ """Invalid X509 certificate.
+
+ This error has two arguments: the certificate filename and the error cause.
+
+ """
+
+
class TagError(GenericError):
"""Generic tag error.
"""External command error.
"""
+
+
+class StorageError(GenericError):
+ """Storage-related exception.
+
+ """
+
+
+class InotifyError(GenericError):
+ """Error raised when there is a failure setting up an inotify watcher.
+
+ """
+
+
+class QuitGanetiException(Exception):
+ """Signal Ganeti that it must quit.
+
+ This is not necessarily an error (and thus not a subclass of
+ GenericError), but it's an exceptional circumstance and it is thus
+ treated. This instance should be instantiated with two values. The
+ first one will specify the return code to the caller, and the second
+ one will be the returned result (either as an error or as a normal
+ result). Usually only the leave cluster rpc call should return
+ status True (as there it's expected we quit), every other call will
+ return status False (as a critical error was encountered).
+
+ Examples::
+
+ # Return a result of "True" to the caller, but quit ganeti afterwards
+ raise QuitGanetiException(True, None)
+ # Send an error to the caller, and quit ganeti
+ raise QuitGanetiException(False, "Fatal safety violation, shutting down")
+
+ """
+
+
+class JobQueueError(GenericError):
+ """Job queue error.
+
+ """
+
+
+class JobQueueDrainError(JobQueueError):
+ """Job queue is marked for drain error.
+
+ This is raised when a job submission attempt is made but the queue
+ is marked for drain.
+
+ """
+
+
+class JobQueueFull(JobQueueError):
+ """Job queue full error.
+
+ Raised when job queue size reached its hard limit.
+
+ """
+
+
+class ConfdRequestError(GenericError):
+ """A request error in Ganeti confd.
+
+ Events that should make confd abort the current request and proceed serving
+ different ones.
+
+ """
+
+
+class ConfdMagicError(GenericError):
+ """A magic fourcc error in Ganeti confd.
+
+ Errors processing the fourcc in ganeti confd datagrams.
+
+ """
+
+
+class ConfdClientError(GenericError):
+ """A magic fourcc error in Ganeti confd.
+
+ Errors in the confd client library.
+
+ """
+
+
+class UdpDataSizeError(GenericError):
+ """UDP payload too big.
+
+ """
+
+
+class NoCtypesError(GenericError):
+ """python ctypes module is not found in the system.
+
+ """
+
+
+class IPAddressError(GenericError):
+ """Generic IP address error.
+
+ """
+
+
+class LuxiError(GenericError):
+ """LUXI error.
+
+ """
+
+
+class QueryFilterParseError(ParseError):
+ """Error while parsing query filter.
+
+ """
+ def GetDetails(self):
+ """Returns a list of strings with details about the error.
+
+ """
+ try:
+ (_, inner) = self.args
+ except IndexError:
+ return None
+
+ return [str(inner.line),
+ (" " * (inner.column - 1)) + "^",
+ str(inner)]
+
+
+# errors should be added above
+
+
+def GetErrorClass(name):
+ """Return the class of an exception.
+
+ Given the class name, return the class itself.
+
+ @type name: str
+ @param name: the exception name
+ @rtype: class
+ @return: the actual class, or None if not found
+
+ """
+ item = globals().get(name, None)
+ if item is not None:
+ if not (isinstance(item, type(Exception)) and
+ issubclass(item, GenericError)):
+ item = None
+ return item
+
+
+def EncodeException(err):
+ """Encodes an exception into a format that L{MaybeRaise} will recognise.
+
+ The passed L{err} argument will be formatted as a tuple (exception
+ name, arguments) that the MaybeRaise function will recognise.
+
+ @type err: GenericError child
+ @param err: usually a child of GenericError (but any exception
+ will be accepted)
+ @rtype: tuple
+ @return: tuple of (exception name, exception arguments)
+
+ """
+ return (err.__class__.__name__, err.args)
+
+
+def GetEncodedError(result):
+ """If this looks like an encoded Ganeti exception, return it.
+
+ This function tries to parse the passed argument and if it looks
+ like an encoding done by EncodeException, it will return the class
+ object and arguments.
+
+ """
+ tlt = (tuple, list)
+ if (isinstance(result, tlt) and len(result) == 2 and
+ isinstance(result[1], tlt)):
+ # custom ganeti errors
+ errcls = GetErrorClass(result[0])
+ if errcls:
+ return (errcls, tuple(result[1]))
+
+ return None
+
+
+def MaybeRaise(result):
+ """If this looks like an encoded Ganeti exception, raise it.
+
+ This function tries to parse the passed argument and if it looks
+ like an encoding done by EncodeException, it will re-raise it.
+
+ """
+ error = GetEncodedError(result)
+ if error:
+ (errcls, args) = error
+ raise errcls(args)