Adopt pyzfs from ClusterHQ

This commit introduces several changes:

 * Update LICENSE and project information

 * Give a good PEP8 talk to existing Python source code

 * Add RPM/DEB packaging for pyzfs

 * Fix some outstanding issues with the existing pyzfs code caused by
   changes in the ABI since the last time the code was updated

 * Integrate pyzfs Python unittest with the ZFS Test Suite

 * Add missing libzfs_core functions: lzc_change_key,
   lzc_channel_program, lzc_channel_program_nosync, lzc_load_key,
   lzc_receive_one, lzc_receive_resumable, lzc_receive_with_cmdprops,
   lzc_receive_with_header, lzc_reopen, lzc_send_resume, lzc_sync,
   lzc_unload_key, lzc_remap

Note: this commit slightly changes zfs_ioc_unload_key() ABI. This allow
to differentiate the case where we tried to unload a key on a
non-existing dataset (ENOENT) from the situation where a dataset has
no key loaded: this is consistent with the "change" case where trying
to zfs_ioc_change_key() from a dataset with no key results in EACCES.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #7230
This commit is contained in:
loli10K
2018-03-18 09:34:45 +01:00
committed by Brian Behlendorf
parent 6abf922574
commit 85ce3f4fd1
35 changed files with 2989 additions and 643 deletions
+4 -3
View File
@@ -1,4 +1,5 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -178,7 +179,7 @@
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
@@ -186,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 ClusterHQ
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+39
View File
@@ -0,0 +1,39 @@
EXTRA_DIST = libzfs_core setup.py README LICENSE docs
if PYZFS_ENABLED
all:
all-local:
$(PYTHON) setup.py build
#
# On Debian (Ubuntu, and other downstream distros) the install location of
# Python packages is "../dist-packages" instead of "../site-packages" [1].
# The install location used by "$(PYTHON) setup.py install" must match the
# location specified in the ZFS specfile (RPM macro "%{python_sitelib}") to
# avoid errors during the rpmbuild process.
# However we cannot pass "--install-layout=deb" to the setup script here because
# it is not supported on RPM-based distros; we use the combination of
# "--prefix", "--root" and "--install-lib" parameters instead which should work
# on every supported system.
#
# [1] https://wiki.debian.org/Python#Deviations_from_upstream
#
# Using "--no-compile" will not generate .pyc files which, in turn, will not be
# packaged: this could result in failures during the uninstall phase if these
# files are later created by manually loading the Python modules.
#
install-exec-local:
$(PYTHON) $(srcdir)/setup.py install \
--prefix $(prefix) \
--root $(DESTDIR)/ \
--install-lib $(pythondir) \
--single-version-externally-managed \
--verbose
clean: clean-local
clean-local:
check-local: all
endif
+1 -1
View File
@@ -25,4 +25,4 @@ a temporary directory specified by, for instance, TMP environment
variable on a memory backed filesystem.
Package documentation: http://pyzfs.readthedocs.org
Package development: https://github.com/ClusterHQ/pyzfs
Package development: https://github.com/zfsonlinux/zfs
+1 -1
View File
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# flake8: noqa
#
# pyzfs documentation build configuration file, created by
# sphinx-quickstart on Mon Apr 6 23:48:40 2015.
@@ -14,7 +15,6 @@
import sys
import os
import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
+85 -36
View File
@@ -1,4 +1,19 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
'''
Python wrappers for **libzfs_core** library.
@@ -17,7 +32,7 @@ of the error codes to the exceptions by interpreting a context
in which the error code is produced.
To submit an issue or contribute to development of this package
please visit its `GitHub repository <https://github.com/ClusterHQ/pyzfs>`_.
please visit its `GitHub repository <https://github.com/zfsonlinux/zfs>`_.
.. data:: MAXNAMELEN
@@ -26,36 +41,53 @@ please visit its `GitHub repository <https://github.com/ClusterHQ/pyzfs>`_.
from ._constants import (
MAXNAMELEN,
ZCP_DEFAULT_INSTRLIMIT,
ZCP_DEFAULT_MEMLIMIT,
WRAPPING_KEY_LEN,
zfs_key_location,
zfs_keyformat,
zio_encrypt
)
from ._libzfs_core import (
lzc_create,
lzc_bookmark,
lzc_change_key,
lzc_channel_program,
lzc_channel_program_nosync,
lzc_clone,
lzc_create,
lzc_destroy_bookmarks,
lzc_destroy_snaps,
lzc_exists,
lzc_get_bookmarks,
lzc_get_holds,
lzc_hold,
lzc_load_key,
lzc_promote,
lzc_receive,
lzc_receive_one,
lzc_receive_resumable,
lzc_receive_with_cmdprops,
lzc_receive_with_header,
lzc_release,
lzc_reopen,
lzc_rollback,
lzc_rollback_to,
lzc_snapshot,
lzc_snap,
lzc_destroy_snaps,
lzc_bookmark,
lzc_get_bookmarks,
lzc_destroy_bookmarks,
lzc_snaprange_space,
lzc_hold,
lzc_release,
lzc_get_holds,
lzc_send,
lzc_send_resume,
lzc_send_space,
lzc_receive,
lzc_receive_with_header,
lzc_recv,
lzc_exists,
lzc_snaprange_space,
lzc_snapshot,
lzc_sync,
lzc_unload_key,
is_supported,
lzc_promote,
lzc_recv,
lzc_snap,
lzc_rename,
lzc_destroy,
lzc_inherit_prop,
lzc_set_prop,
lzc_get_props,
lzc_set_props,
lzc_list_children,
lzc_list_snaps,
receive_header,
@@ -65,33 +97,50 @@ __all__ = [
'ctypes',
'exceptions',
'MAXNAMELEN',
'lzc_create',
'ZCP_DEFAULT_INSTRLIMIT',
'ZCP_DEFAULT_MEMLIMIT',
'WRAPPING_KEY_LEN',
'zfs_key_location',
'zfs_keyformat',
'zio_encrypt',
'lzc_bookmark',
'lzc_change_key',
'lzc_channel_program',
'lzc_channel_program_nosync',
'lzc_clone',
'lzc_create',
'lzc_destroy_bookmarks',
'lzc_destroy_snaps',
'lzc_exists',
'lzc_get_bookmarks',
'lzc_get_holds',
'lzc_hold',
'lzc_load_key',
'lzc_promote',
'lzc_receive',
'lzc_receive_one',
'lzc_receive_resumable',
'lzc_receive_with_cmdprops',
'lzc_receive_with_header',
'lzc_release',
'lzc_reopen',
'lzc_rollback',
'lzc_rollback_to',
'lzc_snapshot',
'lzc_snap',
'lzc_destroy_snaps',
'lzc_bookmark',
'lzc_get_bookmarks',
'lzc_destroy_bookmarks',
'lzc_snaprange_space',
'lzc_hold',
'lzc_release',
'lzc_get_holds',
'lzc_send',
'lzc_send_resume',
'lzc_send_space',
'lzc_receive',
'lzc_receive_with_header',
'lzc_recv',
'lzc_exists',
'lzc_snaprange_space',
'lzc_snapshot',
'lzc_sync',
'lzc_unload_key',
'is_supported',
'lzc_promote',
'lzc_recv',
'lzc_snap',
'lzc_rename',
'lzc_destroy',
'lzc_inherit_prop',
'lzc_set_prop',
'lzc_get_props',
'lzc_set_props',
'lzc_list_children',
'lzc_list_snaps',
'receive_header',
+52 -1
View File
@@ -1,10 +1,61 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
Important `libzfs_core` constants.
"""
# https://stackoverflow.com/a/1695250
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
#: Maximum length of any ZFS name.
MAXNAMELEN = 255
#: Default channel program limits
ZCP_DEFAULT_INSTRLIMIT = 10 * 1000 * 1000
ZCP_DEFAULT_MEMLIMIT = 10 * 1024 * 1024
#: Encryption wrapping key length
WRAPPING_KEY_LEN = 32
#: Encryption key location enum
zfs_key_location = enum(
'ZFS_KEYLOCATION_NONE',
'ZFS_KEYLOCATION_PROMPT',
'ZFS_KEYLOCATION_URI'
)
#: Encryption key format enum
zfs_keyformat = enum(
'ZFS_KEYFORMAT_NONE',
'ZFS_KEYFORMAT_RAW',
'ZFS_KEYFORMAT_HEX',
'ZFS_KEYFORMAT_PASSPHRASE'
)
# Encryption algorithms enum
zio_encrypt = enum(
'ZIO_CRYPT_INHERIT',
'ZIO_CRYPT_ON',
'ZIO_CRYPT_OFF',
'ZIO_CRYPT_AES_128_CCM',
'ZIO_CRYPT_AES_192_CCM',
'ZIO_CRYPT_AES_256_CCM',
'ZIO_CRYPT_AES_128_GCM',
'ZIO_CRYPT_AES_192_GCM',
'ZIO_CRYPT_AES_256_GCM'
)
# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
+194 -44
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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
@@ -24,9 +38,9 @@ def lzc_create_translate_error(ret, name, ds_type, props):
if ret == 0:
return
if ret == errno.EINVAL:
# XXX: should raise lzc_exc.WrongParent if parent is ZVOL
_validate_fs_name(name)
raise lzc_exc.PropertyInvalid(name)
if ret == errno.EEXIST:
raise lzc_exc.FilesystemExists(name)
if ret == errno.ENOENT:
@@ -40,11 +54,9 @@ def lzc_clone_translate_error(ret, name, origin, props):
if ret == errno.EINVAL:
_validate_fs_name(name)
_validate_snap_name(origin)
if _pool_name(name) != _pool_name(origin):
raise lzc_exc.PoolsDiffer(name) # see https://www.illumos.org/issues/5824
else:
raise lzc_exc.PropertyInvalid(name)
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:
@@ -57,9 +69,11 @@ def lzc_clone_translate_error(ret, name, origin, props):
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.SnapshotNotFound(name)
raise lzc_exc.NameInvalid(name)
if ret == errno.ENOENT:
if not _is_valid_fs_name(name):
raise lzc_exc.NameInvalid(name)
@@ -67,12 +81,13 @@ def lzc_rollback_translate_error(ret, name):
raise lzc_exc.FilesystemNotFound(name)
raise _generic_exception(ret, name, "Failed to rollback")
def lzc_rollback_to_translate_error(ret, name, snap):
if ret == 0:
return
if ret == errno.EEXIST:
raise lzc_exc.SnapshotNotLatest(snap)
raise _generic_exception(ret, name, "Failed to rollback")
else:
lzc_rollback_translate_error(ret, name)
def lzc_snapshot_translate_errors(ret, errlist, snaps, props):
if ret == 0:
@@ -116,7 +131,8 @@ def lzc_destroy_snaps_translate_errors(ret, errlist, snaps, defer):
return lzc_exc.SnapshotIsHeld(name)
return _generic_exception(ret, name, "Failed to destroy snapshot")
_handle_err_list(ret, errlist, snaps, lzc_exc.SnapshotDestructionFailure, _map)
_handle_err_list(
ret, errlist, snaps, lzc_exc.SnapshotDestructionFailure, _map)
def lzc_bookmark_translate_errors(ret, errlist, bookmarks):
@@ -137,7 +153,8 @@ def lzc_bookmark_translate_errors(ret, errlist, bookmarks):
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)]
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:
@@ -148,7 +165,8 @@ def lzc_bookmark_translate_errors(ret, errlist, bookmarks):
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)
_handle_err_list(
ret, errlist, bookmarks.keys(), lzc_exc.BookmarkFailure, _map)
def lzc_get_bookmarks_translate_error(ret, fsname, props):
@@ -168,7 +186,8 @@ def lzc_destroy_bookmarks_translate_errors(ret, errlist, bookmarks):
return lzc_exc.NameInvalid(name)
return _generic_exception(ret, name, "Failed to destroy bookmark")
_handle_err_list(ret, errlist, bookmarks, lzc_exc.BookmarkDestructionFailure, _map)
_handle_err_list(
ret, errlist, bookmarks, lzc_exc.BookmarkDestructionFailure, _map)
def lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap):
@@ -194,7 +213,8 @@ def lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap):
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")
raise _generic_exception(
ret, lastsnap, "Failed to calculate space used by range of snapshots")
def lzc_hold_translate_errors(ret, errlist, holds, fd):
@@ -214,7 +234,8 @@ def lzc_hold_translate_errors(ret, errlist, holds, fd):
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)]
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
@@ -259,7 +280,8 @@ def lzc_release_translate_errors(ret, errlist, holds):
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)]
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:
@@ -274,9 +296,11 @@ def lzc_release_translate_errors(ret, errlist, holds):
pool_name = _pool_name(name)
return lzc_exc.FeatureNotSupported(pool_name)
else:
return _generic_exception(ret, name, "Failed to release snapshot hold")
return _generic_exception(
ret, name, "Failed to release snapshot hold")
_handle_err_list(ret, errlist, holds.keys(), lzc_exc.HoldReleaseFailure, _map)
_handle_err_list(
ret, errlist, holds.keys(), lzc_exc.HoldReleaseFailure, _map)
def lzc_get_holds_translate_error(ret, snapname):
@@ -303,13 +327,15 @@ def lzc_send_translate_error(ret, snapname, fromsnap, fd, flags):
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):
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):
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
@@ -341,26 +367,44 @@ def lzc_send_space_translate_error(ret, snapname, fromsnap):
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):
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")
raise _generic_exception(
ret, snapname, "Failed to estimate backup stream size")
def lzc_receive_translate_error(ret, snapname, fd, force, origin, props):
def lzc_receive_translate_errors(
ret, snapname, fd, force, raw, resumable, embedded, origin, properrs
):
if ret == 0:
return
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):
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:
@@ -388,6 +432,8 @@ def lzc_receive_translate_error(ret, snapname, fd, force, origin, props):
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()
raise lzc_exc.StreamIOError(ret)
@@ -407,6 +453,101 @@ def lzc_promote_translate_error(ret, 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_rename_translate_error(ret, source, target):
if ret == 0:
return
@@ -495,28 +636,36 @@ 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.
: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.
: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
thati fact.
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.
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
@@ -613,6 +762,7 @@ def _generic_exception(err, name, message):
else:
return lzc_exc.ZFSGenericError(err, message, name)
_error_to_exception = {e.errno: e for e in [
lzc_exc.ZIOError,
lzc_exc.NoSpace,
File diff suppressed because it is too large Load Diff
+69 -34
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
nvlist_in and nvlist_out provide support for converting between
@@ -19,14 +33,17 @@ will follow the same format.
Format:
- keys are always byte strings
- a value can be None in which case it represents boolean truth by its mere presence
- a value can be None in which case it represents boolean truth by its mere
presence
- a value can be a bool
- a value can be a byte string
- a value can be an integer
- a value can be a CFFI CData object representing one of the following C types:
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, boolean_t, uchar_t
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t,
boolean_t, uchar_t
- a value can be a dictionary that recursively adheres to this format
- a value can be a list of bools, byte strings, integers or CData objects of types specified above
- a value can be a list of bools, byte strings, integers or CData objects of
types specified above
- a value can be a list of dictionaries that adhere to this format
- all elements of a list value must be of the same type
"""
@@ -70,7 +87,8 @@ def nvlist_out(props):
and also populates the 'props' dictionary with data from the nvlist_t
upon leaving the 'with' block.
:param dict props: the dictionary to be populated with data from the nvlist.
:param dict props: the dictionary to be populated with data from the
nvlist.
:return: an FFI CData object representing the pointer to nvlist_t pointer.
:rtype: CData
"""
@@ -87,39 +105,58 @@ def nvlist_out(props):
nvlistp[0] = _ffi.NULL
def packed_nvlist_out(packed_nvlist, packed_size):
"""
This function converts a packed C nvlist_t to a python dictionary and
provides automatic memory management for the former.
:param bytes packed_nvlist: packed nvlist_t.
:param int packed_size: nvlist_t packed size.
:return: an `dict` of values representing the data containted by nvlist_t.
:rtype: dict
"""
props = {}
with nvlist_out(props) as nvp:
ret = _lib.nvlist_unpack(packed_nvlist, packed_size, nvp, 0)
if ret != 0:
raise MemoryError('nvlist_unpack failed')
return props
_TypeInfo = namedtuple('_TypeInfo', ['suffix', 'ctype', 'is_array', 'convert'])
def _type_info(typeid):
return {
_lib.DATA_TYPE_BOOLEAN: _TypeInfo(None, None, None, None),
_lib.DATA_TYPE_BOOLEAN_VALUE: _TypeInfo("boolean_value", "boolean_t *", False, bool),
_lib.DATA_TYPE_BYTE: _TypeInfo("byte", "uchar_t *", False, int),
_lib.DATA_TYPE_INT8: _TypeInfo("int8", "int8_t *", False, int),
_lib.DATA_TYPE_UINT8: _TypeInfo("uint8", "uint8_t *", False, int),
_lib.DATA_TYPE_INT16: _TypeInfo("int16", "int16_t *", False, int),
_lib.DATA_TYPE_UINT16: _TypeInfo("uint16", "uint16_t *", False, int),
_lib.DATA_TYPE_INT32: _TypeInfo("int32", "int32_t *", False, int),
_lib.DATA_TYPE_UINT32: _TypeInfo("uint32", "uint32_t *", False, int),
_lib.DATA_TYPE_INT64: _TypeInfo("int64", "int64_t *", False, int),
_lib.DATA_TYPE_UINT64: _TypeInfo("uint64", "uint64_t *", False, int),
_lib.DATA_TYPE_STRING: _TypeInfo("string", "char **", False, _ffi.string),
_lib.DATA_TYPE_NVLIST: _TypeInfo("nvlist", "nvlist_t **", False, lambda x: _nvlist_to_dict(x, {})),
_lib.DATA_TYPE_BOOLEAN_ARRAY: _TypeInfo("boolean_array", "boolean_t **", True, bool),
_lib.DATA_TYPE_BOOLEAN_VALUE: _TypeInfo("boolean_value", "boolean_t *", False, bool), # noqa: E501
_lib.DATA_TYPE_BYTE: _TypeInfo("byte", "uchar_t *", False, int), # noqa: E501
_lib.DATA_TYPE_INT8: _TypeInfo("int8", "int8_t *", False, int), # noqa: E501
_lib.DATA_TYPE_UINT8: _TypeInfo("uint8", "uint8_t *", False, int), # noqa: E501
_lib.DATA_TYPE_INT16: _TypeInfo("int16", "int16_t *", False, int), # noqa: E501
_lib.DATA_TYPE_UINT16: _TypeInfo("uint16", "uint16_t *", False, int), # noqa: E501
_lib.DATA_TYPE_INT32: _TypeInfo("int32", "int32_t *", False, int), # noqa: E501
_lib.DATA_TYPE_UINT32: _TypeInfo("uint32", "uint32_t *", False, int), # noqa: E501
_lib.DATA_TYPE_INT64: _TypeInfo("int64", "int64_t *", False, int), # noqa: E501
_lib.DATA_TYPE_UINT64: _TypeInfo("uint64", "uint64_t *", False, int), # noqa: E501
_lib.DATA_TYPE_STRING: _TypeInfo("string", "char **", False, _ffi.string), # noqa: E501
_lib.DATA_TYPE_NVLIST: _TypeInfo("nvlist", "nvlist_t **", False, lambda x: _nvlist_to_dict(x, {})), # noqa: E501
_lib.DATA_TYPE_BOOLEAN_ARRAY: _TypeInfo("boolean_array", "boolean_t **", True, bool), # noqa: E501
# XXX use bytearray ?
_lib.DATA_TYPE_BYTE_ARRAY: _TypeInfo("byte_array", "uchar_t **", True, int),
_lib.DATA_TYPE_INT8_ARRAY: _TypeInfo("int8_array", "int8_t **", True, int),
_lib.DATA_TYPE_UINT8_ARRAY: _TypeInfo("uint8_array", "uint8_t **", True, int),
_lib.DATA_TYPE_INT16_ARRAY: _TypeInfo("int16_array", "int16_t **", True, int),
_lib.DATA_TYPE_UINT16_ARRAY: _TypeInfo("uint16_array", "uint16_t **", True, int),
_lib.DATA_TYPE_INT32_ARRAY: _TypeInfo("int32_array", "int32_t **", True, int),
_lib.DATA_TYPE_UINT32_ARRAY: _TypeInfo("uint32_array", "uint32_t **", True, int),
_lib.DATA_TYPE_INT64_ARRAY: _TypeInfo("int64_array", "int64_t **", True, int),
_lib.DATA_TYPE_UINT64_ARRAY: _TypeInfo("uint64_array", "uint64_t **", True, int),
_lib.DATA_TYPE_STRING_ARRAY: _TypeInfo("string_array", "char ***", True, _ffi.string),
_lib.DATA_TYPE_NVLIST_ARRAY: _TypeInfo("nvlist_array", "nvlist_t ***", True, lambda x: _nvlist_to_dict(x, {})),
_lib.DATA_TYPE_BYTE_ARRAY: _TypeInfo("byte_array", "uchar_t **", True, int), # noqa: E501
_lib.DATA_TYPE_INT8_ARRAY: _TypeInfo("int8_array", "int8_t **", True, int), # noqa: E501
_lib.DATA_TYPE_UINT8_ARRAY: _TypeInfo("uint8_array", "uint8_t **", True, int), # noqa: E501
_lib.DATA_TYPE_INT16_ARRAY: _TypeInfo("int16_array", "int16_t **", True, int), # noqa: E501
_lib.DATA_TYPE_UINT16_ARRAY: _TypeInfo("uint16_array", "uint16_t **", True, int), # noqa: E501
_lib.DATA_TYPE_INT32_ARRAY: _TypeInfo("int32_array", "int32_t **", True, int), # noqa: E501
_lib.DATA_TYPE_UINT32_ARRAY: _TypeInfo("uint32_array", "uint32_t **", True, int), # noqa: E501
_lib.DATA_TYPE_INT64_ARRAY: _TypeInfo("int64_array", "int64_t **", True, int), # noqa: E501
_lib.DATA_TYPE_UINT64_ARRAY: _TypeInfo("uint64_array", "uint64_t **", True, int), # noqa: E501
_lib.DATA_TYPE_STRING_ARRAY: _TypeInfo("string_array", "char ***", True, _ffi.string), # noqa: E501
_lib.DATA_TYPE_NVLIST_ARRAY: _TypeInfo("nvlist_array", "nvlist_t ***", True, lambda x: _nvlist_to_dict(x, {})), # noqa: E501
}[typeid]
# only integer properties need to be here
_prop_name_to_type_str = {
"rewind-request": "uint32",
@@ -180,7 +217,8 @@ def _nvlist_add_array(nvlist, key, array):
suffix = _prop_name_to_type_str.get(key, "uint64")
cfunc = getattr(_lib, "nvlist_add_%s_array" % (suffix,))
ret = cfunc(nvlist, key, array, len(array))
elif isinstance(specimen, _ffi.CData) and _ffi.typeof(specimen) in _type_to_suffix:
elif isinstance(
specimen, _ffi.CData) and _ffi.typeof(specimen) in _type_to_suffix:
suffix = _type_to_suffix[_ffi.typeof(specimen)][True]
cfunc = getattr(_lib, "nvlist_add_%s_array" % (suffix,))
ret = cfunc(nvlist, key, array, len(array))
@@ -196,10 +234,7 @@ def _nvlist_to_dict(nvlist, props):
name = _ffi.string(_lib.nvpair_name(pair))
typeid = int(_lib.nvpair_type(pair))
typeinfo = _type_info(typeid)
# XXX nvpair_type_is_array() is broken for DATA_TYPE_INT8_ARRAY at the moment
# see https://www.illumos.org/issues/5778
# is_array = bool(_lib.nvpair_type_is_array(pair))
is_array = typeinfo.is_array
is_array = bool(_lib.nvpair_type_is_array(pair))
cfunc = getattr(_lib, "nvpair_value_%s" % (typeinfo.suffix,), None)
val = None
ret = 0
+15 -1
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
The package that contains a module per each C library that
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
Python bindings for ``libnvpair``.
@@ -64,7 +78,8 @@ CDEF = """
int nvlist_add_uint64(nvlist_t *, const char *, uint64_t);
int nvlist_add_string(nvlist_t *, const char *, const char *);
int nvlist_add_nvlist(nvlist_t *, const char *, nvlist_t *);
int nvlist_add_boolean_array(nvlist_t *, const char *, boolean_t *, uint_t);
int nvlist_add_boolean_array(nvlist_t *, const char *, boolean_t *,
uint_t);
int nvlist_add_byte_array(nvlist_t *, const char *, uchar_t *, uint_t);
int nvlist_add_int8_array(nvlist_t *, const char *, int8_t *, uint_t);
int nvlist_add_uint8_array(nvlist_t *, const char *, uint8_t *, uint_t);
@@ -74,7 +89,8 @@ CDEF = """
int nvlist_add_uint32_array(nvlist_t *, const char *, uint32_t *, uint_t);
int nvlist_add_int64_array(nvlist_t *, const char *, int64_t *, uint_t);
int nvlist_add_uint64_array(nvlist_t *, const char *, uint64_t *, uint_t);
int nvlist_add_string_array(nvlist_t *, const char *, char *const *, uint_t);
int nvlist_add_string_array(nvlist_t *, const char *, char *const *,
uint_t);
int nvlist_add_nvlist_array(nvlist_t *, const char *, nvlist_t **, uint_t);
nvpair_t *nvlist_next_nvpair(nvlist_t *, nvpair_t *);
@@ -1,13 +1,30 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
Python bindings for ``libzfs_core``.
"""
CDEF = """
enum lzc_send_flags {
LZC_SEND_FLAG_EMBED_DATA = 1,
LZC_SEND_FLAG_LARGE_BLOCK = 2
LZC_SEND_FLAG_EMBED_DATA = 1,
LZC_SEND_FLAG_LARGE_BLOCK = 2,
LZC_SEND_FLAG_COMPRESS = 4,
LZC_SEND_FLAG_RAW = 8
};
typedef enum {
@@ -34,7 +51,7 @@ CDEF = """
};
typedef struct zio_cksum {
uint64_t zc_word[4];
uint64_t zc_word[4];
} zio_cksum_t;
typedef struct dmu_replay_record {
@@ -54,35 +71,63 @@ CDEF = """
} drr_u;
} dmu_replay_record_t;
typedef enum {
DCP_CMD_NONE,
DCP_CMD_RAW_RECV,
DCP_CMD_NEW_KEY,
DCP_CMD_INHERIT,
DCP_CMD_FORCE_NEW_KEY,
DCP_CMD_FORCE_INHERIT
} dcp_cmd_t;
int libzfs_core_init(void);
void libzfs_core_fini(void);
int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **);
int lzc_create(const char *, dmu_objset_type_t, nvlist_t *);
int lzc_clone(const char *, const char *, nvlist_t *);
int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **);
int lzc_bookmark(nvlist_t *, nvlist_t **);
int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **);
int lzc_change_key(const char *, uint64_t, nvlist_t *, uint8_t *, uint_t);
int lzc_channel_program(const char *, const char *, uint64_t, uint64_t,
nvlist_t *, nvlist_t **);
int lzc_channel_program_nosync(const char *, const char *, uint64_t,
uint64_t, nvlist_t *, nvlist_t **);
int lzc_clone(const char *, const char *, nvlist_t *);
int lzc_create(const char *, dmu_objset_type_t, nvlist_t *, uint8_t *,
uint_t);
int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **);
int lzc_snaprange_space(const char *, const char *, uint64_t *);
int lzc_hold(nvlist_t *, int, nvlist_t **);
int lzc_release(nvlist_t *, nvlist_t **);
int lzc_get_holds(const char *, nvlist_t **);
int lzc_send(const char *, const char *, int, enum lzc_send_flags);
int lzc_send_space(const char *, const char *, enum lzc_send_flags, uint64_t *);
int lzc_receive(const char *, nvlist_t *, const char *, boolean_t, int);
int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
boolean_t, int, const struct dmu_replay_record *);
int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **);
boolean_t lzc_exists(const char *);
int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **);
int lzc_get_holds(const char *, nvlist_t **);
int lzc_hold(nvlist_t *, int, nvlist_t **);
int lzc_load_key(const char *, boolean_t, uint8_t *, uint_t);
int lzc_promote(const char *, nvlist_t *, nvlist_t **);
int lzc_receive(const char *, nvlist_t *, const char *, boolean_t,
boolean_t, int);
int lzc_receive_one(const char *, nvlist_t *, const char *, boolean_t,
boolean_t, boolean_t, int, const dmu_replay_record_t *, int,
uint64_t *, uint64_t *, uint64_t *, nvlist_t **);
int lzc_receive_resumable(const char *, nvlist_t *, const char *,
boolean_t, boolean_t, int);
int lzc_receive_with_cmdprops(const char *, nvlist_t *, nvlist_t *,
const char *, boolean_t, boolean_t, boolean_t, int,
const dmu_replay_record_t *, int, uint64_t *, uint64_t *, uint64_t *,
nvlist_t **);
int lzc_receive_with_header(const char *, nvlist_t *, const char *,
boolean_t, boolean_t, boolean_t, int, const dmu_replay_record_t *);
int lzc_release(nvlist_t *, nvlist_t **);
int lzc_reopen(const char *, boolean_t);
int lzc_rollback(const char *, char *, int);
int lzc_rollback_to(const char *, const char *);
int lzc_send(const char *, const char *, int, enum lzc_send_flags);
int lzc_send_resume(const char *, const char *, int, enum lzc_send_flags,
uint64_t, uint64_t);
int lzc_send_space(const char *, const char *, enum lzc_send_flags,
uint64_t *);
int lzc_snaprange_space(const char *, const char *, uint64_t *);
int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **);
int lzc_sync(const char *, nvlist_t *, nvlist_t **);
int lzc_unload_key(const char *);
int lzc_remap(const char *);
int lzc_promote(const char *, nvlist_t *, nvlist_t **);
int lzc_rename(const char *, const char *, nvlist_t *, char **);
int lzc_destroy_one(const char *fsname, nvlist_t *);
int lzc_inherit(const char *fsname, const char *name, nvlist_t *);
+25 -11
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
Utility functions for casting to a specific C type.
@@ -25,16 +39,16 @@ def _ffi_cast(type_name):
return _func
uint8_t = _ffi_cast('uint8_t')
int8_t = _ffi_cast('int8_t')
uint16_t = _ffi_cast('uint16_t')
int16_t = _ffi_cast('int16_t')
uint32_t = _ffi_cast('uint32_t')
int32_t = _ffi_cast('int32_t')
uint64_t = _ffi_cast('uint64_t')
int64_t = _ffi_cast('int64_t')
boolean_t = _ffi_cast('boolean_t')
uchar_t = _ffi_cast('uchar_t')
uint8_t = _ffi_cast('uint8_t')
int8_t = _ffi_cast('int8_t')
uint16_t = _ffi_cast('uint16_t')
int16_t = _ffi_cast('int16_t')
uint32_t = _ffi_cast('uint32_t')
int32_t = _ffi_cast('int32_t')
uint64_t = _ffi_cast('uint64_t')
int64_t = _ffi_cast('int64_t')
boolean_t = _ffi_cast('boolean_t')
uchar_t = _ffi_cast('uchar_t')
# First element of the value tuple is a suffix for a single value function
+123 -16
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
@@ -14,12 +28,14 @@ class ZFSError(Exception):
def __str__(self):
if self.name is not None:
return "[Errno %d] %s: '%s'" % (self.errno, self.message, self.name)
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)
return "%s(%r, %r)" % (
self.__class__.__name__, self.errno, self.message)
class ZFSGenericError(ZFSError):
@@ -44,24 +60,25 @@ class MultipleOperationsFailure(ZFSError):
# 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
# 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)
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, supressed=%r)" % (self.__class__.__name__,
self.errno, self.message, self.errors, self.suppressed_count)
return "%s(%r, %r, errors=%r, supressed=%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.
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"
@@ -73,8 +90,8 @@ class DatasetNotFound(ZFSError):
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
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
@@ -135,6 +152,7 @@ class SnapshotNotFound(DatasetNotFound):
def __init__(self, name):
self.name = name
class SnapshotNotLatest(ZFSError):
errno = errno.EEXIST
message = "Snapshot is not the latest"
@@ -142,6 +160,7 @@ class SnapshotNotLatest(ZFSError):
def __init__(self, name):
self.name = name
class SnapshotIsCloned(ZFSError):
errno = errno.EEXIST
message = "Snapshot is cloned"
@@ -177,7 +196,8 @@ 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)
super(SnapshotDestructionFailure, self).__init__(
errors, suppressed_count)
class BookmarkExists(ZFSError):
@@ -223,7 +243,8 @@ 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)
super(BookmarkDestructionFailure, self).__init__(
errors, suppressed_count)
class BadHoldCleanupFD(ZFSError):
@@ -286,7 +307,7 @@ class DestinationModified(ZFSError):
class BadStream(ZFSError):
errno = errno.EINVAL
errno = errno.EBADE
message = "Bad backup stream"
@@ -300,6 +321,23 @@ class UnknownStreamFeature(ZFSError):
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"
@@ -440,4 +478,73 @@ class DatasetTypeInvalid(ZFSError):
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"
# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
File diff suppressed because it is too large Load Diff
+31 -8
View File
@@ -1,4 +1,18 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
"""
Tests for _nvlist module.
@@ -27,16 +41,21 @@ class TestNVList(unittest.TestCase):
return res
def _assertIntDictsEqual(self, dict1, dict2):
self.assertEqual(len(dict1), len(dict1), "resulting dictionary is of different size")
self.assertEqual(
len(dict1), len(dict1),
"resulting dictionary is of different size")
for key in dict1.keys():
self.assertEqual(int(dict1[key]), int(dict2[key]))
def _assertIntArrayDictsEqual(self, dict1, dict2):
self.assertEqual(len(dict1), len(dict1), "resulting dictionary is of different size")
self.assertEqual(
len(dict1), len(dict1),
"resulting dictionary is of different size")
for key in dict1.keys():
val1 = dict1[key]
val2 = dict2[key]
self.assertEqual(len(val1), len(val2), "array values of different sizes")
self.assertEqual(
len(val1), len(val2), "array values of different sizes")
for x, y in zip(val1, val2):
self.assertEqual(int(x), int(y))
@@ -455,7 +474,8 @@ class TestNVList(unittest.TestCase):
self._dict_to_nvlist_to_dict(props)
def test_explict_int64_array(self):
props = {"key": [int64_t(0), int64_t(1), int64_t(2 ** 63 - 1), int64_t(-(2 ** 63))]}
props = {"key": [
int64_t(0), int64_t(1), int64_t(2 ** 63 - 1), int64_t(-(2 ** 63))]}
res = self._dict_to_nvlist_to_dict(props)
self._assertIntArrayDictsEqual(props, res)
@@ -470,7 +490,8 @@ class TestNVList(unittest.TestCase):
self._dict_to_nvlist_to_dict(props)
def test_explict_int32_array(self):
props = {"key": [int32_t(0), int32_t(1), int32_t(2 ** 31 - 1), int32_t(-(2 ** 31))]}
props = {"key": [
int32_t(0), int32_t(1), int32_t(2 ** 31 - 1), int32_t(-(2 ** 31))]}
res = self._dict_to_nvlist_to_dict(props)
self._assertIntArrayDictsEqual(props, res)
@@ -485,7 +506,8 @@ class TestNVList(unittest.TestCase):
self._dict_to_nvlist_to_dict(props)
def test_explict_int16_array(self):
props = {"key": [int16_t(0), int16_t(1), int16_t(2 ** 15 - 1), int16_t(-(2 ** 15))]}
props = {"key": [
int16_t(0), int16_t(1), int16_t(2 ** 15 - 1), int16_t(-(2 ** 15))]}
res = self._dict_to_nvlist_to_dict(props)
self._assertIntArrayDictsEqual(props, res)
@@ -500,7 +522,8 @@ class TestNVList(unittest.TestCase):
self._dict_to_nvlist_to_dict(props)
def test_explict_int8_array(self):
props = {"key": [int8_t(0), int8_t(1), int8_t(2 ** 7 - 1), int8_t(-(2 ** 7))]}
props = {"key": [
int8_t(0), int8_t(1), int8_t(2 ** 7 - 1), int8_t(-(2 ** 7))]}
res = self._dict_to_nvlist_to_dict(props)
self._assertIntArrayDictsEqual(props, res)
+1
View File
@@ -0,0 +1 @@
cffi
+17 -2
View File
@@ -1,10 +1,24 @@
# Copyright 2015 ClusterHQ. See LICENSE file for details.
#
# 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.
#
from setuptools import setup, find_packages
setup(
name="pyzfs",
version="0.2.3",
version="1.0.0",
description="Wrapper for libzfs_core",
author="ClusterHQ",
author_email="support@clusterhq.com",
@@ -33,6 +47,7 @@ setup(
setup_requires=[
"cffi",
],
python_requires='>=2.7,<3',
zip_safe=False,
test_suite="libzfs_core.test",
)