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

125
config/always-pyzfs.m4 Normal file
View File

@ -0,0 +1,125 @@
dnl #
dnl # ZFS_AC_PYTHON_MODULE(module_name, [action-if-true], [action-if-false])
dnl #
dnl # Checks for Python module. Freely inspired by AX_PYTHON_MODULE
dnl # https://www.gnu.org/software/autoconf-archive/ax_python_module.html
dnl #
AC_DEFUN([ZFS_AC_PYTHON_MODULE],[
PYTHON_NAME=`basename $PYTHON`
AC_MSG_CHECKING([for $PYTHON_NAME module: $1])
$PYTHON -c "import $1" 2>/dev/null
if test $? -eq 0;
then
AC_MSG_RESULT(yes)
m4_ifvaln([$2], [$2])
else
AC_MSG_RESULT(no)
m4_ifvaln([$3], [$3])
fi
])
dnl #
dnl # ZFS_AC_PYTHON_VERSION(version, [action-if-true], [action-if-false])
dnl #
dnl # Verify Python version
dnl #
AC_DEFUN([ZFS_AC_PYTHON_VERSION], [
AC_MSG_CHECKING([for a version of Python $1])
version_check=`$PYTHON -c "import sys; print (sys.version.split()[[0]] $1)"`
if test "$version_check" = "True";
then
AC_MSG_RESULT(yes)
m4_ifvaln([$2], [$2])
else
AC_MSG_RESULT(no)
m4_ifvaln([$3], [$3])
fi
])
AC_DEFUN([ZFS_AC_CONFIG_ALWAYS_PYZFS], [
PYTHON_REQUIRED_VERSION="<= '2.7.x'"
AC_ARG_ENABLE([pyzfs],
AC_HELP_STRING([--enable-pyzfs],
[install libzfs_core python bindings @<:@default=check@:>@]),
[enable_pyzfs=$enableval],
[enable_pyzfs=check])
AM_PATH_PYTHON([2.7], [], [
AS_IF([test ! "x$enable_pyzfs" = xyes], [
AC_MSG_ERROR("python >= 2.7 is not installed")
], [test ! "x$enable_pyzfs" = xno], [
enable_pyzfs=no
])
])
AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :])
dnl #
dnl # Python 2.7.x is supported, other versions (3.5) are not yet
dnl #
AS_IF([test "x$enable_pyzfs" = xcheck], [
ZFS_AC_PYTHON_VERSION([$PYTHON_REQUIRED_VERSION], [], [
AS_IF([test "x$enable_pyzfs" = xyes], [
AC_MSG_ERROR("Python $PYTHON_REQUIRED_VERSION is not available")
], [test ! "x$enable_pyzfs" = xno], [
enable_pyzfs=no
])
])
])
dnl #
dnl # Require python-devel libraries
dnl #
AS_IF([test "x$enable_pyzfs" = xcheck], [
AX_PYTHON_DEVEL([$PYTHON_REQUIRED_VERSION], [
AS_IF([test "x$enable_pyzfs" = xyes], [
AC_MSG_ERROR("Python development library is not available")
], [test ! "x$enable_pyzfs" = xno], [
enable_pyzfs=no
])
])
])
dnl #
dnl # Python "setuptools" module is required to build and install pyzfs
dnl #
AS_IF([test "x$enable_pyzfs" = xcheck], [
ZFS_AC_PYTHON_MODULE([setuptools], [], [
AS_IF([test "x$enable_pyzfs" = xyes], [
AC_MSG_ERROR("python-setuptools is not installed")
], [test ! "x$enable_pyzfs" = xno], [
enable_pyzfs=no
])
])
])
dnl #
dnl # Python "cffi" module is required to run pyzfs
dnl #
AS_IF([test "x$enable_pyzfs" = xcheck], [
ZFS_AC_PYTHON_MODULE([cffi], [], [
AS_IF([test "x$enable_pyzfs" = xyes], [
AC_MSG_ERROR("python-cffi is not installed")
], [test ! "x$enable_pyzfs" = xno], [
enable_pyzfs=no
])
])
])
dnl #
dnl # Set enable_pyzfs to 'yes' if every check passed
dnl #
AS_IF([test "x$enable_pyzfs" = xcheck], [enable_pyzfs=yes])
AM_CONDITIONAL([PYZFS_ENABLED], [test x$enable_pyzfs = xyes])
AC_SUBST([PYZFS_ENABLED], [$enable_pyzfs])
AS_IF([test "x$enable_pyzfs" = xyes], [
DEFINE_PYZFS='--define "_pyzfs 1"'
],[
DEFINE_PYZFS=''
])
AC_SUBST(DEFINE_PYZFS)
AC_SUBST(pythonsitedir, [$PYTHON_SITE_PKG])
])

