diff --git a/contrib/pyzfs/docs/source/index.rst b/contrib/pyzfs/docs/source/index.rst index 36c227a49..f63117c62 100644 --- a/contrib/pyzfs/docs/source/index.rst +++ b/contrib/pyzfs/docs/source/index.rst @@ -25,8 +25,7 @@ Documentation for the libzfs_core .. automodule:: libzfs_core :members: - :exclude-members: lzc_snap, lzc_recv, lzc_destroy_one, - lzc_inherit, lzc_set_props, lzc_list + :exclude-members: lzc_snap, lzc_recv, lzc_destroy_one Documentation for the libzfs_core exceptions ******************************************** diff --git a/contrib/pyzfs/libzfs_core/__init__.py b/contrib/pyzfs/libzfs_core/__init__.py index 13b50ca43..9165b178f 100644 --- a/contrib/pyzfs/libzfs_core/__init__.py +++ b/contrib/pyzfs/libzfs_core/__init__.py @@ -90,11 +90,6 @@ from ._libzfs_core import ( lzc_snap, lzc_rename, lzc_destroy, - lzc_inherit_prop, - lzc_get_props, - lzc_set_props, - lzc_list_children, - lzc_list_snaps, receive_header, ) @@ -146,11 +141,6 @@ __all__ = [ 'lzc_snap', 'lzc_rename', 'lzc_destroy', - 'lzc_inherit_prop', - 'lzc_get_props', - 'lzc_set_props', - 'lzc_list_children', - 'lzc_list_snaps', 'receive_header', ] diff --git a/contrib/pyzfs/libzfs_core/_error_translation.py b/contrib/pyzfs/libzfs_core/_error_translation.py index d5491a324..79a03b49f 100644 --- a/contrib/pyzfs/libzfs_core/_error_translation.py +++ b/contrib/pyzfs/libzfs_core/_error_translation.py @@ -638,64 +638,6 @@ def lzc_destroy_translate_error(ret, name): raise _generic_exception(ret, name, "Failed to destroy dataset") -def lzc_inherit_prop_translate_error(ret, name, prop): - if ret == 0: - return - if ret == errno.EINVAL: - _validate_fs_name(name) - raise lzc_exc.PropertyInvalid(prop) - if ret == errno.ENOENT: - raise lzc_exc.DatasetNotFound(name) - raise _generic_exception(ret, name, "Failed to inherit a property") - - -def lzc_set_prop_translate_error(ret, name, prop, val): - if ret == 0: - return - if ret == errno.EINVAL: - _validate_fs_or_snap_name(name) - raise lzc_exc.PropertyInvalid(prop) - if ret == errno.ENOENT: - raise lzc_exc.DatasetNotFound(name) - raise _generic_exception(ret, name, "Failed to set a property") - - -def lzc_get_props_translate_error(ret, name): - if ret == 0: - return - if ret == errno.EINVAL: - _validate_fs_or_snap_name(name) - if ret == errno.ENOENT: - raise lzc_exc.DatasetNotFound(name) - raise _generic_exception(ret, name, "Failed to get properties") - - -def lzc_list_children_translate_error(ret, name): - if ret == 0: - return - if ret == errno.EINVAL: - _validate_fs_name(name) - raise _generic_exception(ret, name, "Error while iterating children") - - -def lzc_list_snaps_translate_error(ret, name): - if ret == 0: - return - if ret == errno.EINVAL: - _validate_fs_name(name) - raise _generic_exception(ret, name, "Error while iterating snapshots") - - -def lzc_list_translate_error(ret, name, opts): - if ret == 0: - return - if ret == errno.ENOENT: - raise lzc_exc.DatasetNotFound(name) - if ret == errno.EINVAL: - _validate_fs_or_snap_name(name) - raise _generic_exception(ret, name, "Error obtaining a list") - - def _handle_err_list(ret, errlist, names, exception, mapper): ''' Convert one or more errors from an operation into the requested exception. diff --git a/contrib/pyzfs/libzfs_core/_libzfs_core.py b/contrib/pyzfs/libzfs_core/_libzfs_core.py index 0ebf99be6..113fde253 100644 --- a/contrib/pyzfs/libzfs_core/_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/_libzfs_core.py @@ -30,10 +30,7 @@ rather than by integer error codes. from __future__ import absolute_import, division, print_function import errno -import functools -import fcntl import os -import struct import threading from . import exceptions from . import _error_translation as errors @@ -47,46 +44,10 @@ from ._constants import ( # noqa: F401 zfs_keyformat, zio_encrypt ) -from .ctypes import ( - int32_t, - uint64_t -) +from .ctypes import uint64_t from ._nvlist import nvlist_in, nvlist_out -def _uncommitted(depends_on=None): - ''' - Mark an API function as being an uncommitted extension that might not be - available. - - :param function depends_on: the function that would be checked instead of - a decorated function. For example, if the decorated function uses - another uncommitted function. - - This decorator transforms a decorated function to raise - :exc:`NotImplementedError` if the C libzfs_core library does not provide - a function with the same name as the decorated function. - - The optional `depends_on` parameter can be provided if the decorated - function does not directly call the C function but instead calls another - Python function that follows the typical convention. - One example is :func:`lzc_list_snaps` that calls :func:`lzc_list` that - calls ``lzc_list`` in libzfs_core. - - This decorator is implemented using :func:`is_supported`. - ''' - def _uncommitted_decorator(func, depends_on=depends_on): - @functools.wraps(func) - def _f(*args, **kwargs): - if not is_supported(_f): - raise NotImplementedError(func.__name__) - return func(*args, **kwargs) - if depends_on is not None: - _f._check_func = depends_on - return _f - return _uncommitted_decorator - - def lzc_create(name, ds_type='zfs', props=None, key=None): ''' Create a ZFS filesystem or a ZFS volume ("zvol"). @@ -827,7 +788,6 @@ def lzc_exists(name): return bool(ret) -@_uncommitted() def lzc_change_key(fsname, crypt_cmd, props=None, key=None): ''' Change encryption key on the specified dataset. @@ -868,7 +828,6 @@ def lzc_change_key(fsname, crypt_cmd, props=None, key=None): errors.lzc_change_key_translate_error(ret, fsname) -@_uncommitted() def lzc_load_key(fsname, noop, key): ''' Load or verify encryption key on the specified dataset. @@ -888,7 +847,6 @@ def lzc_load_key(fsname, noop, key): errors.lzc_load_key_translate_error(ret, fsname, noop) -@_uncommitted() def lzc_unload_key(fsname): ''' Unload encryption key from the specified dataset. @@ -1200,7 +1158,6 @@ def receive_header(fd): return (header, record) -@_uncommitted() def lzc_receive_one( snapname, fd, begin_record, force=False, resumable=False, raw=False, origin=None, props=None, cleanup_fd=-1, action_handle=0 @@ -1306,7 +1263,6 @@ def lzc_receive_one( return (int(c_read_bytes[0]), action_handle) -@_uncommitted() def lzc_receive_with_cmdprops( snapname, fd, begin_record, force=False, resumable=False, raw=False, origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1, @@ -1427,7 +1383,6 @@ def lzc_receive_with_cmdprops( return (int(c_read_bytes[0]), action_handle) -@_uncommitted() def lzc_receive_with_heal( snapname, fd, begin_record, force=False, corrective=True, resumable=False, raw=False, origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1, @@ -1556,7 +1511,6 @@ def lzc_receive_with_heal( return (int(c_read_bytes[0]), action_handle) -@_uncommitted() def lzc_reopen(poolname, restart=True): ''' Reopen a pool @@ -1627,7 +1581,6 @@ def lzc_send_resume( errors.lzc_send_translate_error(ret, snapname, fromsnap, fd, flags) -@_uncommitted() def lzc_sync(poolname, force=False): ''' Forces all in-core dirty data to be written to the primary pool storage @@ -1674,7 +1627,6 @@ def is_supported(func): return getattr(_lib, fname, None) is not None -@_uncommitted() def lzc_promote(name): ''' Promotes the ZFS dataset. @@ -1693,7 +1645,6 @@ def lzc_promote(name): errors.lzc_promote_translate_error(ret, name) -@_uncommitted() def lzc_pool_checkpoint(name): ''' Creates a checkpoint for the specified pool. @@ -1710,7 +1661,6 @@ def lzc_pool_checkpoint(name): errors.lzc_pool_checkpoint_translate_error(ret, name) -@_uncommitted() def lzc_pool_checkpoint_discard(name): ''' Discard the checkpoint from the specified pool. @@ -1758,304 +1708,6 @@ def lzc_destroy(name): errors.lzc_destroy_translate_error(ret, name) -@_uncommitted() -def lzc_inherit(name, prop): - ''' - Inherit properties from a parent dataset of the given ZFS dataset. - - :param bytes name: the name of the dataset. - :param bytes prop: the name of the property to inherit. - :raises NameInvalid: if the dataset name is invalid. - :raises NameTooLong: if the dataset name is too long. - :raises DatasetNotFound: if the dataset does not exist. - :raises PropertyInvalid: if one or more of the specified properties is - invalid or has an invalid type or value. - - Inheriting a property actually resets it to its default value - or removes it if it's a user property, so that the property could be - inherited if it's inheritable. If the property is not inheritable - then it would just have its default value. - - This function can be used on snapshots to inherit user defined properties. - ''' - ret = _lib.lzc_inherit(name, prop, _ffi.NULL) - errors.lzc_inherit_prop_translate_error(ret, name, prop) - - -# As the extended API is not committed yet, the names of the new interfaces -# are not settled down yet. -# lzc_inherit_prop makes it clearer what is to be inherited. -lzc_inherit_prop = lzc_inherit - - -@_uncommitted() -def lzc_set_props(name, prop, val): - ''' - Set properties of the ZFS dataset. - - :param bytes name: the name of the dataset. - :param bytes prop: the name of the property. - :param Any val: the value of the property. - :raises NameInvalid: if the dataset name is invalid. - :raises NameTooLong: if the dataset name is too long. - :raises DatasetNotFound: if the dataset does not exist. - :raises NoSpace: if the property controls a quota and the values is too - small for that quota. - :raises PropertyInvalid: if one or more of the specified properties is - invalid or has an invalid type or value. - - This function can be used on snapshots to set user defined properties. - - .. note:: - An attempt to set a readonly / statistic property is ignored - without reporting any error. - ''' - props = {prop: val} - props_nv = nvlist_in(props) - ret = _lib.lzc_set_props(name, props_nv, _ffi.NULL, _ffi.NULL) - errors.lzc_set_prop_translate_error(ret, name, prop, val) - - -# As the extended API is not committed yet, the names of the new interfaces -# are not settled down yet. -# It's not clear if atomically setting multiple properties is an achievable -# goal and an interface acting on multiple entities must do so atomically -# by convention. -# Being able to set a single property at a time is sufficient for ClusterHQ. -lzc_set_prop = lzc_set_props - - -@_uncommitted() -def lzc_list(name, options): - ''' - List subordinate elements of the given dataset. - - This function can be used to list child datasets and snapshots of the given - dataset. The listed elements can be filtered by their type and by their - depth relative to the starting dataset. - - :param bytes name: the name of the dataset to be listed, could be a - snapshot or a dataset. - :param options: a `dict` of the options that control the listing behavior. - :type options: dict of bytes:Any - :return: a pair of file descriptors the first of which can be used to read - the listing. - :rtype: tuple of (int, int) - :raises DatasetNotFound: if the dataset does not exist. - - Two options are currently available: - - recurse : integer or None - specifies depth of the recursive listing. If ``None`` the depth is not - limited. - Absence of this option means that only the given dataset is listed. - - type : dict of bytes:None - specifies dataset types to include into the listing. - Currently allowed keys are "filesystem", "volume", "snapshot". - Absence of this option implies all types. - - The first of the returned file descriptors can be used to - read the listing in a binary encoded format. The data is - a series of variable sized records each starting with a fixed - size header, the header is followed by a serialized ``nvlist``. - Each record describes a single element and contains the element's - name as well as its properties. - The file descriptor must be closed after reading from it. - - The second file descriptor represents a pipe end to which the - kernel driver is writing information. It should not be closed - until all interesting information has been read and it must - be explicitly closed afterwards. - ''' - (rfd, wfd) = os.pipe() - fcntl.fcntl(rfd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) - fcntl.fcntl(wfd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) - options = options.copy() - options['fd'] = int32_t(wfd) - opts_nv = nvlist_in(options) - ret = _lib.lzc_list(name, opts_nv) - if ret == errno.ESRCH: - return (None, None) - errors.lzc_list_translate_error(ret, name, options) - return (rfd, wfd) - - -# Description of the binary format used to pass data from the kernel. -_PIPE_RECORD_FORMAT = 'IBBBB' -_PIPE_RECORD_SIZE = struct.calcsize(_PIPE_RECORD_FORMAT) - - -def _list(name, recurse=None, types=None): - ''' - A wrapper for :func:`lzc_list` that hides details of working - with the file descriptors and provides data in an easy to - consume format. - - :param bytes name: the name of the dataset to be listed, could be a - snapshot, a volume or a filesystem. - :param recurse: specifies depth of the recursive listing. If ``None`` the - depth is not limited. - :param types: specifies dataset types to include into the listing. - Currently allowed keys are "filesystem", "volume", "snapshot". ``None`` - is equivalent to specifying the type of the dataset named by `name`. - :type types: list of bytes or None - :type recurse: integer or None - :return: a list of dictionaries each describing a single listed element. - :rtype: list of dict - ''' - options = {} - - # Convert types to a dict suitable for mapping to an nvlist. - if types is not None: - types = {x: None for x in types} - options['type'] = types - if recurse is None or recurse > 0: - options['recurse'] = recurse - - # Note that other_fd is used by the kernel side to write - # the data, so we have to keep that descriptor open until - # we are done. - # Also, we have to explicitly close the descriptor as the - # kernel doesn't do that. - (fd, other_fd) = lzc_list(name, options) - if fd is None: - return - - try: - while True: - record_bytes = os.read(fd, _PIPE_RECORD_SIZE) - if not record_bytes: - break - (size, _, err, _, _) = struct.unpack( - _PIPE_RECORD_FORMAT, record_bytes) - if err == errno.ESRCH: - break - errors.lzc_list_translate_error(err, name, options) - if size == 0: - break - data_bytes = os.read(fd, size) - result = {} - with nvlist_out(result) as nvp: - ret = _lib.nvlist_unpack(data_bytes, size, nvp, 0) - if ret != 0: - raise exceptions.ZFSGenericError( - ret, None, "Failed to unpack list data") - yield result - finally: - os.close(other_fd) - os.close(fd) - - -@_uncommitted(lzc_list) -def lzc_get_props(name): - ''' - Get properties of the ZFS dataset. - - :param bytes name: the name of the dataset. - :raises DatasetNotFound: if the dataset does not exist. - :raises NameInvalid: if the dataset name is invalid. - :raises NameTooLong: if the dataset name is too long. - :return: a dictionary mapping the property names to their values. - :rtype: dict of bytes:Any - - .. note:: - The value of ``clones`` property is a `list` of clone names as byte - strings. - - .. warning:: - The returned dictionary does not contain entries for properties - with default values. One exception is the ``mountpoint`` property - for which the default value is derived from the dataset name. - ''' - result = next(_list(name, recurse=0)) - is_snapshot = result['dmu_objset_stats']['dds_is_snapshot'] - result = result['properties'] - # In most cases the source of the property is uninteresting and the - # value alone is sufficient. One exception is the 'mountpoint' - # property the final value of which is not the same as the inherited - # value. - mountpoint = result.get('mountpoint') - if mountpoint is not None: - mountpoint_src = mountpoint['source'] - mountpoint_val = mountpoint['value'] - # 'source' is the name of the dataset that has 'mountpoint' set - # to a non-default value and from which the current dataset inherits - # the property. 'source' can be the current dataset if its - # 'mountpoint' is explicitly set. - # 'source' can also be a special value like '$recvd', that case - # is equivalent to the property being set on the current dataset. - # Note that a normal mountpoint value should start with '/' - # unlike the special values "none" and "legacy". - if (mountpoint_val.startswith('/') and - not mountpoint_src.startswith('$')): - mountpoint_val = mountpoint_val + name[len(mountpoint_src):] - elif not is_snapshot: - mountpoint_val = '/' + name - else: - mountpoint_val = None - result = {k: result[k]['value'] for k in result} - if 'clones' in result: - result['clones'] = list(result['clones'].keys()) - if mountpoint_val is not None: - result['mountpoint'] = mountpoint_val - return result - - -@_uncommitted(lzc_list) -def lzc_list_children(name): - ''' - List the children of the ZFS dataset. - - :param bytes name: the name of the dataset. - :return: an iterator that produces the names of the children. - :raises NameInvalid: if the dataset name is invalid. - :raises NameTooLong: if the dataset name is too long. - :raises DatasetNotFound: if the dataset does not exist. - - .. warning:: - If the dataset does not exist, then the returned iterator would produce - no results and no error is reported. - That case is indistinguishable from the dataset having no children. - - An attempt to list children of a snapshot is silently ignored as well. - ''' - children = [] - for entry in _list(name, recurse=1, types=['filesystem', 'volume']): - child = entry['name'] - if child != name: - children.append(child) - - return iter(children) - - -@_uncommitted(lzc_list) -def lzc_list_snaps(name): - ''' - List the snapshots of the ZFS dataset. - - :param bytes name: the name of the dataset. - :return: an iterator that produces the names of the snapshots. - :raises NameInvalid: if the dataset name is invalid. - :raises NameTooLong: if the dataset name is too long. - :raises DatasetNotFound: if the dataset does not exist. - - .. warning:: - If the dataset does not exist, then the returned iterator would produce - no results and no error is reported. - That case is indistinguishable from the dataset having no snapshots. - - An attempt to list snapshots of a snapshot is silently ignored as well. - ''' - snaps = [] - for entry in _list(name, recurse=1, types=['snapshot']): - snap = entry['name'] - if snap != name: - snaps.append(snap) - - return iter(snaps) - - # TODO: a better way to init and uninit the library def _initialize(): class LazyInit(object): diff --git a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py index ca752f654..8db9ff3a2 100644 --- a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py @@ -136,10 +136,6 @@ CDEF = """ int lzc_pool_checkpoint_discard(const char *); int lzc_rename(const char *, const char *); int lzc_destroy(const char *fsname); - - int lzc_inherit(const char *fsname, const char *name, nvlist_t *); - int lzc_set_props(const char *, nvlist_t *, nvlist_t *, nvlist_t *); - int lzc_list (const char *, nvlist_t *); """ SOURCE = """ diff --git a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py index bad1af2d1..0587a5b77 100644 --- a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py @@ -3727,182 +3727,6 @@ zfs.sync.snapshot('""" + pool + b"""@zcp') with self.assertRaises(lzc_exc.CheckpointNotFound): lzc.lzc_pool_checkpoint_discard(pool) - @needs_support(lzc.lzc_list_children) - def test_list_children(self): - name = ZFSTest.pool.makeName(b"fs1/fs") - names = [ZFSTest.pool.makeName(b"fs1/fs/test1"), - ZFSTest.pool.makeName(b"fs1/fs/test2"), - ZFSTest.pool.makeName(b"fs1/fs/test3"), ] - # and one snap to see that it is not listed - snap = ZFSTest.pool.makeName(b"fs1/fs@test") - - for fs in names: - lzc.lzc_create(fs) - lzc.lzc_snapshot([snap]) - - children = list(lzc.lzc_list_children(name)) - self.assertItemsEqual(children, names) - - @needs_support(lzc.lzc_list_children) - def test_list_children_nonexistent(self): - fs = ZFSTest.pool.makeName(b"nonexistent") - - with self.assertRaises(lzc_exc.DatasetNotFound): - list(lzc.lzc_list_children(fs)) - - @needs_support(lzc.lzc_list_children) - def test_list_children_of_snap(self): - snap = ZFSTest.pool.makeName(b"@newsnap") - - lzc.lzc_snapshot([snap]) - children = list(lzc.lzc_list_children(snap)) - self.assertEqual(children, []) - - @needs_support(lzc.lzc_list_snaps) - def test_list_snaps(self): - name = ZFSTest.pool.makeName(b"fs1/fs") - names = [ZFSTest.pool.makeName(b"fs1/fs@test1"), - ZFSTest.pool.makeName(b"fs1/fs@test2"), - ZFSTest.pool.makeName(b"fs1/fs@test3"), ] - # and one filesystem to see that it is not listed - fs = ZFSTest.pool.makeName(b"fs1/fs/test") - - for snap in names: - lzc.lzc_snapshot([snap]) - lzc.lzc_create(fs) - - snaps = list(lzc.lzc_list_snaps(name)) - self.assertItemsEqual(snaps, names) - - @needs_support(lzc.lzc_list_snaps) - def test_list_snaps_nonexistent(self): - fs = ZFSTest.pool.makeName(b"nonexistent") - - with self.assertRaises(lzc_exc.DatasetNotFound): - list(lzc.lzc_list_snaps(fs)) - - @needs_support(lzc.lzc_list_snaps) - def test_list_snaps_of_snap(self): - snap = ZFSTest.pool.makeName(b"@newsnap") - - lzc.lzc_snapshot([snap]) - snaps = list(lzc.lzc_list_snaps(snap)) - self.assertEqual(snaps, []) - - @needs_support(lzc.lzc_get_props) - def test_get_fs_props(self): - fs = ZFSTest.pool.makeName(b"new") - props = {b"user:foo": b"bar"} - - lzc.lzc_create(fs, props=props) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset(props, actual_props) - - @needs_support(lzc.lzc_get_props) - def test_get_fs_props_with_child(self): - parent = ZFSTest.pool.makeName(b"parent") - child = ZFSTest.pool.makeName(b"parent/child") - parent_props = {b"user:foo": b"parent"} - child_props = {b"user:foo": b"child"} - - lzc.lzc_create(parent, props=parent_props) - lzc.lzc_create(child, props=child_props) - actual_parent_props = lzc.lzc_get_props(parent) - actual_child_props = lzc.lzc_get_props(child) - self.assertDictContainsSubset(parent_props, actual_parent_props) - self.assertDictContainsSubset(child_props, actual_child_props) - - @needs_support(lzc.lzc_get_props) - def test_get_snap_props(self): - snapname = ZFSTest.pool.makeName(b"@snap") - snaps = [snapname] - props = {b"user:foo": b"bar"} - - lzc.lzc_snapshot(snaps, props) - actual_props = lzc.lzc_get_props(snapname) - self.assertDictContainsSubset(props, actual_props) - - @needs_support(lzc.lzc_get_props) - def test_get_props_nonexistent(self): - fs = ZFSTest.pool.makeName(b"nonexistent") - - with self.assertRaises(lzc_exc.DatasetNotFound): - lzc.lzc_get_props(fs) - - @needs_support(lzc.lzc_get_props) - def test_get_mountpoint_none(self): - ''' - If the *mountpoint* property is set to none, then its - value is returned as `bytes` "none". - Also, a child filesystem inherits that value. - ''' - fs = ZFSTest.pool.makeName(b"new") - child = ZFSTest.pool.makeName(b"new/child") - props = {b"mountpoint": b"none"} - - lzc.lzc_create(fs, props=props) - lzc.lzc_create(child) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset(props, actual_props) - # check that mountpoint value is correctly inherited - child_props = lzc.lzc_get_props(child) - self.assertDictContainsSubset(props, child_props) - - @needs_support(lzc.lzc_get_props) - def test_get_mountpoint_legacy(self): - ''' - If the *mountpoint* property is set to legacy, then its - value is returned as `bytes` "legacy". - Also, a child filesystem inherits that value. - ''' - fs = ZFSTest.pool.makeName(b"new") - child = ZFSTest.pool.makeName(b"new/child") - props = {b"mountpoint": b"legacy"} - - lzc.lzc_create(fs, props=props) - lzc.lzc_create(child) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset(props, actual_props) - # check that mountpoint value is correctly inherited - child_props = lzc.lzc_get_props(child) - self.assertDictContainsSubset(props, child_props) - - @needs_support(lzc.lzc_get_props) - def test_get_mountpoint_path(self): - ''' - If the *mountpoint* property is set to a path and the property - is not explicitly set on a child filesystem, then its - value is that of the parent filesystem with the child's - name appended using the '/' separator. - ''' - fs = ZFSTest.pool.makeName(b"new") - child = ZFSTest.pool.makeName(b"new/child") - props = {b"mountpoint": b"/mnt"} - - lzc.lzc_create(fs, props=props) - lzc.lzc_create(child) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset(props, actual_props) - # check that mountpoint value is correctly inherited - child_props = lzc.lzc_get_props(child) - self.assertDictContainsSubset( - {b"mountpoint": b"/mnt/child"}, child_props) - - @needs_support(lzc.lzc_get_props) - def test_get_snap_clones(self): - fs = ZFSTest.pool.makeName(b"new") - snap = ZFSTest.pool.makeName(b"@snap") - clone1 = ZFSTest.pool.makeName(b"clone1") - clone2 = ZFSTest.pool.makeName(b"clone2") - - lzc.lzc_create(fs) - lzc.lzc_snapshot([snap]) - lzc.lzc_clone(clone1, snap) - lzc.lzc_clone(clone2, snap) - - clones_prop = lzc.lzc_get_props(snap)["clones"] - self.assertItemsEqual(clones_prop, [clone1, clone2]) - @needs_support(lzc.lzc_rename) def test_rename(self): src = ZFSTest.pool.makeName(b"source") @@ -3967,167 +3791,6 @@ zfs.sync.snapshot('""" + pool + b"""@zcp') with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_destroy(fs) - @needs_support(lzc.lzc_inherit_prop) - def test_inherit_prop(self): - parent = ZFSTest.pool.makeName(b"parent") - child = ZFSTest.pool.makeName(b"parent/child") - the_prop = b"user:foo" - parent_props = {the_prop: b"parent"} - child_props = {the_prop: b"child"} - - lzc.lzc_create(parent, props=parent_props) - lzc.lzc_create(child, props=child_props) - lzc.lzc_inherit_prop(child, the_prop) - actual_props = lzc.lzc_get_props(child) - self.assertDictContainsSubset(parent_props, actual_props) - - @needs_support(lzc.lzc_inherit_prop) - def test_inherit_missing_prop(self): - parent = ZFSTest.pool.makeName(b"parent") - child = ZFSTest.pool.makeName(b"parent/child") - the_prop = "user:foo" - child_props = {the_prop: "child"} - - lzc.lzc_create(parent) - lzc.lzc_create(child, props=child_props) - lzc.lzc_inherit_prop(child, the_prop) - actual_props = lzc.lzc_get_props(child) - self.assertNotIn(the_prop, actual_props) - - @needs_support(lzc.lzc_inherit_prop) - def test_inherit_readonly_prop(self): - parent = ZFSTest.pool.makeName(b"parent") - child = ZFSTest.pool.makeName(b"parent/child") - the_prop = b"createtxg" - - lzc.lzc_create(parent) - lzc.lzc_create(child) - with self.assertRaises(lzc_exc.PropertyInvalid): - lzc.lzc_inherit_prop(child, the_prop) - - @needs_support(lzc.lzc_inherit_prop) - def test_inherit_unknown_prop(self): - parent = ZFSTest.pool.makeName(b"parent") - child = ZFSTest.pool.makeName(b"parent/child") - the_prop = b"nosuchprop" - - lzc.lzc_create(parent) - lzc.lzc_create(child) - with self.assertRaises(lzc_exc.PropertyInvalid): - lzc.lzc_inherit_prop(child, the_prop) - - @needs_support(lzc.lzc_inherit_prop) - def test_inherit_prop_on_snap(self): - fs = ZFSTest.pool.makeName(b"new") - snapname = ZFSTest.pool.makeName(b"new@snap") - prop = b"user:foo" - fs_val = b"fs" - snap_val = b"snap" - - lzc.lzc_create(fs, props={prop: fs_val}) - lzc.lzc_snapshot([snapname], props={prop: snap_val}) - - actual_props = lzc.lzc_get_props(snapname) - self.assertDictContainsSubset({prop: snap_val}, actual_props) - - lzc.lzc_inherit_prop(snapname, prop) - actual_props = lzc.lzc_get_props(snapname) - self.assertDictContainsSubset({prop: fs_val}, actual_props) - - @needs_support(lzc.lzc_set_prop) - def test_set_fs_prop(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"user:foo" - val = b"bar" - - lzc.lzc_create(fs) - lzc.lzc_set_prop(fs, prop, val) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset({prop: val}, actual_props) - - @needs_support(lzc.lzc_set_prop) - def test_set_snap_prop(self): - snapname = ZFSTest.pool.makeName(b"@snap") - prop = b"user:foo" - val = b"bar" - - lzc.lzc_snapshot([snapname]) - lzc.lzc_set_prop(snapname, prop, val) - actual_props = lzc.lzc_get_props(snapname) - self.assertDictContainsSubset({prop: val}, actual_props) - - @needs_support(lzc.lzc_set_prop) - def test_set_prop_nonexistent(self): - fs = ZFSTest.pool.makeName(b"nonexistent") - prop = b"user:foo" - val = b"bar" - - with self.assertRaises(lzc_exc.DatasetNotFound): - lzc.lzc_set_prop(fs, prop, val) - - @needs_support(lzc.lzc_set_prop) - def test_set_sys_prop(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"recordsize" - val = 4096 - - lzc.lzc_create(fs) - lzc.lzc_set_prop(fs, prop, val) - actual_props = lzc.lzc_get_props(fs) - self.assertDictContainsSubset({prop: val}, actual_props) - - @needs_support(lzc.lzc_set_prop) - def test_set_invalid_prop(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"nosuchprop" - val = 0 - - lzc.lzc_create(fs) - with self.assertRaises(lzc_exc.PropertyInvalid): - lzc.lzc_set_prop(fs, prop, val) - - @needs_support(lzc.lzc_set_prop) - def test_set_invalid_value_prop(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"atime" - val = 100 - - lzc.lzc_create(fs) - with self.assertRaises(lzc_exc.PropertyInvalid): - lzc.lzc_set_prop(fs, prop, val) - - @needs_support(lzc.lzc_set_prop) - def test_set_invalid_value_prop_2(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"readonly" - val = 100 - - lzc.lzc_create(fs) - with self.assertRaises(lzc_exc.PropertyInvalid): - lzc.lzc_set_prop(fs, prop, val) - - @needs_support(lzc.lzc_set_prop) - def test_set_prop_too_small_quota(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"refquota" - val = 1 - - lzc.lzc_create(fs) - with self.assertRaises(lzc_exc.NoSpace): - lzc.lzc_set_prop(fs, prop, val) - - @needs_support(lzc.lzc_set_prop) - def test_set_readonly_prop(self): - fs = ZFSTest.pool.makeName(b"new") - prop = b"creation" - val = 0 - - lzc.lzc_create(fs) - lzc.lzc_set_prop(fs, prop, val) - actual_props = lzc.lzc_get_props(fs) - # the change is silently ignored - self.assertTrue(actual_props[prop] != val) - class _TempPool(object): SNAPSHOTS = [b'snap', b'snap1', b'snap2']