diff --git a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py index a841f96af36f..08b58b5d1e0d 100644 --- a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py @@ -1,4380 +1,4380 @@ # # 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 `libzfs_core` operations. These are mostly functional and conformance tests that validate that the operations produce expected effects or fail with expected exceptions. """ from __future__ import absolute_import, division, print_function import unittest import contextlib import errno import filecmp import os import platform import resource import shutil import stat import subprocess import sys import tempfile import time import uuid import itertools import zlib from .. import _libzfs_core as lzc from .. import exceptions as lzc_exc from .._nvlist import packed_nvlist_out def _print(*args): for arg in args: print(arg, end=' ') print() @contextlib.contextmanager def suppress(exceptions=None): try: yield except BaseException as e: if exceptions is None or isinstance(e, exceptions): pass else: raise @contextlib.contextmanager def _zfs_mount(fs): mntdir = tempfile.mkdtemp() if platform.system() == 'SunOS': mount_cmd = ['mount', '-F', 'zfs', fs, mntdir] else: mount_cmd = ['mount', '-t', 'zfs', fs, mntdir] unmount_cmd = ['umount', '-f', mntdir] try: subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT) try: yield mntdir finally: with suppress(): subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print('failed to mount %s @ %s : %s' % (fs, mntdir, e.output)) raise finally: os.rmdir(mntdir) # XXX On illumos it is impossible to explicitly mount a snapshot. # So, either we need to implicitly mount it using .zfs/snapshot/ # or we need to create a clone and mount it readonly (and discard # it afterwards). # At the moment the former approach is implemented. # This dictionary is used to keep track of mounted filesystems # (not snapshots), so that we do not try to mount a filesystem # more than once in the case more than one snapshot of the # filesystem is accessed from the same context or the filesystem # and its snapshot are accessed. _mnttab = {} @contextlib.contextmanager def _illumos_mount_fs(fs): if fs in _mnttab: yield _mnttab[fs] else: with _zfs_mount(fs) as mntdir: _mnttab[fs] = mntdir try: yield mntdir finally: _mnttab.pop(fs, None) @contextlib.contextmanager def _illumos_mount_snap(fs): (base, snap) = fs.split('@', 1) with _illumos_mount_fs(base) as mntdir: yield os.path.join(mntdir, '.zfs', 'snapshot', snap) @contextlib.contextmanager def _zfs_mount_illumos(fs): if '@' not in fs: with _illumos_mount_fs(fs) as mntdir: yield mntdir else: with _illumos_mount_snap(fs) as mntdir: yield mntdir if platform.system() == 'SunOS': zfs_mount = _zfs_mount_illumos else: zfs_mount = _zfs_mount @contextlib.contextmanager def cleanup_fd(): fd = os.open('/dev/zfs', os.O_EXCL) try: yield fd finally: os.close(fd) @contextlib.contextmanager def os_open(name, mode): fd = os.open(name, mode) try: yield fd finally: os.close(fd) @contextlib.contextmanager def dev_null(): - with os_open('/dev/null', os.O_WRONLY) as fd: - yield fd + with tempfile.TemporaryFile(suffix='.zstream') as fd: + yield fd.fileno() @contextlib.contextmanager def dev_zero(): with os_open('/dev/zero', os.O_RDONLY) as fd: yield fd @contextlib.contextmanager def temp_file_in_fs(fs): with zfs_mount(fs) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() yield f.name def make_snapshots(fs, before, modified, after): def _maybe_snap(snap): if snap is not None: if not snap.startswith(fs): snap = fs + b'@' + snap lzc.lzc_snapshot([snap]) return snap before = _maybe_snap(before) with temp_file_in_fs(fs) as name: modified = _maybe_snap(modified) after = _maybe_snap(after) return (name, (before, modified, after)) @contextlib.contextmanager def streams(fs, first, second): (filename, snaps) = make_snapshots(fs, None, first, second) with tempfile.TemporaryFile(suffix='.zstream') as full: lzc.lzc_send(snaps[1], None, full.fileno()) full.seek(0) if snaps[2] is not None: with tempfile.TemporaryFile(suffix='.zstream') as incremental: lzc.lzc_send(snaps[2], snaps[1], incremental.fileno()) incremental.seek(0) yield (filename, (full, incremental)) else: yield (filename, (full, None)) @contextlib.contextmanager def encrypted_filesystem(): fs = ZFSTest.pool.getFilesystem(b"encrypted") name = fs.getName() filename = None key = os.urandom(lzc.WRAPPING_KEY_LEN) with tempfile.NamedTemporaryFile() as f: filename = "file://" + f.name props = { b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, b"keylocation": filename.encode(), b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, } lzc.lzc_create(name, 'zfs', props=props, key=key) yield (name, key) def runtimeSkipIf(check_method, message): def _decorator(f): def _f(_self, *args, **kwargs): if check_method(_self): return _self.skipTest(message) else: return f(_self, *args, **kwargs) _f.__name__ = f.__name__ return _f return _decorator def skipIfFeatureAvailable(feature, message): return runtimeSkipIf( lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature), message) def skipUnlessFeatureEnabled(feature, message): return runtimeSkipIf( lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature), message) def skipUnlessBookmarksSupported(f): return skipUnlessFeatureEnabled( 'bookmarks', 'bookmarks are not enabled')(f) def snap_always_unmounted_before_destruction(): # Apparently ZoL automatically unmounts the snapshot # only if it is mounted at its default .zfs/snapshot # mountpoint. return ( platform.system() != 'Linux', 'snapshot is not auto-unmounted') def illumos_bug_6379(): # zfs_ioc_hold() panics on a bad cleanup fd return ( platform.system() == 'SunOS', 'see https://www.illumos.org/issues/6379') def needs_support(function): return unittest.skipUnless( lzc.is_supported(function), '{} not available'.format(function.__name__)) class ZFSTest(unittest.TestCase): POOL_FILE_SIZE = 128 * 1024 * 1024 FILESYSTEMS = [b'fs1', b'fs2', b'fs1/fs'] pool = None misc_pool = None readonly_pool = None @classmethod def setUpClass(cls): try: cls.pool = _TempPool(filesystems=cls.FILESYSTEMS) cls.misc_pool = _TempPool() cls.readonly_pool = _TempPool( filesystems=cls.FILESYSTEMS, readonly=True) cls.pools = [cls.pool, cls.misc_pool, cls.readonly_pool] except Exception: cls._cleanUp() raise @classmethod def tearDownClass(cls): cls._cleanUp() @classmethod def _cleanUp(cls): for pool in [cls.pool, cls.misc_pool, cls.readonly_pool]: if pool is not None: pool.cleanUp() def setUp(self): pass def tearDown(self): for pool in ZFSTest.pools: pool.reset() def assertExists(self, name): self.assertTrue( lzc.lzc_exists(name), 'ZFS dataset %s does not exist' % (name, )) def assertNotExists(self, name): self.assertFalse( lzc.lzc_exists(name), 'ZFS dataset %s exists' % (name, )) def test_exists(self): self.assertExists(ZFSTest.pool.makeName()) def test_exists_in_ro_pool(self): self.assertExists(ZFSTest.readonly_pool.makeName()) def test_exists_failure(self): self.assertNotExists(ZFSTest.pool.makeName(b'nonexistent')) def test_create_fs(self): name = ZFSTest.pool.makeName(b"fs1/fs/test1") lzc.lzc_create(name) self.assertExists(name) def test_create_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) self.assertExists(name) # On Gentoo with ZFS 0.6.5.4 the volume is busy # and can not be destroyed right after its creation. # A reason for this is unknown at the moment. # Because of that the post-test clean up could fail. time.sleep(0.1) def test_create_fs_with_prop(self): name = ZFSTest.pool.makeName(b"fs1/fs/test2") props = {b"atime": 0} lzc.lzc_create(name, props=props) self.assertExists(name) def test_create_fs_wrong_ds_type(self): name = ZFSTest.pool.makeName(b"fs1/fs/test1") with self.assertRaises(lzc_exc.DatasetTypeInvalid): lzc.lzc_create(name, ds_type='wrong') def test_create_fs_below_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_create(name + b'/fs') def test_create_zvol_below_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_create(name + b'/zvol', ds_type='zvol', props=props) def test_create_fs_duplicate(self): name = ZFSTest.pool.makeName(b"fs1/fs/test6") lzc.lzc_create(name) with self.assertRaises(lzc_exc.FilesystemExists): lzc.lzc_create(name) def test_create_fs_in_ro_pool(self): name = ZFSTest.readonly_pool.makeName(b"fs") with self.assertRaises(lzc_exc.ReadOnlyPool): lzc.lzc_create(name) def test_create_fs_without_parent(self): name = ZFSTest.pool.makeName(b"fs1/nonexistent/test") with self.assertRaises(lzc_exc.ParentNotFound): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_in_nonexistent_pool(self): name = b"no-such-pool/fs" with self.assertRaises(lzc_exc.ParentNotFound): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_with_invalid_prop(self): name = ZFSTest.pool.makeName(b"fs1/fs/test3") props = {b"BOGUS": 0} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_prop_type(self): name = ZFSTest.pool.makeName(b"fs1/fs/test4") props = {b"recordsize": b"128k"} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_prop_val(self): name = ZFSTest.pool.makeName(b"fs1/fs/test5") props = {b"atime": 20} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_name(self): name = ZFSTest.pool.makeName(b"@badname") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_with_invalid_pool_name(self): name = b"bad!pool/fs" with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_create(name) self.assertNotExists(name) def test_create_encrypted_fs(self): fs = ZFSTest.pool.getFilesystem(b"encrypted") name = fs.getName() filename = None with tempfile.NamedTemporaryFile() as f: filename = "file://" + f.name props = { b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, b"keylocation": filename.encode(), b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, } key = os.urandom(lzc.WRAPPING_KEY_LEN) lzc.lzc_create(name, 'zfs', props=props, key=key) self.assertEqual(fs.getProperty("encryption"), b"aes-256-ccm") self.assertEqual(fs.getProperty("encryptionroot"), name) self.assertEqual(fs.getProperty("keylocation"), filename.encode()) self.assertEqual(fs.getProperty("keyformat"), b"raw") def test_snapshot(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] lzc.lzc_snapshot(snaps) self.assertExists(snapname) def test_snapshot_empty_list(self): lzc.lzc_snapshot([]) def test_snapshot_user_props(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] props = {b"user:foo": b"bar"} lzc.lzc_snapshot(snaps, props) self.assertExists(snapname) def test_snapshot_invalid_props(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] props = {b"foo": b"bar"} with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps, props) self.assertEqual(len(ctx.exception.errors), len(snaps)) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PropertyInvalid) self.assertNotExists(snapname) def test_snapshot_ro_pool(self): snapname1 = ZFSTest.readonly_pool.makeName(b"@snap") snapname2 = ZFSTest.readonly_pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.ReadOnlyPool) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_nonexistent_pool(self): snapname = b"no-such-pool@snap" snaps = [snapname] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) def test_snapshot_nonexistent_fs(self): snapname = ZFSTest.pool.makeName(b"nonexistent@snap") snaps = [snapname] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) def test_snapshot_nonexistent_and_existent_fs(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots_nonexistent_fs(self): snapname1 = ZFSTest.pool.makeName(b"nonexistent@snap1") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap2") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # XXX two errors should be reported but alas self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots_multiple_nonexistent_fs(self): snapname1 = ZFSTest.pool.makeName(b"nonexistent1@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent2@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_already_exists(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) def test_multiple_snapshots_for_same_fs(self): snapname1 = ZFSTest.pool.makeName(b"@snap1") snapname2 = ZFSTest.pool.makeName(b"@snap2") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) self.assertExists(snapname1) self.assertExists(snapname2) def test_multiple_existing_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) def test_multiple_new_and_existing_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snapname3 = ZFSTest.pool.makeName(b"fs2@snap") snaps = [snapname1, snapname2] more_snaps = snaps + [snapname3] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(more_snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) self.assertNotExists(snapname3) def test_snapshot_multiple_errors(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") snapname3 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1] more_snaps = [snapname1, snapname2, snapname3] # create 'snapname1' snapshot lzc.lzc_snapshot(snaps) # attempt to create 3 snapshots: # 1. duplicate snapshot name # 2. refers to filesystem that doesn't exist # 3. could have succeeded if not for 1 and 2 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(more_snaps) # It seems that FilesystemNotFound overrides the other error, # but it doesn't have to. self.assertGreater(len(ctx.exception.errors), 0) for e in ctx.exception.errors: self.assertIsInstance( e, (lzc_exc.SnapshotExists, lzc_exc.FilesystemNotFound)) self.assertNotExists(snapname2) self.assertNotExists(snapname3) def test_snapshot_different_pools(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.misc_pool.makeName(b"@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_different_pools_ro_pool(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.readonly_pool.makeName(b"@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: # NB: depending on whether the first attempted snapshot is # for the read-only pool a different error is reported. self.assertIsInstance( e, (lzc_exc.PoolsDiffer, lzc_exc.ReadOnlyPool)) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_invalid_name(self): snapname1 = ZFSTest.pool.makeName(b"@bad&name") snapname2 = ZFSTest.pool.makeName(b"fs1@bad*name") snapname3 = ZFSTest.pool.makeName(b"fs2@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertIsNone(e.name) def test_snapshot_too_long_complete_name(self): snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongName(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertIsNotNone(e.name) def test_snapshot_too_long_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertIsNone(e.name) def test_destroy_nonexistent_snapshot(self): lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], False) lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], True) def test_destroy_snapshot_of_nonexistent_pool(self): with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([b"no-such-pool@snap"], False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolNotFound) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([b"no-such-pool@snap"], True) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolNotFound) # NB: note the difference from the nonexistent pool test. def test_destroy_snapshot_of_nonexistent_fs(self): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"nonexistent@snap")], False) lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"nonexistent@snap")], True) # Apparently the name is not checked for validity. @unittest.expectedFailure def test_destroy_invalid_snap_name(self): with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"@non$&*existent")], False) with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"@non$&*existent")], True) # Apparently the full name is not checked for length. @unittest.expectedFailure def test_destroy_too_long_full_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") snaps = [snapname1] with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps(snaps, False) with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps(snaps, True) def test_destroy_too_long_short_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps(snaps, False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @unittest.skipUnless(*snap_always_unmounted_before_destruction()) def test_destroy_mounted_snap(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with zfs_mount(snap): # the snapshot should be force-unmounted lzc.lzc_destroy_snaps([snap], defer=False) self.assertNotExists(snap) def test_clone(self): # NB: note the special name for the snapshot. # Since currently we can not destroy filesystems, # it would be impossible to destroy the snapshot, # so no point in attempting to clean it up. snapname = ZFSTest.pool.makeName(b"fs2@origin1") name = ZFSTest.pool.makeName(b"fs1/fs/clone1") lzc.lzc_snapshot([snapname]) lzc.lzc_clone(name, snapname) self.assertExists(name) def test_clone_nonexistent_snapshot(self): snapname = ZFSTest.pool.makeName(b"fs2@nonexistent") name = ZFSTest.pool.makeName(b"fs1/fs/clone2") # XXX The error should be SnapshotNotFound # but limitations of C interface do not allow # to differentiate between the errors. with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_nonexistent_parent_fs(self): snapname = ZFSTest.pool.makeName(b"fs2@origin3") name = ZFSTest.pool.makeName(b"fs1/nonexistent/clone3") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_to_nonexistent_pool(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = b"no-such-pool/fs" lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_snap_name(self): # Use a valid filesystem name of filesystem that # exists as a snapshot name snapname = ZFSTest.pool.makeName(b"fs1/fs") name = ZFSTest.pool.makeName(b"fs2/clone") with self.assertRaises(lzc_exc.SnapshotNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_snap_name_2(self): # Use a valid filesystem name of filesystem that # doesn't exist as a snapshot name snapname = ZFSTest.pool.makeName(b"fs1/nonexistent") name = ZFSTest.pool.makeName(b"fs2/clone") with self.assertRaises(lzc_exc.SnapshotNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_name(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.pool.makeName(b"fs1/bad#name") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.FilesystemNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_pool_name(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = b"bad!pool/fs1" lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.FilesystemNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_across_pools(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.misc_pool.makeName(b"clone1") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_across_pools_to_ro_pool(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.readonly_pool.makeName(b"fs1/clone1") lzc.lzc_snapshot([snapname]) # it's legal to report either of the conditions with self.assertRaises((lzc_exc.ReadOnlyPool, lzc_exc.PoolsDiffer)): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_destroy_cloned_fs(self): snapname1 = ZFSTest.pool.makeName(b"fs2@origin4") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") clonename = ZFSTest.pool.makeName(b"fs1/fs/clone4") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) lzc.lzc_clone(clonename, snapname1) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps(snaps, False) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotIsCloned) for snap in snaps: self.assertExists(snap) def test_deferred_destroy_cloned_fs(self): snapname1 = ZFSTest.pool.makeName(b"fs2@origin5") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") clonename = ZFSTest.pool.makeName(b"fs1/fs/clone5") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) lzc.lzc_clone(clonename, snapname1) lzc.lzc_destroy_snaps(snaps, defer=True) self.assertExists(snapname1) self.assertNotExists(snapname2) def test_rollback(self): name = ZFSTest.pool.makeName(b"fs1") snapname = name + b"@snap" lzc.lzc_snapshot([snapname]) ret = lzc.lzc_rollback(name) self.assertEqual(ret, snapname) def test_rollback_2(self): name = ZFSTest.pool.makeName(b"fs1") snapname1 = name + b"@snap1" snapname2 = name + b"@snap2" lzc.lzc_snapshot([snapname1]) lzc.lzc_snapshot([snapname2]) ret = lzc.lzc_rollback(name) self.assertEqual(ret, snapname2) def test_rollback_no_snaps(self): name = ZFSTest.pool.makeName(b"fs1") with self.assertRaises(lzc_exc.SnapshotNotFound): lzc.lzc_rollback(name) def test_rollback_non_existent_fs(self): name = ZFSTest.pool.makeName(b"nonexistent") with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rollback(name) def test_rollback_invalid_fs_name(self): name = ZFSTest.pool.makeName(b"bad~name") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_snap_name(self): name = ZFSTest.pool.makeName(b"fs1@snap") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_snap_name_2(self): name = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([name]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_too_long_fs_name(self): name = ZFSTest.pool.makeTooLongName() with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_rollback(name) def test_rollback_to_snap_name(self): name = ZFSTest.pool.makeName(b"fs1") snap = name + b"@snap" lzc.lzc_snapshot([snap]) lzc.lzc_rollback_to(name, snap) def test_rollback_to_not_latest(self): fsname = ZFSTest.pool.makeName(b'fs1') snap1 = fsname + b"@snap1" snap2 = fsname + b"@snap2" lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotNotLatest): lzc.lzc_rollback_to(fsname, fsname + b"@snap1") @skipUnlessBookmarksSupported def test_bookmarks(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) lzc.lzc_bookmark(bmark_dict) @skipUnlessBookmarksSupported def test_bookmarks_2(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) lzc.lzc_bookmark(bmark_dict) lzc.lzc_destroy_snaps(snaps, defer=False) @skipUnlessBookmarksSupported def test_bookmark_copying(self): snaps = [ZFSTest.pool.makeName(s) for s in [ b'fs1@snap1', b'fs1@snap2', b'fs2@snap1']] bmarks = [ZFSTest.pool.makeName(x) for x in [ b'fs1#bmark1', b'fs1#bmark2', b'fs2#bmark1']] bmarks_copies = [ZFSTest.pool.makeName(x) for x in [ b'fs1#bmark1_copy', b'fs1#bmark2_copy', b'fs2#bmark1_copy']] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} bmark_copies_dict = {x: y for x, y in zip(bmarks_copies, bmarks)} for snap in snaps: lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) lzc.lzc_bookmark(bmark_copies_dict) lzc.lzc_destroy_bookmarks(bmarks_copies) lzc.lzc_destroy_bookmarks(bmarks) lzc.lzc_destroy_snaps(snaps, defer=False) @skipUnlessBookmarksSupported def test_bookmarks_empty(self): lzc.lzc_bookmark({}) @skipUnlessBookmarksSupported def test_bookmarks_foregin_source(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_invalid_name(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs1#bmark!')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) @skipUnlessBookmarksSupported def test_bookmarks_invalid_name_2(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs1@bmark')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) @skipUnlessBookmarksSupported def test_bookmarks_too_long_name(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeTooLongName(b'fs1#')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @skipUnlessBookmarksSupported def test_bookmarks_too_long_name_2(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeTooLongComponent(b'fs1#')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @skipUnlessBookmarksSupported def test_bookmarks_foreign_sources(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs2#bmark1'), ZFSTest.pool.makeName(b'fs1#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_partially_foreign_sources(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs2#bmark'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_cross_pool(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.misc_pool.makeName(b'@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.misc_pool.makeName(b'#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps[0:1]) lzc.lzc_snapshot(snaps[1:2]) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) @skipUnlessBookmarksSupported def test_bookmarks_missing_snap(self): fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps[0:1]) # only create fs1@snap1 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotNotFound) # no new bookmarks are created if one or more sources do not exist for fs in fss: fsbmarks = lzc.lzc_get_bookmarks(fs) self.assertEqual(len(fsbmarks), 0) @skipUnlessBookmarksSupported def test_bookmarks_missing_snaps(self): fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} # do not create any snapshots with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotNotFound) # no new bookmarks are created if one or more sources do not exist for fs in fss: fsbmarks = lzc.lzc_get_bookmarks(fs) self.assertEqual(len(fsbmarks), 0) @skipUnlessBookmarksSupported def test_bookmarks_for_the_same_snap(self): snap = ZFSTest.pool.makeName(b'fs1@snap1') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict = {bmark1: snap, bmark2: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) @skipUnlessBookmarksSupported def test_bookmarks_for_the_same_snap_2(self): snap = ZFSTest.pool.makeName(b'fs1@snap1') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict1 = {bmark1: snap} bmark_dict2 = {bmark2: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict1) lzc.lzc_bookmark(bmark_dict2) @skipUnlessBookmarksSupported def test_bookmarks_duplicate_name(self): snap1 = ZFSTest.pool.makeName(b'fs1@snap1') snap2 = ZFSTest.pool.makeName(b'fs1@snap2') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict1 = {bmark: snap1} bmark_dict2 = {bmark: snap2} lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark(bmark_dict1) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkExists) @skipUnlessBookmarksSupported def test_get_bookmarks(self): snap1 = ZFSTest.pool.makeName(b'fs1@snap1') snap2 = ZFSTest.pool.makeName(b'fs1@snap2') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict1 = {bmark1: snap1, bmark2: snap2} bmark_dict2 = {bmark: snap2} lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark(bmark_dict1) lzc.lzc_bookmark(bmark_dict2) lzc.lzc_destroy_snaps([snap1, snap2], defer=False) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 3) for b in b'bmark', b'bmark1', b'bmark2': self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 0) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'), [b'guid', b'createtxg', b'creation']) self.assertEqual(len(bmarks), 3) for b in b'bmark', b'bmark1', b'bmark2': self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 3) @skipUnlessBookmarksSupported def test_get_bookmarks_invalid_property(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) bmarks = lzc.lzc_get_bookmarks( ZFSTest.pool.makeName(b'fs1'), [b'badprop']) self.assertEqual(len(bmarks), 1) for b in (b'bmark', ): self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 0) @skipUnlessBookmarksSupported def test_get_bookmarks_nonexistent_fs(self): with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'nonexistent')) @skipUnlessBookmarksSupported def test_destroy_bookmarks(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) lzc.lzc_destroy_bookmarks( [bmark, ZFSTest.pool.makeName(b'fs1#nonexistent')]) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 0) @skipUnlessBookmarksSupported def test_destroy_bookmarks_invalid_name(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) with self.assertRaises(lzc_exc.BookmarkDestructionFailure) as ctx: lzc.lzc_destroy_bookmarks( [bmark, ZFSTest.pool.makeName(b'fs1/nonexistent')]) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 1) self.assertIn(b'bmark', bmarks) @skipUnlessBookmarksSupported def test_destroy_bookmark_nonexistent_fs(self): lzc.lzc_destroy_bookmarks( [ZFSTest.pool.makeName(b'nonexistent#bmark')]) @skipUnlessBookmarksSupported def test_destroy_bookmarks_empty(self): lzc.lzc_bookmark({}) def test_snaprange_space(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_snaprange_space(snap1, snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_snaprange_space(snap2, snap3) self.assertIsInstance(space, (int, int)) space = lzc.lzc_snaprange_space(snap1, snap3) self.assertIsInstance(space, (int, int)) def test_snaprange_space_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_snaprange_space(snap1, snap2) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_snaprange_space(snap2, snap3) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_snaprange_space(snap1, snap3) self.assertGreater(space, 1024 * 1024) def test_snaprange_space_same_snap(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) space = lzc.lzc_snaprange_space(snap, snap) self.assertGreater(space, 1024 * 1024) self.assertAlmostEqual(space, 1024 * 1024, delta=1024 * 1024 // 20) def test_snaprange_space_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_snaprange_space(snap2, snap1) def test_snaprange_space_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_snaprange_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_snaprange_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap1) def test_snaprange_space_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn#p") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_not_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap2, snap1) def test_snaprange_space_not_snap_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap2, snap1) def test_send_space(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_send_space(snap2, snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3, snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3, snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3) self.assertIsInstance(space, (int, int)) def test_send_space_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_send_space(snap2, snap1) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_send_space(snap3, snap2) space = lzc.lzc_send_space(snap3, snap1) space_empty = lzc.lzc_send_space(snap1) space = lzc.lzc_send_space(snap2) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_send_space(snap3) self.assertEqual(space, space_empty) def test_send_space_same_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap1) def test_send_space_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap2) def test_send_space_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap2) def test_send_space_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_send_space(snap1, snap2) def test_send_space_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap1) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap2) self.assertEqual(ctx.exception.name, snap2) def test_send_space_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap2) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap2) def test_send_space_not_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2, snap1) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2) def test_send_space_not_snap_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2, snap1) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2) def test_send_full(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: estimate = lzc.lzc_send_space(snap) fd = output.fileno() lzc.lzc_send(snap, None, fd) st = os.fstat(fd) # 5%, arbitrary. self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) def test_send_incremental(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: estimate = lzc.lzc_send_space(snap2, snap1) fd = output.fileno() lzc.lzc_send(snap2, snap1, fd) st = os.fstat(fd) # 5%, arbitrary. self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) def test_send_flags(self): flags = ['embedded_data', 'large_blocks', 'compress', 'raw'] snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) for c in range(len(flags)): for flag in itertools.permutations(flags, c + 1): with dev_null() as fd: lzc.lzc_send(snap, None, fd, list(flag)) def test_send_unknown_flags(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with dev_null() as fd: with self.assertRaises(lzc_exc.UnknownStreamFeature): lzc.lzc_send(snap, None, fd, ['embedded_data', 'UNKNOWN']) def test_send_same_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap1, fd) def test_send_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap2, fd) def test_send_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap2, fd) def test_send_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_send(snap1, snap2, fd) def test_send_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap1, snap2, fd) self.assertEqual(ctx.exception.name, snap1) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap2, snap1, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap2, None, fd) self.assertEqual(ctx.exception.name, snap2) def test_send_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap2, snap1, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap2, None, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap1, snap2, fd) self.assertEqual(ctx.exception.name, snap2) # XXX Although undocumented the API allows to create an incremental # or full stream for a filesystem as if a temporary unnamed snapshot # is taken at some time after the call is made and before the stream # starts being produced. def test_send_filesystem(self): snap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() lzc.lzc_send(fs, snap, fd) lzc.lzc_send(fs, None, fd) def test_send_from_filesystem(self): snap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(snap, fs, fd) @skipUnlessBookmarksSupported def test_send_bookmark(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") bmark = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark({bmark: snap2}) lzc.lzc_destroy_snaps([snap2], defer=False) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(bmark, snap1, fd) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(bmark, None, fd) @skipUnlessBookmarksSupported def test_send_from_bookmark(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") bmark = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark({bmark: snap1}) lzc.lzc_destroy_snaps([snap1], defer=False) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() lzc.lzc_send(snap2, bmark, fd) def test_send_bad_fd(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, bad_fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_bad_fd_2(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, -2) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_bad_fd_3(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) bad_fd = hard + 1 with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, bad_fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_to_broken_pipe(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) if sys.version_info < (3, 0): proc = subprocess.Popen(['true'], stdin=subprocess.PIPE) proc.wait() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, proc.stdin.fileno()) self.assertEqual(ctx.exception.errno, errno.EPIPE) else: with subprocess.Popen(['true'], stdin=subprocess.PIPE) as proc: proc.wait() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, proc.stdin.fileno()) self.assertEqual(ctx.exception.errno, errno.EPIPE) def test_send_to_broken_pipe_2(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) if sys.version_info < (3, 0): p = subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, p.stdin.fileno()) self.assertTrue(ctx.exception.errno == errno.EPIPE or ctx.exception.errno == errno.EINTR) else: with subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) as p: with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, p.stdin.fileno()) self.assertTrue(ctx.exception.errno == errno.EPIPE or ctx.exception.errno == errno.EINTR) def test_send_to_ro_file(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.NamedTemporaryFile( suffix='.zstream', delete=False) as output: # tempfile always opens a temporary file in read-write mode # regardless of the specified mode, so we have to open it again. os.chmod(output.name, stat.S_IRUSR) fd = os.open(output.name, os.O_RDONLY) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, fd) os.close(fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_recv_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/received-1@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno()) name = os.path.basename(name) with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) def test_recv_incremental(self): src1 = ZFSTest.pool.makeName(b"fs1@snap1") src2 = ZFSTest.pool.makeName(b"fs1@snap2") dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1") dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2") lzc.lzc_snapshot([src1]) with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src2]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src1, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src2, src1, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst2, stream.fileno()) name = os.path.basename(name) with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) # This test case fails unless a patch from # https://clusterhq.atlassian.net/browse/ZFS-20 # is applied to libzfs_core, otherwise it succeeds. @unittest.skip("fails with unpatched libzfs_core") def test_recv_without_explicit_snap_name(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-100") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dstfs, full.fileno()) lzc.lzc_receive(dstfs, incr.fileno()) self.assertExists(dst1) self.assertExists(dst2) def test_recv_clone(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst) def test_recv_full_already_existing_empty_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-3") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_into_root_empty_pool(self): empty_pool = None try: srcfs = ZFSTest.pool.makeName(b"fs1") empty_pool = _TempPool() dst = empty_pool.makeName(b'@snap') with streams(srcfs, b"snap", None) as (_, (stream, _)): with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) finally: if empty_pool is not None: empty_pool.cleanUp() def test_recv_full_into_ro_pool(self): srcfs = ZFSTest.pool.makeName(b"fs1") dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap') with streams(srcfs, b"snap", None) as (_, (stream, _)): with self.assertRaises(lzc_exc.ReadOnlyPool): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_modified_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-5") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_with_snapshots(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-4") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) lzc.lzc_snapshot([dstfs + b"@snap1"]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.StreamMismatch, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_snapshot(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-6") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) lzc.lzc_snapshot([dst]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_missing_parent_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_but_specify_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src = srcfs + b"@snap" dstfs = ZFSTest.pool.makeName(b"fs2/received-30") dst = dstfs + b'@snap' origin1 = ZFSTest.pool.makeName(b"fs2@snap1") origin2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([origin1]) with streams(srcfs, src, None) as (_, (stream, _)): lzc.lzc_receive(dst, stream.fileno(), origin=origin1) origin = ZFSTest.pool.getFilesystem( b"fs2/received-30").getProperty('origin') self.assertEqual(origin, origin1) stream.seek(0) # because origin snap does not exist can't receive as a clone of it with self.assertRaises(( lzc_exc.DatasetNotFound, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin2) def test_recv_full_existing_empty_fs_and_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src = srcfs + b"@snap" dstfs = ZFSTest.pool.makeName(b"fs2/received-31") dst = dstfs + b'@snap' origin = dstfs + b'@dummy' lzc.lzc_create(dstfs) with streams(srcfs, src, None) as (_, (stream, _)): # because the destination fs already exists and has no snaps with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin) lzc.lzc_snapshot([origin]) stream.seek(0) # because the destination fs already exists and has the snap with self.assertRaises(( lzc_exc.StreamMismatch, lzc_exc.DatasetExists, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin) def test_recv_incremental_mounted_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-7") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with zfs_mount(dstfs): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_modified_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-15") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_snapname_used(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-8") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst2]) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_more_recent_snap_with_no_changes(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-9") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_non_clone_but_set_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-20") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) # because cannot receive incremental and set origin on a non-clone with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(dst2, incr.fileno(), origin=dst1) def test_recv_incremental_non_clone_but_set_random_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-21") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) # because origin snap does not exist can't receive as a clone of it with self.assertRaises(( lzc_exc.DatasetNotFound, lzc_exc.BadStream)): lzc.lzc_receive( dst2, incr.fileno(), origin=ZFSTest.pool.makeName(b"fs2/fs@snap")) def test_recv_incremental_more_recent_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-10") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): lzc.lzc_snapshot([dst_snap]) with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_duplicate(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-11") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_receive(dst2, incr.fileno()) incr.seek(0) with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_unrelated_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-12") dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): lzc.lzc_create(dstfs) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_nonexistent_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-13") dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_same_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" src_snap = srcfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(src_snap, incr.fileno()) def test_recv_clone_without_specifying_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(clone_dst, stream.fileno()) def test_recv_clone_invalid_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_receive( clone_dst, stream.fileno(), origin=ZFSTest.pool.makeName(b"fs1/fs")) def test_recv_clone_wrong_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap") wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) lzc.lzc_snapshot([wrong_origin]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive( clone_dst, stream.fileno(), origin=wrong_origin) def test_recv_clone_nonexistent_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap") wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive( clone_dst, stream.fileno(), origin=wrong_origin) def test_force_recv_full_existing_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-50") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_existing_modified_mounted_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-53") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with zfs_mount(dstfs) as mntdir: f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) for i in range(1024): f.write(b'x' * 1024) lzc.lzc_receive(dst, stream.fileno(), force=True) # The temporary file disappears and any access, even close(), # results in EIO. self.assertFalse(os.path.exists(f.name)) with self.assertRaises(IOError): f.close() # This test-case expects the behavior that should be there, # at the moment it may fail with DatasetExists or StreamMismatch # depending on the implementation. def test_force_recv_full_already_existing_with_snapshots(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-51") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dstfs + b"@snap1"]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_already_existing_with_same_snap(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-52") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_missing_parent_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_incremental_modified_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-60") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_receive(dst2, incr.fileno(), force=True) def test_force_recv_incremental_modified_mounted_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-64") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with zfs_mount(dstfs) as mntdir: f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) for i in range(1024): f.write(b'x' * 1024) lzc.lzc_receive(dst2, incr.fileno(), force=True) # The temporary file disappears and any access, even close(), # results in EIO. self.assertFalse(os.path.exists(f.name)) with self.assertRaises(IOError): f.close() def test_force_recv_incremental_modified_fs_plus_later_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-61") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertExists(dst2) self.assertNotExists(dst3) def test_force_recv_incremental_modified_fs_plus_same_name_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-62") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst2]) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno(), force=True) def test_force_recv_incremental_modified_fs_plus_held_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-63") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) with cleanup_fd() as cfd: lzc.lzc_hold({dst3: b'tag'}, cfd) with self.assertRaises(lzc_exc.DatasetBusy): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) self.assertExists(dst3) def test_force_recv_incremental_modified_fs_plus_cloned_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-70") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70") with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) lzc.lzc_clone(cloned, dst3) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) self.assertExists(dst3) def test_recv_incremental_into_cloned_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-71") dst1 = dstfs + b'@snap1' cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71") dst2 = cloned + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_clone(cloned, dst1) # test both graceful and with-force attempts with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst2, incr.fileno()) incr.seek(0) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) def test_recv_with_header_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/received") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) self.assertEqual(src, header['drr_toname']) snap = header['drr_toname'].split(b'@', 1)[1] lzc.lzc_receive_with_header( dst + b'@' + snap, stream.fileno(), c_header) name = os.path.basename(name) with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) def test_recv_fs_below_zvol(self): send = ZFSTest.pool.makeName(b"fs1@snap") zvol = ZFSTest.pool.makeName(b"fs1/zvol") dest = zvol + b"/fs@snap" props = {b"volsize": 1024 * 1024} lzc.lzc_snapshot([send]) lzc.lzc_create(zvol, ds_type='zvol', props=props) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(send, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(dest, stream.fileno()) def test_recv_zvol_over_fs_with_children(self): parent = ZFSTest.pool.makeName(b"fs1") child = parent + b"subfs" zvol = ZFSTest.pool.makeName(b"fs1/zvol") send = zvol + b"@snap" props = {b"volsize": 1024 * 1024} lzc.lzc_create(child) lzc.lzc_create(zvol, ds_type='zvol', props=props) lzc.lzc_snapshot([send]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(send, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True) def test_recv_zvol_overwrite_rootds(self): zvol = ZFSTest.pool.makeName(b"fs1/zvol") snap = zvol + b"@snap" rootds = ZFSTest.pool.getRoot().getName() props = {b"volsize": 1024 * 1024} lzc.lzc_create(zvol, ds_type='zvol', props=props) lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True) def test_send_full_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-20", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, None, stream.fileno()) def test_send_incr_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-21", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) def test_send_resume_token_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") dst = dstfs.getSnap() with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: for i in range(1, 10): with tempfile.NamedTemporaryFile(dir=mntdir) as f: f.write(b'x' * 1024 * i) f.flush() lzc.lzc_snapshot([src]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # XXX: if used more than twice move this code into an external func # format: --- token = dstfs.getProperty("receive_resume_token") self.assertNotEqual(token, b'-') tokens = token.split(b'-') self.assertEqual(len(tokens), 4) version = tokens[0] packed_size = int(tokens[2], 16) compressed_nvs = tokens[3] # Validate resume token self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION if sys.version_info < (3, 0): payload = ( zlib.decompress(str(bytearray.fromhex(compressed_nvs))) ) else: payload = ( zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) ) self.assertEqual(len(payload), packed_size) # Unpack resume_values = packed_nvlist_out(payload, packed_size) resumeobj = resume_values.get(b'object') resumeoff = resume_values.get(b'offset') with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: lzc.lzc_send_resume( src, None, rstream.fileno(), None, resumeobj, resumeoff) rstream.seek(0) lzc.lzc_receive_resumable(dst, rstream.fileno()) def test_send_resume_token_incremental(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") dst1 = dstfs.getSnap() dst2 = dstfs.getSnap() lzc.lzc_snapshot([snap1]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap1, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst1, stream.fileno()) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: for i in range(1, 10): with tempfile.NamedTemporaryFile(dir=mntdir) as f: f.write(b'x' * 1024 * i) f.flush() lzc.lzc_snapshot([snap2]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap2, snap1, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst2, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # format: --- token = dstfs.getProperty("receive_resume_token") self.assertNotEqual(token, '-') tokens = token.split(b'-') self.assertEqual(len(tokens), 4) version = tokens[0] packed_size = int(tokens[2], 16) compressed_nvs = tokens[3] # Validate resume token self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION if sys.version_info < (3, 0): payload = ( zlib.decompress(str(bytearray.fromhex(compressed_nvs))) ) else: payload = ( zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) ) self.assertEqual(len(payload), packed_size) # Unpack resume_values = packed_nvlist_out(payload, packed_size) resumeobj = resume_values.get(b'object') resumeoff = resume_values.get(b'offset') with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: lzc.lzc_send_resume( snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff) rstream.seek(0) lzc.lzc_receive_resumable(dst2, rstream.fileno()) def test_recv_full_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-30", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30") recvsnap = recvfs + b"@snap" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap, stream.fileno()) def test_recv_one(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") tosnap = ZFSTest.pool.makeName(b"recv@snap1") lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) def test_recv_one_size(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") tosnap = ZFSTest.pool.makeName(b"recv@snap1") lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) size = os.fstat(stream.fileno()).st_size stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) self.assertAlmostEqual(read, size, delta=read * 0.05) def test_recv_one_props(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"compression": 0x01, b"ns:prop": b"val" } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props) self.assertExists(tosnap) self.assertEqual(fs.getProperty("compression", "received"), b"on") self.assertEqual(fs.getProperty("ns:prop", "received"), b"val") def test_recv_one_invalid_prop(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"exec": 0xff, b"atime": 0x00 } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx: lzc.lzc_receive_one( tosnap, stream.fileno(), c_header, props=props) self.assertExists(tosnap) self.assertEqual(fs.getProperty("atime", "received"), b"off") for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PropertyInvalid) self.assertEqual(e.name, b"exec") def test_recv_with_cmdprops(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = {} cmdprops = { b"compression": 0x01, b"ns:prop": b"val" } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_with_cmdprops( tosnap, stream.fileno(), c_header, props=props, cmdprops=cmdprops) self.assertExists(tosnap) self.assertEqual(fs.getProperty("compression"), b"on") self.assertEqual(fs.getProperty("ns:prop"), b"val") def test_recv_with_cmdprops_and_recvprops(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"atime": 0x01, b"exec": 0x00, b"ns:prop": b"abc" } cmdprops = { b"compression": 0x01, b"ns:prop": b"def", b"exec": None, } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_with_cmdprops( tosnap, stream.fileno(), c_header, props=props, cmdprops=cmdprops) self.assertExists(tosnap) self.assertEqual(fs.getProperty("atime", True), b"on") self.assertEqual(fs.getProperty("exec", True), b"off") self.assertEqual(fs.getProperty("ns:prop", True), b"abc") self.assertEqual(fs.getProperty("compression"), b"on") self.assertEqual(fs.getProperty("ns:prop"), b"def") self.assertEqual(fs.getProperty("exec"), b"on") def test_recv_incr_across_clone_branch_point_no_origin(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-32", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32") recvsnap1 = recvfs + b"@snap1" recvsnap2 = recvfs + b"@snap2" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(recvsnap2, stream.fileno()) def test_recv_incr_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-31", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31") recvsnap1 = recvfs + b"@snap1" recvsnap2 = recvfs + b"@snap2" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) def test_recv_incr_across_clone_branch_point_new_fs(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-33", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33") recvsnap1 = recvfs1 + b"@snap" recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2") recvsnap2 = recvfs2 + b"@snap" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) def test_recv_bad_stream(self): dstfs = ZFSTest.pool.makeName(b"fs2/received") dst_snap = dstfs + b'@snap' with dev_zero() as fd: with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(dst_snap, fd) @needs_support(lzc.lzc_promote) def test_promote(self): origfs = ZFSTest.pool.makeName(b"fs2") snap = b"@promote-snap-1" origsnap = origfs + snap lzc.lzc_snap([origsnap]) clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1") lzc.lzc_clone(clonefs, origsnap) lzc.lzc_promote(clonefs) # the snapshot now should belong to the promoted fs self.assertExists(clonefs + snap) @needs_support(lzc.lzc_promote) def test_promote_too_long_snapname(self): # origfs name must be shorter than clonefs name origfs = ZFSTest.pool.makeName(b"fs2") clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2") snapprefix = b"@promote-snap-2-" pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix) snap = snapprefix + b'x' * pad_len origsnap = origfs + snap lzc.lzc_snap([origsnap]) lzc.lzc_clone(clonefs, origsnap) # This may fail on older buggy systems. # See: https://www.illumos.org/issues/5909 with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_promote(clonefs) @needs_support(lzc.lzc_promote) def test_promote_not_cloned(self): fs = ZFSTest.pool.makeName(b"fs2") with self.assertRaises(lzc_exc.NotClone): lzc.lzc_promote(fs) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, bad_fd) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_2(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, -2) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_3(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) bad_fd = hard + 1 with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, bad_fd) @unittest.skipIf(*illumos_bug_6379()) def test_hold_wrong_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: fd = tmp.fileno() with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, fd) def test_hold_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) def test_hold_empty(self): with cleanup_fd() as fd: lzc.lzc_hold({}, fd) def test_hold_empty_2(self): lzc.lzc_hold({}) def test_hold_vs_snap_destroy(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([snap], defer=False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotIsHeld) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) # after automatic hold cleanup and deferred destruction self.assertNotExists(snap) def test_hold_many_tags(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) def test_hold_many_snaps(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) def test_hold_many_with_one_missing(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) with cleanup_fd() as fd: missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) self.assertEqual(len(missing), 1) self.assertEqual(missing[0], snap2) def test_hold_many_with_all_missing(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() with cleanup_fd() as fd: missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) self.assertEqual(len(missing), 2) self.assertEqual(sorted(missing), sorted([snap1, snap2])) def test_hold_missing_fs(self): # XXX skip pre-created filesystems ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() snaps = lzc.lzc_hold({snap: b'tag'}) self.assertEqual([snap], snaps) def test_hold_missing_fs_auto_cleanup(self): # XXX skip pre-created filesystems ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() with cleanup_fd() as fd: snaps = lzc.lzc_hold({snap: b'tag'}, fd) self.assertEqual([snap], snaps) def test_hold_duplicate(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.HoldExists) def test_hold_across_pools(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.misc_pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) def test_hold_too_long_tag(self): snap = ZFSTest.pool.getRoot().getSnap() tag = b't' * 256 lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: tag}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, tag) # Apparently the full snapshot name is not checked for length # and this snapshot is treated as simply missing. @unittest.expectedFailure def test_hold_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_hold_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_hold_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_hold_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_get_holds(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) holds = lzc.lzc_get_holds(snap) self.assertEqual(len(holds), 2) self.assertIn(b'tag1', holds) self.assertIn(b'tag2', holds) self.assertIsInstance(holds[b'tag1'], (int, int)) def test_get_holds_after_auto_cleanup(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) holds = lzc.lzc_get_holds(snap) self.assertEqual(len(holds), 0) self.assertIsInstance(holds, dict) def test_get_holds_nonexistent_snap(self): snap = ZFSTest.pool.getRoot().getSnap() with self.assertRaises(lzc_exc.SnapshotNotFound): lzc.lzc_get_holds(snap) def test_get_holds_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_get_holds(snap) def test_get_holds_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_get_holds(snap) def test_get_holds_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_get_holds(snap) # A filesystem-like snapshot name is not recognized as # an invalid name. @unittest.expectedFailure def test_get_holds_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_get_holds(snap) def test_release_hold(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) lzc.lzc_hold({snap: b'tag'}) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 0) def test_release_hold_empty(self): ret = lzc.lzc_release({}) self.assertEqual(len(ret), 0) def test_release_hold_complex(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2, snap3]) lzc.lzc_hold({snap1: b'tag1'}) lzc.lzc_hold({snap1: b'tag2'}) lzc.lzc_hold({snap2: b'tag'}) lzc.lzc_hold({snap3: b'tag1'}) lzc.lzc_hold({snap3: b'tag2'}) holds = lzc.lzc_get_holds(snap1) self.assertEqual(len(holds), 2) holds = lzc.lzc_get_holds(snap2) self.assertEqual(len(holds), 1) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 2) release = { snap1: [b'tag1', b'tag2'], snap2: [b'tag'], snap3: [b'tag2'], } ret = lzc.lzc_release(release) self.assertEqual(len(ret), 0) holds = lzc.lzc_get_holds(snap1) self.assertEqual(len(holds), 0) holds = lzc.lzc_get_holds(snap2) self.assertEqual(len(holds), 0) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 1) ret = lzc.lzc_release({snap3: [b'tag1']}) self.assertEqual(len(ret), 0) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 0) def test_release_hold_before_auto_cleanup(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 0) def test_release_hold_and_snap_destruction(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_release({snap: [b'tag1']}) self.assertExists(snap) lzc.lzc_release({snap: [b'tag2']}) self.assertNotExists(snap) def test_release_hold_and_multiple_snap_destruction(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_release({snap: [b'tag']}) self.assertNotExists(snap) def test_release_hold_missing_tag(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap + b'#tag') def test_release_hold_missing_snap(self): snap = ZFSTest.pool.getRoot().getSnap() ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap) def test_release_hold_missing_snap_2(self): snap = ZFSTest.pool.getRoot().getSnap() ret = lzc.lzc_release({snap: [b'tag', b'another']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap) def test_release_hold_across_pools(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.misc_pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: lzc.lzc_hold({snap1: b'tag'}, fd) lzc.lzc_hold({snap2: b'tag'}, fd) with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) # Apparently the tag name is not verified, # only its existence is checked. @unittest.expectedFailure def test_release_hold_too_long_tag(self): snap = ZFSTest.pool.getRoot().getSnap() tag = b't' * 256 lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.HoldReleaseFailure): lzc.lzc_release({snap: [tag]}) # Apparently the full snapshot name is not checked for length # and this snapshot is treated as simply missing. @unittest.expectedFailure def test_release_hold_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with self.assertRaises(lzc_exc.HoldReleaseFailure): lzc.lzc_release({snap: [b'tag']}) def test_release_hold_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_release_hold_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_release_hold_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_sync_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_sync(pool) def test_sync_pool_forced(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_sync(pool, True) def test_reopen_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_reopen(pool) def test_reopen_pool_no_restart(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_reopen(pool, False) def test_channel_program_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_channel_program(pool, b"return {}") def test_channel_program_timeout(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" for i = 1,10000 do zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) end """ with self.assertRaises(lzc_exc.ZCPTimeout): lzc.lzc_channel_program(pool, zcp, instrlimit=1) def test_channel_program_memory_limit(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" for i = 1,10000 do zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) end """ with self.assertRaises(lzc_exc.ZCPSpaceError): lzc.lzc_channel_program(pool, zcp, memlimit=1) def test_channel_program_invalid_limits(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" return {} """ with self.assertRaises(lzc_exc.ZCPLimitInvalid): lzc.lzc_channel_program(pool, zcp, instrlimit=0) with self.assertRaises(lzc_exc.ZCPLimitInvalid): lzc.lzc_channel_program(pool, zcp, memlimit=0) def test_channel_program_syntax_error(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" inv+val:id """ with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx: lzc.lzc_channel_program(pool, zcp) self.assertTrue(b"syntax error" in ctx.exception.details) def test_channel_program_sync_snapshot(self): pool = ZFSTest.pool.getRoot().getName() snapname = ZFSTest.pool.makeName(b"@zcp") zcp = b""" zfs.sync.snapshot('""" + snapname + b"""') """ lzc.lzc_channel_program(pool, zcp) self.assertExists(snapname) def test_channel_program_runtime_error(self): pool = ZFSTest.pool.getRoot().getName() # failing an assertion raises a runtime error with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program(pool, b"assert(1 == 2)") self.assertTrue( b"assertion failed" in ctx.exception.details) # invoking the error() function raises a runtime error with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program(pool, b"error()") def test_channel_program_nosync_runtime_error(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" zfs.sync.snapshot('""" + pool + b"""@zcp') """ # lzc_channel_program_nosync() allows only "read-only" operations with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program_nosync(pool, zcp) self.assertTrue( b"running functions from the zfs.sync" in ctx.exception.details) def test_change_key_new(self): with encrypted_filesystem() as (fs, _): lzc.lzc_change_key( fs, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_change_key( name, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_not_loaded(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): lzc.lzc_change_key( fs, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_invalid_property(self): with encrypted_filesystem() as (fs, _): with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"}) def test_change_key_invalid_crypt_command(self): with encrypted_filesystem() as (fs, _): with self.assertRaises(lzc_exc.UnknownCryptCommand): lzc.lzc_change_key(fs, 'duplicate_key') def test_load_key(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) lzc.lzc_load_key(fs, False, key) def test_load_key_invalid(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyInvalid): lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN)) def test_load_key_already_loaded(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) lzc.lzc_load_key(fs, False, key) with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded): lzc.lzc_load_key(fs, False, key) def test_load_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_unload_key(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) def test_unload_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_unload_key(name) def test_unload_key_busy(self): with encrypted_filesystem() as (fs, _): with zfs_mount(fs): with self.assertRaises(lzc_exc.DatasetBusy): lzc.lzc_unload_key(fs) def test_unload_key_not_loaded(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): lzc.lzc_unload_key(fs) def test_checkpoint(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) def test_checkpoint_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_pool_checkpoint(pool) def test_checkpoint_already_exists(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) with self.assertRaises(lzc_exc.CheckpointExists): lzc.lzc_pool_checkpoint(pool) def test_checkpoint_discard(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) lzc.lzc_pool_checkpoint_discard(pool) def test_checkpoint_discard_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_pool_checkpoint_discard(pool) def test_checkpoint_discard_missing_checkpoint(self): pool = ZFSTest.pool.getRoot().getName() 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") tgt = ZFSTest.pool.makeName(b"target") lzc.lzc_create(src) lzc.lzc_rename(src, tgt) self.assertNotExists(src) self.assertExists(tgt) @needs_support(lzc.lzc_rename) def test_rename_nonexistent(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"target") with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_existing_target(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"target") lzc.lzc_create(src) lzc.lzc_create(tgt) with self.assertRaises(lzc_exc.FilesystemExists): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_nonexistent_target_parent(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"parent/target") lzc.lzc_create(src) with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_parent_is_zvol(self): src = ZFSTest.pool.makeName(b"source") zvol = ZFSTest.pool.makeName(b"parent") tgt = zvol + b"/target" props = {b"volsize": 1024 * 1024} lzc.lzc_create(src) lzc.lzc_create(zvol, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_destroy) def test_destroy(self): fs = ZFSTest.pool.makeName(b"test-fs") lzc.lzc_create(fs) lzc.lzc_destroy(fs) self.assertNotExists(fs) @needs_support(lzc.lzc_destroy) def test_destroy_nonexistent(self): fs = ZFSTest.pool.makeName(b"test-fs") 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'] BOOKMARKS = [b'bmark', b'bmark1', b'bmark2'] _cachefile_suffix = ".cachefile" # XXX Whether to do a sloppy but much faster cleanup # or a proper but slower one. _recreate_pools = True def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]): self._filesystems = filesystems self._readonly = readonly if sys.version_info < (3, 0): self._pool_name = b'pool.' + bytes(uuid.uuid4()) else: self._pool_name = b'pool.' + bytes(str(uuid.uuid4()), encoding='utf-8') self._root = _Filesystem(self._pool_name) (fd, self._pool_file_path) = tempfile.mkstemp( suffix='.zpool', prefix='tmp-') if readonly: cachefile = self._pool_file_path + _TempPool._cachefile_suffix else: cachefile = 'none' self._zpool_create = [ 'zpool', 'create', '-o', 'cachefile=' + cachefile, '-O', 'mountpoint=legacy', self._pool_name, self._pool_file_path] try: os.ftruncate(fd, size) os.close(fd) subprocess.check_output( self._zpool_create, stderr=subprocess.STDOUT) for fs in filesystems: lzc.lzc_create(self.makeName(fs)) self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks') if readonly: # To make a pool read-only it must exported and re-imported # with readonly option. # The most deterministic way to re-import the pool is by using # a cache file. # But the cache file has to be stashed away before the pool is # exported, because otherwise the pool is removed from the # cache. shutil.copyfile(cachefile, cachefile + '.tmp') subprocess.check_output( ['zpool', 'export', '-f', self._pool_name], stderr=subprocess.STDOUT) os.rename(cachefile + '.tmp', cachefile) subprocess.check_output( ['zpool', 'import', '-f', '-N', '-c', cachefile, '-o', 'readonly=on', self._pool_name], stderr=subprocess.STDOUT) os.remove(cachefile) except subprocess.CalledProcessError as e: self.cleanUp() if b'permission denied' in e.output: raise unittest.SkipTest( 'insufficient privileges to run libzfs_core tests') print('command failed: ', e.output) raise except Exception: self.cleanUp() raise def reset(self): if self._readonly: return if not self.__class__._recreate_pools: snaps = [] for fs in [''] + self._filesystems: for snap in self.__class__.SNAPSHOTS: snaps.append(self.makeName(fs + '@' + snap)) self.getRoot().visitSnaps(lambda snap: snaps.append(snap)) lzc.lzc_destroy_snaps(snaps, defer=False) if self._bmarks_supported: bmarks = [] for fs in [''] + self._filesystems: for bmark in self.__class__.BOOKMARKS: bmarks.append(self.makeName(fs + '#' + bmark)) self.getRoot().visitBookmarks( lambda bmark: bmarks.append(bmark)) lzc.lzc_destroy_bookmarks(bmarks) self.getRoot().reset() return # On the Buildbot builders this may fail with "pool is busy" # Retry 5 times before raising an error retry = 0 while True: try: subprocess.check_output( ['zpool', 'destroy', '-f', self._pool_name], stderr=subprocess.STDOUT) subprocess.check_output( self._zpool_create, stderr=subprocess.STDOUT) break except subprocess.CalledProcessError as e: if b'pool is busy' in e.output and retry < 5: retry += 1 time.sleep(1) continue else: print('command failed: ', e.output) raise for fs in self._filesystems: lzc.lzc_create(self.makeName(fs)) self.getRoot().reset() def cleanUp(self): try: subprocess.check_output( ['zpool', 'destroy', '-f', self._pool_name], stderr=subprocess.STDOUT) except Exception: pass try: os.remove(self._pool_file_path) except Exception: pass try: os.remove(self._pool_file_path + _TempPool._cachefile_suffix) except Exception: pass try: os.remove( self._pool_file_path + _TempPool._cachefile_suffix + '.tmp') except Exception: pass def makeName(self, relative=None): if not relative: return self._pool_name if relative.startswith((b'@', b'#')): return self._pool_name + relative return self._pool_name + b'/' + relative def makeTooLongName(self, prefix=None): if not prefix: prefix = b'x' prefix = self.makeName(prefix) pad_len = lzc.MAXNAMELEN + 1 - len(prefix) if pad_len > 0: return prefix + b'x' * pad_len else: return prefix def makeTooLongComponent(self, prefix=None): padding = b'x' * (lzc.MAXNAMELEN + 1) if not prefix: prefix = padding else: prefix = prefix + padding return self.makeName(prefix) def getRoot(self): return self._root def getFilesystem(self, fsname): return _Filesystem(self._pool_name + b'/' + fsname) def isPoolFeatureAvailable(self, feature): output = subprocess.check_output( ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) output = output.strip() return output != '' def isPoolFeatureEnabled(self, feature): output = subprocess.check_output( ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) output = output.split()[2] return output in [b'active', b'enabled'] class _Filesystem(object): def __init__(self, name): self._name = name self.reset() def getName(self): return self._name def reset(self): self._children = [] self._fs_id = 0 self._snap_id = 0 self._bmark_id = 0 def getFilesystem(self): self._fs_id += 1 fsname = self._name + b'/fs' + str(self._fs_id).encode() fs = _Filesystem(fsname) self._children.append(fs) return fs def getProperty(self, propname, received=False): if received: output = subprocess.check_output( ['zfs', 'get', '-pH', '-o', 'received', propname, self._name]) else: output = subprocess.check_output( ['zfs', 'get', '-pH', '-o', 'value', propname, self._name]) return output.strip() def _makeSnapName(self, i): return self._name + b'@snap' + str(i).encode() def getSnap(self): self._snap_id += 1 return self._makeSnapName(self._snap_id) def _makeBookmarkName(self, i): return self._name + b'#bmark' + bytes(i) def getBookmark(self): self._bmark_id += 1 return self._makeBookmarkName(self._bmark_id) def _makeTooLongName(self, too_long_component): if too_long_component: return b'x' * (lzc.MAXNAMELEN + 1) # Note that another character is used for one of '/', '@', '#'. comp_len = lzc.MAXNAMELEN - len(self._name) if comp_len > 0: return b'x' * comp_len else: return b'x' def getTooLongFilesystemName(self, too_long_component): return self._name + b'/' + self._makeTooLongName(too_long_component) def getTooLongSnap(self, too_long_component): return self._name + b'@' + self._makeTooLongName(too_long_component) def getTooLongBookmark(self, too_long_component): return self._name + b'#' + self._makeTooLongName(too_long_component) def _visitFilesystems(self, visitor): for child in self._children: child._visitFilesystems(visitor) visitor(self) def visitFilesystems(self, visitor): def _fsVisitor(fs): visitor(fs._name) self._visitFilesystems(_fsVisitor) def visitSnaps(self, visitor): def _snapVisitor(fs): for i in range(1, fs._snap_id + 1): visitor(fs._makeSnapName(i)) self._visitFilesystems(_snapVisitor) def visitBookmarks(self, visitor): def _bmarkVisitor(fs): for i in range(1, fs._bmark_id + 1): visitor(fs._makeBookmarkName(i)) self._visitFilesystems(_bmarkVisitor) # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_005_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_005_neg.ksh index 4cbc7e339031..ce89c6835775 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_005_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_005_neg.ksh @@ -1,99 +1,99 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2016 by Delphix. All rights reserved. # . $STF_SUITE/tests/functional/cli_root/cli_common.kshlib # # DESCRIPTION: # Verify 'zfs receive' fails with unsupported scenarios. # including: # (1) Invalid send streams; # (2) The received incremental send doesn't match the filesystem # latest status. # # STRATEGY: # 1. Preparation for unsupported scenarios # 2. Execute 'zfs receive' # 3. Verify the results are failed # verify_runnable "both" function cleanup { typeset snap typeset bkup for snap in $init_snap $inc_snap; do snapexists $snap && \ log_must zfs destroy -f $snap done datasetexists $rst_root && \ log_must zfs destroy -Rf $rst_root for bkup in $full_bkup $inc_bkup; do [[ -e $bkup ]] && \ log_must rm -f $bkup done } log_assert "Verify 'zfs receive' fails with unsupported scenarios." log_onexit cleanup init_snap=$TESTPOOL/$TESTFS@initsnap inc_snap=$TESTPOOL/$TESTFS@incsnap rst_root=$TESTPOOL/rst_ctr rst_init_snap=$rst_root/$TESTFS@init_snap rst_inc_snap=$rst_root/$TESTFS@inc_snap full_bkup=$TEST_BASE_DIR/full_bkup.$$ inc_bkup=$TEST_BASE_DIR/inc_bkup.$$ log_must zfs create $rst_root log_must zfs snapshot $init_snap log_must eval "zfs send $init_snap > $full_bkup" log_note "'zfs receive' fails with invalid send streams." -log_mustnot eval "zfs receive $rst_init_snap < /dev/zero" -log_mustnot eval "zfs receive -d $rst_root $inc_bkup" #make changes on the restoring filesystem log_must touch $ZFSROOT/$rst_root/$TESTFS/tmpfile log_mustnot eval "zfs receive $rst_inc_snap < $inc_bkup" log_mustnot eval "zfs receive -d $rst_root < $inc_bkup" log_pass "Unsupported scenarios to 'zfs receive' fail as expected." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh index 87997e76c245..2105bc4d23e4 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh @@ -1,102 +1,102 @@ #!/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 . All rights reserved. # . $STF_SUITE/include/libtest.shlib . $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib # # DESCRIPTION: # 'zfs send -b' should works as expected. # # STRATEGY: # 1. Create a source dataset and set some properties # 2. Verify command line options interact with '-b' correctly # 3. Send the dataset and its properties to a new "backup" destination # 4. Set some properties on the new "backup" dataset # 5. Restore the "backup" dataset to a new destination # 6. Verify only original (received) properties are sent from "backup" # verify_runnable "both" function cleanup { for ds in "$SENDFS" "$BACKUP" "$RESTORE"; do datasetexists $ds && log_must zfs destroy -r $ds done } log_assert "'zfs send -b' should work as expected." log_onexit cleanup SENDFS="$TESTPOOL/sendfs" BACKUP="$TESTPOOL/backup" RESTORE="$TESTPOOL/restore" # 1. Create a source dataset and set some properties log_must zfs create $SENDFS log_must zfs snapshot "$SENDFS@s1" log_must zfs bookmark "$SENDFS@s1" "$SENDFS#bm" log_must zfs snapshot "$SENDFS@s2" log_must zfs set "compression=gzip" $SENDFS log_must zfs set "org.openzfs:prop=val" $SENDFS log_must zfs set "org.openzfs:snapprop=val" "$SENDFS@s1" # 2. Verify command line options interact with '-b' correctly typeset opts=("" "p" "Rp" "cew" "nv" "D" "DLPRcenpvw") for opt in ${opts[@]}; do - log_must eval "zfs send -b$opt $SENDFS@s1 > /dev/null" - log_must eval "zfs send -b$opt -i $SENDFS@s1 $SENDFS@s2 > /dev/null" - log_must eval "zfs send -b$opt -I $SENDFS@s1 $SENDFS@s2 > /dev/null" + log_must eval "zfs send -b$opt $SENDFS@s1 >$TEST_BASE_DIR/devnull" + log_must eval "zfs send -b$opt -i $SENDFS@s1 $SENDFS@s2 >$TEST_BASE_DIR/devnull" + log_must eval "zfs send -b$opt -I $SENDFS@s1 $SENDFS@s2 >$TEST_BASE_DIR/devnull" done for opt in ${opts[@]}; do - log_mustnot eval "zfs send -b$opt $SENDFS > /dev/null" - log_mustnot eval "zfs send -b$opt $SENDFS#bm > /dev/null" + log_mustnot eval "zfs send -b$opt $SENDFS >$TEST_BASE_DIR/devnull" + log_mustnot eval "zfs send -b$opt $SENDFS#bm >$TEST_BASE_DIR/devnull" done # Do 3..6 in a loop to verify various combination of "zfs send" options typeset opts=("" "p" "R" "pR" "cew") for opt in ${opts[@]}; do # 3. Send the dataset and its properties to a new "backup" destination # NOTE: only need to send properties (-p) here log_must eval "zfs send -p $SENDFS@s1 | zfs recv $BACKUP" # 4. Set some properties on the new "backup" dataset # NOTE: override "received" values and set some new properties as well log_must zfs set "compression=lz4" $BACKUP log_must zfs set "exec=off" $BACKUP log_must zfs set "org.openzfs:prop=newval" $BACKUP log_must zfs set "org.openzfs:newprop=newval" $BACKUP log_must zfs set "org.openzfs:snapprop=newval" "$BACKUP@s1" log_must zfs set "org.openzfs:newsnapprop=newval" "$BACKUP@s1" # 5. Restore the "backup" dataset to a new destination log_must eval "zfs send -b$opt $BACKUP@s1 | zfs recv $RESTORE" # 6. Verify only original (received) properties are sent from "backup" log_must eval "check_prop_source $RESTORE compression gzip received" log_must eval "check_prop_source $RESTORE org.openzfs:prop val received" log_must eval "check_prop_source $RESTORE@s1 org.openzfs:snapprop val received" log_must eval "check_prop_source $RESTORE exec on default" log_must eval "check_prop_missing $RESTORE org.openzfs:newprop" log_must eval "check_prop_missing $RESTORE@s1 org.openzfs:newsnapprop" # cleanup log_must zfs destroy -r $BACKUP log_must zfs destroy -r $RESTORE done log_pass "'zfs send -b' works as expected." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_003_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_003_pos.ksh index 825a10d0f8a2..0b55254f75d6 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_003_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_003_pos.ksh @@ -1,69 +1,69 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2016 by Delphix. All rights reserved. # . $STF_SUITE/include/libtest.shlib # # DESCRIPTION: # 'zfs send -i' can deal with abbreviated snapshot name. # # STRATEGY: # 1. Create pool, fs and two snapshots. # 2. Make sure 'zfs send -i' support abbreviated snapshot name. # verify_runnable "both" function cleanup { datasetexists $snap1 && log_must zfs destroy $snap1 datasetexists $snap2 && log_must zfs destroy $snap2 } log_assert "'zfs send -i' can deal with abbreviated snapshot name." log_onexit cleanup snap1=$TESTPOOL/$TESTFS@snap1; snap2=$TESTPOOL/$TESTFS@snap2 set -A args "$snap1 $snap2" \ "${snap1##*@} $snap2" "@${snap1##*@} $snap2" log_must zfs snapshot $snap1 log_must zfs snapshot $snap2 typeset -i i=0 while (( i < ${#args[*]} )); do - log_must eval "zfs send -i ${args[i]} > /dev/null" + log_must eval "zfs send -i ${args[i]} >$TEST_BASE_DIR/devnull" (( i += 1 )) done log_pass "'zfs send -i' deal with abbreviated snapshot name passed." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_004_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_004_neg.ksh index 4a9d29fce1cf..dfa9fc251d6b 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_004_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_004_neg.ksh @@ -1,109 +1,109 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2016 by Delphix. All rights reserved. # . $STF_SUITE/tests/functional/cli_root/cli_common.kshlib # # DESCRIPTION: # Verify 'zfs send' fails with malformed parameters. # # STRATEGY: # 1. Define malformed parameters in array # 2. Feed the parameters to 'zfs send' # 3. Verify the result # verify_runnable "both" function cleanup { typeset snap f for snap in $snap1 $snap2 $snap3; do snapexists $snap && \ log_must zfs destroy -f $snap done for f in $tmpfile1 $tmpfile2; do if [[ -e $f ]]; then rm -f $f fi done } fs=$TESTPOOL/$TESTFS snap1=$fs@snap1 snap2=$fs@snap2 snap3=$fs@snap3 set -A badargs \ "" "$TESTPOOL" "$TESTFS" "$fs" "$fs@nonexistent_snap" "?" \ "$snap1/blah" "$snap1@blah" "-i" "-x" "-i $fs" \ "-x $snap1 $snap2" "-i $snap1" \ "-i $snap2 $snap1" "$snap1 $snap2" "-i $snap1 $snap2 $snap3" \ "-ii $snap1 $snap2" "-iii $snap1 $snap2" " -i $snap2 $snap1/blah" \ "-i $snap2/blah $snap1" \ "-i $snap2/blah $snap1/blah" \ "-i $snap1 blah@blah" \ "-i blah@blah $snap1" \ "-i $snap1 ${snap2##*@}" "-i $snap1 @${snap2##*@}" \ "-i ${snap1##*@} ${snap2##*@}" "-i @${snap1##*@} @${snap2##*@}" \ "-i ${snap1##*@} $snap2/blah" "-i @${snap1##*@} $snap2/blah" \ "-i @@${snap1##*@} $snap2" "-i $snap1 -i $snap1 $snap2" \ "-i snap1 snap2" "-i $snap1 snap2" \ "-i $snap1 $snap2 -i $snap1 $snap2" \ "-i snap1 $snap2 -i snap1 $snap2" log_assert "Verify that invalid parameters to 'zfs send' are caught." log_onexit cleanup log_must zfs snapshot $snap1 tmpfile1=$TESTDIR/testfile1.$$ log_must touch $tmpfile1 log_must zfs snapshot $snap2 tmpfile2=$TESTDIR/testfile2.$$ log_must touch $tmpfile2 log_must zfs snapshot $snap3 typeset -i i=0 while (( i < ${#badargs[*]} )) do - log_mustnot eval "zfs send ${badargs[i]} >/dev/null" + log_mustnot eval "zfs send ${badargs[i]} >$TEST_BASE_DIR/devnull" (( i = i + 1 )) done #Testing zfs send fails by send backup stream to terminal for arg in "$snap1" "-i $snap1 $snap2"; do log_mustnot eval "zfs send $arg >/dev/console" done log_pass "Invalid parameters to 'zfs send' are caught as expected." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_005_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_005_pos.ksh index 9f369e372dee..c9e37cbbad8e 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_005_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_005_pos.ksh @@ -1,66 +1,66 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2012, 2016 by Delphix. All rights reserved. # . $STF_SUITE/include/libtest.shlib # # DESCRIPTION: # 'zfs send -R' can send from read-only imported pool. It needs to # detect that the pool is read-only and not try to place holds on # datasets being sent. # # STRATEGY: # 1. Create a recursive snapshot on the whole pool. # 2. 'zfs send -R' the recursive snapshots. # verify_runnable "both" function cleanup { poolexists $TESTPOOL && log_must_busy zpool export $TESTPOOL log_must zpool import $TESTPOOL datasetexists $TESTPOOL@snap && \ log_must zfs destroy -r $TESTPOOL@snap } log_assert "'zfs send -R' can send from read-only pools" log_onexit cleanup log_must zfs snapshot -r $TESTPOOL@snap log_must zpool export $TESTPOOL log_must zpool import -o readonly=on $TESTPOOL -log_must eval "zfs send -R $TESTPOOL@snap >/dev/null" +log_must eval "zfs send -R $TESTPOOL@snap >$TEST_BASE_DIR/devnull" log_pass "'zfs send -R' can send from read-only pools" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted.ksh index 490e146ba6f0..1e63b29ade1f 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted.ksh @@ -1,76 +1,76 @@ #!/bin/ksh -p # # CDDL HEADER START # # 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. # # CDDL HEADER END # # # Copyright (c) 2017, Datto, Inc. All rights reserved. # . $STF_SUITE/include/libtest.shlib # # DESCRIPTION: # ZFS should perform unencrypted sends of encrypted datasets, unless the '-p' # or '-R' options are specified. # # STRATEGY: # 1. Create an encrypted dataset # 6. Create a child encryption root # 2. Snapshot the dataset # 3. Attempt a send # 4. Attempt a send with properties # 5. Attempt a replication send # 7. Unmount the parent and unload its key # 8. Attempt a send of the parent dataset # 9. Attempt a send of the child encryption root # verify_runnable "both" function cleanup { datasetexists $TESTPOOL/$TESTFS1 && \ log_must zfs destroy -r $TESTPOOL/$TESTFS1 } log_onexit cleanup log_assert "ZFS should perform unencrypted sends of encrypted datasets, " \ "unless the '-p' or '-R' options are specified" typeset passphrase="password" typeset passphrase1="password1" typeset snap="$TESTPOOL/$TESTFS1@snap" log_must eval "echo $passphrase | zfs create -o encryption=on" \ "-o keyformat=passphrase $TESTPOOL/$TESTFS1" log_must eval "echo $passphrase1 | zfs create -o encryption=on" \ "-o keyformat=passphrase $TESTPOOL/$TESTFS1/child" log_must zfs snapshot -r $snap -log_must eval "zfs send $snap > /dev/null" -log_mustnot eval "zfs send -p $snap > /dev/null" -log_mustnot eval "zfs send -R $snap > /dev/null" +log_must eval "zfs send $snap >$TEST_BASE_DIR/devnull" +log_mustnot eval "zfs send -p $snap >$TEST_BASE_DIR/devnull" +log_mustnot eval "zfs send -R $snap >$TEST_BASE_DIR/devnull" log_must zfs unmount $TESTPOOL/$TESTFS1 log_must zfs unload-key $TESTPOOL/$TESTFS1 -log_mustnot eval "zfs send $snap > /dev/null" -log_must eval "zfs send $TESTPOOL/$TESTFS1/child@snap > /dev/null" +log_mustnot eval "zfs send $snap >$TEST_BASE_DIR/devnull" +log_must eval "zfs send $TESTPOOL/$TESTFS1/child@snap >$TEST_BASE_DIR/devnull" log_pass "ZFS performs unencrypted sends of encrypted datasets, unless the" \ "'-p' or '-R' options are specified" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_unloaded.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_unloaded.ksh index 112ee1143d10..9d59494fc635 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_unloaded.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_unloaded.ksh @@ -1,59 +1,59 @@ #!/bin/ksh -p # # CDDL HEADER START # # 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. # # CDDL HEADER END # # # Copyright (c) 2017, Datto, Inc. All rights reserved. # . $STF_SUITE/include/libtest.shlib # # DESCRIPTION: # ZFS should not perform unencrypted sends from encrypted datasets # with unloaded keys. # # STRATEGY: # 1. Create an encrypted dataset # 2. Snapshot the dataset # 3. Unload the dataset key # 4. Verify sending the stream fails # verify_runnable "both" function cleanup { datasetexists $TESTPOOL/$TESTFS1 && \ log_must zfs destroy -r $TESTPOOL/$TESTFS1 } log_onexit cleanup log_assert "ZFS should not perform unencrypted sends from encrypted datasets" \ "with unloaded keys." typeset passphrase="password" typeset snap="$TESTPOOL/$TESTFS1@snap" log_must eval "echo $passphrase | zfs create -o encryption=on" \ "-o keyformat=passphrase $TESTPOOL/$TESTFS1" log_must zfs snapshot $snap log_must zfs unmount $TESTPOOL/$TESTFS1 log_must zfs unload-key $TESTPOOL/$TESTFS1 -log_mustnot eval "zfs send $snap > /dev/null" +log_mustnot eval "zfs send $snap >$TEST_BASE_DIR/devnull" log_pass "ZFS does not perform unencrypted sends from encrypted datasets" \ "with unloaded keys." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_raw.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_raw.ksh index 85cc7407e1a1..065eea3ebd86 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_raw.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_raw.ksh @@ -1,79 +1,79 @@ #!/bin/ksh -p # # CDDL HEADER START # # 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. # # CDDL HEADER END # # # Copyright (c) 2017, Datto, Inc. All rights reserved. # . $STF_SUITE/include/libtest.shlib # # DESCRIPTION: # ZFS should perform raw sends of datasets. # # STRATEGY: # 1. Create an encrypted dataset # 2. Snapshot the default dataset and the encrypted dataset # 3. Attempt a raw send of both datasets # 4. Attempt a raw send with properties of both datasets # 5. Attempt a raw replication send of both datasets # 6. Unmount and unload the encrypted dataset key # 7. Attempt a raw send of the encrypted dataset # verify_runnable "both" function cleanup { snapexists $snap && \ log_must zfs destroy $snap datasetexists $TESTPOOL/$TESTFS1 && \ log_must zfs destroy -r $TESTPOOL/$TESTFS1 } log_onexit cleanup log_assert "ZFS should perform raw sends of datasets" typeset passphrase="password" typeset snap="$TESTPOOL/$TESTFS@snap" typeset snap1="$TESTPOOL/$TESTFS1@snap" log_must eval "echo $passphrase | zfs create -o encryption=on" \ "-o keyformat=passphrase $TESTPOOL/$TESTFS1" log_must zfs snapshot $snap log_must zfs snapshot $snap1 -log_must eval "zfs send -w $snap > /dev/null" -log_must eval "zfs send -w $snap1 > /dev/null" +log_must eval "zfs send -w $snap >$TEST_BASE_DIR/devnull" +log_must eval "zfs send -w $snap1 >$TEST_BASE_DIR/devnull" log_note "Verify ZFS can perform raw sends with properties" -log_must eval "zfs send -wp $snap > /dev/null" -log_must eval "zfs send -wp $snap1 > /dev/null" +log_must eval "zfs send -wp $snap >$TEST_BASE_DIR/devnull" +log_must eval "zfs send -wp $snap1 >$TEST_BASE_DIR/devnull" log_note "Verify ZFS can perform raw replication sends" -log_must eval "zfs send -wR $snap > /dev/null" -log_must eval "zfs send -wR $snap1 > /dev/null" +log_must eval "zfs send -wR $snap >$TEST_BASE_DIR/devnull" +log_must eval "zfs send -wR $snap1 >$TEST_BASE_DIR/devnull" log_note "Verify ZFS can perform a raw send of an encrypted datasets with" \ "its key unloaded" log_must zfs unmount $TESTPOOL/$TESTFS1 log_must zfs unload-key $TESTPOOL/$TESTFS1 -log_must eval "zfs send -w $snap1 > /dev/null" +log_must eval "zfs send -w $snap1 >$TEST_BASE_DIR/devnull" log_pass "ZFS performs raw sends of datasets" diff --git a/tests/zfs-tests/tests/functional/redacted_send/redacted_negative.ksh b/tests/zfs-tests/tests/functional/redacted_send/redacted_negative.ksh index 432460fa2fcd..e591cca0bbde 100755 --- a/tests/zfs-tests/tests/functional/redacted_send/redacted_negative.ksh +++ b/tests/zfs-tests/tests/functional/redacted_send/redacted_negative.ksh @@ -1,94 +1,94 @@ #!/bin/ksh # # 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 (c) 2018 by Delphix. All rights reserved. # . $STF_SUITE/tests/functional/redacted_send/redacted.kshlib # # Description: # Test that redacted send correctly detects invalid arguments. # typeset sendfs="$POOL2/sendfs" typeset recvfs="$POOL2/recvfs" typeset clone1="$POOL2/clone1" typeset clone2="$POOL2/clone2" typeset clone3="$POOL2/clone3" typeset clone3="$POOL2/clone4" typeset tmpdir="$(get_prop mountpoint $POOL)/tmp" typeset stream=$(mktemp $tmpdir/stream.XXXX) log_onexit redacted_cleanup $sendfs $recvfs $clone3 log_must zfs create $sendfs log_must zfs snapshot $sendfs@snap1 log_must zfs snapshot $sendfs@snap2 log_must zfs snapshot $sendfs@snap3 log_must zfs clone $sendfs@snap2 $clone1 log_must zfs snapshot $clone1@snap log_must zfs bookmark $clone1@snap $clone1#book log_must zfs clone $sendfs@snap2 $clone2 log_must zfs snapshot $clone2@snap # Incompatible flags log_must zfs redact $sendfs@snap2 book $clone1@snap -log_mustnot eval "zfs send -R --redact book $sendfs@snap2 >/dev/null" +log_mustnot eval "zfs send -R --redact book $sendfs@snap2 >$TEST_BASE_DIR/devnull" typeset arg for arg in "$sendfs" "$clone1#book"; do - log_mustnot eval "zfs send --redact book $arg >/dev/null" + log_mustnot eval "zfs send --redact book $arg >$TEST_BASE_DIR/devnull" done # Bad redaction list arguments log_mustnot zfs redact $sendfs@snap1 log_mustnot zfs redact $sendfs@snap1 book log_mustnot zfs redact $sendfs#book1 book4 $clone1 log_mustnot zfs redact $sendfs@snap1 book snap2 snap3 log_mustnot zfs redact $sendfs@snap1 book @snap2 @snap3 -log_mustnot eval "zfs send --redact $sendfs#book $sendfs@snap >/dev/null" +log_mustnot eval "zfs send --redact $sendfs#book $sendfs@snap >$TEST_BASE_DIR/devnull" # Redaction snapshots not a descendant of tosnap log_mustnot zfs redact $sendfs@snap2 book $sendfs@snap2 log_must zfs redact $sendfs@snap2 book2 $clone1@snap $clone2@snap log_must eval "zfs send --redact book2 $sendfs@snap2 >$stream" log_must zfs redact $sendfs@snap2 book3 $clone1@snap $clone2@snap log_must eval "zfs send -i $sendfs@snap1 --redact book3 $sendfs@snap2 \ - >/dev/null" + >$TEST_BASE_DIR/devnull" log_mustnot zfs redact $sendfs@snap3 $sendfs@snap3 $clone1@snap # Full redacted sends of redacted datasets are not allowed. log_must eval "zfs recv $recvfs <$stream" log_must zfs snapshot $recvfs@snap log_must zfs clone $recvfs@snap $clone3 log_must zfs snapshot $clone3@snap log_mustnot zfs redact $recvfs@snap book5 $clone3@snap # Nor may a redacted dataset appear in the redaction list. log_mustnot zfs redact testpool2/recvfs@snap2 book7 testpool2/recvfs@snap # Non-redaction bookmark cannot be sent and produces invalid argument error log_must zfs bookmark "$sendfs@snap1" "$sendfs#book8" log_must eval "zfs send --redact book8 -i $sendfs@snap1 $sendfs@snap2 2>&1 | head -n 100 | grep 'not a redaction bookmark'" # Error messages for common usage errors log_mustnot_expect "not contain '#'" zfs redact $sendfs@snap1 \#book $sendfs@snap2 log_mustnot_expect "not contain '#'" zfs redact $sendfs@snap1 $sendfs#book $sendfs@snap2 log_mustnot_expect "full dataset names" zfs redact $sendfs@snap1 book @snap2 log_mustnot_expect "full dataset names" zfs redact $sendfs@snap1 book @snap2 log_mustnot_expect "full dataset names" zfs redact $sendfs@snap1 \#book @snap2 log_mustnot_expect "descendent of snapshot" zfs redact $sendfs@snap2 book $sendfs@snap1 log_pass "Verify that redacted send correctly detects invalid arguments." diff --git a/tests/zfs-tests/tests/functional/redacted_send/redacted_resume.ksh b/tests/zfs-tests/tests/functional/redacted_send/redacted_resume.ksh index 8118ea59ec8b..4ab04a0e5730 100755 --- a/tests/zfs-tests/tests/functional/redacted_send/redacted_resume.ksh +++ b/tests/zfs-tests/tests/functional/redacted_send/redacted_resume.ksh @@ -1,88 +1,88 @@ #!/bin/ksh # # 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 (c) 2018 by Delphix. All rights reserved. # . $STF_SUITE/tests/functional/redacted_send/redacted.kshlib # # Description: # Verify that resumable send works correctly with redacted streams. # # Strategy: # 1. Do a full redacted resumable send. # 2. Verify the received contents are correct. # 3. Do an incremental redacted resumable send. # 4. Verify the received contents are correct. # 5. Verify that recv -A removes a partially received dataset. # typeset ds_name="resume" typeset sendfs="$POOL/$ds_name" typeset recvfs="$POOL2/$ds_name" typeset clone="$POOL/${ds_name}_clone" typeset clone1="$POOL/${ds_name}_clone1" typeset tmpdir="$(get_prop mountpoint $POOL)/tmp" typeset stream=$(mktemp $tmpdir/stream.XXXX) setup_dataset $ds_name '' typeset clone_mnt="$(get_prop mountpoint $clone)" typeset send_mnt="$(get_prop mountpoint $sendfs)" typeset recv_mnt="/$POOL2/$ds_name" log_onexit redacted_cleanup $sendfs $recvfs log_must stride_dd -i /dev/urandom -o $clone_mnt/f2 -b 512 -c 64 -s 512 log_must zfs snapshot $clone@snap1 # Do the full resumable send log_must zfs redact $sendfs@snap book1 $clone@snap1 resume_test "zfs send --redact book1 $sendfs@snap" $tmpdir $recvfs log_must mount_redacted -f $recvfs log_must set_tunable32 ALLOW_REDACTED_DATASET_MOUNT 1 log_must diff $send_mnt/f1 $recv_mnt/f1 log_must eval "get_diff $send_mnt/f2 $recv_mnt/f2 >$tmpdir/get_diff.out" typeset range=$(cat $tmpdir/get_diff.out) [[ "$RANGE9" = "$range" ]] || log_fail "Unexpected range: $range" log_must dd if=/dev/urandom of=$send_mnt/f3 bs=1024k count=3 log_must zfs snapshot $sendfs@snap2 log_must zfs clone $sendfs@snap2 $clone1 typeset clone1_mnt="$(get_prop mountpoint $clone1)" log_must dd if=/dev/urandom of=$clone1_mnt/f3 bs=128k count=3 conv=notrunc log_must zfs snapshot $clone1@snap # Do the incremental resumable send log_must zfs redact $sendfs@snap2 book2 $clone1@snap resume_test "zfs send --redact book2 -i $sendfs#book1 $sendfs@snap2" \ $tmpdir $recvfs log_must diff $send_mnt/f1 $recv_mnt/f1 log_must diff $send_mnt/f2 $recv_mnt/f2 log_must eval "get_diff $send_mnt/f3 $recv_mnt/f3 >$tmpdir/get_diff.out" range=$(cat $tmpdir/get_diff.out) [[ "$RANGE10" = "$range" ]] || log_fail "Unexpected range: $range" # Test recv -A works properly and verify saved sends are not allowed log_mustnot zfs recv -A $recvfs log_must zfs destroy -R $recvfs log_mustnot zfs recv -A $recvfs log_must eval "zfs send --redact book1 $sendfs@snap >$stream" dd if=$stream bs=64k count=1 | log_mustnot zfs receive -s $recvfs [[ "-" = $(get_prop receive_resume_token $recvfs) ]] && \ log_fail "Receive token not found." -log_mustnot eval "zfs send --saved --redact book1 $recvfs > /dev/null" +log_mustnot eval "zfs send --saved --redact book1 $recvfs >$TEST_BASE_DIR/devnull" log_must zfs recv -A $recvfs log_must datasetnonexists $recvfs log_pass "Resumable send works correctly with redacted streams." diff --git a/tests/zfs-tests/tests/functional/removal/removal_with_send.ksh b/tests/zfs-tests/tests/functional/removal/removal_with_send.ksh index 59e66aca5256..a08247838105 100755 --- a/tests/zfs-tests/tests/functional/removal/removal_with_send.ksh +++ b/tests/zfs-tests/tests/functional/removal/removal_with_send.ksh @@ -1,37 +1,37 @@ #! /bin/ksh -p # # CDDL HEADER START # # 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. # # CDDL HEADER END # # # Copyright (c) 2014, 2017 by Delphix. All rights reserved. # . $STF_SUITE/include/libtest.shlib . $STF_SUITE/tests/functional/removal/removal.kshlib default_setup_noexit "$DISKS" log_onexit default_cleanup_noexit function callback { create_snapshot $TESTPOOL/$TESTFS $TESTSNAP log_must ksh -c \ - "zfs send $TESTPOOL/$TESTFS@$TESTSNAP >/dev/null" + "zfs send $TESTPOOL/$TESTFS@$TESTSNAP >$TEST_BASE_DIR/devnull" return 0 } test_removal_with_operation callback log_pass "Can use send during removal" diff --git a/tests/zfs-tests/tests/functional/rsend/send_invalid.ksh b/tests/zfs-tests/tests/functional/rsend/send_invalid.ksh index a0abe64b4cca..2ce7ee4a082f 100755 --- a/tests/zfs-tests/tests/functional/rsend/send_invalid.ksh +++ b/tests/zfs-tests/tests/functional/rsend/send_invalid.ksh @@ -1,52 +1,52 @@ #!/bin/ksh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version a.0. # You may only use this file in accordance with the terms of version # a.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. # # # Portions Copyright 2020 iXsystems, Inc. # . $STF_SUITE/include/libtest.shlib . $STF_SUITE/tests/functional/rsend/rsend.kshlib # # Description: # Verify that send with invalid options will fail gracefully. # # Strategy: # 1. Perform zfs send on the cli with the order of the snapshots reversed # 2. Perform zfs send using libzfs with the order of the snapshots reversed # verify_runnable "both" log_assert "Verify that send with invalid options will fail gracefully." function cleanup { datasetexists $testfs && destroy_dataset $testfs -r } log_onexit cleanup testfs=$POOL/fs log_must zfs create $testfs log_must zfs snap $testfs@snap0 log_must zfs snap $testfs@snap1 # Test bad send with the CLI -log_mustnot eval "zfs send -i $testfs@snap1 $testfs@snap0 >/dev/null" +log_mustnot eval "zfs send -i $testfs@snap1 $testfs@snap0 >$TEST_BASE_DIR/devnull" # Test bad send with libzfs/libzfs_core log_must badsend $testfs@snap0 $testfs@snap1 log_pass "Send with invalid options fails gracefully." diff --git a/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh b/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh index d5eb9a0edc11..c390327a5b57 100755 --- a/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh +++ b/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh @@ -1,110 +1,110 @@ #!/bin/ksh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version a.0. # You may only use this file in accordance with the terms of version # a.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 (c) 2019 Datto Inc. # Copyright (c) 2020 by Delphix. All rights reserved. # . $STF_SUITE/include/libtest.shlib . $STF_SUITE/tests/functional/rsend/rsend.kshlib # # Description: # Verify that a partially received dataset can be sent with # 'zfs send --saved'. # # Strategy: # 1. Setup a pool with partially received filesystem # 2. Perform saved send without incremental # 3. Perform saved send with incremental # 4. Perform saved send with incremental, resuming from a token # 5. Perform negative tests for invalid command inputs # verify_runnable "both" log_assert "Verify that a partially received dataset can be sent with " \ "'zfs send --saved'." function cleanup { destroy_dataset $POOL/testfs2 "-r" destroy_dataset $POOL/stream "-r" destroy_dataset $POOL/recvfs "-r" destroy_dataset $POOL/partialfs "-r" } log_onexit cleanup log_must zfs create $POOL/testfs2 log_must zfs create $POOL/stream mntpnt=$(get_prop mountpoint $POOL/testfs2) # Setup a pool with partially received filesystems log_must mkfile 1m $mntpnt/filea log_must zfs snap $POOL/testfs2@a log_must mkfile 1m $mntpnt/fileb log_must zfs snap $POOL/testfs2@b log_must eval "zfs send $POOL/testfs2@a | zfs recv $POOL/recvfs" log_must eval "zfs send -i $POOL/testfs2@a $POOL/testfs2@b > " \ "/$POOL/stream/inc.send" log_must eval "zfs send $POOL/testfs2@b > /$POOL/stream/full.send" mess_send_file /$POOL/stream/full.send mess_send_file /$POOL/stream/inc.send log_mustnot zfs recv -s $POOL/recvfullfs < /$POOL/stream/full.send log_mustnot zfs recv -s $POOL/recvfs < /$POOL/stream/inc.send # Perform saved send without incremental log_mustnot eval "zfs send --saved $POOL/recvfullfs | zfs recv -s " \ "$POOL/partialfs" token=$(zfs get -Hp -o value receive_resume_token $POOL/partialfs) log_must eval "zfs send -t $token | zfs recv -s $POOL/partialfs" file_check $POOL/recvfullfs $POOL/partialfs log_must zfs destroy -r $POOL/partialfs # Perform saved send with incremental log_must eval "zfs send $POOL/recvfs@a | zfs recv $POOL/partialfs" log_mustnot eval "zfs send --saved $POOL/recvfs | " \ "zfs recv -s $POOL/partialfs" token=$(zfs get -Hp -o value receive_resume_token $POOL/partialfs) log_must eval "zfs send -t $token | zfs recv -s $POOL/partialfs" file_check $POOL/recvfs $POOL/partialfs log_must zfs destroy -r $POOL/partialfs # Perform saved send with incremental, resuming from token log_must eval "zfs send $POOL/recvfs@a | zfs recv $POOL/partialfs" log_must eval "zfs send --saved $POOL/recvfs > " \ "/$POOL/stream/partial.send" mess_send_file /$POOL/stream/partial.send log_mustnot zfs recv -s $POOL/partialfs < /$POOL/stream/partial.send token=$(zfs get -Hp -o value receive_resume_token $POOL/partialfs) log_must eval "zfs send -t $token | zfs recv -s $POOL/partialfs" file_check $POOL/recvfs $POOL/partialfs # Perform negative tests for invalid command inputs set -A badargs \ "" \ "$POOL/recvfs@a" \ "-i $POOL/recvfs@a $POOL/recvfs@b" \ "-R $POOL/recvfs" \ "-p $POOL/recvfs" \ "-I $POOL/recvfs" \ "-h $POOL/recvfs" while (( i < ${#badargs[*]} )) do - log_mustnot eval "zfs send --saved ${badargs[i]} >/dev/null" + log_mustnot eval "zfs send --saved ${badargs[i]} >$TEST_BASE_DIR/devnull" (( i = i + 1 )) done log_pass "A partially received dataset can be sent with 'zfs send --saved'."