diff --git a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py index 08b58b5d1e0d..87138b305f73 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 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): + def test_bookmarks_foreign_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