345
config/ax_python_devel.m4 Normal file
View File

@ -0,0 +1,345 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_python_devel.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PYTHON_DEVEL([version], [action-if-not-found])
#
# DESCRIPTION
#
# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
# in your configure.ac.
#
# Note: this is a slightly modified version of the original AX_PYTHON_DEVEL
# macro which accepts an additional [action-if-not-found] argument. This
# allow to detect if Python development is available without aborting the
# configure phase with an hard error in case it is not.
#
# This macro checks for Python and tries to get the include path to
# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output
# variables. It also exports $(PYTHON_EXTRA_LIBS) and
# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
#
# You can search for some particular version of Python by passing a
# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
# note that you *have* to pass also an operator along with the version to
# match, and pay special attention to the single quotes surrounding the
# version number. Don't use "PYTHON_VERSION" for this: that environment
# variable is declared as precious and thus reserved for the end-user.
#
# This macro should work for all versions of Python >= 2.1.0. As an end
# user, you can disable the check for the python version by setting the
# PYTHON_NOVERSIONCHECK environment variable to something else than the
# empty string.
#
# If you need to use this macro for an older Python version, please
# contact the authors. We're always open for feedback.
#
# LICENSE
#
# Copyright (c) 2009 Sebastian Huber <sebastian-huber@web.de>
# Copyright (c) 2009 Alan W. Irwin
# Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
# Copyright (c) 2009 Andrew Collier
# Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
# Copyright (c) 2009 Horst Knorr <hk_classes@knoda.org>
# Copyright (c) 2013 Daniel Mullner <muellner@math.stanford.edu>
# Copyright (c) 2018 loli10K <ezomori.nozomu@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 21
AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
AC_DEFUN([AX_PYTHON_DEVEL],[
#
# Allow the use of a (user set) custom python version
#
AC_ARG_VAR([PYTHON_VERSION],[The installed Python
version to use, for example '2.3'. This string
will be appended to the Python interpreter
canonical name.])
AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
if test -z "$PYTHON"; then
m4_ifvaln([$2],[$2],[
AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
PYTHON_VERSION=""
])
fi
#
# Check for a version of Python >= 2.1.0
#
AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
ac_supports_python_ver=`$PYTHON -c "import sys; \
ver = sys.version.split ()[[0]]; \
print (ver >= '2.1.0')"`
if test "$ac_supports_python_ver" != "True"; then
if test -z "$PYTHON_NOVERSIONCHECK"; then
AC_MSG_RESULT([no])
m4_ifvaln([$2],[$2],[
AC_MSG_FAILURE([
This version of the AC@&t@_PYTHON_DEVEL macro
doesn't work properly with versions of Python before
2.1.0. You may need to re-run configure, setting the
variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG,
PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
to something else than an empty string.
])
])
else
AC_MSG_RESULT([skip at user request])
fi
else
AC_MSG_RESULT([yes])
fi
#
# if the macro parameter ``version'' is set, honour it
#
if test -n "$1"; then
AC_MSG_CHECKING([for a version of Python $1])
ac_supports_python_ver=`$PYTHON -c "import sys; \
ver = sys.version.split ()[[0]]; \
print (ver $1)"`
if test "$ac_supports_python_ver" = "True"; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
m4_ifvaln([$2],[$2],[
AC_MSG_ERROR([this package requires Python $1.
If you have it installed, but it isn't the default Python
interpreter in your system path, please pass the PYTHON_VERSION
variable to configure. See ``configure --help'' for reference.
])
PYTHON_VERSION=""
])
fi
fi
#
# Check if you have distutils, else fail
#
AC_MSG_CHECKING([for the distutils Python package])
ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
if test $? -eq 0; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
m4_ifvaln([$2],[$2],[
AC_MSG_ERROR([cannot import Python module "distutils".
Please check your Python installation. The error was:
$ac_distutils_result])
PYTHON_VERSION=""
])
fi
#
# Check for Python include path
#
AC_MSG_CHECKING([for Python include path])
if test -z "$PYTHON_CPPFLAGS"; then
python_path=`$PYTHON -c "import distutils.sysconfig; \
print (distutils.sysconfig.get_python_inc ());"`
plat_python_path=`$PYTHON -c "import distutils.sysconfig; \
print (distutils.sysconfig.get_python_inc (plat_specific=1));"`
if test -n "${python_path}"; then
if test "${plat_python_path}" != "${python_path}"; then
python_path="-I$python_path -I$plat_python_path"
else
python_path="-I$python_path"
fi
fi
PYTHON_CPPFLAGS=$python_path
fi
AC_MSG_RESULT([$PYTHON_CPPFLAGS])
AC_SUBST([PYTHON_CPPFLAGS])
#
# Check for Python library path
#
AC_MSG_CHECKING([for Python library path])
if test -z "$PYTHON_LIBS"; then
# (makes two attempts to ensure we've got a version number
# from the interpreter)
ac_python_version=`cat<<EOD | $PYTHON -
# join all versioning strings, on some systems
# major/minor numbers could be in different list elements
from distutils.sysconfig import *
e = get_config_var('VERSION')
if e is not None:
print(e)
EOD`
if test -z "$ac_python_version"; then
if test -n "$PYTHON_VERSION"; then
ac_python_version=$PYTHON_VERSION
else
ac_python_version=`$PYTHON -c "import sys; \
print (sys.version[[:3]])"`
fi
fi
# Make the versioning information available to the compiler
AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
[If available, contains the Python version number currently in use.])
# First, the library directory:
ac_python_libdir=`cat<<EOD | $PYTHON -
# There should be only one
import distutils.sysconfig
e = distutils.sysconfig.get_config_var('LIBDIR')
if e is not None:
print (e)
EOD`
# Now, for the library:
ac_python_library=`cat<<EOD | $PYTHON -
import distutils.sysconfig
c = distutils.sysconfig.get_config_vars()
if 'LDVERSION' in c:
print ('python'+c[['LDVERSION']])
else:
print ('python'+c[['VERSION']])
EOD`
# This small piece shamelessly adapted from PostgreSQL python macro;
# credits goes to momjian, I think. I'd like to put the right name
# in the credits, if someone can point me in the right direction... ?
#
if test -n "$ac_python_libdir" -a -n "$ac_python_library"
then
# use the official shared library
ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
PYTHON_LIBS="-L$ac_python_libdir -l$ac_python_library"
else
# old way: use libpython from python_configdir
ac_python_libdir=`$PYTHON -c \
"from distutils.sysconfig import get_python_lib as f; \
import os; \
print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
fi
if test -z "PYTHON_LIBS"; then
m4_ifvaln([$2],[$2],[
AC_MSG_ERROR([
Cannot determine location of your Python DSO. Please check it was installed with
dynamic libraries enabled, or try setting PYTHON_LIBS by hand.
])
])
fi
fi
AC_MSG_RESULT([$PYTHON_LIBS])
AC_SUBST([PYTHON_LIBS])
#
# Check for site packages
#
AC_MSG_CHECKING([for Python site-packages path])
if test -z "$PYTHON_SITE_PKG"; then
PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
print (distutils.sysconfig.get_python_lib(0,0));"`
fi
AC_MSG_RESULT([$PYTHON_SITE_PKG])
AC_SUBST([PYTHON_SITE_PKG])
#
# libraries which must be linked in when embedding
#
AC_MSG_CHECKING(python extra libraries)
if test -z "$PYTHON_EXTRA_LIBS"; then
PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
conf = distutils.sysconfig.get_config_var; \
print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
fi
AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
AC_SUBST(PYTHON_EXTRA_LIBS)
#
# linking flags needed when embedding
#
AC_MSG_CHECKING(python extra linking flags)
if test -z "$PYTHON_EXTRA_LDFLAGS"; then
PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
conf = distutils.sysconfig.get_config_var; \
print (conf('LINKFORSHARED'))"`
fi
AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
AC_SUBST(PYTHON_EXTRA_LDFLAGS)
#
# final check to see if everything compiles alright
#
AC_MSG_CHECKING([consistency of all components of python development environment])
# save current global flags
ac_save_LIBS="$LIBS"
ac_save_LDFLAGS="$LDFLAGS"
ac_save_CPPFLAGS="$CPPFLAGS"
LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS"
LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS"
CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
AC_LANG_PUSH([C])
AC_LINK_IFELSE([
AC_LANG_PROGRAM([[#include <Python.h>]],
[[Py_Initialize();]])
],[pythonexists=yes],[pythonexists=no])
AC_LANG_POP([C])
# turn back to default flags
CPPFLAGS="$ac_save_CPPFLAGS"
LIBS="$ac_save_LIBS"
LDFLAGS="$ac_save_LDFLAGS"
AC_MSG_RESULT([$pythonexists])
if test ! "x$pythonexists" = "xyes"; then
m4_ifvaln([$2],[$2],[
AC_MSG_FAILURE([
Could not link test program to Python. Maybe the main Python library has been
installed in some non-standard library path. If so, pass it to configure,
via the LIBS environment variable.
Example: ./configure LIBS="-L/usr/non-standard-path/python/lib"
============================================================================
ERROR!
You probably have to install the development version of the Python package
for your distribution. The exact name of this package varies among them.
============================================================================
])
PYTHON_VERSION=""
])
fi
#
# all done!
#
])

View File

@ -47,6 +47,7 @@ deb-utils: deb-local rpm-utils
pkg7=$${name}-test-$${version}.$${arch}.rpm; \
pkg8=$${name}-dracut-$${version}.$${arch}.rpm; \
pkg9=$${name}-initramfs-$${version}.$${arch}.rpm; \
pkg10=pyzfs-$${version}.noarch.rpm; \
## Arguments need to be passed to dh_shlibdeps. Alien provides no mechanism
## to do this, so we install a shim onto the path which calls the real
## dh_shlibdeps with the required arguments.
@ -62,10 +63,10 @@ deb-utils: deb-local rpm-utils
env PATH=$${path_prepend}:$${PATH} \
fakeroot $(ALIEN) --bump=0 --scripts --to-deb --target=$$debarch \
$$pkg1 $$pkg2 $$pkg3 $$pkg4 $$pkg5 $$pkg6 $$pkg7 \
$$pkg8 $$pkg9; \
$$pkg8 $$pkg9 $$pkg10; \
$(RM) $${path_prepend}/dh_shlibdeps; \
rmdir $${path_prepend}; \
$(RM) $$pkg1 $$pkg2 $$pkg3 $$pkg4 $$pkg5 $$pkg6 $$pkg7 \
$$pkg8 $$pkg9;
$$pkg8 $$pkg9 $$pkg10;
deb: deb-kmod deb-dkms deb-utils

View File

@ -103,6 +103,7 @@ AC_DEFUN([ZFS_AC_CONFIG_ALWAYS], [
ZFS_AC_CONFIG_ALWAYS_CC_ASAN
ZFS_AC_CONFIG_ALWAYS_TOOLCHAIN_SIMD
ZFS_AC_CONFIG_ALWAYS_ARCH
ZFS_AC_CONFIG_ALWAYS_PYZFS
])
AC_DEFUN([ZFS_AC_CONFIG], [
@ -204,6 +205,7 @@ AC_DEFUN([ZFS_AC_RPM], [
])
RPM_DEFINE_UTIL+=' $(DEFINE_INITRAMFS)'
RPM_DEFINE_UTIL+=' $(DEFINE_SYSTEMD)'
RPM_DEFINE_UTIL+=' $(DEFINE_PYZFS)'
dnl # Override default lib directory on Debian/Ubuntu systems. The provided
dnl # /usr/lib/rpm/platform/<arch>/macros files do not specify the correct

View File

@ -126,6 +126,7 @@ AC_CONFIG_FILES([
contrib/initramfs/hooks/Makefile
contrib/initramfs/scripts/Makefile
contrib/initramfs/scripts/local-top/Makefile
contrib/pyzfs/Makefile
module/Makefile
module/avl/Makefile
module/nvpair/Makefile
@ -288,6 +289,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/poolversion/Makefile
tests/zfs-tests/tests/functional/privilege/Makefile
tests/zfs-tests/tests/functional/projectquota/Makefile
tests/zfs-tests/tests/functional/pyzfs/Makefile
tests/zfs-tests/tests/functional/quota/Makefile
tests/zfs-tests/tests/functional/raidz/Makefile
tests/zfs-tests/tests/functional/redundancy/Makefile

View File

@ -1,2 +1,2 @@
SUBDIRS = bash_completion.d dracut initramfs
DIST_SUBDIRS = bash_completion.d dracut initramfs
SUBDIRS = bash_completion.d dracut initramfs pyzfs
DIST_SUBDIRS = bash_completion.d dracut initramfs pyzfs

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
contrib/pyzfs/Makefile.am Normal file
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

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

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

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',

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

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

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

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

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.
#
"""
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 *);

View File

@ -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 *);

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

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

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)

View File

@ -0,0 +1 @@
cffi

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",
)

View File

@ -1185,7 +1185,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp)
if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Key already unloaded for '%s'."), zfs_get_name(zhp));
ret = ENOENT;
ret = EACCES;
goto error;
}
@ -1198,7 +1198,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp)
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Permission denied."));
break;
case ENOENT:
case EACCES:
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Key already unloaded for '%s'."),
zfs_get_name(zhp));

View File

@ -546,6 +546,15 @@ lzc_get_holds(const char *snapname, nvlist_t **holdsp)
* to contain DRR_WRITE_EMBEDDED records with drr_etype==BP_EMBEDDED_TYPE_DATA,
* which the receiving system must support (as indicated by support
* for the "embedded_data" feature).
*
* If "flags" contains LZC_SEND_FLAG_COMPRESS, the stream is generated by using
* compressed WRITE records for blocks which are compressed on disk and in
* memory. If the lz4_compress feature is active on the sending system, then
* the receiving system must have that feature enabled as well.
*
* If "flags" contains LZC_SEND_FLAG_RAW, the stream is generated, for encrypted
* datasets, by sending data exactly as it exists on disk. This allows backups
* to be taken even if encryption keys are not currently loaded.
*/
int
lzc_send(const char *snapname, const char *from, int fd,
@ -1156,9 +1165,9 @@ lzc_channel_program_nosync(const char *pool, const char *program,
/*
* Performs key management functions
*
* crypto_cmd should be a value from zfs_ioc_crypto_cmd_t. If the command
* specifies to load or change a wrapping key, the key should be specified in
* the hidden_args nvlist so that it is not logged
* crypto_cmd should be a value from dcp_cmd_t. If the command specifies to
* load or change a wrapping key, the key should be specified in the
* hidden_args nvlist so that it is not logged.
*/
int
lzc_load_key(const char *fsname, boolean_t noop, uint8_t *wkeydata,

View File

@ -1223,7 +1223,7 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags,
* allocated. Rather than adding NULL checks throughout this code
* or adding dummy dcp's to all of the callers we simply create a
* dummy one here and use that. This zero dcp will have the same
* effect as asking for inheritence of all encryption params.
* effect as asking for inheritance of all encryption params.
*/
doca.doca_dcp = (dcp != NULL) ? dcp : &tmp_dcp;

View File

@ -866,7 +866,7 @@ spa_keystore_unload_wkey_impl(spa_t *spa, uint64_t ddobj)
found_wkey = avl_find(&spa->spa_keystore.sk_wkeys,
&search_wkey, NULL);
if (!found_wkey) {
ret = SET_ERROR(ENOENT);
ret = SET_ERROR(EACCES);
goto error_unlock;
} else if (refcount_count(&found_wkey->wk_refcnt) != 0) {
ret = SET_ERROR(EBUSY);
@ -1225,7 +1225,7 @@ spa_keystore_change_key_check(void *arg, dmu_tx_t *tx)
if (ret != 0)
goto error;
/* Handle inheritence */
/* Handle inheritance */
if (dcp->cp_cmd == DCP_CMD_INHERIT ||
dcp->cp_cmd == DCP_CMD_FORCE_INHERIT) {
/* no other encryption params should be given */
@ -1757,7 +1757,7 @@ dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_crypto_params_t *dcp)
return (SET_ERROR(EOPNOTSUPP));
}
/* handle inheritence */
/* handle inheritance */
if (dcp->cp_wkey == NULL) {
ASSERT3P(parentdd, !=, NULL);

View File

@ -216,6 +216,26 @@ Requires: dracut
This package contains a dracut module used to construct an initramfs
image which is ZFS aware.
%if 0%{?_pyzfs}
%package -n pyzfs
Summary: Python wrapper for libzfs_core
Group: Development/Languages/Python
License: Apache-2.0
BuildArch: noarch
Requires: libzfs2 = %{version}
Requires: libnvpair1 = %{version}
Requires: libffi
Requires: python >= 2.7
Requires: python-cffi
%if 0%{?rhel}%{?fedora}%{?suse_version}
BuildRequires: python-devel
BuildRequires: libffi-devel
%endif
%description -n pyzfs
This package provides a python wrapper for the libzfs_core C library.
%endif
%if 0%{?_initramfs}
%package initramfs
Summary: Initramfs module
@ -383,6 +403,15 @@ systemctl --system daemon-reload >/dev/null || true
%doc contrib/dracut/README.dracut.markdown
%{_dracutdir}/modules.d/*
%if 0%{?_pyzfs}
%files -n pyzfs
%doc contrib/pyzfs/README
%doc contrib/pyzfs/LICENSE
%defattr(-,root,root,-)
%{python_sitelib}/libzfs_core/*
%{python_sitelib}/pyzfs*
%endif
%if 0%{?_initramfs}
%files initramfs
%doc contrib/initramfs/README.initramfs.markdown

View File

@ -31,11 +31,14 @@ export ZEDLET_ETC_DIR=$$CMD_DIR/zed/zed.d
export ZEDLET_LIBEXEC_DIR=$$CMD_DIR/zed/zed.d
export ZPOOL_SCRIPT_DIR=$$CMD_DIR/zpool/zpool.d
export ZPOOL_SCRIPTS_PATH=$$CMD_DIR/zpool/zpool.d
export CONTRIB_DIR=@abs_top_builddir@/contrib
export LIB_DIR=@abs_top_builddir@/lib
export INSTALL_UDEV_DIR=@udevdir@
export INSTALL_UDEV_RULE_DIR=@udevruledir@
export INSTALL_MOUNT_HELPER_DIR=@mounthelperdir@
export INSTALL_SYSCONF_DIR=@sysconfdir@
export INSTALL_PYTHON_DIR=@pythonsitedir@
export KMOD_SPL=@SPL_OBJ@/module/spl/spl.ko
export KMOD_SPLAT=@SPL_OBJ@/module/splat/splat.ko

View File

@ -110,6 +110,7 @@ if [ "$VERBOSE" = "yes" ]; then
echo "udevruledir: $INSTALL_UDEV_RULE_DIR"
echo "mounthelperdir: $INSTALL_MOUNT_HELPER_DIR"
echo "sysconfdir: $INSTALL_SYSCONF_DIR"
echo "pythonsitedir: $INSTALL_PYTHON_DIR"
echo "dryrun: $DRYRUN"
echo
fi
@ -165,6 +166,16 @@ if [ "${INSTALL}" = "yes" ]; then
"$INSTALL_UDEV_RULE_DIR/90-zfs.rules"
install "$CMD_DIR/zpool/zpool.d" \
"$INSTALL_SYSCONF_DIR/zfs/zpool.d"
install "$CONTRIB_DIR/pyzfs/libzfs_core" \
"$INSTALL_PYTHON_DIR/libzfs_core"
# Ideally we would install these in the configured ${libdir}, which is
# by default "/usr/local/lib and unfortunately not included in the
# dynamic linker search path.
install "$(find "$LIB_DIR/libzfs_core" -type f -name 'libzfs_core.so*')" \
"/lib/libzfs_core.so"
install "$(find "$LIB_DIR/libnvpair" -type f -name 'libnvpair.so*')" \
"/lib/libnvpair.so"
ldconfig
else
remove "$INSTALL_MOUNT_HELPER_DIR/mount.zfs"
remove "$INSTALL_MOUNT_HELPER_DIR/fsck.zfs"
@ -174,6 +185,10 @@ else
remove "$INSTALL_UDEV_RULE_DIR/69-vdev.rules"
remove "$INSTALL_UDEV_RULE_DIR/90-zfs.rules"
remove "$INSTALL_SYSCONF_DIR/zfs/zpool.d"
remove "$INSTALL_PYTHON_DIR/libzfs_core"
remove "/lib/libzfs_core.so"
remove "/lib/libnvpair.so"
ldconfig
fi
exit 0

View File

@ -646,6 +646,12 @@ tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
'projecttree_001_pos', 'projecttree_002_pos', 'projecttree_003_neg' ]
tags = ['functional', 'projectquota']
[tests/functional/pyzfs]
tests = ['pyzfs_unittest']
pre =
post =
tags = ['functional', 'pyzfs']
[tests/functional/quota]
tests = ['quota_001_pos', 'quota_002_pos', 'quota_003_pos',
'quota_004_pos', 'quota_005_pos', 'quota_006_neg']

View File

@ -31,6 +31,7 @@ SUBDIRS = \
large_files \
largest_pool \
libzfs \
pyzfs \
link_count \
migration \
mmap \

View File

@ -0,0 +1,4 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/pyzfs
dist_pkgdata_SCRIPTS = \
pyzfs_unittest.ksh

View File

@ -0,0 +1,52 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Verify the libzfs_core Python test suite can be run successfully
#
# STRATEGY:
# 1. Run the nvlist and libzfs_core Python unittest
# 2. Verify the exit code is 0 (no errors)
#
verify_runnable "global"
# We don't just try to "import libzfs_core" because we want to skip these tests
# only if pyzfs was not installed due to missing, build-time, dependencies; if
# we cannot load "libzfs_core" due to other reasons, for instance an API/ABI
# mismatch, we want to report it.
python -c '
import pkgutil, sys
sys.exit(pkgutil.find_loader("libzfs_core") is None)'
if [ $? -eq 1 ]
then
log_unsupported "libzfs_core not found by Python"
fi
log_assert "Verify the nvlist and libzfs_core Python unittest run successfully"
# NOTE: don't use log_must() here because it makes output unreadable
python -m unittest --verbose \
libzfs_core.test.test_nvlist.TestNVList \
libzfs_core.test.test_libzfs_core.ZFSTest
if [ $? -ne 0 ]; then
log_fail "Python unittest completed with errors"
fi
log_pass "Python unittest completed without errors"