| # |
| # 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. |
| # |
| |
| """ |
| Helper routines for converting ``errno`` style error codes from C functions |
| to Python exceptions defined by `libzfs_core` API. |
| |
| The conversion heavily depends on the context of the error: the attempted |
| operation and the input parameters. For this reason, there is a conversion |
| routine for each `libzfs_core` interface function. The conversion routines |
| have the return code as a parameter as well as all the parameters of the |
| corresponding interface functions. |
| |
| The parameters and exceptions are documented in the `libzfs_core` interfaces. |
| """ |
| from __future__ import absolute_import, division, print_function |
| |
| import errno |
| import re |
| import string |
| from . import exceptions as lzc_exc |
| from ._constants import ( |
| MAXNAMELEN, |
| 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 |
| ) |
| |
| |
| def lzc_create_translate_error(ret, name, ds_type, props): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.PropertyInvalid(name) |
| if ret == errno.EEXIST: |
| raise lzc_exc.FilesystemExists(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.ParentNotFound(name) |
| if ret == ZFS_ERR_WRONG_PARENT: |
| raise lzc_exc.WrongParent(_fs_name(name)) |
| raise _generic_exception(ret, name, "Failed to create filesystem") |
| |
| |
| def lzc_clone_translate_error(ret, name, origin, props): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| _validate_snap_name(origin) |
| raise lzc_exc.PropertyInvalid(name) |
| if ret == errno.EXDEV: |
| raise lzc_exc.PoolsDiffer(name) |
| if ret == errno.EEXIST: |
| raise lzc_exc.FilesystemExists(name) |
| if ret == errno.ENOENT: |
| if not _is_valid_snap_name(origin): |
| raise lzc_exc.SnapshotNameInvalid(origin) |
| raise lzc_exc.DatasetNotFound(name) |
| raise _generic_exception(ret, name, "Failed to create clone") |
| |
| |
| def lzc_rollback_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.ESRCH: |
| raise lzc_exc.SnapshotNotFound(name) |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.NameInvalid(name) |
| if ret == errno.ENOENT: |
| if not _is_valid_fs_name(name): |
| raise lzc_exc.NameInvalid(name) |
| else: |
| raise lzc_exc.FilesystemNotFound(name) |
| raise _generic_exception(ret, name, "Failed to rollback") |
| |
| |
| def lzc_rollback_to_translate_error(ret, name, snap): |
| if ret == errno.EEXIST: |
| raise lzc_exc.SnapshotNotLatest(snap) |
| else: |
| lzc_rollback_translate_error(ret, name) |
| |
| |
| def lzc_snapshot_translate_errors(ret, errlist, snaps, props): |
| if ret == 0: |
| return |
| |
| def _map(ret, name): |
| if ret == errno.EXDEV: |
| pool_names = iter(map(_pool_name, snaps)) |
| pool_name = next(pool_names, None) |
| same_pool = all(x == pool_name for x in pool_names) |
| if same_pool: |
| return lzc_exc.DuplicateSnapshots(name) |
| else: |
| return lzc_exc.PoolsDiffer(name) |
| elif ret == errno.EINVAL: |
| if any(not _is_valid_snap_name(s) for s in snaps): |
| return lzc_exc.NameInvalid(name) |
| elif any(len(s) > MAXNAMELEN for s in snaps): |
| return lzc_exc.NameTooLong(name) |
| else: |
| return lzc_exc.PropertyInvalid(name) |
| |
| if ret == errno.EEXIST: |
| return lzc_exc.SnapshotExists(name) |
| if ret == errno.ENOENT: |
| return lzc_exc.FilesystemNotFound(name) |
| return _generic_exception(ret, name, "Failed to create snapshot") |
| |
| _handle_err_list(ret, errlist, snaps, lzc_exc.SnapshotFailure, _map) |
| |
| |
| def lzc_destroy_snaps_translate_errors(ret, errlist, snaps, defer): |
| if ret == 0: |
| return |
| |
| def _map(ret, name): |
| if ret == errno.EEXIST: |
| return lzc_exc.SnapshotIsCloned(name) |
| if ret == errno.ENOENT: |
| return lzc_exc.PoolNotFound(name) |
| if ret == errno.EBUSY: |
| return lzc_exc.SnapshotIsHeld(name) |
| return _generic_exception(ret, name, "Failed to destroy snapshot") |
| |
| _handle_err_list( |
| ret, errlist, snaps, lzc_exc.SnapshotDestructionFailure, _map) |
| |
| |
| def lzc_bookmark_translate_errors(ret, errlist, bookmarks): |
| if ret == 0: |
| return |
| |
| def _map(ret, name): |
| if ret == errno.EINVAL: |
| if name: |
| snap = bookmarks[name] |
| pool_names = map(_pool_name, bookmarks.keys()) |
| if not _is_valid_bmark_name(name): |
| return lzc_exc.BookmarkNameInvalid(name) |
| elif not _is_valid_snap_name(snap): |
| return lzc_exc.SnapshotNameInvalid(snap) |
| elif _fs_name(name) != _fs_name(snap): |
| return lzc_exc.BookmarkMismatch(name) |
| elif any(x != _pool_name(name) for x in pool_names): |
| return lzc_exc.PoolsDiffer(name) |
| else: |
| invalid_names = [ |
| b for b in bookmarks.keys() if not _is_valid_bmark_name(b)] |
| if invalid_names: |
| return lzc_exc.BookmarkNameInvalid(invalid_names[0]) |
| if ret == errno.EEXIST: |
| return lzc_exc.BookmarkExists(name) |
| if ret == errno.ENOENT: |
| return lzc_exc.SnapshotNotFound(name) |
| if ret == errno.ENOTSUP: |
| return lzc_exc.BookmarkNotSupported(name) |
| return _generic_exception(ret, name, "Failed to create bookmark") |
| |
| _handle_err_list( |
| ret, errlist, bookmarks.keys(), lzc_exc.BookmarkFailure, _map) |
| |
| |
| def lzc_get_bookmarks_translate_error(ret, fsname, props): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(fsname) |
| raise _generic_exception(ret, fsname, "Failed to list bookmarks") |
| |
| |
| def lzc_destroy_bookmarks_translate_errors(ret, errlist, bookmarks): |
| if ret == 0: |
| return |
| |
| def _map(ret, name): |
| if ret == errno.EINVAL: |
| return lzc_exc.NameInvalid(name) |
| return _generic_exception(ret, name, "Failed to destroy bookmark") |
| |
| _handle_err_list( |
| ret, errlist, bookmarks, lzc_exc.BookmarkDestructionFailure, _map) |
| |
| |
| def lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap): |
| if ret == 0: |
| return |
| if ret == errno.EXDEV and firstsnap is not None: |
| if _pool_name(firstsnap) != _pool_name(lastsnap): |
| raise lzc_exc.PoolsDiffer(lastsnap) |
| else: |
| raise lzc_exc.SnapshotMismatch(lastsnap) |
| if ret == errno.EINVAL: |
| if not _is_valid_snap_name(firstsnap): |
| raise lzc_exc.NameInvalid(firstsnap) |
| elif not _is_valid_snap_name(lastsnap): |
| raise lzc_exc.NameInvalid(lastsnap) |
| elif len(firstsnap) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(firstsnap) |
| elif len(lastsnap) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(lastsnap) |
| elif _pool_name(firstsnap) != _pool_name(lastsnap): |
| raise lzc_exc.PoolsDiffer(lastsnap) |
| else: |
| raise lzc_exc.SnapshotMismatch(lastsnap) |
| if ret == errno.ENOENT: |
| raise lzc_exc.SnapshotNotFound(lastsnap) |
| raise _generic_exception( |
| ret, lastsnap, "Failed to calculate space used by range of snapshots") |
| |
| |
| def lzc_hold_translate_errors(ret, errlist, holds, fd): |
| if ret == 0: |
| return |
| |
| def _map(ret, name): |
| if ret == errno.EXDEV: |
| return lzc_exc.PoolsDiffer(name) |
| elif ret == errno.EINVAL: |
| if name: |
| pool_names = map(_pool_name, holds.keys()) |
| if not _is_valid_snap_name(name): |
| return lzc_exc.NameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| return lzc_exc.NameTooLong(name) |
| elif any(x != _pool_name(name) for x in pool_names): |
| return lzc_exc.PoolsDiffer(name) |
| else: |
| invalid_names = [ |
| b for b in holds.keys() if not _is_valid_snap_name(b)] |
| if invalid_names: |
| return lzc_exc.NameInvalid(invalid_names[0]) |
| fs_name = None |
| hold_name = None |
| pool_name = None |
| if name is not None: |
| fs_name = _fs_name(name) |
| pool_name = _pool_name(name) |
| hold_name = holds[name] |
| if ret == errno.ENOENT: |
| return lzc_exc.FilesystemNotFound(fs_name) |
| if ret == errno.EEXIST: |
| return lzc_exc.HoldExists(name) |
| if ret == errno.E2BIG: |
| return lzc_exc.NameTooLong(hold_name) |
| if ret == errno.ENOTSUP: |
| return lzc_exc.FeatureNotSupported(pool_name) |
| return _generic_exception(ret, name, "Failed to hold snapshot") |
| |
| if ret == errno.EBADF: |
| raise lzc_exc.BadHoldCleanupFD() |
| _handle_err_list(ret, errlist, holds.keys(), lzc_exc.HoldFailure, _map) |
| |
| |
| def lzc_release_translate_errors(ret, errlist, holds): |
| if ret == 0: |
| return |
| for snap in holds: |
| hold_list = holds[snap] |
| if not isinstance(hold_list, list): |
| raise lzc_exc.TypeError('holds must be in a list') |
| |
| def _map(ret, name): |
| if ret == errno.EXDEV: |
| return lzc_exc.PoolsDiffer(name) |
| elif ret == errno.EINVAL: |
| if name: |
| pool_names = map(_pool_name, holds.keys()) |
| if not _is_valid_snap_name(name): |
| return lzc_exc.NameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| return lzc_exc.NameTooLong(name) |
| elif any(x != _pool_name(name) for x in pool_names): |
| return lzc_exc.PoolsDiffer(name) |
| else: |
| invalid_names = [ |
| b for b in holds.keys() if not _is_valid_snap_name(b)] |
| if invalid_names: |
| return lzc_exc.NameInvalid(invalid_names[0]) |
| elif ret == errno.ENOENT: |
| return lzc_exc.HoldNotFound(name) |
| elif ret == errno.E2BIG: |
| tag_list = holds[name] |
| too_long_tags = [t for t in tag_list if len(t) > MAXNAMELEN] |
| return lzc_exc.NameTooLong(too_long_tags[0]) |
| elif ret == errno.ENOTSUP: |
| pool_name = None |
| if name is not None: |
| pool_name = _pool_name(name) |
| return lzc_exc.FeatureNotSupported(pool_name) |
| else: |
| return _generic_exception( |
| ret, name, "Failed to release snapshot hold") |
| |
| _handle_err_list( |
| ret, errlist, holds.keys(), lzc_exc.HoldReleaseFailure, _map) |
| |
| |
| def lzc_get_holds_translate_error(ret, snapname): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_snap_name(snapname) |
| if ret == errno.ENOENT: |
| raise lzc_exc.SnapshotNotFound(snapname) |
| if ret == errno.ENOTSUP: |
| raise lzc_exc.FeatureNotSupported(_pool_name(snapname)) |
| raise _generic_exception(ret, snapname, "Failed to get holds on snapshot") |
| |
| |
| def lzc_send_translate_error(ret, snapname, fromsnap, fd, flags): |
| if ret == 0: |
| return |
| if ret == errno.EXDEV and fromsnap is not None: |
| if _pool_name(fromsnap) != _pool_name(snapname): |
| raise lzc_exc.PoolsDiffer(snapname) |
| else: |
| raise lzc_exc.SnapshotMismatch(snapname) |
| elif ret == errno.EINVAL: |
| if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and |
| not _is_valid_bmark_name(fromsnap)): |
| raise lzc_exc.NameInvalid(fromsnap) |
| elif (not _is_valid_snap_name(snapname) and |
| not _is_valid_fs_name(snapname)): |
| raise lzc_exc.NameInvalid(snapname) |
| elif fromsnap is not None and len(fromsnap) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(fromsnap) |
| elif len(snapname) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(snapname) |
| elif (fromsnap is not None and |
| _pool_name(fromsnap) != _pool_name(snapname)): |
| raise lzc_exc.PoolsDiffer(snapname) |
| elif ret == errno.ENOENT: |
| if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and |
| not _is_valid_bmark_name(fromsnap)): |
| raise lzc_exc.NameInvalid(fromsnap) |
| raise lzc_exc.SnapshotNotFound(snapname) |
| elif ret == errno.ENAMETOOLONG: |
| if fromsnap is not None and len(fromsnap) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(fromsnap) |
| else: |
| raise lzc_exc.NameTooLong(snapname) |
| raise lzc_exc.StreamIOError(ret) |
| |
| |
| def lzc_send_space_translate_error(ret, snapname, fromsnap): |
| if ret == 0: |
| return |
| if ret == errno.EXDEV and fromsnap is not None: |
| if _pool_name(fromsnap) != _pool_name(snapname): |
| raise lzc_exc.PoolsDiffer(snapname) |
| else: |
| raise lzc_exc.SnapshotMismatch(snapname) |
| elif ret == errno.EINVAL: |
| if fromsnap is not None and not _is_valid_snap_name(fromsnap): |
| raise lzc_exc.NameInvalid(fromsnap) |
| elif not _is_valid_snap_name(snapname): |
| raise lzc_exc.NameInvalid(snapname) |
| elif fromsnap is not None and len(fromsnap) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(fromsnap) |
| elif len(snapname) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(snapname) |
| elif (fromsnap is not None and |
| _pool_name(fromsnap) != _pool_name(snapname)): |
| raise lzc_exc.PoolsDiffer(snapname) |
| elif ret == errno.ENOENT and fromsnap is not None: |
| if not _is_valid_snap_name(fromsnap): |
| raise lzc_exc.NameInvalid(fromsnap) |
| if ret == errno.ENOENT: |
| raise lzc_exc.SnapshotNotFound(snapname) |
| raise _generic_exception( |
| ret, snapname, "Failed to estimate backup stream size") |
| |
| |
| def lzc_receive_translate_errors( |
| ret, snapname, fd, force, raw, resumable, embedded, origin, properrs |
| ): |
| if ret == 0: |
| if properrs is not None and len(properrs) > 0: |
| def _map(ret, name): |
| if ret == errno.EINVAL: |
| return lzc_exc.PropertyInvalid(name) |
| return _generic_exception(ret, name, "Failed to set property") |
| _handle_err_list( |
| errno.EINVAL, properrs, [snapname], |
| lzc_exc.ReceivePropertyFailure, _map) |
| else: |
| return |
| if ret == errno.EINVAL: |
| if (not _is_valid_snap_name(snapname) and |
| not _is_valid_fs_name(snapname)): |
| raise lzc_exc.NameInvalid(snapname) |
| elif len(snapname) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(snapname) |
| elif origin is not None and not _is_valid_snap_name(origin): |
| raise lzc_exc.NameInvalid(origin) |
| elif resumable: |
| raise lzc_exc.StreamFeatureInvalid() |
| elif embedded and not raw: |
| raise lzc_exc.StreamFeatureIncompatible() |
| else: |
| raise lzc_exc.BadStream() |
| if ret == errno.ENOENT: |
| if not _is_valid_snap_name(snapname): |
| raise lzc_exc.NameInvalid(snapname) |
| else: |
| raise lzc_exc.DatasetNotFound(snapname) |
| if ret == errno.EEXIST: |
| raise lzc_exc.DatasetExists(snapname) |
| if ret == errno.ENOTSUP: |
| raise lzc_exc.StreamFeatureNotSupported() |
| if ret == errno.ENODEV: |
| raise lzc_exc.StreamMismatch(_fs_name(snapname)) |
| if ret == errno.ETXTBSY: |
| raise lzc_exc.DestinationModified(_fs_name(snapname)) |
| if ret == errno.EBUSY: |
| raise lzc_exc.DatasetBusy(_fs_name(snapname)) |
| if ret == errno.ENOSPC: |
| raise lzc_exc.NoSpace(_fs_name(snapname)) |
| if ret == errno.EDQUOT: |
| raise lzc_exc.QuotaExceeded(_fs_name(snapname)) |
| if ret == errno.ENAMETOOLONG: |
| raise lzc_exc.NameTooLong(snapname) |
| if ret == errno.EROFS: |
| raise lzc_exc.ReadOnlyPool(_pool_name(snapname)) |
| if ret == errno.EAGAIN: |
| raise lzc_exc.SuspendedPool(_pool_name(snapname)) |
| if ret == errno.EBADE: # ECKSUM |
| raise lzc_exc.BadStream() |
| if ret == ZFS_ERR_WRONG_PARENT: |
| raise lzc_exc.WrongParent(_fs_name(snapname)) |
| |
| raise lzc_exc.StreamIOError(ret) |
| |
| |
| def lzc_promote_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.NotClone(name) |
| if ret == errno.ENOTSOCK: |
| raise lzc_exc.NotClone(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(name) |
| if ret == errno.EEXIST: |
| raise lzc_exc.SnapshotExists(name) |
| raise _generic_exception(ret, name, "Failed to promote dataset") |
| |
| |
| def lzc_change_key_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.PropertyInvalid(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(name) |
| if ret == errno.EACCES: |
| raise lzc_exc.EncryptionKeyNotLoaded() |
| raise _generic_exception(ret, name, "Failed to change encryption key") |
| |
| |
| def lzc_load_key_translate_error(ret, name, noop): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.PropertyInvalid(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(name) |
| if ret == errno.EACCES: |
| raise lzc_exc.EncryptionKeyInvalid() |
| if ret == errno.EEXIST: |
| raise lzc_exc.EncryptionKeyAlreadyLoaded() |
| if noop: |
| raise _generic_exception(ret, name, "Failed to load encryption key") |
| else: |
| raise _generic_exception(ret, name, "Failed to verify encryption key") |
| |
| |
| def lzc_unload_key_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.PropertyInvalid(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(name) |
| if ret == errno.EACCES: |
| raise lzc_exc.EncryptionKeyNotLoaded() |
| raise _generic_exception(ret, name, "Failed to unload encryption key") |
| |
| |
| def lzc_sync_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.PoolNotFound(name) |
| raise _generic_exception(ret, name, "Failed to sync pool") |
| |
| |
| def lzc_reopen_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.PoolNotFound(name) |
| raise _generic_exception(ret, name, "Failed to reopen pool") |
| |
| |
| def lzc_channel_program_translate_error(ret, name, error): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.PoolNotFound(name) |
| if ret == errno.ETIME: |
| raise lzc_exc.ZCPTimeout() |
| if ret == errno.ENOMEM: |
| raise lzc_exc.ZCPMemoryError() |
| if ret == errno.ENOSPC: |
| raise lzc_exc.ZCPSpaceError() |
| if ret == errno.EPERM: |
| raise lzc_exc.ZCPPermissionError() |
| if ret == errno.ECHRNG: |
| raise lzc_exc.ZCPRuntimeError(error) |
| if ret == errno.EINVAL: |
| if error is None: |
| raise lzc_exc.ZCPLimitInvalid() |
| else: |
| raise lzc_exc.ZCPSyntaxError(error) |
| raise _generic_exception(ret, name, "Failed to execute channel program") |
| |
| |
| def lzc_remap_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.DatasetNotFound(name) |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| if ret == errno.ENOTSUP: |
| return lzc_exc.FeatureNotSupported(name) |
| raise _generic_exception(ret, name, "Failed to remap dataset") |
| |
| |
| def lzc_pool_checkpoint_translate_error(ret, name, discard=False): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.PoolNotFound(name) |
| if ret == ZFS_ERR_CHECKPOINT_EXISTS: |
| raise lzc_exc.CheckpointExists() |
| if ret == ZFS_ERR_NO_CHECKPOINT: |
| raise lzc_exc.CheckpointNotFound() |
| if ret == ZFS_ERR_DISCARDING_CHECKPOINT: |
| raise lzc_exc.CheckpointDiscarding() |
| if ret == ZFS_ERR_DEVRM_IN_PROGRESS: |
| raise lzc_exc.DeviceRemovalRunning() |
| if ret == ZFS_ERR_VDEV_TOO_BIG: |
| raise lzc_exc.DeviceTooBig() |
| if discard: |
| raise _generic_exception( |
| ret, name, "Failed to discard pool checkpoint") |
| else: |
| raise _generic_exception(ret, name, "Failed to create pool checkpoint") |
| |
| |
| def lzc_pool_checkpoint_discard_translate_error(ret, name): |
| lzc_pool_checkpoint_translate_error(ret, name, discard=True) |
| |
| |
| def lzc_rename_translate_error(ret, source, target): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(source) |
| _validate_fs_name(target) |
| if _pool_name(source) != _pool_name(target): |
| raise lzc_exc.PoolsDiffer(source) |
| if ret == errno.EEXIST: |
| raise lzc_exc.FilesystemExists(target) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(source) |
| if ret == ZFS_ERR_WRONG_PARENT: |
| raise lzc_exc.WrongParent(target) |
| raise _generic_exception(ret, source, "Failed to rename dataset") |
| |
| |
| def lzc_destroy_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.FilesystemNotFound(name) |
| raise _generic_exception(ret, name, "Failed to destroy dataset") |
| |
| |
| def lzc_inherit_prop_translate_error(ret, name, prop): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise lzc_exc.PropertyInvalid(prop) |
| if ret == errno.ENOENT: |
| raise lzc_exc.DatasetNotFound(name) |
| raise _generic_exception(ret, name, "Failed to inherit a property") |
| |
| |
| def lzc_set_prop_translate_error(ret, name, prop, val): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_or_snap_name(name) |
| raise lzc_exc.PropertyInvalid(prop) |
| if ret == errno.ENOENT: |
| raise lzc_exc.DatasetNotFound(name) |
| raise _generic_exception(ret, name, "Failed to set a property") |
| |
| |
| def lzc_get_props_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_or_snap_name(name) |
| if ret == errno.ENOENT: |
| raise lzc_exc.DatasetNotFound(name) |
| raise _generic_exception(ret, name, "Failed to get properties") |
| |
| |
| def lzc_list_children_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise _generic_exception(ret, name, "Error while iterating children") |
| |
| |
| def lzc_list_snaps_translate_error(ret, name): |
| if ret == 0: |
| return |
| if ret == errno.EINVAL: |
| _validate_fs_name(name) |
| raise _generic_exception(ret, name, "Error while iterating snapshots") |
| |
| |
| def lzc_list_translate_error(ret, name, opts): |
| if ret == 0: |
| return |
| if ret == errno.ENOENT: |
| raise lzc_exc.DatasetNotFound(name) |
| if ret == errno.EINVAL: |
| _validate_fs_or_snap_name(name) |
| raise _generic_exception(ret, name, "Error obtaining a list") |
| |
| |
| def _handle_err_list(ret, errlist, names, exception, mapper): |
| ''' |
| Convert one or more errors from an operation into the requested exception. |
| |
| :param int ret: the overall return code. |
| :param errlist: the dictionary that maps entity names to their specific |
| error codes. |
| :type errlist: dict of bytes:int |
| :param names: the list of all names of the entities on which the operation |
| was attempted. |
| :param type exception: the type of the exception to raise if an error |
| occurred. The exception should be a subclass of |
| ``MultipleOperationsFailure``. |
| :param function mapper: the function that maps an error code and a name to |
| a Python exception. |
| |
| Unless ``ret`` is zero this function will raise the ``exception``. |
| If the ``errlist`` is not empty, then the compound exception will contain |
| a list of exceptions corresponding to each individual error code in the |
| ``errlist``. |
| Otherwise, the ``exception`` will contain a list with a single exception |
| corresponding to the ``ret`` value. If the ``names`` list contains only one |
| element, that is, the operation was attempted on a single entity, then the |
| name of that entity is passed to the ``mapper``. |
| If the operation was attempted on multiple entities, but the ``errlist`` |
| is empty, then we can not know which entity caused the error and, thus, |
| ``None`` is used as a name to signify that fact. |
| |
| .. note:: |
| Note that the ``errlist`` can contain a special element with a key of |
| "N_MORE_ERRORS". |
| That element means that there were too many errors to place on the |
| ``errlist``. |
| Those errors are suppressed and only their count is provided as a |
| value of the special ``N_MORE_ERRORS`` element. |
| ''' |
| if ret == 0: |
| return |
| |
| if len(errlist) == 0: |
| suppressed_count = 0 |
| names = list(zip(names, range(2))) |
| if len(names) == 1: |
| name, _ = names[0] |
| else: |
| name = None |
| errors = [mapper(ret, name)] |
| else: |
| errors = [] |
| suppressed_count = errlist.pop('N_MORE_ERRORS', 0) |
| for name in errlist: |
| err = errlist[name] |
| errors.append(mapper(err, name)) |
| |
| raise exception(errors, suppressed_count) |
| |
| |
| def _pool_name(name): |
| ''' |
| Extract a pool name from the given dataset or bookmark name. |
| |
| '/' separates dataset name components. |
| '@' separates a snapshot name from the rest of the dataset name. |
| '#' separates a bookmark name from the rest of the dataset name. |
| ''' |
| return re.split(b'[/@#]', name, 1)[0] |
| |
| |
| def _fs_name(name): |
| ''' |
| Extract a dataset name from the given snapshot or bookmark name. |
| |
| '@' separates a snapshot name from the rest of the dataset name. |
| '#' separates a bookmark name from the rest of the dataset name. |
| ''' |
| return re.split(b'[@#]', name, 1)[0] |
| |
| |
| def _is_valid_name_component(component): |
| allowed = string.ascii_letters + string.digits + u'-_.: ' |
| return component and all(x in allowed.encode() for x in component) |
| |
| |
| def _is_valid_fs_name(name): |
| return name and all(_is_valid_name_component(c) for c in name.split(b'/')) |
| |
| |
| def _is_valid_snap_name(name): |
| parts = name.split(b'@') |
| return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and |
| _is_valid_name_component(parts[1])) |
| |
| |
| def _is_valid_bmark_name(name): |
| parts = name.split(b'#') |
| return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and |
| _is_valid_name_component(parts[1])) |
| |
| |
| def _validate_fs_name(name): |
| if not _is_valid_fs_name(name): |
| raise lzc_exc.FilesystemNameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(name) |
| |
| |
| def _validate_snap_name(name): |
| if not _is_valid_snap_name(name): |
| raise lzc_exc.SnapshotNameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(name) |
| |
| |
| def _validate_bmark_name(name): |
| if not _is_valid_bmark_name(name): |
| raise lzc_exc.BookmarkNameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(name) |
| |
| |
| def _validate_fs_or_snap_name(name): |
| if not _is_valid_fs_name(name) and not _is_valid_snap_name(name): |
| raise lzc_exc.NameInvalid(name) |
| elif len(name) > MAXNAMELEN: |
| raise lzc_exc.NameTooLong(name) |
| |
| |
| def _generic_exception(err, name, message): |
| if err in _error_to_exception: |
| return _error_to_exception[err](name) |
| else: |
| return lzc_exc.ZFSGenericError(err, message, name) |
| |
| |
| _error_to_exception = {e.errno: e for e in [ |
| lzc_exc.ZIOError, |
| lzc_exc.NoSpace, |
| lzc_exc.QuotaExceeded, |
| lzc_exc.DatasetBusy, |
| lzc_exc.NameTooLong, |
| lzc_exc.ReadOnlyPool, |
| lzc_exc.SuspendedPool, |
| lzc_exc.PoolsDiffer, |
| lzc_exc.PropertyNotSupported, |
| ]} |
| |
| |
| # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 |