(grnet) Remove deprecated idx slot from NIC/Disk objects
[ganeti-local] / lib / errors.py
index 006da8e..f205f4a 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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
 # 02110-1301, USA.
 
 
-"""Ganeti exception handling"""
+"""Ganeti exception handling.
 
+"""
 
-class GenericError(Exception):
-  """Base exception for Ganeti.
+from ganeti import compat
 
-  """
-  pass
 
+# OpPrereqError failure types
+
+#: Resolver errors
+ECODE_RESOLVER = "resolver_error"
+
+#: Not enough resources (iallocator failure, disk space, memory, etc.)
+ECODE_NORES = "insufficient_resources"
+
+#: Temporarily out of resources; operation can be tried again
+ECODE_TEMP_NORES = "temp_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"
 
-class LVMError(GenericError):
-  """LVM-related exception.
+#: Entity already exists
+ECODE_EXISTS = "already_exists"
 
-  This exception codifies problems with LVM setup.
+#: 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 = compat.UniqueFrozenset([
+  ECODE_RESOLVER,
+  ECODE_NORES,
+  ECODE_TEMP_NORES,
+  ECODE_INVAL,
+  ECODE_STATE,
+  ECODE_NOENT,
+  ECODE_EXISTS,
+  ECODE_NOTUNIQUE,
+  ECODE_FAULT,
+  ECODE_ENVIRON,
+  ])
+
+
+class GenericError(Exception):
+  """Base exception for Ganeti.
 
   """
-  pass
 
 
 class LockError(GenericError):
@@ -44,7 +85,12 @@ class LockError(GenericError):
   This signifies problems in the locking subsystem.
 
   """
-  pass
+
+
+class PidFileLockError(LockError):
+  """PID file is already locked by another process.
+
+  """
 
 
 class HypervisorError(GenericError):
@@ -54,7 +100,15 @@ class HypervisorError(GenericError):
   properly.
 
   """
-  pass
+
+
+class HotplugError(HypervisorError):
+  """Hotplug-related exception.
+
+  This is raised in case a hotplug action fails or is not supported.
+  It is currently used only by KVM hypervisor.
+
+  """
 
 
 class ProgrammerError(GenericError):
@@ -65,7 +119,6 @@ class ProgrammerError(GenericError):
   parts of our code. It signifies a real programming bug.
 
   """
-  pass
 
 
 class BlockDeviceError(GenericError):
@@ -75,7 +128,6 @@ class BlockDeviceError(GenericError):
   properly.
 
   """
-  pass
 
 
 class ConfigurationError(GenericError):
@@ -85,7 +137,27 @@ class ConfigurationError(GenericError):
   exist in the config or such raise this exception.
 
   """
-  pass
+
+
+class ConfigVersionMismatch(ConfigurationError):
+  """Version mismatch in the configuration file.
+
+  The error has two arguments: the expected and the actual found
+  version.
+
+  """
+
+
+class AddressPoolError(GenericError):
+  """Errors related to IP address pools.
+
+  """
+
+
+class ReservationError(GenericError):
+  """Errors reserving a resource.
+
+  """
 
 
 class RemoteError(GenericError):
@@ -95,19 +167,14 @@ class RemoteError(GenericError):
   remote node.  It usually signifies a real programming bug.
 
   """
-  pass
-
 
-class InvalidOS(GenericError):
-  """Missing OS on node.
 
-  This is raised when an OS exists on the master (or is otherwise
-  requested to the code) but not on the target node.
+class SignatureError(GenericError):
+  """Error authenticating a remote message.
 
-  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.
 
   """
 
@@ -122,12 +189,20 @@ class ParameterError(GenericError):
   The argument to this exception should be the parameter name.
 
   """
-  pass
+
+
+class ResultValidationError(GenericError):
+  """The iallocation results fails validation.
+
+  """
 
 
 class OpPrereqError(GenericError):
   """Prerequisites for the OpCode are not fulfilled.
 
+  This exception has two arguments: an error message, and one of the
+  ECODE_* codes.
+
   """
 
 
@@ -137,10 +212,26 @@ class OpExecError(GenericError):
   """
 
 
-class OpRetryError(OpExecError):
-  """Error during OpCode execution, action can be retried.
+class OpResultError(GenericError):
+  """Issue with OpCode result.
+
+  """
+
+
+class DeviceCreationError(GenericError):
+  """Error during the creation of a device.
+
+  This exception should contain the list of the devices actually created
+  up to now, in the form of pairs (node, device)
 
   """
+  def __init__(self, message, created_devices):
+    GenericError.__init__(self)
+    self.message = message
+    self.created_devices = created_devices
+
+  def __str__(self):
+    return self.message
 
 
 class OpCodeUnknown(GenericError):
@@ -161,6 +252,12 @@ class JobLost(GenericError):
   """
 
 
+class JobFileCorrupted(GenericError):
+  """Job file could not be properly decoded/restored.
+
+  """
+
+
 class ResolverError(GenericError):
   """Host name cannot be resolved.
 
@@ -199,8 +296,24 @@ class UnitParseError(GenericError):
   """
 
 
-class SshKeyError(GenericError):
-  """Invalid SSH key.
+class ParseError(GenericError):
+  """Generic parse error.
+
+  Raised when unable to parse user input.
+
+  """
+
+
+class TypeEnforcementError(GenericError):
+  """Unable to enforce data type.
+
+  """
+
+
+class X509CertError(GenericError):
+  """Invalid X509 certificate.
+
+  This error has two arguments: the certificate filename and the error cause.
 
   """
 
@@ -219,20 +332,36 @@ class CommandError(GenericError):
   """
 
 
+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 that Ganeti that it must quit.
+  """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 exception 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).
 
-  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 whether an
-  error should returned to the caller, and the second one will be the returned
-  result (either as an error or as a normal result).
+  Examples::
 
-  Examples:
     # Return a result of "True" to the caller, but quit ganeti afterwards
-    raise QuitGanetiException(False, True)
+    raise QuitGanetiException(True, None)
     # Send an error to the caller, and quit ganeti
-    raise QuitGanetiException(True, "Fatal safety violation, shutting down")
+    raise QuitGanetiException(False, "Fatal safety violation, shutting down")
 
   """
 
@@ -243,6 +372,98 @@ class JobQueueError(GenericError):
   """
 
 
+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 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.
+
+  This exception must be instantiated with two values. The first one is a
+  string with an error description, the second one is an instance of a subclass
+  of C{pyparsing.ParseBaseException} (used to display the exact error
+  location).
+
+  """
+  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)]
+
+
+class RapiTestResult(GenericError):
+  """Exception containing results from RAPI test utilities.
+
+  """
+
+
+class FileStoragePathError(GenericError):
+  """Error from file storage path validation.
+
+  """
+
+
 # errors should be added above
 
 
@@ -263,3 +484,52 @@ def GetErrorClass(name):
             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
+    # pylint: disable=W0142
+    raise errcls(*args)