585 lines
14 KiB
Python
585 lines
14 KiB
Python
|
#
|
||
|
# Copyright 2015 ClusterHQ
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
#
|
||
|
|
||
|
"""
|
||
|
Exceptions that can be raised by libzfs_core operations.
|
||
|
"""
|
||
|
from __future__ import absolute_import, division, print_function
|
||
|
|
||
|
import errno
|
||
|
from ._constants import (
|
||
|
ZFS_ERR_CHECKPOINT_EXISTS,
|
||
|
ZFS_ERR_DISCARDING_CHECKPOINT,
|
||
|
ZFS_ERR_NO_CHECKPOINT,
|
||
|
ZFS_ERR_DEVRM_IN_PROGRESS,
|
||
|
ZFS_ERR_VDEV_TOO_BIG,
|
||
|
ZFS_ERR_WRONG_PARENT
|
||
|
)
|
||
|
|
||
|
|
||
|
class ZFSError(Exception):
|
||
|
errno = None
|
||
|
message = None
|
||
|
name = None
|
||
|
|
||
|
def __str__(self):
|
||
|
if self.name is not None:
|
||
|
return "[Errno %d] %s: '%s'" % (
|
||
|
self.errno, self.message, self.name)
|
||
|
else:
|
||
|
return "[Errno %d] %s" % (self.errno, self.message)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return "%s(%r, %r)" % (
|
||
|
self.__class__.__name__, self.errno, self.message)
|
||
|
|
||
|
|
||
|
class ZFSGenericError(ZFSError):
|
||
|
|
||
|
def __init__(self, errno, name, message):
|
||
|
self.errno = errno
|
||
|
self.message = message
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class ZFSInitializationFailed(ZFSError):
|
||
|
message = "Failed to initialize libzfs_core"
|
||
|
|
||
|
def __init__(self, errno):
|
||
|
self.errno = errno
|
||
|
|
||
|
|
||
|
class MultipleOperationsFailure(ZFSError):
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
# Use first of the individual error codes
|
||
|
# as an overall error code. This is more consistent.
|
||
|
self.errno = errors[0].errno
|
||
|
self.errors = errors
|
||
|
# this many errors were encountered but not placed on the `errors` list
|
||
|
self.suppressed_count = suppressed_count
|
||
|
|
||
|
def __str__(self):
|
||
|
return "%s, %d errors included, %d suppressed" % (
|
||
|
ZFSError.__str__(self), len(self.errors), self.suppressed_count)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return "%s(%r, %r, errors=%r, suppressed=%r)" % (
|
||
|
self.__class__.__name__, self.errno, self.message, self.errors,
|
||
|
self.suppressed_count)
|
||
|
|
||
|
|
||
|
class DatasetNotFound(ZFSError):
|
||
|
|
||
|
"""
|
||
|
This exception is raised when an operation failure can be caused by a
|
||
|
missing snapshot or a missing filesystem and it is impossible to
|
||
|
distinguish between the causes.
|
||
|
"""
|
||
|
errno = errno.ENOENT
|
||
|
message = "Dataset not found"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class DatasetExists(ZFSError):
|
||
|
|
||
|
"""
|
||
|
This exception is raised when an operation failure can be caused by an
|
||
|
existing snapshot or filesystem and it is impossible to distinguish between
|
||
|
the causes.
|
||
|
"""
|
||
|
errno = errno.EEXIST
|
||
|
message = "Dataset already exists"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class NotClone(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Filesystem is not a clone, can not promote"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class FilesystemExists(DatasetExists):
|
||
|
message = "Filesystem already exists"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class FilesystemNotFound(DatasetNotFound):
|
||
|
message = "Filesystem not found"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class ParentNotFound(ZFSError):
|
||
|
errno = errno.ENOENT
|
||
|
message = "Parent not found"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class WrongParent(ZFSError):
|
||
|
errno = ZFS_ERR_WRONG_PARENT
|
||
|
message = "Parent dataset is not a filesystem"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotExists(DatasetExists):
|
||
|
message = "Snapshot already exists"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotNotFound(DatasetNotFound):
|
||
|
message = "Snapshot not found"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotNotLatest(ZFSError):
|
||
|
errno = errno.EEXIST
|
||
|
message = "Snapshot is not the latest"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotIsCloned(ZFSError):
|
||
|
errno = errno.EEXIST
|
||
|
message = "Snapshot is cloned"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotIsHeld(ZFSError):
|
||
|
errno = errno.EBUSY
|
||
|
message = "Snapshot is held"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class DuplicateSnapshots(ZFSError):
|
||
|
errno = errno.EXDEV
|
||
|
message = "Requested multiple snapshots of the same filesystem"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotFailure(MultipleOperationsFailure):
|
||
|
message = "Creation of snapshot(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(SnapshotFailure, self).__init__(errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class SnapshotDestructionFailure(MultipleOperationsFailure):
|
||
|
message = "Destruction of snapshot(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(SnapshotDestructionFailure, self).__init__(
|
||
|
errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class BookmarkExists(ZFSError):
|
||
|
errno = errno.EEXIST
|
||
|
message = "Bookmark already exists"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BookmarkNotFound(ZFSError):
|
||
|
errno = errno.ENOENT
|
||
|
message = "Bookmark not found"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BookmarkMismatch(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Bookmark is not in snapshot's filesystem"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BookmarkNotSupported(ZFSError):
|
||
|
errno = errno.ENOTSUP
|
||
|
message = "Bookmark feature is not supported"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BookmarkFailure(MultipleOperationsFailure):
|
||
|
message = "Creation of bookmark(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(BookmarkFailure, self).__init__(errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class BookmarkDestructionFailure(MultipleOperationsFailure):
|
||
|
message = "Destruction of bookmark(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(BookmarkDestructionFailure, self).__init__(
|
||
|
errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class BadHoldCleanupFD(ZFSError):
|
||
|
errno = errno.EBADF
|
||
|
message = "Bad file descriptor as cleanup file descriptor"
|
||
|
|
||
|
|
||
|
class HoldExists(ZFSError):
|
||
|
errno = errno.EEXIST
|
||
|
message = "Hold with a given tag already exists on snapshot"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class HoldNotFound(ZFSError):
|
||
|
errno = errno.ENOENT
|
||
|
message = "Hold with a given tag does not exist on snapshot"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class HoldFailure(MultipleOperationsFailure):
|
||
|
message = "Placement of hold(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(HoldFailure, self).__init__(errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class HoldReleaseFailure(MultipleOperationsFailure):
|
||
|
message = "Release of hold(s) failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(HoldReleaseFailure, self).__init__(errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class SnapshotMismatch(ZFSError):
|
||
|
errno = errno.ENODEV
|
||
|
message = "Snapshot is not descendant of source snapshot"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class StreamMismatch(ZFSError):
|
||
|
errno = errno.ENODEV
|
||
|
message = "Stream is not applicable to destination dataset"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class DestinationModified(ZFSError):
|
||
|
errno = errno.ETXTBSY
|
||
|
message = "Destination dataset has modifications that can not be undone"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BadStream(ZFSError):
|
||
|
errno = errno.EBADE
|
||
|
message = "Bad backup stream"
|
||
|
|
||
|
|
||
|
class StreamFeatureNotSupported(ZFSError):
|
||
|
errno = errno.ENOTSUP
|
||
|
message = "Stream contains unsupported feature"
|
||
|
|
||
|
|
||
|
class UnknownStreamFeature(ZFSError):
|
||
|
errno = errno.ENOTSUP
|
||
|
message = "Unknown feature requested for stream"
|
||
|
|
||
|
|
||
|
class StreamFeatureInvalid(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Kernel modules must be upgraded to receive this stream"
|
||
|
|
||
|
|
||
|
class StreamFeatureIncompatible(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Incompatible embedded feature with encrypted receive"
|
||
|
|
||
|
|
||
|
class ReceivePropertyFailure(MultipleOperationsFailure):
|
||
|
message = "Receiving of properties failed for one or more reasons"
|
||
|
|
||
|
def __init__(self, errors, suppressed_count):
|
||
|
super(ReceivePropertyFailure, self).__init__(errors, suppressed_count)
|
||
|
|
||
|
|
||
|
class StreamIOError(ZFSError):
|
||
|
message = "I/O error while writing or reading stream"
|
||
|
|
||
|
def __init__(self, errno):
|
||
|
self.errno = errno
|
||
|
|
||
|
|
||
|
class ZIOError(ZFSError):
|
||
|
errno = errno.EIO
|
||
|
message = "I/O error"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class NoSpace(ZFSError):
|
||
|
errno = errno.ENOSPC
|
||
|
message = "No space left"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class QuotaExceeded(ZFSError):
|
||
|
errno = errno.EDQUOT
|
||
|
message = "Quota exceeded"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class DatasetBusy(ZFSError):
|
||
|
errno = errno.EBUSY
|
||
|
message = "Dataset is busy"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class NameTooLong(ZFSError):
|
||
|
errno = errno.ENAMETOOLONG
|
||
|
message = "Dataset name is too long"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class NameInvalid(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Invalid name"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SnapshotNameInvalid(NameInvalid):
|
||
|
message = "Invalid name for snapshot"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class FilesystemNameInvalid(NameInvalid):
|
||
|
message = "Invalid name for filesystem or volume"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class BookmarkNameInvalid(NameInvalid):
|
||
|
message = "Invalid name for bookmark"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class ReadOnlyPool(ZFSError):
|
||
|
errno = errno.EROFS
|
||
|
message = "Pool is read-only"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class SuspendedPool(ZFSError):
|
||
|
errno = errno.EAGAIN
|
||
|
message = "Pool is suspended"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class PoolNotFound(ZFSError):
|
||
|
errno = errno.EXDEV
|
||
|
message = "No such pool"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class PoolsDiffer(ZFSError):
|
||
|
errno = errno.EXDEV
|
||
|
message = "Source and target belong to different pools"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class FeatureNotSupported(ZFSError):
|
||
|
errno = errno.ENOTSUP
|
||
|
message = "Feature is not supported in this version"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class PropertyNotSupported(ZFSError):
|
||
|
errno = errno.ENOTSUP
|
||
|
message = "Property is not supported in this version"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class PropertyInvalid(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Invalid property or property value"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class DatasetTypeInvalid(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Specified dataset type is unknown"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class UnknownCryptCommand(ZFSError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Specified crypt command is invalid"
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
|
||
|
class EncryptionKeyNotLoaded(ZFSError):
|
||
|
errno = errno.EACCES
|
||
|
message = "Encryption key is not currently loaded"
|
||
|
|
||
|
|
||
|
class EncryptionKeyAlreadyLoaded(ZFSError):
|
||
|
errno = errno.EEXIST
|
||
|
message = "Encryption key is already loaded"
|
||
|
|
||
|
|
||
|
class EncryptionKeyInvalid(ZFSError):
|
||
|
errno = errno.EACCES
|
||
|
message = "Incorrect encryption key provided"
|
||
|
|
||
|
|
||
|
class ZCPError(ZFSError):
|
||
|
errno = None
|
||
|
message = None
|
||
|
|
||
|
|
||
|
class ZCPSyntaxError(ZCPError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Channel program contains syntax errors"
|
||
|
|
||
|
def __init__(self, details):
|
||
|
self.details = details
|
||
|
|
||
|
|
||
|
class ZCPRuntimeError(ZCPError):
|
||
|
errno = errno.ECHRNG
|
||
|
message = "Channel programs encountered a runtime error"
|
||
|
|
||
|
def __init__(self, details):
|
||
|
self.details = details
|
||
|
|
||
|
|
||
|
class ZCPLimitInvalid(ZCPError):
|
||
|
errno = errno.EINVAL
|
||
|
message = "Channel program called with invalid limits"
|
||
|
|
||
|
|
||
|
class ZCPTimeout(ZCPError):
|
||
|
errno = errno.ETIME
|
||
|
message = "Channel program timed out"
|
||
|
|
||
|
|
||
|
class ZCPSpaceError(ZCPError):
|
||
|
errno = errno.ENOSPC
|
||
|
message = "Channel program exhausted the memory limit"
|
||
|
|
||
|
|
||
|
class ZCPMemoryError(ZCPError):
|
||
|
errno = errno.ENOMEM
|
||
|
message = "Channel program return value too large"
|
||
|
|
||
|
|
||
|
class ZCPPermissionError(ZCPError):
|
||
|
errno = errno.EPERM
|
||
|
message = "Channel programs must be run as root"
|
||
|
|
||
|
|
||
|
class CheckpointExists(ZFSError):
|
||
|
errno = ZFS_ERR_CHECKPOINT_EXISTS
|
||
|
message = "Pool already has a checkpoint"
|
||
|
|
||
|
|
||
|
class CheckpointNotFound(ZFSError):
|
||
|
errno = ZFS_ERR_NO_CHECKPOINT
|
||
|
message = "Pool does not have a checkpoint"
|
||
|
|
||
|
|
||
|
class CheckpointDiscarding(ZFSError):
|
||
|
errno = ZFS_ERR_DISCARDING_CHECKPOINT
|
||
|
message = "Pool checkpoint is being discarded"
|
||
|
|
||
|
|
||
|
class DeviceRemovalRunning(ZFSError):
|
||
|
errno = ZFS_ERR_DEVRM_IN_PROGRESS
|
||
|
message = "A vdev is currently being removed"
|
||
|
|
||
|
|
||
|
class DeviceTooBig(ZFSError):
|
||
|
errno = ZFS_ERR_VDEV_TOO_BIG
|
||
|
message = "One or more top-level vdevs exceed the maximum vdev size"
|
||
|
|
||
|
|
||
|
# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
|