-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adopt pyzfs from ClusterHQ #7230
Conversation
@loli10K I went ahead and merged the buildbot patch. Feel free to refresh this. |
1e46482
to
88615b6
Compare
I've been trying to squash some FIXMEs and either i have found a bug in the existing In but doing this we fail to detect, for instance, ENOENT errors from Consider the following example where we try to "zfs hold" a snapshot that does not (no longer) exist; i'm using gdb to modify the input nvl and "simulate" a race condition where "testpool/fs2@snap1" gets deleted just before we try to "hold" it:
No error is reported to the user running "zfs hold tag testpool/fs2@snap1"
|
def test_recv_incremental(self): | ||
src1 = ZFSTest.pool.makeName("fs1@snap1") | ||
src2 = ZFSTest.pool.makeName("fs1@snap2") | ||
dst1 = ZFSTest.pool.makeName("fs2/received-2@snap1") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is failing on current master but passes on zfs-0.6.5.11:
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/libzfs_core/test/test_libzfs_core.py", line 431, in test_hold_missing_fs
lzc.lzc_hold({snap: 'tag'})
AssertionError: HoldFailure not raised
Actually this seems to be caused by "OpenZFS 7071 - lzc_snapshot does not fill in errlist on ENOENT" (e88551d): before this commit lzc_hold()
will return ENOENT which raises the expected HoldFailure
in test_hold_missing_fs()
, but the ENOENT is actually from zfs_secpolicy_write_perms()
, not zfs_ioc_hold()
.
After said commit both zfs_secpolicy_write_perms()
and zfs_ioc_hold()
return 0, and so does lzc_hold()
: HoldFailure
doesn't get raised in the Python code and thus the test fails:
root@linux:~# cat /sys/module/zfs/version
0.7.0-339_g2a0428f
root@linux:~# cat > ./zfs_hold.sh <<'EOF'
> POOLNAME='testpool'
> TMPDIR='/var/tmp'
> mountpoint -q $TMPDIR || mount -t tmpfs tmpfs $TMPDIR
> zpool destroy -f $POOLNAME
> rm -f $TMPDIR/zpool.dat
> fallocate -l 128m $TMPDIR/zpool.dat
> zpool create -f $POOLNAME $TMPDIR/zpool.dat
> zfs create $POOLNAME/fs1
> zfs snap $POOLNAME/fs1@snap1
> python -c "import libzfs_core as lzc; print lzc.lzc_hold({'$POOLNAME/fs2@snap2': 'tag'})"
> EOF
root@linux:~# chmod +x ./zfs_hold.sh
root@linux:~#
root@linux:~# stap -d zfs -e '
> probe
> module("zfs").function("zfs_ioc_hold").return,
> module("zfs").function("zfs_secpolicy_write_perms").return,
> module("zfs").function("dsl_dataset_user_hold").return,
> module("zfs").function("dsl_dataset_hold").return
> { printf(" <- %s ret=%s\n", ppfunc(), $$return$$); }' -c ./zfs_hold.sh
<- zfs_secpolicy_write_perms ret=return=0
<- zfs_secpolicy_write_perms ret=return=0
<- zfs_secpolicy_write_perms ret=return=0
['testpool/fs2@snap2']
<- zfs_secpolicy_write_perms ret=return=0
<- dsl_dataset_hold ret=return=2
<- dsl_dataset_user_hold ret=return=0
<- zfs_ioc_hold ret=return=0
root@linux:~#
@loli10K Merged openzfs/zfs-buildbot#135 |
e8f7145
to
2ef944f
Compare
@dinatale2 I will be very happy with that. Especially, if it will help with new features like Python 3 support (see a PR in the ClusterHQ repo). I can also transfer ownership of the project on pypi and readthedocs. The ClusterHQ GitHub repository is readonly since the company went out of business and I personally didn't have time to look after the project. |
@loli10K Just as a thought, I'm wondering if it would make the code cleaner if the large |
@scotws thanks, i will gladly welcome any form of cleanup / optimization, it that doesn't make the code less readable, though my primary objective now is to have all the temporary disabled "FIXME" test cases up and running. |
a61d0f7
to
19902e6
Compare
dst1 = ZFSTest.pool.makeName("fs2/received-2@snap1") | ||
dst2 = ZFSTest.pool.makeName("fs2/received-2@snap2") | ||
|
||
lzc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only remaining tests that are still failing ("test_recv_..._origin") seem to be broken by Illumos 5925 zfs receive -o origin= (fcff0f3):
root@linux:~# stap -g -DMAXSTRINGLEN=10240 -v -d zfs -e '
> probe
> module("zfs").function("zfs_ioc_recv*").call,
> module("zfs").function("recv_begin_check_existing_impl").call,
> module("zfs").function("dmu_recv_resume_begin_check").call,
> module("zfs").function("dmu_recv_begin_sync").call,
> module("zfs").function("dmu_recv_begin_check").call,
> module("zfs").function("dsl_sync_task").call,
> module("zfs").function("dmu_recv_begin").call
> { printf(" -> %s %s\n", ppfunc(), $$parms$$); }
> probe
> module("zfs").function("recv_begin_check_existing_impl").return,
> module("zfs").function("dmu_recv_resume_begin_check").return,
> module("zfs").function("dmu_recv_begin_sync").return,
> module("zfs").function("dmu_recv_begin_check").return,
> module("zfs").function("dsl_sync_task").return,
> module("zfs").function("dmu_recv_begin").return,
> module("zfs").function("zfs_ioc_recv*").return
> { printf(" <- %s %s\n", ppfunc(), $$return$$); }' -c "python -m unittest --verbose libzfs_core.test.test_libzfs_core.ZFSTest.test_recv_incremental_non_clone_but_set_origin"
...
-> zfs_ioc_recv_impl tofs="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20" tosnap="snap2" origin="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20@snap1" ...
-> dmu_recv_begin tofs="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20" tosnap="snap2" drr_begin={... .drr_u={.drr_begin={.... .drr_flags=4, ..., .drr_toname="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs1@snap2"}, ...} force=0 resumable=0 origin="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20@snap1", ...}
-> dsl_sync_task pool="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20" ...
-> dmu_recv_begin_check ...
<- dmu_recv_begin_check return=22
<- dsl_sync_task return=22
<- dmu_recv_begin return=22
<- zfs_ioc_recv_impl return=22
<- zfs_ioc_recv return=22
With drr_flags=DRR_FLAG_FREERECORDS
and origin="pool.fc6cfadf-7ce8-45c4-9a0d-8c29cc94d0f5/fs2/received-20@snap1"
we are failing here:
2e8377b
to
0425b46
Compare
@scotws i considered converting "if" chains to dictionary-based dispatch functions in "_nvlist.py" (mainly in |
@loli10K Yes, I think you're right, changing the code would be change for formal sake and wouldn't help anybody. The Zen of Python reference would seem to be "Practicality beats purity" 😃 |
The following additional interfaces are going to be marked "uncommitted":
We are missing
I think this is because libzfs_core.py is not installed in Python's sys.path when working in-tree. It seems we may have to change the logic in https://github.com/zfsonlinux/zfs/blob/master/scripts/zfs-tests.sh#L203 to copy pyzfs source in the "constrained" path and export PYTHONPATH environment variable in "scripts/zfs-tests.sh". I am going to do some tests locally and push the change (along with changes to "uncommitted" interfaces) if it looks good. The ABI Compliance Checker seems very nice to have but it needs binaries compiled with "-g -Og" and an old version of the library to work properly; we could ask the abicc maintainer to be added to the ABI tracker: https://abi-laboratory.pro/tracker/ |
Unless i am missing something obvious it seems running pyzfs in-tree would require quite some changes:
|
How about |
@avg-I i just tried
Python cwd during test: /var/tmp/test_results/20180410T193920 Unless i copy libzfs_core into one of those directories (which defeats the purpose of running in-tree, i think) python will not be able to load the module. |
Could you extend the This script it never invoked by the ZTS directly so it has the advantage that this is a manual customization. Otherwise the test cases which depend on installed system components are skipped.
That's unfortunate. Perhaps there are alternate tools out there we could take advantage of. |
c1875d0
to
ab3fd0d
Compare
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, unfortunately, the only way i managed to run pyzfs ztests in-tree: "libzfs_core" code coverage is now at ~97% according to codecov, which i think is good compared to the decreased (!?) coverage (-0.2%) introduced by the zpool split tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this isn't pretty, but it is something we can live with. The target audience for this script is only in-tree developer and it'll be nice to benefit from the additional coverage. As for the zpool split
tests I was surprised by that too.
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this isn't pretty, but it is something we can live with. The target audience for this script is only in-tree developer and it'll be nice to benefit from the additional coverage. As for the zpool split
tests I was surprised by that too.
libzfs_core is intended to be a stable interface for programmatic administration of ZFS. This wrapper provides one-to-one wrappers for libzfs_core API functions, but the signatures and types are more natural to Python. nvlists are wrapped as dictionaries or lists depending on their usage. Some parameters have default values depending on typical use for increased convenience. Enumerations and bit flags become strings and lists of strings in Python. Errors are reported as exceptions rather than integer errno-style error codes. The wrapper takes care to provide one-to-many mapping of the error codes to the exceptions by interpreting a context in which the error code is produced. Unit tests and automated test for the libzfs_core API are provided with this package. Please note that the API tests perform lots of ZFS dataset level operations and ZFS tries hard to ensure that any modifications do reach stable storage. That means that the operations are done synchronously and that, for example, disk caches are flushed. Thus, the tests can be very slow on real hardware. It is recommended to place the default temporary directory or a temporary directory specified by, for instance, TMP environment variable on a memory backed filesystem. Original-patch-by: Andriy Gapon <avg@FreeBSD.org> Ported-by: loli10K <ezomori.nozomu@gmail.com> Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
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. Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
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 *); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Latest push adds lzc_remap()
(and tests).
Codecov Report
@@ Coverage Diff @@
## master #7230 +/- ##
==========================================
+ Coverage 76.53% 77.08% +0.55%
==========================================
Files 335 335
Lines 107100 107100
==========================================
+ Hits 81965 82556 +591
+ Misses 25135 24544 -591
Continue to review full report at Codecov.
|
@loli10K the out of band feedback I've gotten on this PR has all been positive. Unless you have additional proposed changes I'd like to go ahead an integrate this. |
@behlendorf nice, positive feedback is good. This should be good to go, i was mostly waiting to integrate lzc_pool_checkpoint, lzc_pool_checkpoint_discard and lzc_initialize which we can always do later when the relevant OpenZFS changes gets merged. |
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
Is there some guide / tutorial / basic example available for installing and using the pyzfs library? |
Description
This commit introduces several changes:
Import pyzfs source code from ClusterHQ GitHub repository into contrib/
Give existing source a good PEP8 talk
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
Enable temporarily disabled tests in the Python libzfs_core test suite.
Credit the original authors: pushed existing pyzfs code retaining original authorship
Better integration with autotools & rpmbuild (disable pyzfs when python/setuptools/cffi are not available?)
Add missing libzfs_core lzc_* functions to pyzfs (EDIT: this is going to take a while)
Motivation and Context
pyzfs is a python wrapper around the libzfs_core C library developed by ClusterHQ (https://github.com/ClusterHQ/pyzfs): unfortunately the original code does not work correctly due to ABI changes and therfore cannot be used with the current master branch.
Importing this code will allow the project to test/verify changes in the ABI and provide users with a programmatical way to access ZFS objects/properties and run different tasks from a Python shell or script.
How Has This Been Tested?
Tested locally on Debian8 builder running the (updated) libzfs_core Python test suite. The buildbots will probably lack some -dev(el) packages and will need to be updated.
Types of changes
Checklist:
Signed-off-by
.