1# SPDX-License-Identifier: Apache-2.0 2# 3# Copyright 2015 ClusterHQ 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18""" 19Tests for `libzfs_core` operations. 20 21These are mostly functional and conformance tests that validate 22that the operations produce expected effects or fail with expected 23exceptions. 24""" 25from __future__ import absolute_import, division, print_function 26 27import unittest 28import contextlib 29import errno 30import filecmp 31import os 32import platform 33import resource 34import shutil 35import stat 36import subprocess 37import sys 38import tempfile 39import time 40import uuid 41import itertools 42import zlib 43from .. import _libzfs_core as lzc 44from .. import exceptions as lzc_exc 45from .._nvlist import packed_nvlist_out 46 47 48def _print(*args): 49 for arg in args: 50 print(arg, end=' ') 51 print() 52 53 54@contextlib.contextmanager 55def suppress(exceptions=None): 56 try: 57 yield 58 except BaseException as e: 59 if exceptions is None or isinstance(e, exceptions): 60 pass 61 else: 62 raise 63 64 65@contextlib.contextmanager 66def _zfs_mount(fs): 67 mntdir = tempfile.mkdtemp() 68 if platform.system() == 'SunOS': 69 mount_cmd = ['mount', '-F', 'zfs', fs, mntdir] 70 else: 71 mount_cmd = ['mount', '-t', 'zfs', fs, mntdir] 72 unmount_cmd = ['umount', '-f', mntdir] 73 74 try: 75 subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT) 76 try: 77 yield mntdir 78 finally: 79 with suppress(): 80 subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT) 81 except subprocess.CalledProcessError as e: 82 print('failed to mount %s @ %s : %s' % (fs, mntdir, e.output)) 83 raise 84 finally: 85 os.rmdir(mntdir) 86 87 88# XXX On illumos it is impossible to explicitly mount a snapshot. 89# So, either we need to implicitly mount it using .zfs/snapshot/ 90# or we need to create a clone and mount it readonly (and discard 91# it afterwards). 92# At the moment the former approach is implemented. 93 94# This dictionary is used to keep track of mounted filesystems 95# (not snapshots), so that we do not try to mount a filesystem 96# more than once in the case more than one snapshot of the 97# filesystem is accessed from the same context or the filesystem 98# and its snapshot are accessed. 99_mnttab = {} 100 101 102@contextlib.contextmanager 103def _illumos_mount_fs(fs): 104 if fs in _mnttab: 105 yield _mnttab[fs] 106 else: 107 with _zfs_mount(fs) as mntdir: 108 _mnttab[fs] = mntdir 109 try: 110 yield mntdir 111 finally: 112 _mnttab.pop(fs, None) 113 114 115@contextlib.contextmanager 116def _illumos_mount_snap(fs): 117 (base, snap) = fs.split('@', 1) 118 with _illumos_mount_fs(base) as mntdir: 119 yield os.path.join(mntdir, '.zfs', 'snapshot', snap) 120 121 122@contextlib.contextmanager 123def _zfs_mount_illumos(fs): 124 if '@' not in fs: 125 with _illumos_mount_fs(fs) as mntdir: 126 yield mntdir 127 else: 128 with _illumos_mount_snap(fs) as mntdir: 129 yield mntdir 130 131 132if platform.system() == 'SunOS': 133 zfs_mount = _zfs_mount_illumos 134else: 135 zfs_mount = _zfs_mount 136 137 138@contextlib.contextmanager 139def cleanup_fd(): 140 fd = os.open('/dev/zfs', os.O_EXCL) 141 try: 142 yield fd 143 finally: 144 os.close(fd) 145 146 147@contextlib.contextmanager 148def os_open(name, mode): 149 fd = os.open(name, mode) 150 try: 151 yield fd 152 finally: 153 os.close(fd) 154 155 156@contextlib.contextmanager 157def dev_null(): 158 with tempfile.TemporaryFile(suffix='.zstream') as fd: 159 yield fd.fileno() 160 161 162@contextlib.contextmanager 163def dev_zero(): 164 with os_open('/dev/zero', os.O_RDONLY) as fd: 165 yield fd 166 167 168@contextlib.contextmanager 169def temp_file_in_fs(fs): 170 with zfs_mount(fs) as mntdir: 171 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 172 for i in range(1024): 173 f.write(b'x' * 1024) 174 f.flush() 175 yield f.name 176 177 178def make_snapshots(fs, before, modified, after): 179 def _maybe_snap(snap): 180 if snap is not None: 181 if not snap.startswith(fs): 182 snap = fs + b'@' + snap 183 lzc.lzc_snapshot([snap]) 184 return snap 185 186 before = _maybe_snap(before) 187 with temp_file_in_fs(fs) as name: 188 modified = _maybe_snap(modified) 189 after = _maybe_snap(after) 190 191 return (name, (before, modified, after)) 192 193 194@contextlib.contextmanager 195def streams(fs, first, second): 196 (filename, snaps) = make_snapshots(fs, None, first, second) 197 with tempfile.TemporaryFile(suffix='.zstream') as full: 198 lzc.lzc_send(snaps[1], None, full.fileno()) 199 full.seek(0) 200 if snaps[2] is not None: 201 with tempfile.TemporaryFile(suffix='.zstream') as incremental: 202 lzc.lzc_send(snaps[2], snaps[1], incremental.fileno()) 203 incremental.seek(0) 204 yield (filename, (full, incremental)) 205 else: 206 yield (filename, (full, None)) 207 208 209@contextlib.contextmanager 210def encrypted_filesystem(): 211 fs = ZFSTest.pool.getFilesystem(b"encrypted") 212 name = fs.getName() 213 filename = None 214 key = os.urandom(lzc.WRAPPING_KEY_LEN) 215 with tempfile.NamedTemporaryFile() as f: 216 filename = "file://" + f.name 217 props = { 218 b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, 219 b"keylocation": filename.encode(), 220 b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, 221 } 222 lzc.lzc_create(name, 'zfs', props=props, key=key) 223 yield (name, key) 224 225 226def runtimeSkipIf(check_method, message): 227 def _decorator(f): 228 def _f(_self, *args, **kwargs): 229 if check_method(_self): 230 return _self.skipTest(message) 231 else: 232 return f(_self, *args, **kwargs) 233 _f.__name__ = f.__name__ 234 return _f 235 return _decorator 236 237 238def skipIfFeatureAvailable(feature, message): 239 return runtimeSkipIf( 240 lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature), 241 message) 242 243 244def skipUnlessFeatureEnabled(feature, message): 245 return runtimeSkipIf( 246 lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature), 247 message) 248 249 250def skipUnlessBookmarksSupported(f): 251 return skipUnlessFeatureEnabled( 252 'bookmarks', 'bookmarks are not enabled')(f) 253 254 255def snap_always_unmounted_before_destruction(): 256 # Apparently OpenZFS automatically unmounts the snapshot 257 # only if it is mounted at its default .zfs/snapshot 258 # mountpoint under Linux. 259 return ( 260 platform.system() != 'Linux', 'snapshot is not auto-unmounted') 261 262 263def illumos_bug_6379(): 264 # zfs_ioc_hold() panics on a bad cleanup fd 265 return ( 266 platform.system() == 'SunOS', 267 'see https://www.illumos.org/issues/6379') 268 269 270def needs_support(function): 271 return unittest.skipUnless( 272 lzc.is_supported(function), 273 '{} not available'.format(function.__name__)) 274 275 276class ZFSTest(unittest.TestCase): 277 POOL_FILE_SIZE = 128 * 1024 * 1024 278 FILESYSTEMS = [b'fs1', b'fs2', b'fs1/fs'] 279 280 pool = None 281 misc_pool = None 282 readonly_pool = None 283 284 @classmethod 285 def setUpClass(cls): 286 try: 287 cls.pool = _TempPool(filesystems=cls.FILESYSTEMS) 288 cls.misc_pool = _TempPool() 289 cls.readonly_pool = _TempPool( 290 filesystems=cls.FILESYSTEMS, readonly=True) 291 cls.pools = [cls.pool, cls.misc_pool, cls.readonly_pool] 292 except Exception: 293 cls._cleanUp() 294 raise 295 296 @classmethod 297 def tearDownClass(cls): 298 cls._cleanUp() 299 300 @classmethod 301 def _cleanUp(cls): 302 for pool in [cls.pool, cls.misc_pool, cls.readonly_pool]: 303 if pool is not None: 304 pool.cleanUp() 305 306 def setUp(self): 307 pass 308 309 def tearDown(self): 310 for pool in ZFSTest.pools: 311 pool.reset() 312 313 def assertExists(self, name): 314 self.assertTrue( 315 lzc.lzc_exists(name), 'ZFS dataset %s does not exist' % (name, )) 316 317 def assertNotExists(self, name): 318 self.assertFalse( 319 lzc.lzc_exists(name), 'ZFS dataset %s exists' % (name, )) 320 321 def test_exists(self): 322 self.assertExists(ZFSTest.pool.makeName()) 323 324 def test_exists_in_ro_pool(self): 325 self.assertExists(ZFSTest.readonly_pool.makeName()) 326 327 def test_exists_failure(self): 328 self.assertNotExists(ZFSTest.pool.makeName(b'nonexistent')) 329 330 def test_create_fs(self): 331 name = ZFSTest.pool.makeName(b"fs1/fs/test1") 332 333 lzc.lzc_create(name) 334 self.assertExists(name) 335 336 def test_create_zvol(self): 337 name = ZFSTest.pool.makeName(b"fs1/fs/zvol") 338 props = {b"volsize": 1024 * 1024} 339 340 lzc.lzc_create(name, ds_type='zvol', props=props) 341 self.assertExists(name) 342 # On Gentoo with ZFS 0.6.5.4 the volume is busy 343 # and can not be destroyed right after its creation. 344 # A reason for this is unknown at the moment. 345 # Because of that the post-test clean up could fail. 346 time.sleep(0.1) 347 348 def test_create_fs_with_prop(self): 349 name = ZFSTest.pool.makeName(b"fs1/fs/test2") 350 props = {b"atime": 0} 351 352 lzc.lzc_create(name, props=props) 353 self.assertExists(name) 354 355 def test_create_fs_wrong_ds_type(self): 356 name = ZFSTest.pool.makeName(b"fs1/fs/test1") 357 358 with self.assertRaises(lzc_exc.DatasetTypeInvalid): 359 lzc.lzc_create(name, ds_type='wrong') 360 361 def test_create_fs_below_zvol(self): 362 name = ZFSTest.pool.makeName(b"fs1/fs/zvol") 363 props = {b"volsize": 1024 * 1024} 364 365 lzc.lzc_create(name, ds_type='zvol', props=props) 366 with self.assertRaises(lzc_exc.WrongParent): 367 lzc.lzc_create(name + b'/fs') 368 369 def test_create_zvol_below_zvol(self): 370 name = ZFSTest.pool.makeName(b"fs1/fs/zvol") 371 props = {b"volsize": 1024 * 1024} 372 373 lzc.lzc_create(name, ds_type='zvol', props=props) 374 with self.assertRaises(lzc_exc.WrongParent): 375 lzc.lzc_create(name + b'/zvol', ds_type='zvol', props=props) 376 377 def test_create_fs_duplicate(self): 378 name = ZFSTest.pool.makeName(b"fs1/fs/test6") 379 380 lzc.lzc_create(name) 381 382 with self.assertRaises(lzc_exc.FilesystemExists): 383 lzc.lzc_create(name) 384 385 def test_create_fs_in_ro_pool(self): 386 name = ZFSTest.readonly_pool.makeName(b"fs") 387 388 with self.assertRaises(lzc_exc.ReadOnlyPool): 389 lzc.lzc_create(name) 390 391 def test_create_fs_without_parent(self): 392 name = ZFSTest.pool.makeName(b"fs1/nonexistent/test") 393 394 with self.assertRaises(lzc_exc.ParentNotFound): 395 lzc.lzc_create(name) 396 self.assertNotExists(name) 397 398 def test_create_fs_in_nonexistent_pool(self): 399 name = b"no-such-pool/fs" 400 401 with self.assertRaises(lzc_exc.ParentNotFound): 402 lzc.lzc_create(name) 403 self.assertNotExists(name) 404 405 def test_create_fs_with_invalid_prop(self): 406 name = ZFSTest.pool.makeName(b"fs1/fs/test3") 407 props = {b"BOGUS": 0} 408 409 with self.assertRaises(lzc_exc.PropertyInvalid): 410 lzc.lzc_create(name, 'zfs', props) 411 self.assertNotExists(name) 412 413 def test_create_fs_with_invalid_prop_type(self): 414 name = ZFSTest.pool.makeName(b"fs1/fs/test4") 415 props = {b"recordsize": b"128k"} 416 417 with self.assertRaises(lzc_exc.PropertyInvalid): 418 lzc.lzc_create(name, 'zfs', props) 419 self.assertNotExists(name) 420 421 def test_create_fs_with_invalid_prop_val(self): 422 name = ZFSTest.pool.makeName(b"fs1/fs/test5") 423 props = {b"atime": 20} 424 425 with self.assertRaises(lzc_exc.PropertyInvalid): 426 lzc.lzc_create(name, 'zfs', props) 427 self.assertNotExists(name) 428 429 def test_create_fs_with_invalid_name(self): 430 name = ZFSTest.pool.makeName(b"@badname") 431 432 with self.assertRaises(lzc_exc.NameInvalid): 433 lzc.lzc_create(name) 434 self.assertNotExists(name) 435 436 def test_create_fs_with_invalid_pool_name(self): 437 name = b"bad!pool/fs" 438 439 with self.assertRaises(lzc_exc.NameInvalid): 440 lzc.lzc_create(name) 441 self.assertNotExists(name) 442 443 def test_create_encrypted_fs(self): 444 fs = ZFSTest.pool.getFilesystem(b"encrypted") 445 name = fs.getName() 446 filename = None 447 with tempfile.NamedTemporaryFile() as f: 448 filename = "file://" + f.name 449 props = { 450 b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, 451 b"keylocation": filename.encode(), 452 b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, 453 } 454 key = os.urandom(lzc.WRAPPING_KEY_LEN) 455 lzc.lzc_create(name, 'zfs', props=props, key=key) 456 self.assertEqual(fs.getProperty("encryption"), b"aes-256-ccm") 457 self.assertEqual(fs.getProperty("encryptionroot"), name) 458 self.assertEqual(fs.getProperty("keylocation"), filename.encode()) 459 self.assertEqual(fs.getProperty("keyformat"), b"raw") 460 461 def test_snapshot(self): 462 snapname = ZFSTest.pool.makeName(b"@snap") 463 snaps = [snapname] 464 465 lzc.lzc_snapshot(snaps) 466 self.assertExists(snapname) 467 468 def test_snapshot_empty_list(self): 469 lzc.lzc_snapshot([]) 470 471 def test_snapshot_user_props(self): 472 snapname = ZFSTest.pool.makeName(b"@snap") 473 snaps = [snapname] 474 props = {b"user:foo": b"bar"} 475 476 lzc.lzc_snapshot(snaps, props) 477 self.assertExists(snapname) 478 479 def test_snapshot_invalid_props(self): 480 snapname = ZFSTest.pool.makeName(b"@snap") 481 snaps = [snapname] 482 props = {b"foo": b"bar"} 483 484 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 485 lzc.lzc_snapshot(snaps, props) 486 487 self.assertEqual(len(ctx.exception.errors), len(snaps)) 488 for e in ctx.exception.errors: 489 self.assertIsInstance(e, lzc_exc.PropertyInvalid) 490 self.assertNotExists(snapname) 491 492 def test_snapshot_ro_pool(self): 493 snapname1 = ZFSTest.readonly_pool.makeName(b"@snap") 494 snapname2 = ZFSTest.readonly_pool.makeName(b"fs1@snap") 495 snaps = [snapname1, snapname2] 496 497 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 498 lzc.lzc_snapshot(snaps) 499 500 # NB: one common error is reported. 501 self.assertEqual(len(ctx.exception.errors), 1) 502 for e in ctx.exception.errors: 503 self.assertIsInstance(e, lzc_exc.ReadOnlyPool) 504 self.assertNotExists(snapname1) 505 self.assertNotExists(snapname2) 506 507 def test_snapshot_nonexistent_pool(self): 508 snapname = b"no-such-pool@snap" 509 snaps = [snapname] 510 511 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 512 lzc.lzc_snapshot(snaps) 513 514 self.assertEqual(len(ctx.exception.errors), 1) 515 for e in ctx.exception.errors: 516 self.assertIsInstance(e, lzc_exc.FilesystemNotFound) 517 518 def test_snapshot_nonexistent_fs(self): 519 snapname = ZFSTest.pool.makeName(b"nonexistent@snap") 520 snaps = [snapname] 521 522 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 523 lzc.lzc_snapshot(snaps) 524 525 self.assertEqual(len(ctx.exception.errors), 1) 526 for e in ctx.exception.errors: 527 self.assertIsInstance(e, lzc_exc.FilesystemNotFound) 528 529 def test_snapshot_nonexistent_and_existent_fs(self): 530 snapname1 = ZFSTest.pool.makeName(b"@snap") 531 snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") 532 snaps = [snapname1, snapname2] 533 534 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 535 lzc.lzc_snapshot(snaps) 536 537 self.assertEqual(len(ctx.exception.errors), 1) 538 for e in ctx.exception.errors: 539 self.assertIsInstance(e, lzc_exc.FilesystemNotFound) 540 self.assertNotExists(snapname1) 541 self.assertNotExists(snapname2) 542 543 def test_multiple_snapshots_nonexistent_fs(self): 544 snapname1 = ZFSTest.pool.makeName(b"nonexistent@snap1") 545 snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap2") 546 snaps = [snapname1, snapname2] 547 548 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 549 lzc.lzc_snapshot(snaps) 550 551 # XXX two errors should be reported but alas 552 self.assertEqual(len(ctx.exception.errors), 1) 553 for e in ctx.exception.errors: 554 self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) 555 self.assertNotExists(snapname1) 556 self.assertNotExists(snapname2) 557 558 def test_multiple_snapshots_multiple_nonexistent_fs(self): 559 snapname1 = ZFSTest.pool.makeName(b"nonexistent1@snap") 560 snapname2 = ZFSTest.pool.makeName(b"nonexistent2@snap") 561 snaps = [snapname1, snapname2] 562 563 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 564 lzc.lzc_snapshot(snaps) 565 566 self.assertEqual(len(ctx.exception.errors), 2) 567 for e in ctx.exception.errors: 568 self.assertIsInstance(e, lzc_exc.FilesystemNotFound) 569 self.assertNotExists(snapname1) 570 self.assertNotExists(snapname2) 571 572 def test_snapshot_already_exists(self): 573 snapname = ZFSTest.pool.makeName(b"@snap") 574 snaps = [snapname] 575 576 lzc.lzc_snapshot(snaps) 577 578 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 579 lzc.lzc_snapshot(snaps) 580 581 self.assertEqual(len(ctx.exception.errors), 1) 582 for e in ctx.exception.errors: 583 self.assertIsInstance(e, lzc_exc.SnapshotExists) 584 585 def test_multiple_snapshots_for_same_fs(self): 586 snapname1 = ZFSTest.pool.makeName(b"@snap1") 587 snapname2 = ZFSTest.pool.makeName(b"@snap2") 588 snaps = [snapname1, snapname2] 589 590 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 591 lzc.lzc_snapshot(snaps) 592 593 self.assertEqual(len(ctx.exception.errors), 1) 594 for e in ctx.exception.errors: 595 self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) 596 self.assertNotExists(snapname1) 597 self.assertNotExists(snapname2) 598 599 def test_multiple_snapshots(self): 600 snapname1 = ZFSTest.pool.makeName(b"@snap") 601 snapname2 = ZFSTest.pool.makeName(b"fs1@snap") 602 snaps = [snapname1, snapname2] 603 604 lzc.lzc_snapshot(snaps) 605 self.assertExists(snapname1) 606 self.assertExists(snapname2) 607 608 def test_multiple_existing_snapshots(self): 609 snapname1 = ZFSTest.pool.makeName(b"@snap") 610 snapname2 = ZFSTest.pool.makeName(b"fs1@snap") 611 snaps = [snapname1, snapname2] 612 613 lzc.lzc_snapshot(snaps) 614 615 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 616 lzc.lzc_snapshot(snaps) 617 618 self.assertEqual(len(ctx.exception.errors), 2) 619 for e in ctx.exception.errors: 620 self.assertIsInstance(e, lzc_exc.SnapshotExists) 621 622 def test_multiple_new_and_existing_snapshots(self): 623 snapname1 = ZFSTest.pool.makeName(b"@snap") 624 snapname2 = ZFSTest.pool.makeName(b"fs1@snap") 625 snapname3 = ZFSTest.pool.makeName(b"fs2@snap") 626 snaps = [snapname1, snapname2] 627 more_snaps = snaps + [snapname3] 628 629 lzc.lzc_snapshot(snaps) 630 631 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 632 lzc.lzc_snapshot(more_snaps) 633 634 self.assertEqual(len(ctx.exception.errors), 2) 635 for e in ctx.exception.errors: 636 self.assertIsInstance(e, lzc_exc.SnapshotExists) 637 self.assertNotExists(snapname3) 638 639 def test_snapshot_multiple_errors(self): 640 snapname1 = ZFSTest.pool.makeName(b"@snap") 641 snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") 642 snapname3 = ZFSTest.pool.makeName(b"fs1@snap") 643 snaps = [snapname1] 644 more_snaps = [snapname1, snapname2, snapname3] 645 646 # create 'snapname1' snapshot 647 lzc.lzc_snapshot(snaps) 648 649 # attempt to create 3 snapshots: 650 # 1. duplicate snapshot name 651 # 2. refers to filesystem that doesn't exist 652 # 3. could have succeeded if not for 1 and 2 653 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 654 lzc.lzc_snapshot(more_snaps) 655 656 # It seems that FilesystemNotFound overrides the other error, 657 # but it doesn't have to. 658 self.assertGreater(len(ctx.exception.errors), 0) 659 for e in ctx.exception.errors: 660 self.assertIsInstance( 661 e, (lzc_exc.SnapshotExists, lzc_exc.FilesystemNotFound)) 662 self.assertNotExists(snapname2) 663 self.assertNotExists(snapname3) 664 665 def test_snapshot_different_pools(self): 666 snapname1 = ZFSTest.pool.makeName(b"@snap") 667 snapname2 = ZFSTest.misc_pool.makeName(b"@snap") 668 snaps = [snapname1, snapname2] 669 670 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 671 lzc.lzc_snapshot(snaps) 672 673 # NB: one common error is reported. 674 self.assertEqual(len(ctx.exception.errors), 1) 675 for e in ctx.exception.errors: 676 self.assertIsInstance(e, lzc_exc.PoolsDiffer) 677 self.assertNotExists(snapname1) 678 self.assertNotExists(snapname2) 679 680 def test_snapshot_different_pools_ro_pool(self): 681 snapname1 = ZFSTest.pool.makeName(b"@snap") 682 snapname2 = ZFSTest.readonly_pool.makeName(b"@snap") 683 snaps = [snapname1, snapname2] 684 685 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 686 lzc.lzc_snapshot(snaps) 687 688 # NB: one common error is reported. 689 self.assertEqual(len(ctx.exception.errors), 1) 690 for e in ctx.exception.errors: 691 # NB: depending on whether the first attempted snapshot is 692 # for the read-only pool a different error is reported. 693 self.assertIsInstance( 694 e, (lzc_exc.PoolsDiffer, lzc_exc.ReadOnlyPool)) 695 self.assertNotExists(snapname1) 696 self.assertNotExists(snapname2) 697 698 def test_snapshot_invalid_name(self): 699 snapname1 = ZFSTest.pool.makeName(b"@bad&name") 700 snapname2 = ZFSTest.pool.makeName(b"fs1@bad*name") 701 snapname3 = ZFSTest.pool.makeName(b"fs2@snap") 702 snaps = [snapname1, snapname2, snapname3] 703 704 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 705 lzc.lzc_snapshot(snaps) 706 707 # NB: one common error is reported. 708 self.assertEqual(len(ctx.exception.errors), 1) 709 for e in ctx.exception.errors: 710 self.assertIsInstance(e, lzc_exc.NameInvalid) 711 self.assertIsNone(e.name) 712 713 def test_snapshot_too_long_complete_name(self): 714 snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") 715 snapname2 = ZFSTest.pool.makeTooLongName(b"fs2@") 716 snapname3 = ZFSTest.pool.makeName(b"@snap") 717 snaps = [snapname1, snapname2, snapname3] 718 719 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 720 lzc.lzc_snapshot(snaps) 721 722 self.assertEqual(len(ctx.exception.errors), 2) 723 for e in ctx.exception.errors: 724 self.assertIsInstance(e, lzc_exc.NameTooLong) 725 self.assertIsNotNone(e.name) 726 727 def test_snapshot_too_long_snap_name(self): 728 snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") 729 snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") 730 snapname3 = ZFSTest.pool.makeName(b"@snap") 731 snaps = [snapname1, snapname2, snapname3] 732 733 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: 734 lzc.lzc_snapshot(snaps) 735 736 # NB: one common error is reported. 737 self.assertEqual(len(ctx.exception.errors), 1) 738 for e in ctx.exception.errors: 739 self.assertIsInstance(e, lzc_exc.NameTooLong) 740 self.assertIsNone(e.name) 741 742 def test_destroy_nonexistent_snapshot(self): 743 lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], False) 744 lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], True) 745 746 def test_destroy_snapshot_of_nonexistent_pool(self): 747 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: 748 lzc.lzc_destroy_snaps([b"no-such-pool@snap"], False) 749 750 for e in ctx.exception.errors: 751 self.assertIsInstance(e, lzc_exc.PoolNotFound) 752 753 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: 754 lzc.lzc_destroy_snaps([b"no-such-pool@snap"], True) 755 756 for e in ctx.exception.errors: 757 self.assertIsInstance(e, lzc_exc.PoolNotFound) 758 759 # NB: note the difference from the nonexistent pool test. 760 def test_destroy_snapshot_of_nonexistent_fs(self): 761 lzc.lzc_destroy_snaps( 762 [ZFSTest.pool.makeName(b"nonexistent@snap")], False) 763 lzc.lzc_destroy_snaps( 764 [ZFSTest.pool.makeName(b"nonexistent@snap")], True) 765 766 # Apparently the name is not checked for validity. 767 @unittest.expectedFailure 768 def test_destroy_invalid_snap_name(self): 769 with self.assertRaises(lzc_exc.SnapshotDestructionFailure): 770 lzc.lzc_destroy_snaps( 771 [ZFSTest.pool.makeName(b"@non$&*existent")], False) 772 with self.assertRaises(lzc_exc.SnapshotDestructionFailure): 773 lzc.lzc_destroy_snaps( 774 [ZFSTest.pool.makeName(b"@non$&*existent")], True) 775 776 # Apparently the full name is not checked for length. 777 @unittest.expectedFailure 778 def test_destroy_too_long_full_snap_name(self): 779 snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") 780 snaps = [snapname1] 781 782 with self.assertRaises(lzc_exc.SnapshotDestructionFailure): 783 lzc.lzc_destroy_snaps(snaps, False) 784 with self.assertRaises(lzc_exc.SnapshotDestructionFailure): 785 lzc.lzc_destroy_snaps(snaps, True) 786 787 def test_destroy_too_long_short_snap_name(self): 788 snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") 789 snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") 790 snapname3 = ZFSTest.pool.makeName(b"@snap") 791 snaps = [snapname1, snapname2, snapname3] 792 793 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: 794 lzc.lzc_destroy_snaps(snaps, False) 795 796 for e in ctx.exception.errors: 797 self.assertIsInstance(e, lzc_exc.NameTooLong) 798 799 @unittest.skipUnless(*snap_always_unmounted_before_destruction()) 800 def test_destroy_mounted_snap(self): 801 snap = ZFSTest.pool.getRoot().getSnap() 802 803 lzc.lzc_snapshot([snap]) 804 with zfs_mount(snap): 805 # the snapshot should be force-unmounted 806 lzc.lzc_destroy_snaps([snap], defer=False) 807 self.assertNotExists(snap) 808 809 def test_clone(self): 810 # NB: note the special name for the snapshot. 811 # Since currently we can not destroy filesystems, 812 # it would be impossible to destroy the snapshot, 813 # so no point in attempting to clean it up. 814 snapname = ZFSTest.pool.makeName(b"fs2@origin1") 815 name = ZFSTest.pool.makeName(b"fs1/fs/clone1") 816 817 lzc.lzc_snapshot([snapname]) 818 819 lzc.lzc_clone(name, snapname) 820 self.assertExists(name) 821 822 def test_clone_nonexistent_snapshot(self): 823 snapname = ZFSTest.pool.makeName(b"fs2@nonexistent") 824 name = ZFSTest.pool.makeName(b"fs1/fs/clone2") 825 826 # XXX The error should be SnapshotNotFound 827 # but limitations of C interface do not allow 828 # to differentiate between the errors. 829 with self.assertRaises(lzc_exc.DatasetNotFound): 830 lzc.lzc_clone(name, snapname) 831 self.assertNotExists(name) 832 833 def test_clone_nonexistent_parent_fs(self): 834 snapname = ZFSTest.pool.makeName(b"fs2@origin3") 835 name = ZFSTest.pool.makeName(b"fs1/nonexistent/clone3") 836 837 lzc.lzc_snapshot([snapname]) 838 839 with self.assertRaises(lzc_exc.DatasetNotFound): 840 lzc.lzc_clone(name, snapname) 841 self.assertNotExists(name) 842 843 def test_clone_to_nonexistent_pool(self): 844 snapname = ZFSTest.pool.makeName(b"fs2@snap") 845 name = b"no-such-pool/fs" 846 847 lzc.lzc_snapshot([snapname]) 848 849 with self.assertRaises(lzc_exc.DatasetNotFound): 850 lzc.lzc_clone(name, snapname) 851 self.assertNotExists(name) 852 853 def test_clone_invalid_snap_name(self): 854 # Use a valid filesystem name of filesystem that 855 # exists as a snapshot name 856 snapname = ZFSTest.pool.makeName(b"fs1/fs") 857 name = ZFSTest.pool.makeName(b"fs2/clone") 858 859 with self.assertRaises(lzc_exc.SnapshotNameInvalid): 860 lzc.lzc_clone(name, snapname) 861 self.assertNotExists(name) 862 863 def test_clone_invalid_snap_name_2(self): 864 # Use a valid filesystem name of filesystem that 865 # doesn't exist as a snapshot name 866 snapname = ZFSTest.pool.makeName(b"fs1/nonexistent") 867 name = ZFSTest.pool.makeName(b"fs2/clone") 868 869 with self.assertRaises(lzc_exc.SnapshotNameInvalid): 870 lzc.lzc_clone(name, snapname) 871 self.assertNotExists(name) 872 873 def test_clone_invalid_name(self): 874 snapname = ZFSTest.pool.makeName(b"fs2@snap") 875 name = ZFSTest.pool.makeName(b"fs1/bad#name") 876 877 lzc.lzc_snapshot([snapname]) 878 879 with self.assertRaises(lzc_exc.FilesystemNameInvalid): 880 lzc.lzc_clone(name, snapname) 881 self.assertNotExists(name) 882 883 def test_clone_invalid_pool_name(self): 884 snapname = ZFSTest.pool.makeName(b"fs2@snap") 885 name = b"bad!pool/fs1" 886 887 lzc.lzc_snapshot([snapname]) 888 889 with self.assertRaises(lzc_exc.FilesystemNameInvalid): 890 lzc.lzc_clone(name, snapname) 891 self.assertNotExists(name) 892 893 def test_clone_across_pools(self): 894 snapname = ZFSTest.pool.makeName(b"fs2@snap") 895 name = ZFSTest.misc_pool.makeName(b"clone1") 896 897 lzc.lzc_snapshot([snapname]) 898 899 with self.assertRaises(lzc_exc.PoolsDiffer): 900 lzc.lzc_clone(name, snapname) 901 self.assertNotExists(name) 902 903 def test_clone_across_pools_to_ro_pool(self): 904 snapname = ZFSTest.pool.makeName(b"fs2@snap") 905 name = ZFSTest.readonly_pool.makeName(b"fs1/clone1") 906 907 lzc.lzc_snapshot([snapname]) 908 909 # it's legal to report either of the conditions 910 with self.assertRaises((lzc_exc.ReadOnlyPool, lzc_exc.PoolsDiffer)): 911 lzc.lzc_clone(name, snapname) 912 self.assertNotExists(name) 913 914 def test_destroy_cloned_fs(self): 915 snapname1 = ZFSTest.pool.makeName(b"fs2@origin4") 916 snapname2 = ZFSTest.pool.makeName(b"fs1@snap") 917 clonename = ZFSTest.pool.makeName(b"fs1/fs/clone4") 918 snaps = [snapname1, snapname2] 919 920 lzc.lzc_snapshot(snaps) 921 lzc.lzc_clone(clonename, snapname1) 922 923 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: 924 lzc.lzc_destroy_snaps(snaps, False) 925 926 self.assertEqual(len(ctx.exception.errors), 1) 927 for e in ctx.exception.errors: 928 self.assertIsInstance(e, lzc_exc.SnapshotIsCloned) 929 for snap in snaps: 930 self.assertExists(snap) 931 932 def test_deferred_destroy_cloned_fs(self): 933 snapname1 = ZFSTest.pool.makeName(b"fs2@origin5") 934 snapname2 = ZFSTest.pool.makeName(b"fs1@snap") 935 clonename = ZFSTest.pool.makeName(b"fs1/fs/clone5") 936 snaps = [snapname1, snapname2] 937 938 lzc.lzc_snapshot(snaps) 939 lzc.lzc_clone(clonename, snapname1) 940 941 lzc.lzc_destroy_snaps(snaps, defer=True) 942 943 self.assertExists(snapname1) 944 self.assertNotExists(snapname2) 945 946 def test_rollback(self): 947 name = ZFSTest.pool.makeName(b"fs1") 948 snapname = name + b"@snap" 949 950 lzc.lzc_snapshot([snapname]) 951 ret = lzc.lzc_rollback(name) 952 self.assertEqual(ret, snapname) 953 954 def test_rollback_2(self): 955 name = ZFSTest.pool.makeName(b"fs1") 956 snapname1 = name + b"@snap1" 957 snapname2 = name + b"@snap2" 958 959 lzc.lzc_snapshot([snapname1]) 960 lzc.lzc_snapshot([snapname2]) 961 ret = lzc.lzc_rollback(name) 962 self.assertEqual(ret, snapname2) 963 964 def test_rollback_no_snaps(self): 965 name = ZFSTest.pool.makeName(b"fs1") 966 967 with self.assertRaises(lzc_exc.SnapshotNotFound): 968 lzc.lzc_rollback(name) 969 970 def test_rollback_non_existent_fs(self): 971 name = ZFSTest.pool.makeName(b"nonexistent") 972 973 with self.assertRaises(lzc_exc.FilesystemNotFound): 974 lzc.lzc_rollback(name) 975 976 def test_rollback_invalid_fs_name(self): 977 name = ZFSTest.pool.makeName(b"bad~name") 978 979 with self.assertRaises(lzc_exc.NameInvalid): 980 lzc.lzc_rollback(name) 981 982 def test_rollback_snap_name(self): 983 name = ZFSTest.pool.makeName(b"fs1@snap") 984 985 with self.assertRaises(lzc_exc.NameInvalid): 986 lzc.lzc_rollback(name) 987 988 def test_rollback_snap_name_2(self): 989 name = ZFSTest.pool.makeName(b"fs1@snap") 990 991 lzc.lzc_snapshot([name]) 992 with self.assertRaises(lzc_exc.NameInvalid): 993 lzc.lzc_rollback(name) 994 995 def test_rollback_too_long_fs_name(self): 996 name = ZFSTest.pool.makeTooLongName() 997 998 with self.assertRaises(lzc_exc.NameTooLong): 999 lzc.lzc_rollback(name) 1000 1001 def test_rollback_to_snap_name(self): 1002 name = ZFSTest.pool.makeName(b"fs1") 1003 snap = name + b"@snap" 1004 1005 lzc.lzc_snapshot([snap]) 1006 lzc.lzc_rollback_to(name, snap) 1007 1008 def test_rollback_to_not_latest(self): 1009 fsname = ZFSTest.pool.makeName(b'fs1') 1010 snap1 = fsname + b"@snap1" 1011 snap2 = fsname + b"@snap2" 1012 1013 lzc.lzc_snapshot([snap1]) 1014 lzc.lzc_snapshot([snap2]) 1015 with self.assertRaises(lzc_exc.SnapshotNotLatest): 1016 lzc.lzc_rollback_to(fsname, fsname + b"@snap1") 1017 1018 @skipUnlessBookmarksSupported 1019 def test_bookmarks(self): 1020 snaps = [ZFSTest.pool.makeName( 1021 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1022 bmarks = [ZFSTest.pool.makeName( 1023 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] 1024 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1025 1026 lzc.lzc_snapshot(snaps) 1027 lzc.lzc_bookmark(bmark_dict) 1028 1029 @skipUnlessBookmarksSupported 1030 def test_bookmarks_2(self): 1031 snaps = [ZFSTest.pool.makeName( 1032 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1033 bmarks = [ZFSTest.pool.makeName( 1034 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] 1035 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1036 lzc.lzc_snapshot(snaps) 1037 lzc.lzc_bookmark(bmark_dict) 1038 lzc.lzc_destroy_snaps(snaps, defer=False) 1039 1040 @skipUnlessBookmarksSupported 1041 def test_bookmark_copying(self): 1042 snaps = [ZFSTest.pool.makeName(s) for s in [ 1043 b'fs1@snap1', b'fs1@snap2', b'fs2@snap1']] 1044 bmarks = [ZFSTest.pool.makeName(x) for x in [ 1045 b'fs1#bmark1', b'fs1#bmark2', b'fs2#bmark1']] 1046 bmarks_copies = [ZFSTest.pool.makeName(x) for x in [ 1047 b'fs1#bmark1_copy', b'fs1#bmark2_copy', b'fs2#bmark1_copy']] 1048 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1049 bmark_copies_dict = {x: y for x, y in zip(bmarks_copies, bmarks)} 1050 1051 for snap in snaps: 1052 lzc.lzc_snapshot([snap]) 1053 lzc.lzc_bookmark(bmark_dict) 1054 1055 lzc.lzc_bookmark(bmark_copies_dict) 1056 lzc.lzc_destroy_bookmarks(bmarks_copies) 1057 1058 lzc.lzc_destroy_bookmarks(bmarks) 1059 lzc.lzc_destroy_snaps(snaps, defer=False) 1060 1061 @skipUnlessBookmarksSupported 1062 def test_bookmarks_empty(self): 1063 lzc.lzc_bookmark({}) 1064 1065 @skipUnlessBookmarksSupported 1066 def test_bookmarks_foreign_source(self): 1067 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] 1068 bmarks = [ZFSTest.pool.makeName(b'fs2#bmark1')] 1069 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1070 1071 lzc.lzc_snapshot(snaps) 1072 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1073 lzc.lzc_bookmark(bmark_dict) 1074 1075 for e in ctx.exception.errors: 1076 self.assertIsInstance(e, lzc_exc.BookmarkMismatch) 1077 1078 @skipUnlessBookmarksSupported 1079 def test_bookmarks_invalid_name(self): 1080 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] 1081 bmarks = [ZFSTest.pool.makeName(b'fs1#bmark!')] 1082 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1083 1084 lzc.lzc_snapshot(snaps) 1085 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1086 lzc.lzc_bookmark(bmark_dict) 1087 1088 for e in ctx.exception.errors: 1089 self.assertIsInstance(e, lzc_exc.NameInvalid) 1090 1091 @skipUnlessBookmarksSupported 1092 def test_bookmarks_invalid_name_2(self): 1093 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] 1094 bmarks = [ZFSTest.pool.makeName(b'fs1@bmark')] 1095 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1096 1097 lzc.lzc_snapshot(snaps) 1098 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1099 lzc.lzc_bookmark(bmark_dict) 1100 1101 for e in ctx.exception.errors: 1102 self.assertIsInstance(e, lzc_exc.NameInvalid) 1103 1104 @skipUnlessBookmarksSupported 1105 def test_bookmarks_too_long_name(self): 1106 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] 1107 bmarks = [ZFSTest.pool.makeTooLongName(b'fs1#')] 1108 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1109 1110 lzc.lzc_snapshot(snaps) 1111 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1112 lzc.lzc_bookmark(bmark_dict) 1113 1114 for e in ctx.exception.errors: 1115 self.assertIsInstance(e, lzc_exc.NameTooLong) 1116 1117 @skipUnlessBookmarksSupported 1118 def test_bookmarks_too_long_name_2(self): 1119 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] 1120 bmarks = [ZFSTest.pool.makeTooLongComponent(b'fs1#')] 1121 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1122 1123 lzc.lzc_snapshot(snaps) 1124 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1125 lzc.lzc_bookmark(bmark_dict) 1126 1127 for e in ctx.exception.errors: 1128 self.assertIsInstance(e, lzc_exc.NameTooLong) 1129 1130 @skipUnlessBookmarksSupported 1131 def test_bookmarks_foreign_sources(self): 1132 snaps = [ZFSTest.pool.makeName( 1133 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1134 bmarks = [ZFSTest.pool.makeName( 1135 b'fs2#bmark1'), ZFSTest.pool.makeName(b'fs1#bmark1')] 1136 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1137 1138 lzc.lzc_snapshot(snaps) 1139 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1140 lzc.lzc_bookmark(bmark_dict) 1141 1142 for e in ctx.exception.errors: 1143 self.assertIsInstance(e, lzc_exc.BookmarkMismatch) 1144 1145 @skipUnlessBookmarksSupported 1146 def test_bookmarks_partially_foreign_sources(self): 1147 snaps = [ZFSTest.pool.makeName( 1148 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1149 bmarks = [ZFSTest.pool.makeName( 1150 b'fs2#bmark'), ZFSTest.pool.makeName(b'fs2#bmark1')] 1151 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1152 1153 lzc.lzc_snapshot(snaps) 1154 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1155 lzc.lzc_bookmark(bmark_dict) 1156 1157 for e in ctx.exception.errors: 1158 self.assertIsInstance(e, lzc_exc.BookmarkMismatch) 1159 1160 @skipUnlessBookmarksSupported 1161 def test_bookmarks_cross_pool(self): 1162 snaps = [ZFSTest.pool.makeName( 1163 b'fs1@snap1'), ZFSTest.misc_pool.makeName(b'@snap1')] 1164 bmarks = [ZFSTest.pool.makeName( 1165 b'fs1#bmark1'), ZFSTest.misc_pool.makeName(b'#bmark1')] 1166 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1167 1168 lzc.lzc_snapshot(snaps[0:1]) 1169 lzc.lzc_snapshot(snaps[1:2]) 1170 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1171 lzc.lzc_bookmark(bmark_dict) 1172 1173 for e in ctx.exception.errors: 1174 self.assertIsInstance(e, lzc_exc.PoolsDiffer) 1175 1176 @skipUnlessBookmarksSupported 1177 def test_bookmarks_missing_snap(self): 1178 fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] 1179 snaps = [ZFSTest.pool.makeName( 1180 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1181 bmarks = [ZFSTest.pool.makeName( 1182 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] 1183 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1184 1185 lzc.lzc_snapshot(snaps[0:1]) # only create fs1@snap1 1186 1187 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1188 lzc.lzc_bookmark(bmark_dict) 1189 1190 for e in ctx.exception.errors: 1191 self.assertIsInstance(e, lzc_exc.SnapshotNotFound) 1192 1193 # no new bookmarks are created if one or more sources do not exist 1194 for fs in fss: 1195 fsbmarks = lzc.lzc_get_bookmarks(fs) 1196 self.assertEqual(len(fsbmarks), 0) 1197 1198 @skipUnlessBookmarksSupported 1199 def test_bookmarks_missing_snaps(self): 1200 fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] 1201 snaps = [ZFSTest.pool.makeName( 1202 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] 1203 bmarks = [ZFSTest.pool.makeName( 1204 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] 1205 bmark_dict = {x: y for x, y in zip(bmarks, snaps)} 1206 1207 # do not create any snapshots 1208 1209 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1210 lzc.lzc_bookmark(bmark_dict) 1211 1212 for e in ctx.exception.errors: 1213 self.assertIsInstance(e, lzc_exc.SnapshotNotFound) 1214 1215 # no new bookmarks are created if one or more sources do not exist 1216 for fs in fss: 1217 fsbmarks = lzc.lzc_get_bookmarks(fs) 1218 self.assertEqual(len(fsbmarks), 0) 1219 1220 @skipUnlessBookmarksSupported 1221 def test_bookmarks_for_the_same_snap(self): 1222 snap = ZFSTest.pool.makeName(b'fs1@snap1') 1223 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') 1224 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') 1225 bmark_dict = {bmark1: snap, bmark2: snap} 1226 1227 lzc.lzc_snapshot([snap]) 1228 lzc.lzc_bookmark(bmark_dict) 1229 1230 @skipUnlessBookmarksSupported 1231 def test_bookmarks_for_the_same_snap_2(self): 1232 snap = ZFSTest.pool.makeName(b'fs1@snap1') 1233 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') 1234 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') 1235 bmark_dict1 = {bmark1: snap} 1236 bmark_dict2 = {bmark2: snap} 1237 1238 lzc.lzc_snapshot([snap]) 1239 lzc.lzc_bookmark(bmark_dict1) 1240 lzc.lzc_bookmark(bmark_dict2) 1241 1242 @skipUnlessBookmarksSupported 1243 def test_bookmarks_duplicate_name(self): 1244 snap1 = ZFSTest.pool.makeName(b'fs1@snap1') 1245 snap2 = ZFSTest.pool.makeName(b'fs1@snap2') 1246 bmark = ZFSTest.pool.makeName(b'fs1#bmark') 1247 bmark_dict1 = {bmark: snap1} 1248 bmark_dict2 = {bmark: snap2} 1249 1250 lzc.lzc_snapshot([snap1]) 1251 lzc.lzc_snapshot([snap2]) 1252 lzc.lzc_bookmark(bmark_dict1) 1253 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: 1254 lzc.lzc_bookmark(bmark_dict2) 1255 1256 for e in ctx.exception.errors: 1257 self.assertIsInstance(e, lzc_exc.BookmarkExists) 1258 1259 @skipUnlessBookmarksSupported 1260 def test_get_bookmarks(self): 1261 snap1 = ZFSTest.pool.makeName(b'fs1@snap1') 1262 snap2 = ZFSTest.pool.makeName(b'fs1@snap2') 1263 bmark = ZFSTest.pool.makeName(b'fs1#bmark') 1264 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') 1265 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') 1266 bmark_dict1 = {bmark1: snap1, bmark2: snap2} 1267 bmark_dict2 = {bmark: snap2} 1268 1269 lzc.lzc_snapshot([snap1]) 1270 lzc.lzc_snapshot([snap2]) 1271 lzc.lzc_bookmark(bmark_dict1) 1272 lzc.lzc_bookmark(bmark_dict2) 1273 lzc.lzc_destroy_snaps([snap1, snap2], defer=False) 1274 1275 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) 1276 self.assertEqual(len(bmarks), 3) 1277 for b in b'bmark', b'bmark1', b'bmark2': 1278 self.assertIn(b, bmarks) 1279 self.assertIsInstance(bmarks[b], dict) 1280 self.assertEqual(len(bmarks[b]), 0) 1281 1282 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'), 1283 [b'guid', b'createtxg', b'creation']) 1284 self.assertEqual(len(bmarks), 3) 1285 for b in b'bmark', b'bmark1', b'bmark2': 1286 self.assertIn(b, bmarks) 1287 self.assertIsInstance(bmarks[b], dict) 1288 self.assertEqual(len(bmarks[b]), 3) 1289 1290 @skipUnlessBookmarksSupported 1291 def test_get_bookmarks_invalid_property(self): 1292 snap = ZFSTest.pool.makeName(b'fs1@snap') 1293 bmark = ZFSTest.pool.makeName(b'fs1#bmark') 1294 bmark_dict = {bmark: snap} 1295 1296 lzc.lzc_snapshot([snap]) 1297 lzc.lzc_bookmark(bmark_dict) 1298 1299 bmarks = lzc.lzc_get_bookmarks( 1300 ZFSTest.pool.makeName(b'fs1'), [b'badprop']) 1301 self.assertEqual(len(bmarks), 1) 1302 for b in (b'bmark', ): 1303 self.assertIn(b, bmarks) 1304 self.assertIsInstance(bmarks[b], dict) 1305 self.assertEqual(len(bmarks[b]), 0) 1306 1307 @skipUnlessBookmarksSupported 1308 def test_get_bookmarks_nonexistent_fs(self): 1309 with self.assertRaises(lzc_exc.FilesystemNotFound): 1310 lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'nonexistent')) 1311 1312 @skipUnlessBookmarksSupported 1313 def test_destroy_bookmarks(self): 1314 snap = ZFSTest.pool.makeName(b'fs1@snap') 1315 bmark = ZFSTest.pool.makeName(b'fs1#bmark') 1316 bmark_dict = {bmark: snap} 1317 1318 lzc.lzc_snapshot([snap]) 1319 lzc.lzc_bookmark(bmark_dict) 1320 1321 lzc.lzc_destroy_bookmarks( 1322 [bmark, ZFSTest.pool.makeName(b'fs1#nonexistent')]) 1323 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) 1324 self.assertEqual(len(bmarks), 0) 1325 1326 @skipUnlessBookmarksSupported 1327 def test_destroy_bookmarks_invalid_name(self): 1328 snap = ZFSTest.pool.makeName(b'fs1@snap') 1329 bmark = ZFSTest.pool.makeName(b'fs1#bmark') 1330 bmark_dict = {bmark: snap} 1331 1332 lzc.lzc_snapshot([snap]) 1333 lzc.lzc_bookmark(bmark_dict) 1334 1335 with self.assertRaises(lzc_exc.BookmarkDestructionFailure) as ctx: 1336 lzc.lzc_destroy_bookmarks( 1337 [bmark, ZFSTest.pool.makeName(b'fs1/nonexistent')]) 1338 for e in ctx.exception.errors: 1339 self.assertIsInstance(e, lzc_exc.NameInvalid) 1340 1341 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) 1342 self.assertEqual(len(bmarks), 1) 1343 self.assertIn(b'bmark', bmarks) 1344 1345 @skipUnlessBookmarksSupported 1346 def test_destroy_bookmark_nonexistent_fs(self): 1347 lzc.lzc_destroy_bookmarks( 1348 [ZFSTest.pool.makeName(b'nonexistent#bmark')]) 1349 1350 @skipUnlessBookmarksSupported 1351 def test_destroy_bookmarks_empty(self): 1352 lzc.lzc_bookmark({}) 1353 1354 def test_snaprange_space(self): 1355 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1356 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1357 snap3 = ZFSTest.pool.makeName(b"fs1@snap") 1358 1359 lzc.lzc_snapshot([snap1]) 1360 lzc.lzc_snapshot([snap2]) 1361 lzc.lzc_snapshot([snap3]) 1362 1363 space = lzc.lzc_snaprange_space(snap1, snap2) 1364 self.assertIsInstance(space, (int, int)) 1365 space = lzc.lzc_snaprange_space(snap2, snap3) 1366 self.assertIsInstance(space, (int, int)) 1367 space = lzc.lzc_snaprange_space(snap1, snap3) 1368 self.assertIsInstance(space, (int, int)) 1369 1370 def test_snaprange_space_2(self): 1371 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1372 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1373 snap3 = ZFSTest.pool.makeName(b"fs1@snap") 1374 1375 lzc.lzc_snapshot([snap1]) 1376 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1377 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1378 for i in range(1024): 1379 f.write(b'x' * 1024) 1380 f.flush() 1381 lzc.lzc_snapshot([snap2]) 1382 lzc.lzc_snapshot([snap3]) 1383 1384 space = lzc.lzc_snaprange_space(snap1, snap2) 1385 self.assertGreater(space, 1024 * 1024) 1386 space = lzc.lzc_snaprange_space(snap2, snap3) 1387 self.assertGreater(space, 1024 * 1024) 1388 space = lzc.lzc_snaprange_space(snap1, snap3) 1389 self.assertGreater(space, 1024 * 1024) 1390 1391 def test_snaprange_space_same_snap(self): 1392 snap = ZFSTest.pool.makeName(b"fs1@snap") 1393 1394 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1395 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1396 for i in range(1024): 1397 f.write(b'x' * 1024) 1398 f.flush() 1399 lzc.lzc_snapshot([snap]) 1400 1401 space = lzc.lzc_snaprange_space(snap, snap) 1402 self.assertGreater(space, 1024 * 1024) 1403 self.assertAlmostEqual(space, 1024 * 1024, delta=1024 * 1024 // 20) 1404 1405 def test_snaprange_space_wrong_order(self): 1406 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1407 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1408 1409 lzc.lzc_snapshot([snap1]) 1410 lzc.lzc_snapshot([snap2]) 1411 1412 with self.assertRaises(lzc_exc.SnapshotMismatch): 1413 lzc.lzc_snaprange_space(snap2, snap1) 1414 1415 def test_snaprange_space_unrelated(self): 1416 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1417 snap2 = ZFSTest.pool.makeName(b"fs2@snap2") 1418 1419 lzc.lzc_snapshot([snap1]) 1420 lzc.lzc_snapshot([snap2]) 1421 1422 with self.assertRaises(lzc_exc.SnapshotMismatch): 1423 lzc.lzc_snaprange_space(snap1, snap2) 1424 1425 def test_snaprange_space_across_pools(self): 1426 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1427 snap2 = ZFSTest.misc_pool.makeName(b"@snap2") 1428 1429 lzc.lzc_snapshot([snap1]) 1430 lzc.lzc_snapshot([snap2]) 1431 1432 with self.assertRaises(lzc_exc.PoolsDiffer): 1433 lzc.lzc_snaprange_space(snap1, snap2) 1434 1435 def test_snaprange_space_nonexistent(self): 1436 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1437 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1438 1439 lzc.lzc_snapshot([snap1]) 1440 1441 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1442 lzc.lzc_snaprange_space(snap1, snap2) 1443 self.assertEqual(ctx.exception.name, snap2) 1444 1445 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1446 lzc.lzc_snaprange_space(snap2, snap1) 1447 self.assertEqual(ctx.exception.name, snap1) 1448 1449 def test_snaprange_space_invalid_name(self): 1450 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1451 snap2 = ZFSTest.pool.makeName(b"fs1@sn#p") 1452 1453 lzc.lzc_snapshot([snap1]) 1454 1455 with self.assertRaises(lzc_exc.NameInvalid): 1456 lzc.lzc_snaprange_space(snap1, snap2) 1457 1458 def test_snaprange_space_not_snap(self): 1459 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1460 snap2 = ZFSTest.pool.makeName(b"fs1") 1461 1462 lzc.lzc_snapshot([snap1]) 1463 1464 with self.assertRaises(lzc_exc.NameInvalid): 1465 lzc.lzc_snaprange_space(snap1, snap2) 1466 with self.assertRaises(lzc_exc.NameInvalid): 1467 lzc.lzc_snaprange_space(snap2, snap1) 1468 1469 def test_snaprange_space_not_snap_2(self): 1470 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1471 snap2 = ZFSTest.pool.makeName(b"fs1#bmark") 1472 1473 lzc.lzc_snapshot([snap1]) 1474 1475 with self.assertRaises(lzc_exc.NameInvalid): 1476 lzc.lzc_snaprange_space(snap1, snap2) 1477 with self.assertRaises(lzc_exc.NameInvalid): 1478 lzc.lzc_snaprange_space(snap2, snap1) 1479 1480 def test_send_space(self): 1481 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1482 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1483 snap3 = ZFSTest.pool.makeName(b"fs1@snap") 1484 1485 lzc.lzc_snapshot([snap1]) 1486 lzc.lzc_snapshot([snap2]) 1487 lzc.lzc_snapshot([snap3]) 1488 1489 space = lzc.lzc_send_space(snap2, snap1) 1490 self.assertIsInstance(space, (int, int)) 1491 space = lzc.lzc_send_space(snap3, snap2) 1492 self.assertIsInstance(space, (int, int)) 1493 space = lzc.lzc_send_space(snap3, snap1) 1494 self.assertIsInstance(space, (int, int)) 1495 space = lzc.lzc_send_space(snap1) 1496 self.assertIsInstance(space, (int, int)) 1497 space = lzc.lzc_send_space(snap2) 1498 self.assertIsInstance(space, (int, int)) 1499 space = lzc.lzc_send_space(snap3) 1500 self.assertIsInstance(space, (int, int)) 1501 1502 def test_send_space_2(self): 1503 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1504 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1505 snap3 = ZFSTest.pool.makeName(b"fs1@snap") 1506 1507 lzc.lzc_snapshot([snap1]) 1508 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1509 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1510 for i in range(1024): 1511 f.write(b'x' * 1024) 1512 f.flush() 1513 lzc.lzc_snapshot([snap2]) 1514 lzc.lzc_snapshot([snap3]) 1515 1516 space = lzc.lzc_send_space(snap2, snap1) 1517 self.assertGreater(space, 1024 * 1024) 1518 1519 space = lzc.lzc_send_space(snap3, snap2) 1520 1521 space = lzc.lzc_send_space(snap3, snap1) 1522 1523 space_empty = lzc.lzc_send_space(snap1) 1524 1525 space = lzc.lzc_send_space(snap2) 1526 self.assertGreater(space, 1024 * 1024) 1527 1528 space = lzc.lzc_send_space(snap3) 1529 self.assertEqual(space, space_empty) 1530 1531 def test_send_space_same_snap(self): 1532 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1533 lzc.lzc_snapshot([snap1]) 1534 with self.assertRaises(lzc_exc.SnapshotMismatch): 1535 lzc.lzc_send_space(snap1, snap1) 1536 1537 def test_send_space_wrong_order(self): 1538 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1539 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1540 1541 lzc.lzc_snapshot([snap1]) 1542 lzc.lzc_snapshot([snap2]) 1543 1544 with self.assertRaises(lzc_exc.SnapshotMismatch): 1545 lzc.lzc_send_space(snap1, snap2) 1546 1547 def test_send_space_unrelated(self): 1548 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1549 snap2 = ZFSTest.pool.makeName(b"fs2@snap2") 1550 1551 lzc.lzc_snapshot([snap1]) 1552 lzc.lzc_snapshot([snap2]) 1553 1554 with self.assertRaises(lzc_exc.SnapshotMismatch): 1555 lzc.lzc_send_space(snap1, snap2) 1556 1557 def test_send_space_across_pools(self): 1558 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1559 snap2 = ZFSTest.misc_pool.makeName(b"@snap2") 1560 1561 lzc.lzc_snapshot([snap1]) 1562 lzc.lzc_snapshot([snap2]) 1563 1564 with self.assertRaises(lzc_exc.PoolsDiffer): 1565 lzc.lzc_send_space(snap1, snap2) 1566 1567 def test_send_space_nonexistent(self): 1568 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1569 snap2 = ZFSTest.pool.makeName(b"fs2@snap2") 1570 1571 lzc.lzc_snapshot([snap1]) 1572 1573 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1574 lzc.lzc_send_space(snap1, snap2) 1575 self.assertEqual(ctx.exception.name, snap1) 1576 1577 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1578 lzc.lzc_send_space(snap2, snap1) 1579 self.assertEqual(ctx.exception.name, snap2) 1580 1581 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1582 lzc.lzc_send_space(snap2) 1583 self.assertEqual(ctx.exception.name, snap2) 1584 1585 def test_send_space_invalid_name(self): 1586 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1587 snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") 1588 1589 lzc.lzc_snapshot([snap1]) 1590 1591 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1592 lzc.lzc_send_space(snap2, snap1) 1593 self.assertEqual(ctx.exception.name, snap2) 1594 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1595 lzc.lzc_send_space(snap2) 1596 self.assertEqual(ctx.exception.name, snap2) 1597 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1598 lzc.lzc_send_space(snap1, snap2) 1599 self.assertEqual(ctx.exception.name, snap2) 1600 1601 def test_send_space_not_snap(self): 1602 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1603 snap2 = ZFSTest.pool.makeName(b"fs1") 1604 1605 lzc.lzc_snapshot([snap1]) 1606 1607 with self.assertRaises(lzc_exc.NameInvalid): 1608 lzc.lzc_send_space(snap1, snap2) 1609 with self.assertRaises(lzc_exc.NameInvalid): 1610 lzc.lzc_send_space(snap2, snap1) 1611 with self.assertRaises(lzc_exc.NameInvalid): 1612 lzc.lzc_send_space(snap2) 1613 1614 def test_send_space_not_snap_2(self): 1615 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1616 snap2 = ZFSTest.pool.makeName(b"fs1#bmark") 1617 1618 lzc.lzc_snapshot([snap1]) 1619 1620 with self.assertRaises(lzc_exc.NameInvalid): 1621 lzc.lzc_send_space(snap2, snap1) 1622 with self.assertRaises(lzc_exc.NameInvalid): 1623 lzc.lzc_send_space(snap2) 1624 1625 def test_send_full(self): 1626 snap = ZFSTest.pool.makeName(b"fs1@snap") 1627 1628 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1629 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1630 for i in range(1024): 1631 f.write(b'x' * 1024) 1632 f.flush() 1633 lzc.lzc_snapshot([snap]) 1634 1635 with tempfile.TemporaryFile(suffix='.zstream') as output: 1636 estimate = lzc.lzc_send_space(snap) 1637 1638 fd = output.fileno() 1639 lzc.lzc_send(snap, None, fd) 1640 st = os.fstat(fd) 1641 # 5%, arbitrary. 1642 self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) 1643 1644 def test_send_incremental(self): 1645 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1646 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1647 1648 lzc.lzc_snapshot([snap1]) 1649 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1650 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1651 for i in range(1024): 1652 f.write(b'x' * 1024) 1653 f.flush() 1654 lzc.lzc_snapshot([snap2]) 1655 1656 with tempfile.TemporaryFile(suffix='.zstream') as output: 1657 estimate = lzc.lzc_send_space(snap2, snap1) 1658 1659 fd = output.fileno() 1660 lzc.lzc_send(snap2, snap1, fd) 1661 st = os.fstat(fd) 1662 # 5%, arbitrary. 1663 self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) 1664 1665 def test_send_flags(self): 1666 flags = ['embedded_data', 'large_blocks', 'compress', 'raw'] 1667 snap = ZFSTest.pool.makeName(b"fs1@snap") 1668 lzc.lzc_snapshot([snap]) 1669 1670 for c in range(len(flags)): 1671 for flag in itertools.permutations(flags, c + 1): 1672 with dev_null() as fd: 1673 lzc.lzc_send(snap, None, fd, list(flag)) 1674 1675 def test_send_unknown_flags(self): 1676 snap = ZFSTest.pool.makeName(b"fs1@snap") 1677 lzc.lzc_snapshot([snap]) 1678 with dev_null() as fd: 1679 with self.assertRaises(lzc_exc.UnknownStreamFeature): 1680 lzc.lzc_send(snap, None, fd, ['embedded_data', 'UNKNOWN']) 1681 1682 def test_send_same_snap(self): 1683 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1684 lzc.lzc_snapshot([snap1]) 1685 with tempfile.TemporaryFile(suffix='.zstream') as output: 1686 fd = output.fileno() 1687 with self.assertRaises(lzc_exc.SnapshotMismatch): 1688 lzc.lzc_send(snap1, snap1, fd) 1689 1690 def test_send_wrong_order(self): 1691 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1692 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1693 1694 lzc.lzc_snapshot([snap1]) 1695 lzc.lzc_snapshot([snap2]) 1696 1697 with tempfile.TemporaryFile(suffix='.zstream') as output: 1698 fd = output.fileno() 1699 with self.assertRaises(lzc_exc.SnapshotMismatch): 1700 lzc.lzc_send(snap1, snap2, fd) 1701 1702 def test_send_unrelated(self): 1703 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1704 snap2 = ZFSTest.pool.makeName(b"fs2@snap2") 1705 1706 lzc.lzc_snapshot([snap1]) 1707 lzc.lzc_snapshot([snap2]) 1708 1709 with tempfile.TemporaryFile(suffix='.zstream') as output: 1710 fd = output.fileno() 1711 with self.assertRaises(lzc_exc.SnapshotMismatch): 1712 lzc.lzc_send(snap1, snap2, fd) 1713 1714 def test_send_across_pools(self): 1715 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1716 snap2 = ZFSTest.misc_pool.makeName(b"@snap2") 1717 1718 lzc.lzc_snapshot([snap1]) 1719 lzc.lzc_snapshot([snap2]) 1720 1721 with tempfile.TemporaryFile(suffix='.zstream') as output: 1722 fd = output.fileno() 1723 with self.assertRaises(lzc_exc.PoolsDiffer): 1724 lzc.lzc_send(snap1, snap2, fd) 1725 1726 def test_send_nonexistent(self): 1727 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1728 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1729 1730 lzc.lzc_snapshot([snap1]) 1731 1732 with tempfile.TemporaryFile(suffix='.zstream') as output: 1733 fd = output.fileno() 1734 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1735 lzc.lzc_send(snap1, snap2, fd) 1736 self.assertEqual(ctx.exception.name, snap1) 1737 1738 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1739 lzc.lzc_send(snap2, snap1, fd) 1740 self.assertEqual(ctx.exception.name, snap2) 1741 1742 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: 1743 lzc.lzc_send(snap2, None, fd) 1744 self.assertEqual(ctx.exception.name, snap2) 1745 1746 def test_send_invalid_name(self): 1747 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1748 snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") 1749 1750 lzc.lzc_snapshot([snap1]) 1751 1752 with tempfile.TemporaryFile(suffix='.zstream') as output: 1753 fd = output.fileno() 1754 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1755 lzc.lzc_send(snap2, snap1, fd) 1756 self.assertEqual(ctx.exception.name, snap2) 1757 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1758 lzc.lzc_send(snap2, None, fd) 1759 self.assertEqual(ctx.exception.name, snap2) 1760 with self.assertRaises(lzc_exc.NameInvalid) as ctx: 1761 lzc.lzc_send(snap1, snap2, fd) 1762 self.assertEqual(ctx.exception.name, snap2) 1763 1764 # XXX Although undocumented the API allows to create an incremental 1765 # or full stream for a filesystem as if a temporary unnamed snapshot 1766 # is taken at some time after the call is made and before the stream 1767 # starts being produced. 1768 def test_send_filesystem(self): 1769 snap = ZFSTest.pool.makeName(b"fs1@snap1") 1770 fs = ZFSTest.pool.makeName(b"fs1") 1771 1772 lzc.lzc_snapshot([snap]) 1773 1774 with tempfile.TemporaryFile(suffix='.zstream') as output: 1775 fd = output.fileno() 1776 lzc.lzc_send(fs, snap, fd) 1777 lzc.lzc_send(fs, None, fd) 1778 1779 def test_send_from_filesystem(self): 1780 snap = ZFSTest.pool.makeName(b"fs1@snap1") 1781 fs = ZFSTest.pool.makeName(b"fs1") 1782 1783 lzc.lzc_snapshot([snap]) 1784 1785 with tempfile.TemporaryFile(suffix='.zstream') as output: 1786 fd = output.fileno() 1787 with self.assertRaises(lzc_exc.NameInvalid): 1788 lzc.lzc_send(snap, fs, fd) 1789 1790 @skipUnlessBookmarksSupported 1791 def test_send_bookmark(self): 1792 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1793 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1794 bmark = ZFSTest.pool.makeName(b"fs1#bmark") 1795 1796 lzc.lzc_snapshot([snap1]) 1797 lzc.lzc_snapshot([snap2]) 1798 lzc.lzc_bookmark({bmark: snap2}) 1799 lzc.lzc_destroy_snaps([snap2], defer=False) 1800 1801 with tempfile.TemporaryFile(suffix='.zstream') as output: 1802 fd = output.fileno() 1803 with self.assertRaises(lzc_exc.NameInvalid): 1804 lzc.lzc_send(bmark, snap1, fd) 1805 with self.assertRaises(lzc_exc.NameInvalid): 1806 lzc.lzc_send(bmark, None, fd) 1807 1808 @skipUnlessBookmarksSupported 1809 def test_send_from_bookmark(self): 1810 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 1811 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 1812 bmark = ZFSTest.pool.makeName(b"fs1#bmark") 1813 1814 lzc.lzc_snapshot([snap1]) 1815 lzc.lzc_snapshot([snap2]) 1816 lzc.lzc_bookmark({bmark: snap1}) 1817 lzc.lzc_destroy_snaps([snap1], defer=False) 1818 1819 with tempfile.TemporaryFile(suffix='.zstream') as output: 1820 fd = output.fileno() 1821 lzc.lzc_send(snap2, bmark, fd) 1822 1823 def test_send_bad_fd(self): 1824 snap = ZFSTest.pool.makeName(b"fs1@snap") 1825 lzc.lzc_snapshot([snap]) 1826 1827 with tempfile.TemporaryFile() as tmp: 1828 bad_fd = tmp.fileno() 1829 1830 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1831 lzc.lzc_send(snap, None, bad_fd) 1832 self.assertEqual(ctx.exception.errno, errno.EBADF) 1833 1834 def test_send_bad_fd_2(self): 1835 snap = ZFSTest.pool.makeName(b"fs1@snap") 1836 lzc.lzc_snapshot([snap]) 1837 1838 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1839 lzc.lzc_send(snap, None, -2) 1840 self.assertEqual(ctx.exception.errno, errno.EBADF) 1841 1842 def test_send_bad_fd_3(self): 1843 snap = ZFSTest.pool.makeName(b"fs1@snap") 1844 lzc.lzc_snapshot([snap]) 1845 1846 with tempfile.TemporaryFile() as tmp: 1847 bad_fd = tmp.fileno() 1848 1849 (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) 1850 bad_fd = hard + 1 1851 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1852 lzc.lzc_send(snap, None, bad_fd) 1853 self.assertEqual(ctx.exception.errno, errno.EBADF) 1854 1855 def test_send_to_broken_pipe(self): 1856 snap = ZFSTest.pool.makeName(b"fs1@snap") 1857 lzc.lzc_snapshot([snap]) 1858 1859 if sys.version_info < (3, 0): 1860 proc = subprocess.Popen(['true'], stdin=subprocess.PIPE) 1861 proc.wait() 1862 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1863 lzc.lzc_send(snap, None, proc.stdin.fileno()) 1864 self.assertEqual(ctx.exception.errno, errno.EPIPE) 1865 else: 1866 with subprocess.Popen(['true'], stdin=subprocess.PIPE) as proc: 1867 proc.wait() 1868 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1869 lzc.lzc_send(snap, None, proc.stdin.fileno()) 1870 self.assertEqual(ctx.exception.errno, errno.EPIPE) 1871 1872 def test_send_to_broken_pipe_2(self): 1873 snap = ZFSTest.pool.makeName(b"fs1@snap") 1874 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 1875 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 1876 for i in range(1024): 1877 f.write(b'x' * 1024) 1878 f.flush() 1879 lzc.lzc_snapshot([snap]) 1880 1881 if sys.version_info < (3, 0): 1882 p = subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) 1883 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1884 lzc.lzc_send(snap, None, p.stdin.fileno()) 1885 self.assertTrue(ctx.exception.errno == errno.EPIPE or 1886 ctx.exception.errno == errno.EINTR) 1887 else: 1888 with subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) as p: 1889 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1890 lzc.lzc_send(snap, None, p.stdin.fileno()) 1891 self.assertTrue(ctx.exception.errno == errno.EPIPE or 1892 ctx.exception.errno == errno.EINTR) 1893 1894 def test_send_to_ro_file(self): 1895 snap = ZFSTest.pool.makeName(b"fs1@snap") 1896 lzc.lzc_snapshot([snap]) 1897 1898 with tempfile.NamedTemporaryFile( 1899 suffix='.zstream', delete=False) as output: 1900 # tempfile always opens a temporary file in read-write mode 1901 # regardless of the specified mode, so we have to open it again. 1902 os.chmod(output.name, stat.S_IRUSR) 1903 fd = os.open(output.name, os.O_RDONLY) 1904 with self.assertRaises(lzc_exc.StreamIOError) as ctx: 1905 lzc.lzc_send(snap, None, fd) 1906 os.close(fd) 1907 os.unlink(output.name) 1908 1909 self.assertEqual(ctx.exception.errno, errno.EBADF) 1910 1911 def test_recv_full(self): 1912 src = ZFSTest.pool.makeName(b"fs1@snap") 1913 dst = ZFSTest.pool.makeName(b"fs2/received-1@snap") 1914 1915 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: 1916 lzc.lzc_snapshot([src]) 1917 1918 with tempfile.TemporaryFile(suffix='.zstream') as stream: 1919 lzc.lzc_send(src, None, stream.fileno()) 1920 stream.seek(0) 1921 lzc.lzc_receive(dst, stream.fileno()) 1922 1923 name = os.path.basename(name) 1924 with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: 1925 self.assertTrue( 1926 filecmp.cmp( 1927 os.path.join(mnt1, name), os.path.join(mnt2, name), False)) 1928 1929 def test_recv_incremental(self): 1930 src1 = ZFSTest.pool.makeName(b"fs1@snap1") 1931 src2 = ZFSTest.pool.makeName(b"fs1@snap2") 1932 dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1") 1933 dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2") 1934 1935 lzc.lzc_snapshot([src1]) 1936 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: 1937 lzc.lzc_snapshot([src2]) 1938 1939 with tempfile.TemporaryFile(suffix='.zstream') as stream: 1940 lzc.lzc_send(src1, None, stream.fileno()) 1941 stream.seek(0) 1942 lzc.lzc_receive(dst1, stream.fileno()) 1943 with tempfile.TemporaryFile(suffix='.zstream') as stream: 1944 lzc.lzc_send(src2, src1, stream.fileno()) 1945 stream.seek(0) 1946 lzc.lzc_receive(dst2, stream.fileno()) 1947 1948 name = os.path.basename(name) 1949 with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2: 1950 self.assertTrue( 1951 filecmp.cmp( 1952 os.path.join(mnt1, name), os.path.join(mnt2, name), False)) 1953 1954 # This test case fails unless a patch from 1955 # https://clusterhq.atlassian.net/browse/ZFS-20 1956 # is applied to libzfs_core, otherwise it succeeds. 1957 @unittest.skip("fails with unpatched libzfs_core") 1958 def test_recv_without_explicit_snap_name(self): 1959 srcfs = ZFSTest.pool.makeName(b"fs1") 1960 src1 = srcfs + b"@snap1" 1961 src2 = srcfs + b"@snap2" 1962 dstfs = ZFSTest.pool.makeName(b"fs2/received-100") 1963 dst1 = dstfs + b'@snap1' 1964 dst2 = dstfs + b'@snap2' 1965 1966 with streams(srcfs, src1, src2) as (_, (full, incr)): 1967 lzc.lzc_receive(dstfs, full.fileno()) 1968 lzc.lzc_receive(dstfs, incr.fileno()) 1969 self.assertExists(dst1) 1970 self.assertExists(dst2) 1971 1972 def test_recv_clone(self): 1973 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin") 1974 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone") 1975 clone_snap = clone + b"@snap" 1976 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap") 1977 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap") 1978 1979 lzc.lzc_snapshot([orig_src]) 1980 with tempfile.TemporaryFile(suffix='.zstream') as stream: 1981 lzc.lzc_send(orig_src, None, stream.fileno()) 1982 stream.seek(0) 1983 lzc.lzc_receive(orig_dst, stream.fileno()) 1984 1985 lzc.lzc_clone(clone, orig_src) 1986 lzc.lzc_snapshot([clone_snap]) 1987 with tempfile.TemporaryFile(suffix='.zstream') as stream: 1988 lzc.lzc_send(clone_snap, orig_src, stream.fileno()) 1989 stream.seek(0) 1990 lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst) 1991 1992 def test_recv_full_already_existing_empty_fs(self): 1993 src = ZFSTest.pool.makeName(b"fs1@snap") 1994 dstfs = ZFSTest.pool.makeName(b"fs2/received-3") 1995 dst = dstfs + b'@snap' 1996 1997 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 1998 lzc.lzc_snapshot([src]) 1999 lzc.lzc_create(dstfs) 2000 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2001 lzc.lzc_send(src, None, stream.fileno()) 2002 stream.seek(0) 2003 with self.assertRaises(( 2004 lzc_exc.DestinationModified, lzc_exc.DatasetExists)): 2005 lzc.lzc_receive(dst, stream.fileno()) 2006 2007 def test_recv_full_into_root_empty_pool(self): 2008 empty_pool = None 2009 try: 2010 srcfs = ZFSTest.pool.makeName(b"fs1") 2011 empty_pool = _TempPool() 2012 dst = empty_pool.makeName(b'@snap') 2013 2014 with streams(srcfs, b"snap", None) as (_, (stream, _)): 2015 with self.assertRaises(( 2016 lzc_exc.DestinationModified, lzc_exc.DatasetExists)): 2017 lzc.lzc_receive(dst, stream.fileno()) 2018 finally: 2019 if empty_pool is not None: 2020 empty_pool.cleanUp() 2021 2022 def test_recv_full_into_ro_pool(self): 2023 srcfs = ZFSTest.pool.makeName(b"fs1") 2024 dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap') 2025 2026 with streams(srcfs, b"snap", None) as (_, (stream, _)): 2027 with self.assertRaises(lzc_exc.ReadOnlyPool): 2028 lzc.lzc_receive(dst, stream.fileno()) 2029 2030 def test_recv_full_already_existing_modified_fs(self): 2031 src = ZFSTest.pool.makeName(b"fs1@snap") 2032 dstfs = ZFSTest.pool.makeName(b"fs2/received-5") 2033 dst = dstfs + b'@snap' 2034 2035 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2036 lzc.lzc_snapshot([src]) 2037 lzc.lzc_create(dstfs) 2038 with temp_file_in_fs(dstfs): 2039 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2040 lzc.lzc_send(src, None, stream.fileno()) 2041 stream.seek(0) 2042 with self.assertRaises(( 2043 lzc_exc.DestinationModified, lzc_exc.DatasetExists)): 2044 lzc.lzc_receive(dst, stream.fileno()) 2045 2046 def test_recv_full_already_existing_with_snapshots(self): 2047 src = ZFSTest.pool.makeName(b"fs1@snap") 2048 dstfs = ZFSTest.pool.makeName(b"fs2/received-4") 2049 dst = dstfs + b'@snap' 2050 2051 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2052 lzc.lzc_snapshot([src]) 2053 lzc.lzc_create(dstfs) 2054 lzc.lzc_snapshot([dstfs + b"@snap1"]) 2055 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2056 lzc.lzc_send(src, None, stream.fileno()) 2057 stream.seek(0) 2058 with self.assertRaises(( 2059 lzc_exc.StreamMismatch, lzc_exc.DatasetExists)): 2060 lzc.lzc_receive(dst, stream.fileno()) 2061 2062 def test_recv_full_already_existing_snapshot(self): 2063 src = ZFSTest.pool.makeName(b"fs1@snap") 2064 dstfs = ZFSTest.pool.makeName(b"fs2/received-6") 2065 dst = dstfs + b'@snap' 2066 2067 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2068 lzc.lzc_snapshot([src]) 2069 lzc.lzc_create(dstfs) 2070 lzc.lzc_snapshot([dst]) 2071 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2072 lzc.lzc_send(src, None, stream.fileno()) 2073 stream.seek(0) 2074 with self.assertRaises(lzc_exc.DatasetExists): 2075 lzc.lzc_receive(dst, stream.fileno()) 2076 2077 def test_recv_full_missing_parent_fs(self): 2078 src = ZFSTest.pool.makeName(b"fs1@snap") 2079 dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") 2080 2081 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2082 lzc.lzc_snapshot([src]) 2083 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2084 lzc.lzc_send(src, None, stream.fileno()) 2085 stream.seek(0) 2086 with self.assertRaises(lzc_exc.DatasetNotFound): 2087 lzc.lzc_receive(dst, stream.fileno()) 2088 2089 def test_recv_full_but_specify_origin(self): 2090 srcfs = ZFSTest.pool.makeName(b"fs1") 2091 src = srcfs + b"@snap" 2092 dstfs = ZFSTest.pool.makeName(b"fs2/received-30") 2093 dst = dstfs + b'@snap' 2094 origin1 = ZFSTest.pool.makeName(b"fs2@snap1") 2095 origin2 = ZFSTest.pool.makeName(b"fs2@snap2") 2096 2097 lzc.lzc_snapshot([origin1]) 2098 with streams(srcfs, src, None) as (_, (stream, _)): 2099 lzc.lzc_receive(dst, stream.fileno(), origin=origin1) 2100 origin = ZFSTest.pool.getFilesystem( 2101 b"fs2/received-30").getProperty('origin') 2102 self.assertEqual(origin, origin1) 2103 stream.seek(0) 2104 # because origin snap does not exist can't receive as a clone of it 2105 with self.assertRaises(( 2106 lzc_exc.DatasetNotFound, 2107 lzc_exc.BadStream)): 2108 lzc.lzc_receive(dst, stream.fileno(), origin=origin2) 2109 2110 def test_recv_full_existing_empty_fs_and_origin(self): 2111 srcfs = ZFSTest.pool.makeName(b"fs1") 2112 src = srcfs + b"@snap" 2113 dstfs = ZFSTest.pool.makeName(b"fs2/received-31") 2114 dst = dstfs + b'@snap' 2115 origin = dstfs + b'@dummy' 2116 2117 lzc.lzc_create(dstfs) 2118 with streams(srcfs, src, None) as (_, (stream, _)): 2119 # because the destination fs already exists and has no snaps 2120 with self.assertRaises(( 2121 lzc_exc.DestinationModified, 2122 lzc_exc.DatasetExists, 2123 lzc_exc.BadStream)): 2124 lzc.lzc_receive(dst, stream.fileno(), origin=origin) 2125 lzc.lzc_snapshot([origin]) 2126 stream.seek(0) 2127 # because the destination fs already exists and has the snap 2128 with self.assertRaises(( 2129 lzc_exc.StreamMismatch, 2130 lzc_exc.DatasetExists, 2131 lzc_exc.BadStream)): 2132 lzc.lzc_receive(dst, stream.fileno(), origin=origin) 2133 2134 def test_recv_incremental_mounted_fs(self): 2135 srcfs = ZFSTest.pool.makeName(b"fs1") 2136 src1 = srcfs + b"@snap1" 2137 src2 = srcfs + b"@snap2" 2138 dstfs = ZFSTest.pool.makeName(b"fs2/received-7") 2139 dst1 = dstfs + b'@snap1' 2140 dst2 = dstfs + b'@snap2' 2141 2142 with streams(srcfs, src1, src2) as (_, (full, incr)): 2143 lzc.lzc_receive(dst1, full.fileno()) 2144 with zfs_mount(dstfs): 2145 lzc.lzc_receive(dst2, incr.fileno()) 2146 2147 def test_recv_incremental_modified_fs(self): 2148 srcfs = ZFSTest.pool.makeName(b"fs1") 2149 src1 = srcfs + b"@snap1" 2150 src2 = srcfs + b"@snap2" 2151 dstfs = ZFSTest.pool.makeName(b"fs2/received-15") 2152 dst1 = dstfs + b'@snap1' 2153 dst2 = dstfs + b'@snap2' 2154 2155 with streams(srcfs, src1, src2) as (_, (full, incr)): 2156 lzc.lzc_receive(dst1, full.fileno()) 2157 with temp_file_in_fs(dstfs): 2158 with self.assertRaises(lzc_exc.DestinationModified): 2159 lzc.lzc_receive(dst2, incr.fileno()) 2160 2161 def test_recv_incremental_snapname_used(self): 2162 srcfs = ZFSTest.pool.makeName(b"fs1") 2163 src1 = srcfs + b"@snap1" 2164 src2 = srcfs + b"@snap2" 2165 dstfs = ZFSTest.pool.makeName(b"fs2/received-8") 2166 dst1 = dstfs + b'@snap1' 2167 dst2 = dstfs + b'@snap2' 2168 2169 with streams(srcfs, src1, src2) as (_, (full, incr)): 2170 lzc.lzc_receive(dst1, full.fileno()) 2171 lzc.lzc_snapshot([dst2]) 2172 with self.assertRaises(lzc_exc.DatasetExists): 2173 lzc.lzc_receive(dst2, incr.fileno()) 2174 2175 def test_recv_incremental_more_recent_snap_with_no_changes(self): 2176 srcfs = ZFSTest.pool.makeName(b"fs1") 2177 src1 = srcfs + b"@snap1" 2178 src2 = srcfs + b"@snap2" 2179 dstfs = ZFSTest.pool.makeName(b"fs2/received-9") 2180 dst1 = dstfs + b'@snap1' 2181 dst2 = dstfs + b'@snap2' 2182 dst_snap = dstfs + b'@snap' 2183 2184 with streams(srcfs, src1, src2) as (_, (full, incr)): 2185 lzc.lzc_receive(dst1, full.fileno()) 2186 lzc.lzc_snapshot([dst_snap]) 2187 lzc.lzc_receive(dst2, incr.fileno()) 2188 2189 def test_recv_incremental_non_clone_but_set_origin(self): 2190 srcfs = ZFSTest.pool.makeName(b"fs1") 2191 src1 = srcfs + b"@snap1" 2192 src2 = srcfs + b"@snap2" 2193 dstfs = ZFSTest.pool.makeName(b"fs2/received-20") 2194 dst1 = dstfs + b'@snap1' 2195 dst2 = dstfs + b'@snap2' 2196 dst_snap = dstfs + b'@snap' 2197 2198 with streams(srcfs, src1, src2) as (_, (full, incr)): 2199 lzc.lzc_receive(dst1, full.fileno()) 2200 lzc.lzc_snapshot([dst_snap]) 2201 # because cannot receive incremental and set origin on a non-clone 2202 with self.assertRaises(lzc_exc.BadStream): 2203 lzc.lzc_receive(dst2, incr.fileno(), origin=dst1) 2204 2205 def test_recv_incremental_non_clone_but_set_random_origin(self): 2206 srcfs = ZFSTest.pool.makeName(b"fs1") 2207 src1 = srcfs + b"@snap1" 2208 src2 = srcfs + b"@snap2" 2209 dstfs = ZFSTest.pool.makeName(b"fs2/received-21") 2210 dst1 = dstfs + b'@snap1' 2211 dst2 = dstfs + b'@snap2' 2212 dst_snap = dstfs + b'@snap' 2213 2214 with streams(srcfs, src1, src2) as (_, (full, incr)): 2215 lzc.lzc_receive(dst1, full.fileno()) 2216 lzc.lzc_snapshot([dst_snap]) 2217 # because origin snap does not exist can't receive as a clone of it 2218 with self.assertRaises(( 2219 lzc_exc.DatasetNotFound, 2220 lzc_exc.BadStream)): 2221 lzc.lzc_receive( 2222 dst2, incr.fileno(), 2223 origin=ZFSTest.pool.makeName(b"fs2/fs@snap")) 2224 2225 def test_recv_incremental_more_recent_snap(self): 2226 srcfs = ZFSTest.pool.makeName(b"fs1") 2227 src1 = srcfs + b"@snap1" 2228 src2 = srcfs + b"@snap2" 2229 dstfs = ZFSTest.pool.makeName(b"fs2/received-10") 2230 dst1 = dstfs + b'@snap1' 2231 dst2 = dstfs + b'@snap2' 2232 dst_snap = dstfs + b'@snap' 2233 2234 with streams(srcfs, src1, src2) as (_, (full, incr)): 2235 lzc.lzc_receive(dst1, full.fileno()) 2236 with temp_file_in_fs(dstfs): 2237 lzc.lzc_snapshot([dst_snap]) 2238 with self.assertRaises(lzc_exc.DestinationModified): 2239 lzc.lzc_receive(dst2, incr.fileno()) 2240 2241 def test_recv_incremental_duplicate(self): 2242 srcfs = ZFSTest.pool.makeName(b"fs1") 2243 src1 = srcfs + b"@snap1" 2244 src2 = srcfs + b"@snap2" 2245 dstfs = ZFSTest.pool.makeName(b"fs2/received-11") 2246 dst1 = dstfs + b'@snap1' 2247 dst2 = dstfs + b'@snap2' 2248 dst_snap = dstfs + b'@snap' 2249 2250 with streams(srcfs, src1, src2) as (_, (full, incr)): 2251 lzc.lzc_receive(dst1, full.fileno()) 2252 lzc.lzc_receive(dst2, incr.fileno()) 2253 incr.seek(0) 2254 with self.assertRaises(lzc_exc.DestinationModified): 2255 lzc.lzc_receive(dst_snap, incr.fileno()) 2256 2257 def test_recv_incremental_unrelated_fs(self): 2258 srcfs = ZFSTest.pool.makeName(b"fs1") 2259 src1 = srcfs + b"@snap1" 2260 src2 = srcfs + b"@snap2" 2261 dstfs = ZFSTest.pool.makeName(b"fs2/received-12") 2262 dst_snap = dstfs + b'@snap' 2263 2264 with streams(srcfs, src1, src2) as (_, (_, incr)): 2265 lzc.lzc_create(dstfs) 2266 with self.assertRaises(lzc_exc.StreamMismatch): 2267 lzc.lzc_receive(dst_snap, incr.fileno()) 2268 2269 def test_recv_incremental_nonexistent_fs(self): 2270 srcfs = ZFSTest.pool.makeName(b"fs1") 2271 src1 = srcfs + b"@snap1" 2272 src2 = srcfs + b"@snap2" 2273 dstfs = ZFSTest.pool.makeName(b"fs2/received-13") 2274 dst_snap = dstfs + b'@snap' 2275 2276 with streams(srcfs, src1, src2) as (_, (_, incr)): 2277 with self.assertRaises(lzc_exc.DatasetNotFound): 2278 lzc.lzc_receive(dst_snap, incr.fileno()) 2279 2280 def test_recv_incremental_same_fs(self): 2281 srcfs = ZFSTest.pool.makeName(b"fs1") 2282 src1 = srcfs + b"@snap1" 2283 src2 = srcfs + b"@snap2" 2284 src_snap = srcfs + b'@snap' 2285 2286 with streams(srcfs, src1, src2) as (_, (_, incr)): 2287 with self.assertRaises(lzc_exc.DestinationModified): 2288 lzc.lzc_receive(src_snap, incr.fileno()) 2289 2290 def test_recv_clone_without_specifying_origin(self): 2291 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2") 2292 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2") 2293 clone_snap = clone + b"@snap" 2294 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap") 2295 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap") 2296 2297 lzc.lzc_snapshot([orig_src]) 2298 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2299 lzc.lzc_send(orig_src, None, stream.fileno()) 2300 stream.seek(0) 2301 lzc.lzc_receive(orig_dst, stream.fileno()) 2302 2303 lzc.lzc_clone(clone, orig_src) 2304 lzc.lzc_snapshot([clone_snap]) 2305 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2306 lzc.lzc_send(clone_snap, orig_src, stream.fileno()) 2307 stream.seek(0) 2308 with self.assertRaises(lzc_exc.BadStream): 2309 lzc.lzc_receive(clone_dst, stream.fileno()) 2310 2311 def test_recv_clone_invalid_origin(self): 2312 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3") 2313 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3") 2314 clone_snap = clone + b"@snap" 2315 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap") 2316 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap") 2317 2318 lzc.lzc_snapshot([orig_src]) 2319 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2320 lzc.lzc_send(orig_src, None, stream.fileno()) 2321 stream.seek(0) 2322 lzc.lzc_receive(orig_dst, stream.fileno()) 2323 2324 lzc.lzc_clone(clone, orig_src) 2325 lzc.lzc_snapshot([clone_snap]) 2326 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2327 lzc.lzc_send(clone_snap, orig_src, stream.fileno()) 2328 stream.seek(0) 2329 with self.assertRaises(lzc_exc.NameInvalid): 2330 lzc.lzc_receive( 2331 clone_dst, stream.fileno(), 2332 origin=ZFSTest.pool.makeName(b"fs1/fs")) 2333 2334 def test_recv_clone_wrong_origin(self): 2335 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4") 2336 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4") 2337 clone_snap = clone + b"@snap" 2338 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap") 2339 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap") 2340 wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") 2341 2342 lzc.lzc_snapshot([orig_src]) 2343 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2344 lzc.lzc_send(orig_src, None, stream.fileno()) 2345 stream.seek(0) 2346 lzc.lzc_receive(orig_dst, stream.fileno()) 2347 2348 lzc.lzc_clone(clone, orig_src) 2349 lzc.lzc_snapshot([clone_snap]) 2350 lzc.lzc_snapshot([wrong_origin]) 2351 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2352 lzc.lzc_send(clone_snap, orig_src, stream.fileno()) 2353 stream.seek(0) 2354 with self.assertRaises(lzc_exc.StreamMismatch): 2355 lzc.lzc_receive( 2356 clone_dst, stream.fileno(), origin=wrong_origin) 2357 2358 def test_recv_clone_nonexistent_origin(self): 2359 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5") 2360 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5") 2361 clone_snap = clone + b"@snap" 2362 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap") 2363 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap") 2364 wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") 2365 2366 lzc.lzc_snapshot([orig_src]) 2367 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2368 lzc.lzc_send(orig_src, None, stream.fileno()) 2369 stream.seek(0) 2370 lzc.lzc_receive(orig_dst, stream.fileno()) 2371 2372 lzc.lzc_clone(clone, orig_src) 2373 lzc.lzc_snapshot([clone_snap]) 2374 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2375 lzc.lzc_send(clone_snap, orig_src, stream.fileno()) 2376 stream.seek(0) 2377 with self.assertRaises(lzc_exc.DatasetNotFound): 2378 lzc.lzc_receive( 2379 clone_dst, stream.fileno(), origin=wrong_origin) 2380 2381 def test_force_recv_full_existing_fs(self): 2382 src = ZFSTest.pool.makeName(b"fs1@snap") 2383 dstfs = ZFSTest.pool.makeName(b"fs2/received-50") 2384 dst = dstfs + b'@snap' 2385 2386 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2387 lzc.lzc_snapshot([src]) 2388 2389 lzc.lzc_create(dstfs) 2390 with temp_file_in_fs(dstfs): 2391 pass # enough to taint the fs 2392 2393 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2394 lzc.lzc_send(src, None, stream.fileno()) 2395 stream.seek(0) 2396 lzc.lzc_receive(dst, stream.fileno(), force=True) 2397 2398 def test_force_recv_full_existing_modified_mounted_fs(self): 2399 src = ZFSTest.pool.makeName(b"fs1@snap") 2400 dstfs = ZFSTest.pool.makeName(b"fs2/received-53") 2401 dst = dstfs + b'@snap' 2402 2403 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2404 lzc.lzc_snapshot([src]) 2405 2406 lzc.lzc_create(dstfs) 2407 2408 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2409 lzc.lzc_send(src, None, stream.fileno()) 2410 stream.seek(0) 2411 with zfs_mount(dstfs) as mntdir: 2412 f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) 2413 for i in range(1024): 2414 f.write(b'x' * 1024) 2415 lzc.lzc_receive(dst, stream.fileno(), force=True) 2416 # The temporary file disappears and any access, even close(), 2417 # results in EIO. 2418 self.assertFalse(os.path.exists(f.name)) 2419 with self.assertRaises(IOError): 2420 f.close() 2421 2422 # This test-case expects the behavior that should be there, 2423 # at the moment it may fail with DatasetExists or StreamMismatch 2424 # depending on the implementation. 2425 def test_force_recv_full_already_existing_with_snapshots(self): 2426 src = ZFSTest.pool.makeName(b"fs1@snap") 2427 dstfs = ZFSTest.pool.makeName(b"fs2/received-51") 2428 dst = dstfs + b'@snap' 2429 2430 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2431 lzc.lzc_snapshot([src]) 2432 2433 lzc.lzc_create(dstfs) 2434 with temp_file_in_fs(dstfs): 2435 pass # enough to taint the fs 2436 lzc.lzc_snapshot([dstfs + b"@snap1"]) 2437 2438 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2439 lzc.lzc_send(src, None, stream.fileno()) 2440 stream.seek(0) 2441 lzc.lzc_receive(dst, stream.fileno(), force=True) 2442 2443 def test_force_recv_full_already_existing_with_same_snap(self): 2444 src = ZFSTest.pool.makeName(b"fs1@snap") 2445 dstfs = ZFSTest.pool.makeName(b"fs2/received-52") 2446 dst = dstfs + b'@snap' 2447 2448 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2449 lzc.lzc_snapshot([src]) 2450 2451 lzc.lzc_create(dstfs) 2452 with temp_file_in_fs(dstfs): 2453 pass # enough to taint the fs 2454 lzc.lzc_snapshot([dst]) 2455 2456 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2457 lzc.lzc_send(src, None, stream.fileno()) 2458 stream.seek(0) 2459 with self.assertRaises(lzc_exc.DatasetExists): 2460 lzc.lzc_receive(dst, stream.fileno(), force=True) 2461 2462 def test_force_recv_full_missing_parent_fs(self): 2463 src = ZFSTest.pool.makeName(b"fs1@snap") 2464 dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") 2465 2466 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): 2467 lzc.lzc_snapshot([src]) 2468 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2469 lzc.lzc_send(src, None, stream.fileno()) 2470 stream.seek(0) 2471 with self.assertRaises(lzc_exc.DatasetNotFound): 2472 lzc.lzc_receive(dst, stream.fileno(), force=True) 2473 2474 def test_force_recv_incremental_modified_fs(self): 2475 srcfs = ZFSTest.pool.makeName(b"fs1") 2476 src1 = srcfs + b"@snap1" 2477 src2 = srcfs + b"@snap2" 2478 dstfs = ZFSTest.pool.makeName(b"fs2/received-60") 2479 dst1 = dstfs + b'@snap1' 2480 dst2 = dstfs + b'@snap2' 2481 2482 with streams(srcfs, src1, src2) as (_, (full, incr)): 2483 lzc.lzc_receive(dst1, full.fileno()) 2484 with temp_file_in_fs(dstfs): 2485 pass # enough to taint the fs 2486 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2487 2488 def test_force_recv_incremental_modified_mounted_fs(self): 2489 srcfs = ZFSTest.pool.makeName(b"fs1") 2490 src1 = srcfs + b"@snap1" 2491 src2 = srcfs + b"@snap2" 2492 dstfs = ZFSTest.pool.makeName(b"fs2/received-64") 2493 dst1 = dstfs + b'@snap1' 2494 dst2 = dstfs + b'@snap2' 2495 2496 with streams(srcfs, src1, src2) as (_, (full, incr)): 2497 lzc.lzc_receive(dst1, full.fileno()) 2498 with zfs_mount(dstfs) as mntdir: 2499 f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) 2500 for i in range(1024): 2501 f.write(b'x' * 1024) 2502 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2503 # The temporary file disappears and any access, even close(), 2504 # results in EIO. 2505 self.assertFalse(os.path.exists(f.name)) 2506 with self.assertRaises(IOError): 2507 f.close() 2508 2509 def test_force_recv_incremental_modified_fs_plus_later_snap(self): 2510 srcfs = ZFSTest.pool.makeName(b"fs1") 2511 src1 = srcfs + b"@snap1" 2512 src2 = srcfs + b"@snap2" 2513 dstfs = ZFSTest.pool.makeName(b"fs2/received-61") 2514 dst1 = dstfs + b'@snap1' 2515 dst2 = dstfs + b'@snap2' 2516 dst3 = dstfs + b'@snap' 2517 2518 with streams(srcfs, src1, src2) as (_, (full, incr)): 2519 lzc.lzc_receive(dst1, full.fileno()) 2520 with temp_file_in_fs(dstfs): 2521 pass # enough to taint the fs 2522 lzc.lzc_snapshot([dst3]) 2523 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2524 self.assertExists(dst1) 2525 self.assertExists(dst2) 2526 self.assertNotExists(dst3) 2527 2528 def test_force_recv_incremental_modified_fs_plus_same_name_snap(self): 2529 srcfs = ZFSTest.pool.makeName(b"fs1") 2530 src1 = srcfs + b"@snap1" 2531 src2 = srcfs + b"@snap2" 2532 dstfs = ZFSTest.pool.makeName(b"fs2/received-62") 2533 dst1 = dstfs + b'@snap1' 2534 dst2 = dstfs + b'@snap2' 2535 2536 with streams(srcfs, src1, src2) as (_, (full, incr)): 2537 lzc.lzc_receive(dst1, full.fileno()) 2538 with temp_file_in_fs(dstfs): 2539 pass # enough to taint the fs 2540 lzc.lzc_snapshot([dst2]) 2541 with self.assertRaises(lzc_exc.DatasetExists): 2542 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2543 2544 def test_force_recv_incremental_modified_fs_plus_held_snap(self): 2545 srcfs = ZFSTest.pool.makeName(b"fs1") 2546 src1 = srcfs + b"@snap1" 2547 src2 = srcfs + b"@snap2" 2548 dstfs = ZFSTest.pool.makeName(b"fs2/received-63") 2549 dst1 = dstfs + b'@snap1' 2550 dst2 = dstfs + b'@snap2' 2551 dst3 = dstfs + b'@snap' 2552 2553 with streams(srcfs, src1, src2) as (_, (full, incr)): 2554 lzc.lzc_receive(dst1, full.fileno()) 2555 with temp_file_in_fs(dstfs): 2556 pass # enough to taint the fs 2557 lzc.lzc_snapshot([dst3]) 2558 with cleanup_fd() as cfd: 2559 lzc.lzc_hold({dst3: b'tag'}, cfd) 2560 with self.assertRaises(lzc_exc.DatasetBusy): 2561 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2562 self.assertExists(dst1) 2563 self.assertNotExists(dst2) 2564 self.assertExists(dst3) 2565 2566 def test_force_recv_incremental_modified_fs_plus_cloned_snap(self): 2567 srcfs = ZFSTest.pool.makeName(b"fs1") 2568 src1 = srcfs + b"@snap1" 2569 src2 = srcfs + b"@snap2" 2570 dstfs = ZFSTest.pool.makeName(b"fs2/received-70") 2571 dst1 = dstfs + b'@snap1' 2572 dst2 = dstfs + b'@snap2' 2573 dst3 = dstfs + b'@snap' 2574 cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70") 2575 2576 with streams(srcfs, src1, src2) as (_, (full, incr)): 2577 lzc.lzc_receive(dst1, full.fileno()) 2578 with temp_file_in_fs(dstfs): 2579 pass # enough to taint the fs 2580 lzc.lzc_snapshot([dst3]) 2581 lzc.lzc_clone(cloned, dst3) 2582 with self.assertRaises(lzc_exc.DatasetExists): 2583 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2584 self.assertExists(dst1) 2585 self.assertNotExists(dst2) 2586 self.assertExists(dst3) 2587 2588 def test_recv_incremental_into_cloned_fs(self): 2589 srcfs = ZFSTest.pool.makeName(b"fs1") 2590 src1 = srcfs + b"@snap1" 2591 src2 = srcfs + b"@snap2" 2592 dstfs = ZFSTest.pool.makeName(b"fs2/received-71") 2593 dst1 = dstfs + b'@snap1' 2594 cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71") 2595 dst2 = cloned + b'@snap' 2596 2597 with streams(srcfs, src1, src2) as (_, (full, incr)): 2598 lzc.lzc_receive(dst1, full.fileno()) 2599 lzc.lzc_clone(cloned, dst1) 2600 # test both graceful and with-force attempts 2601 with self.assertRaises(lzc_exc.StreamMismatch): 2602 lzc.lzc_receive(dst2, incr.fileno()) 2603 incr.seek(0) 2604 with self.assertRaises(lzc_exc.StreamMismatch): 2605 lzc.lzc_receive(dst2, incr.fileno(), force=True) 2606 self.assertExists(dst1) 2607 self.assertNotExists(dst2) 2608 2609 def test_recv_with_header_full(self): 2610 src = ZFSTest.pool.makeName(b"fs1@snap") 2611 dst = ZFSTest.pool.makeName(b"fs2/received") 2612 2613 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: 2614 lzc.lzc_snapshot([src]) 2615 2616 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2617 lzc.lzc_send(src, None, stream.fileno()) 2618 stream.seek(0) 2619 2620 (header, c_header) = lzc.receive_header(stream.fileno()) 2621 self.assertEqual(src, header['drr_toname']) 2622 snap = header['drr_toname'].split(b'@', 1)[1] 2623 lzc.lzc_receive_with_header( 2624 dst + b'@' + snap, stream.fileno(), c_header) 2625 2626 name = os.path.basename(name) 2627 with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: 2628 self.assertTrue( 2629 filecmp.cmp( 2630 os.path.join(mnt1, name), os.path.join(mnt2, name), False)) 2631 2632 def test_recv_fs_below_zvol(self): 2633 send = ZFSTest.pool.makeName(b"fs1@snap") 2634 zvol = ZFSTest.pool.makeName(b"fs1/zvol") 2635 dest = zvol + b"/fs@snap" 2636 props = {b"volsize": 1024 * 1024} 2637 2638 lzc.lzc_snapshot([send]) 2639 lzc.lzc_create(zvol, ds_type='zvol', props=props) 2640 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2641 lzc.lzc_send(send, None, stream.fileno()) 2642 stream.seek(0) 2643 with self.assertRaises(lzc_exc.WrongParent): 2644 lzc.lzc_receive(dest, stream.fileno()) 2645 2646 def test_recv_zvol_over_fs_with_children(self): 2647 parent = ZFSTest.pool.makeName(b"fs1") 2648 child = parent + b"subfs" 2649 zvol = ZFSTest.pool.makeName(b"fs1/zvol") 2650 send = zvol + b"@snap" 2651 props = {b"volsize": 1024 * 1024} 2652 2653 lzc.lzc_create(child) 2654 lzc.lzc_create(zvol, ds_type='zvol', props=props) 2655 lzc.lzc_snapshot([send]) 2656 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2657 lzc.lzc_send(send, None, stream.fileno()) 2658 stream.seek(0) 2659 with self.assertRaises(lzc_exc.WrongParent): 2660 lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True) 2661 2662 def test_recv_zvol_overwrite_rootds(self): 2663 zvol = ZFSTest.pool.makeName(b"fs1/zvol") 2664 snap = zvol + b"@snap" 2665 rootds = ZFSTest.pool.getRoot().getName() 2666 props = {b"volsize": 1024 * 1024} 2667 2668 lzc.lzc_create(zvol, ds_type='zvol', props=props) 2669 lzc.lzc_snapshot([snap]) 2670 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2671 lzc.lzc_send(snap, None, stream.fileno()) 2672 stream.seek(0) 2673 with self.assertRaises(lzc_exc.WrongParent): 2674 lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True) 2675 2676 def test_send_full_across_clone_branch_point(self): 2677 origfs = ZFSTest.pool.makeName(b"fs2") 2678 2679 (_, (fromsnap, origsnap, _)) = make_snapshots( 2680 origfs, b"snap1", b"send-origin-20", None) 2681 2682 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20") 2683 lzc.lzc_clone(clonefs, origsnap) 2684 2685 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 2686 2687 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2688 lzc.lzc_send(tosnap, None, stream.fileno()) 2689 2690 def test_send_incr_across_clone_branch_point(self): 2691 origfs = ZFSTest.pool.makeName(b"fs2") 2692 2693 (_, (fromsnap, origsnap, _)) = make_snapshots( 2694 origfs, b"snap1", b"send-origin-21", None) 2695 2696 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21") 2697 lzc.lzc_clone(clonefs, origsnap) 2698 2699 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 2700 2701 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2702 lzc.lzc_send(tosnap, fromsnap, stream.fileno()) 2703 2704 def test_send_resume_token_full(self): 2705 src = ZFSTest.pool.makeName(b"fs1@snap") 2706 dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") 2707 dst = dstfs.getSnap() 2708 2709 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 2710 for i in range(1, 10): 2711 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 2712 f.write(b'x' * 1024 * i) 2713 f.flush() 2714 lzc.lzc_snapshot([src]) 2715 2716 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: 2717 lzc.lzc_send(src, None, stream.fileno()) 2718 stream.seek(0) 2719 stream.truncate(1024 * 3) 2720 with self.assertRaises(lzc_exc.StreamTruncated): 2721 lzc.lzc_receive_resumable(dst, stream.fileno()) 2722 # Resume token code from zfs_send_resume_token_to_nvlist() 2723 # XXX: if used more than twice move this code into an external func 2724 # format: <version>-<cksum>-<packed-size>-<compressed-payload> 2725 token = dstfs.getProperty("receive_resume_token") 2726 self.assertNotEqual(token, b'-') 2727 tokens = token.split(b'-') 2728 self.assertEqual(len(tokens), 4) 2729 version = tokens[0] 2730 packed_size = int(tokens[2], 16) 2731 compressed_nvs = tokens[3] 2732 # Validate resume token 2733 self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION 2734 if sys.version_info < (3, 0): 2735 payload = ( 2736 zlib.decompress(str(bytearray.fromhex(compressed_nvs))) 2737 ) 2738 else: 2739 payload = ( 2740 zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) 2741 ) 2742 self.assertEqual(len(payload), packed_size) 2743 # Unpack 2744 resume_values = packed_nvlist_out(payload, packed_size) 2745 resumeobj = resume_values.get(b'object') 2746 resumeoff = resume_values.get(b'offset') 2747 with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: 2748 lzc.lzc_send_resume( 2749 src, None, rstream.fileno(), None, resumeobj, resumeoff) 2750 rstream.seek(0) 2751 lzc.lzc_receive_resumable(dst, rstream.fileno()) 2752 2753 def test_send_resume_token_incremental(self): 2754 snap1 = ZFSTest.pool.makeName(b"fs1@snap1") 2755 snap2 = ZFSTest.pool.makeName(b"fs1@snap2") 2756 dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") 2757 dst1 = dstfs.getSnap() 2758 dst2 = dstfs.getSnap() 2759 2760 lzc.lzc_snapshot([snap1]) 2761 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: 2762 lzc.lzc_send(snap1, None, stream.fileno()) 2763 stream.seek(0) 2764 lzc.lzc_receive(dst1, stream.fileno()) 2765 2766 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: 2767 for i in range(1, 10): 2768 with tempfile.NamedTemporaryFile(dir=mntdir) as f: 2769 f.write(b'x' * 1024 * i) 2770 f.flush() 2771 lzc.lzc_snapshot([snap2]) 2772 2773 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: 2774 lzc.lzc_send(snap2, snap1, stream.fileno()) 2775 stream.seek(0) 2776 stream.truncate(1024 * 3) 2777 with self.assertRaises(lzc_exc.StreamTruncated): 2778 lzc.lzc_receive_resumable(dst2, stream.fileno()) 2779 # Resume token code from zfs_send_resume_token_to_nvlist() 2780 # format: <version>-<cksum>-<packed-size>-<compressed-payload> 2781 token = dstfs.getProperty("receive_resume_token") 2782 self.assertNotEqual(token, '-') 2783 tokens = token.split(b'-') 2784 self.assertEqual(len(tokens), 4) 2785 version = tokens[0] 2786 packed_size = int(tokens[2], 16) 2787 compressed_nvs = tokens[3] 2788 # Validate resume token 2789 self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION 2790 if sys.version_info < (3, 0): 2791 payload = ( 2792 zlib.decompress(str(bytearray.fromhex(compressed_nvs))) 2793 ) 2794 else: 2795 payload = ( 2796 zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) 2797 ) 2798 self.assertEqual(len(payload), packed_size) 2799 # Unpack 2800 resume_values = packed_nvlist_out(payload, packed_size) 2801 resumeobj = resume_values.get(b'object') 2802 resumeoff = resume_values.get(b'offset') 2803 with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: 2804 lzc.lzc_send_resume( 2805 snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff) 2806 rstream.seek(0) 2807 lzc.lzc_receive_resumable(dst2, rstream.fileno()) 2808 2809 def test_recv_full_across_clone_branch_point(self): 2810 origfs = ZFSTest.pool.makeName(b"fs2") 2811 2812 (_, (fromsnap, origsnap, _)) = make_snapshots( 2813 origfs, b"snap1", b"send-origin-30", None) 2814 2815 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30") 2816 lzc.lzc_clone(clonefs, origsnap) 2817 2818 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 2819 2820 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30") 2821 recvsnap = recvfs + b"@snap" 2822 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2823 lzc.lzc_send(tosnap, None, stream.fileno()) 2824 stream.seek(0) 2825 lzc.lzc_receive(recvsnap, stream.fileno()) 2826 2827 def test_recv_one(self): 2828 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2829 tosnap = ZFSTest.pool.makeName(b"recv@snap1") 2830 2831 lzc.lzc_snapshot([fromsnap]) 2832 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2833 lzc.lzc_send(fromsnap, None, stream.fileno()) 2834 stream.seek(0) 2835 (header, c_header) = lzc.receive_header(stream.fileno()) 2836 lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) 2837 2838 def test_recv_one_size(self): 2839 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2840 tosnap = ZFSTest.pool.makeName(b"recv@snap1") 2841 2842 lzc.lzc_snapshot([fromsnap]) 2843 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2844 lzc.lzc_send(fromsnap, None, stream.fileno()) 2845 size = os.fstat(stream.fileno()).st_size 2846 stream.seek(0) 2847 (header, c_header) = lzc.receive_header(stream.fileno()) 2848 (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) 2849 self.assertAlmostEqual(read, size, delta=read * 0.05) 2850 2851 def test_recv_one_props(self): 2852 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2853 fs = ZFSTest.pool.getFilesystem(b"recv") 2854 tosnap = fs.getName() + b"@snap1" 2855 props = { 2856 b"compression": 0x01, 2857 b"ns:prop": b"val" 2858 } 2859 2860 lzc.lzc_snapshot([fromsnap]) 2861 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2862 lzc.lzc_send(fromsnap, None, stream.fileno()) 2863 stream.seek(0) 2864 (header, c_header) = lzc.receive_header(stream.fileno()) 2865 lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props) 2866 self.assertExists(tosnap) 2867 self.assertEqual(fs.getProperty("compression", "received"), b"on") 2868 self.assertEqual(fs.getProperty("ns:prop", "received"), b"val") 2869 2870 def test_recv_one_invalid_prop(self): 2871 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2872 fs = ZFSTest.pool.getFilesystem(b"recv") 2873 tosnap = fs.getName() + b"@snap1" 2874 props = { 2875 b"exec": 0xff, 2876 b"atime": 0x00 2877 } 2878 2879 lzc.lzc_snapshot([fromsnap]) 2880 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2881 lzc.lzc_send(fromsnap, None, stream.fileno()) 2882 stream.seek(0) 2883 (header, c_header) = lzc.receive_header(stream.fileno()) 2884 with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx: 2885 lzc.lzc_receive_one( 2886 tosnap, stream.fileno(), c_header, props=props) 2887 self.assertExists(tosnap) 2888 self.assertEqual(fs.getProperty("atime", "received"), b"off") 2889 for e in ctx.exception.errors: 2890 self.assertIsInstance(e, lzc_exc.PropertyInvalid) 2891 self.assertEqual(e.name, b"exec") 2892 2893 def test_recv_with_cmdprops(self): 2894 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2895 fs = ZFSTest.pool.getFilesystem(b"recv") 2896 tosnap = fs.getName() + b"@snap1" 2897 props = {} 2898 cmdprops = { 2899 b"compression": 0x01, 2900 b"ns:prop": b"val" 2901 } 2902 2903 lzc.lzc_snapshot([fromsnap]) 2904 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2905 lzc.lzc_send(fromsnap, None, stream.fileno()) 2906 stream.seek(0) 2907 (header, c_header) = lzc.receive_header(stream.fileno()) 2908 lzc.lzc_receive_with_cmdprops( 2909 tosnap, stream.fileno(), c_header, props=props, 2910 cmdprops=cmdprops) 2911 self.assertExists(tosnap) 2912 self.assertEqual(fs.getProperty("compression"), b"on") 2913 self.assertEqual(fs.getProperty("ns:prop"), b"val") 2914 2915 def test_recv_with_heal(self): 2916 snap = ZFSTest.pool.makeName(b"fs1@snap1") 2917 fs = ZFSTest.pool.getFilesystem(b"fs1") 2918 props = {} 2919 cmdprops = { 2920 b"compression": 0x01, 2921 b"ns:prop": b"val" 2922 } 2923 2924 lzc.lzc_snapshot([snap]) 2925 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2926 lzc.lzc_send(snap, None, stream.fileno()) 2927 stream.seek(0) 2928 (header, c_header) = lzc.receive_header(stream.fileno()) 2929 lzc.lzc_receive_with_heal( 2930 snap, stream.fileno(), c_header, props=props, 2931 cmdprops=cmdprops) 2932 self.assertExists(snap) 2933 self.assertEqual(fs.getProperty("compression"), b"on") 2934 self.assertEqual(fs.getProperty("ns:prop"), b"val") 2935 2936 def test_recv_with_cmdprops_and_recvprops(self): 2937 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") 2938 fs = ZFSTest.pool.getFilesystem(b"recv") 2939 tosnap = fs.getName() + b"@snap1" 2940 props = { 2941 b"atime": 0x01, 2942 b"exec": 0x00, 2943 b"ns:prop": b"abc" 2944 } 2945 cmdprops = { 2946 b"compression": 0x01, 2947 b"ns:prop": b"def", 2948 b"exec": None, 2949 } 2950 2951 lzc.lzc_snapshot([fromsnap]) 2952 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2953 lzc.lzc_send(fromsnap, None, stream.fileno()) 2954 stream.seek(0) 2955 (header, c_header) = lzc.receive_header(stream.fileno()) 2956 lzc.lzc_receive_with_cmdprops( 2957 tosnap, stream.fileno(), c_header, props=props, 2958 cmdprops=cmdprops) 2959 self.assertExists(tosnap) 2960 self.assertEqual(fs.getProperty("atime", True), b"on") 2961 self.assertEqual(fs.getProperty("exec", True), b"off") 2962 self.assertEqual(fs.getProperty("ns:prop", True), b"abc") 2963 self.assertEqual(fs.getProperty("compression"), b"on") 2964 self.assertEqual(fs.getProperty("ns:prop"), b"def") 2965 self.assertEqual(fs.getProperty("exec"), b"on") 2966 2967 def test_recv_incr_across_clone_branch_point_no_origin(self): 2968 origfs = ZFSTest.pool.makeName(b"fs2") 2969 2970 (_, (fromsnap, origsnap, _)) = make_snapshots( 2971 origfs, b"snap1", b"send-origin-32", None) 2972 2973 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32") 2974 lzc.lzc_clone(clonefs, origsnap) 2975 2976 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 2977 2978 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32") 2979 recvsnap1 = recvfs + b"@snap1" 2980 recvsnap2 = recvfs + b"@snap2" 2981 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2982 lzc.lzc_send(fromsnap, None, stream.fileno()) 2983 stream.seek(0) 2984 lzc.lzc_receive(recvsnap1, stream.fileno()) 2985 with tempfile.TemporaryFile(suffix='.zstream') as stream: 2986 lzc.lzc_send(tosnap, fromsnap, stream.fileno()) 2987 stream.seek(0) 2988 with self.assertRaises(lzc_exc.BadStream): 2989 lzc.lzc_receive(recvsnap2, stream.fileno()) 2990 2991 def test_recv_incr_across_clone_branch_point(self): 2992 origfs = ZFSTest.pool.makeName(b"fs2") 2993 2994 (_, (fromsnap, origsnap, _)) = make_snapshots( 2995 origfs, b"snap1", b"send-origin-31", None) 2996 2997 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31") 2998 lzc.lzc_clone(clonefs, origsnap) 2999 3000 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 3001 3002 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31") 3003 recvsnap1 = recvfs + b"@snap1" 3004 recvsnap2 = recvfs + b"@snap2" 3005 with tempfile.TemporaryFile(suffix='.zstream') as stream: 3006 lzc.lzc_send(fromsnap, None, stream.fileno()) 3007 stream.seek(0) 3008 lzc.lzc_receive(recvsnap1, stream.fileno()) 3009 with tempfile.TemporaryFile(suffix='.zstream') as stream: 3010 lzc.lzc_send(tosnap, fromsnap, stream.fileno()) 3011 stream.seek(0) 3012 with self.assertRaises(lzc_exc.BadStream): 3013 lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) 3014 3015 def test_recv_incr_across_clone_branch_point_new_fs(self): 3016 origfs = ZFSTest.pool.makeName(b"fs2") 3017 3018 (_, (fromsnap, origsnap, _)) = make_snapshots( 3019 origfs, b"snap1", b"send-origin-33", None) 3020 3021 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33") 3022 lzc.lzc_clone(clonefs, origsnap) 3023 3024 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) 3025 3026 recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33") 3027 recvsnap1 = recvfs1 + b"@snap" 3028 recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2") 3029 recvsnap2 = recvfs2 + b"@snap" 3030 with tempfile.TemporaryFile(suffix='.zstream') as stream: 3031 lzc.lzc_send(fromsnap, None, stream.fileno()) 3032 stream.seek(0) 3033 lzc.lzc_receive(recvsnap1, stream.fileno()) 3034 with tempfile.TemporaryFile(suffix='.zstream') as stream: 3035 lzc.lzc_send(tosnap, fromsnap, stream.fileno()) 3036 stream.seek(0) 3037 lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) 3038 3039 def test_recv_bad_stream(self): 3040 dstfs = ZFSTest.pool.makeName(b"fs2/received") 3041 dst_snap = dstfs + b'@snap' 3042 3043 with dev_zero() as fd: 3044 with self.assertRaises(lzc_exc.BadStream): 3045 lzc.lzc_receive(dst_snap, fd) 3046 3047 @needs_support(lzc.lzc_promote) 3048 def test_promote(self): 3049 origfs = ZFSTest.pool.makeName(b"fs2") 3050 snap = b"@promote-snap-1" 3051 origsnap = origfs + snap 3052 lzc.lzc_snap([origsnap]) 3053 3054 clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1") 3055 lzc.lzc_clone(clonefs, origsnap) 3056 3057 lzc.lzc_promote(clonefs) 3058 # the snapshot now should belong to the promoted fs 3059 self.assertExists(clonefs + snap) 3060 3061 @needs_support(lzc.lzc_promote) 3062 def test_promote_too_long_snapname(self): 3063 # origfs name must be shorter than clonefs name 3064 origfs = ZFSTest.pool.makeName(b"fs2") 3065 clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2") 3066 snapprefix = b"@promote-snap-2-" 3067 pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix) 3068 snap = snapprefix + b'x' * pad_len 3069 origsnap = origfs + snap 3070 3071 lzc.lzc_snap([origsnap]) 3072 lzc.lzc_clone(clonefs, origsnap) 3073 3074 # This may fail on older buggy systems. 3075 # See: https://www.illumos.org/issues/5909 3076 with self.assertRaises(lzc_exc.NameTooLong): 3077 lzc.lzc_promote(clonefs) 3078 3079 @needs_support(lzc.lzc_promote) 3080 def test_promote_not_cloned(self): 3081 fs = ZFSTest.pool.makeName(b"fs2") 3082 with self.assertRaises(lzc_exc.NotClone): 3083 lzc.lzc_promote(fs) 3084 3085 @unittest.skipIf(*illumos_bug_6379()) 3086 def test_hold_bad_fd(self): 3087 snap = ZFSTest.pool.getRoot().getSnap() 3088 lzc.lzc_snapshot([snap]) 3089 3090 with tempfile.TemporaryFile() as tmp: 3091 bad_fd = tmp.fileno() 3092 3093 with self.assertRaises(lzc_exc.BadHoldCleanupFD): 3094 lzc.lzc_hold({snap: b'tag'}, bad_fd) 3095 3096 @unittest.skipIf(*illumos_bug_6379()) 3097 def test_hold_bad_fd_2(self): 3098 snap = ZFSTest.pool.getRoot().getSnap() 3099 lzc.lzc_snapshot([snap]) 3100 3101 with self.assertRaises(lzc_exc.BadHoldCleanupFD): 3102 lzc.lzc_hold({snap: b'tag'}, -2) 3103 3104 @unittest.skipIf(*illumos_bug_6379()) 3105 def test_hold_bad_fd_3(self): 3106 snap = ZFSTest.pool.getRoot().getSnap() 3107 lzc.lzc_snapshot([snap]) 3108 3109 (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) 3110 bad_fd = hard + 1 3111 with self.assertRaises(lzc_exc.BadHoldCleanupFD): 3112 lzc.lzc_hold({snap: b'tag'}, bad_fd) 3113 3114 @unittest.skipIf(*illumos_bug_6379()) 3115 def test_hold_wrong_fd(self): 3116 snap = ZFSTest.pool.getRoot().getSnap() 3117 lzc.lzc_snapshot([snap]) 3118 3119 with tempfile.TemporaryFile() as tmp: 3120 fd = tmp.fileno() 3121 with self.assertRaises(lzc_exc.BadHoldCleanupFD): 3122 lzc.lzc_hold({snap: b'tag'}, fd) 3123 3124 def test_hold_fd(self): 3125 snap = ZFSTest.pool.getRoot().getSnap() 3126 lzc.lzc_snapshot([snap]) 3127 3128 with cleanup_fd() as fd: 3129 lzc.lzc_hold({snap: b'tag'}, fd) 3130 3131 def test_hold_empty(self): 3132 with cleanup_fd() as fd: 3133 lzc.lzc_hold({}, fd) 3134 3135 def test_hold_empty_2(self): 3136 lzc.lzc_hold({}) 3137 3138 def test_hold_vs_snap_destroy(self): 3139 snap = ZFSTest.pool.getRoot().getSnap() 3140 lzc.lzc_snapshot([snap]) 3141 3142 with cleanup_fd() as fd: 3143 lzc.lzc_hold({snap: b'tag'}, fd) 3144 3145 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: 3146 lzc.lzc_destroy_snaps([snap], defer=False) 3147 for e in ctx.exception.errors: 3148 self.assertIsInstance(e, lzc_exc.SnapshotIsHeld) 3149 3150 lzc.lzc_destroy_snaps([snap], defer=True) 3151 self.assertExists(snap) 3152 3153 # after automatic hold cleanup and deferred destruction 3154 self.assertNotExists(snap) 3155 3156 def test_hold_many_tags(self): 3157 snap = ZFSTest.pool.getRoot().getSnap() 3158 lzc.lzc_snapshot([snap]) 3159 3160 with cleanup_fd() as fd: 3161 lzc.lzc_hold({snap: b'tag1'}, fd) 3162 lzc.lzc_hold({snap: b'tag2'}, fd) 3163 3164 def test_hold_many_snaps(self): 3165 snap1 = ZFSTest.pool.getRoot().getSnap() 3166 snap2 = ZFSTest.pool.getRoot().getSnap() 3167 lzc.lzc_snapshot([snap1]) 3168 lzc.lzc_snapshot([snap2]) 3169 3170 with cleanup_fd() as fd: 3171 lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) 3172 3173 def test_hold_many_with_one_missing(self): 3174 snap1 = ZFSTest.pool.getRoot().getSnap() 3175 snap2 = ZFSTest.pool.getRoot().getSnap() 3176 lzc.lzc_snapshot([snap1]) 3177 3178 with cleanup_fd() as fd: 3179 missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) 3180 self.assertEqual(len(missing), 1) 3181 self.assertEqual(missing[0], snap2) 3182 3183 def test_hold_many_with_all_missing(self): 3184 snap1 = ZFSTest.pool.getRoot().getSnap() 3185 snap2 = ZFSTest.pool.getRoot().getSnap() 3186 3187 with cleanup_fd() as fd: 3188 missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) 3189 self.assertEqual(len(missing), 2) 3190 self.assertEqual(sorted(missing), sorted([snap1, snap2])) 3191 3192 def test_hold_missing_fs(self): 3193 # XXX skip pre-created filesystems 3194 ZFSTest.pool.getRoot().getFilesystem() 3195 ZFSTest.pool.getRoot().getFilesystem() 3196 ZFSTest.pool.getRoot().getFilesystem() 3197 ZFSTest.pool.getRoot().getFilesystem() 3198 ZFSTest.pool.getRoot().getFilesystem() 3199 snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() 3200 3201 snaps = lzc.lzc_hold({snap: b'tag'}) 3202 self.assertEqual([snap], snaps) 3203 3204 def test_hold_missing_fs_auto_cleanup(self): 3205 # XXX skip pre-created filesystems 3206 ZFSTest.pool.getRoot().getFilesystem() 3207 ZFSTest.pool.getRoot().getFilesystem() 3208 ZFSTest.pool.getRoot().getFilesystem() 3209 ZFSTest.pool.getRoot().getFilesystem() 3210 ZFSTest.pool.getRoot().getFilesystem() 3211 snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() 3212 3213 with cleanup_fd() as fd: 3214 snaps = lzc.lzc_hold({snap: b'tag'}, fd) 3215 self.assertEqual([snap], snaps) 3216 3217 def test_hold_duplicate(self): 3218 snap = ZFSTest.pool.getRoot().getSnap() 3219 lzc.lzc_snapshot([snap]) 3220 3221 with cleanup_fd() as fd: 3222 lzc.lzc_hold({snap: b'tag'}, fd) 3223 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3224 lzc.lzc_hold({snap: b'tag'}, fd) 3225 for e in ctx.exception.errors: 3226 self.assertIsInstance(e, lzc_exc.HoldExists) 3227 3228 def test_hold_across_pools(self): 3229 snap1 = ZFSTest.pool.getRoot().getSnap() 3230 snap2 = ZFSTest.misc_pool.getRoot().getSnap() 3231 lzc.lzc_snapshot([snap1]) 3232 lzc.lzc_snapshot([snap2]) 3233 3234 with cleanup_fd() as fd: 3235 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3236 lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) 3237 for e in ctx.exception.errors: 3238 self.assertIsInstance(e, lzc_exc.PoolsDiffer) 3239 3240 def test_hold_too_long_tag(self): 3241 snap = ZFSTest.pool.getRoot().getSnap() 3242 tag = b't' * 256 3243 lzc.lzc_snapshot([snap]) 3244 3245 with cleanup_fd() as fd: 3246 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3247 lzc.lzc_hold({snap: tag}, fd) 3248 for e in ctx.exception.errors: 3249 self.assertIsInstance(e, lzc_exc.NameTooLong) 3250 self.assertEqual(e.name, tag) 3251 3252 # Apparently the full snapshot name is not checked for length 3253 # and this snapshot is treated as simply missing. 3254 @unittest.expectedFailure 3255 def test_hold_too_long_snap_name(self): 3256 snap = ZFSTest.pool.getRoot().getTooLongSnap(False) 3257 with cleanup_fd() as fd: 3258 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3259 lzc.lzc_hold({snap: b'tag'}, fd) 3260 for e in ctx.exception.errors: 3261 self.assertIsInstance(e, lzc_exc.NameTooLong) 3262 self.assertEqual(e.name, snap) 3263 3264 def test_hold_too_long_snap_name_2(self): 3265 snap = ZFSTest.pool.getRoot().getTooLongSnap(True) 3266 with cleanup_fd() as fd: 3267 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3268 lzc.lzc_hold({snap: b'tag'}, fd) 3269 for e in ctx.exception.errors: 3270 self.assertIsInstance(e, lzc_exc.NameTooLong) 3271 self.assertEqual(e.name, snap) 3272 3273 def test_hold_invalid_snap_name(self): 3274 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' 3275 with cleanup_fd() as fd: 3276 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3277 lzc.lzc_hold({snap: b'tag'}, fd) 3278 for e in ctx.exception.errors: 3279 self.assertIsInstance(e, lzc_exc.NameInvalid) 3280 self.assertEqual(e.name, snap) 3281 3282 def test_hold_invalid_snap_name_2(self): 3283 snap = ZFSTest.pool.getRoot().getFilesystem().getName() 3284 with cleanup_fd() as fd: 3285 with self.assertRaises(lzc_exc.HoldFailure) as ctx: 3286 lzc.lzc_hold({snap: b'tag'}, fd) 3287 for e in ctx.exception.errors: 3288 self.assertIsInstance(e, lzc_exc.NameInvalid) 3289 self.assertEqual(e.name, snap) 3290 3291 def test_get_holds(self): 3292 snap = ZFSTest.pool.getRoot().getSnap() 3293 lzc.lzc_snapshot([snap]) 3294 3295 with cleanup_fd() as fd: 3296 lzc.lzc_hold({snap: b'tag1'}, fd) 3297 lzc.lzc_hold({snap: b'tag2'}, fd) 3298 3299 holds = lzc.lzc_get_holds(snap) 3300 self.assertEqual(len(holds), 2) 3301 self.assertIn(b'tag1', holds) 3302 self.assertIn(b'tag2', holds) 3303 self.assertIsInstance(holds[b'tag1'], (int, int)) 3304 3305 def test_get_holds_after_auto_cleanup(self): 3306 snap = ZFSTest.pool.getRoot().getSnap() 3307 lzc.lzc_snapshot([snap]) 3308 3309 with cleanup_fd() as fd: 3310 lzc.lzc_hold({snap: b'tag1'}, fd) 3311 lzc.lzc_hold({snap: b'tag2'}, fd) 3312 3313 holds = lzc.lzc_get_holds(snap) 3314 self.assertEqual(len(holds), 0) 3315 self.assertIsInstance(holds, dict) 3316 3317 def test_get_holds_nonexistent_snap(self): 3318 snap = ZFSTest.pool.getRoot().getSnap() 3319 with self.assertRaises(lzc_exc.SnapshotNotFound): 3320 lzc.lzc_get_holds(snap) 3321 3322 def test_get_holds_too_long_snap_name(self): 3323 snap = ZFSTest.pool.getRoot().getTooLongSnap(False) 3324 with self.assertRaises(lzc_exc.NameTooLong): 3325 lzc.lzc_get_holds(snap) 3326 3327 def test_get_holds_too_long_snap_name_2(self): 3328 snap = ZFSTest.pool.getRoot().getTooLongSnap(True) 3329 with self.assertRaises(lzc_exc.NameTooLong): 3330 lzc.lzc_get_holds(snap) 3331 3332 def test_get_holds_invalid_snap_name(self): 3333 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' 3334 with self.assertRaises(lzc_exc.NameInvalid): 3335 lzc.lzc_get_holds(snap) 3336 3337 # A filesystem-like snapshot name is not recognized as 3338 # an invalid name. 3339 @unittest.expectedFailure 3340 def test_get_holds_invalid_snap_name_2(self): 3341 snap = ZFSTest.pool.getRoot().getFilesystem().getName() 3342 with self.assertRaises(lzc_exc.NameInvalid): 3343 lzc.lzc_get_holds(snap) 3344 3345 def test_release_hold(self): 3346 snap = ZFSTest.pool.getRoot().getSnap() 3347 lzc.lzc_snapshot([snap]) 3348 3349 lzc.lzc_hold({snap: b'tag'}) 3350 ret = lzc.lzc_release({snap: [b'tag']}) 3351 self.assertEqual(len(ret), 0) 3352 3353 def test_release_hold_empty(self): 3354 ret = lzc.lzc_release({}) 3355 self.assertEqual(len(ret), 0) 3356 3357 def test_release_hold_complex(self): 3358 snap1 = ZFSTest.pool.getRoot().getSnap() 3359 snap2 = ZFSTest.pool.getRoot().getSnap() 3360 snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap() 3361 lzc.lzc_snapshot([snap1]) 3362 lzc.lzc_snapshot([snap2, snap3]) 3363 3364 lzc.lzc_hold({snap1: b'tag1'}) 3365 lzc.lzc_hold({snap1: b'tag2'}) 3366 lzc.lzc_hold({snap2: b'tag'}) 3367 lzc.lzc_hold({snap3: b'tag1'}) 3368 lzc.lzc_hold({snap3: b'tag2'}) 3369 3370 holds = lzc.lzc_get_holds(snap1) 3371 self.assertEqual(len(holds), 2) 3372 holds = lzc.lzc_get_holds(snap2) 3373 self.assertEqual(len(holds), 1) 3374 holds = lzc.lzc_get_holds(snap3) 3375 self.assertEqual(len(holds), 2) 3376 3377 release = { 3378 snap1: [b'tag1', b'tag2'], 3379 snap2: [b'tag'], 3380 snap3: [b'tag2'], 3381 } 3382 ret = lzc.lzc_release(release) 3383 self.assertEqual(len(ret), 0) 3384 3385 holds = lzc.lzc_get_holds(snap1) 3386 self.assertEqual(len(holds), 0) 3387 holds = lzc.lzc_get_holds(snap2) 3388 self.assertEqual(len(holds), 0) 3389 holds = lzc.lzc_get_holds(snap3) 3390 self.assertEqual(len(holds), 1) 3391 3392 ret = lzc.lzc_release({snap3: [b'tag1']}) 3393 self.assertEqual(len(ret), 0) 3394 holds = lzc.lzc_get_holds(snap3) 3395 self.assertEqual(len(holds), 0) 3396 3397 def test_release_hold_before_auto_cleanup(self): 3398 snap = ZFSTest.pool.getRoot().getSnap() 3399 lzc.lzc_snapshot([snap]) 3400 3401 with cleanup_fd() as fd: 3402 lzc.lzc_hold({snap: b'tag'}, fd) 3403 ret = lzc.lzc_release({snap: [b'tag']}) 3404 self.assertEqual(len(ret), 0) 3405 3406 def test_release_hold_and_snap_destruction(self): 3407 snap = ZFSTest.pool.getRoot().getSnap() 3408 lzc.lzc_snapshot([snap]) 3409 3410 with cleanup_fd() as fd: 3411 lzc.lzc_hold({snap: b'tag1'}, fd) 3412 lzc.lzc_hold({snap: b'tag2'}, fd) 3413 3414 lzc.lzc_destroy_snaps([snap], defer=True) 3415 self.assertExists(snap) 3416 3417 lzc.lzc_release({snap: [b'tag1']}) 3418 self.assertExists(snap) 3419 3420 lzc.lzc_release({snap: [b'tag2']}) 3421 self.assertNotExists(snap) 3422 3423 def test_release_hold_and_multiple_snap_destruction(self): 3424 snap = ZFSTest.pool.getRoot().getSnap() 3425 lzc.lzc_snapshot([snap]) 3426 3427 with cleanup_fd() as fd: 3428 lzc.lzc_hold({snap: b'tag'}, fd) 3429 3430 lzc.lzc_destroy_snaps([snap], defer=True) 3431 self.assertExists(snap) 3432 3433 lzc.lzc_destroy_snaps([snap], defer=True) 3434 self.assertExists(snap) 3435 3436 lzc.lzc_release({snap: [b'tag']}) 3437 self.assertNotExists(snap) 3438 3439 def test_release_hold_missing_tag(self): 3440 snap = ZFSTest.pool.getRoot().getSnap() 3441 lzc.lzc_snapshot([snap]) 3442 3443 ret = lzc.lzc_release({snap: [b'tag']}) 3444 self.assertEqual(len(ret), 1) 3445 self.assertEqual(ret[0], snap + b'#tag') 3446 3447 def test_release_hold_missing_snap(self): 3448 snap = ZFSTest.pool.getRoot().getSnap() 3449 3450 ret = lzc.lzc_release({snap: [b'tag']}) 3451 self.assertEqual(len(ret), 1) 3452 self.assertEqual(ret[0], snap) 3453 3454 def test_release_hold_missing_snap_2(self): 3455 snap = ZFSTest.pool.getRoot().getSnap() 3456 3457 ret = lzc.lzc_release({snap: [b'tag', b'another']}) 3458 self.assertEqual(len(ret), 1) 3459 self.assertEqual(ret[0], snap) 3460 3461 def test_release_hold_across_pools(self): 3462 snap1 = ZFSTest.pool.getRoot().getSnap() 3463 snap2 = ZFSTest.misc_pool.getRoot().getSnap() 3464 lzc.lzc_snapshot([snap1]) 3465 lzc.lzc_snapshot([snap2]) 3466 3467 with cleanup_fd() as fd: 3468 lzc.lzc_hold({snap1: b'tag'}, fd) 3469 lzc.lzc_hold({snap2: b'tag'}, fd) 3470 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: 3471 lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']}) 3472 for e in ctx.exception.errors: 3473 self.assertIsInstance(e, lzc_exc.PoolsDiffer) 3474 3475 # Apparently the tag name is not verified, 3476 # only its existence is checked. 3477 @unittest.expectedFailure 3478 def test_release_hold_too_long_tag(self): 3479 snap = ZFSTest.pool.getRoot().getSnap() 3480 tag = b't' * 256 3481 lzc.lzc_snapshot([snap]) 3482 3483 with self.assertRaises(lzc_exc.HoldReleaseFailure): 3484 lzc.lzc_release({snap: [tag]}) 3485 3486 # Apparently the full snapshot name is not checked for length 3487 # and this snapshot is treated as simply missing. 3488 @unittest.expectedFailure 3489 def test_release_hold_too_long_snap_name(self): 3490 snap = ZFSTest.pool.getRoot().getTooLongSnap(False) 3491 3492 with self.assertRaises(lzc_exc.HoldReleaseFailure): 3493 lzc.lzc_release({snap: [b'tag']}) 3494 3495 def test_release_hold_too_long_snap_name_2(self): 3496 snap = ZFSTest.pool.getRoot().getTooLongSnap(True) 3497 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: 3498 lzc.lzc_release({snap: [b'tag']}) 3499 for e in ctx.exception.errors: 3500 self.assertIsInstance(e, lzc_exc.NameTooLong) 3501 self.assertEqual(e.name, snap) 3502 3503 def test_release_hold_invalid_snap_name(self): 3504 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' 3505 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: 3506 lzc.lzc_release({snap: [b'tag']}) 3507 for e in ctx.exception.errors: 3508 self.assertIsInstance(e, lzc_exc.NameInvalid) 3509 self.assertEqual(e.name, snap) 3510 3511 def test_release_hold_invalid_snap_name_2(self): 3512 snap = ZFSTest.pool.getRoot().getFilesystem().getName() 3513 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: 3514 lzc.lzc_release({snap: [b'tag']}) 3515 for e in ctx.exception.errors: 3516 self.assertIsInstance(e, lzc_exc.NameInvalid) 3517 self.assertEqual(e.name, snap) 3518 3519 def test_sync_missing_pool(self): 3520 pool = b"nonexistent" 3521 with self.assertRaises(lzc_exc.PoolNotFound): 3522 lzc.lzc_sync(pool) 3523 3524 def test_sync_pool_forced(self): 3525 pool = ZFSTest.pool.getRoot().getName() 3526 lzc.lzc_sync(pool, True) 3527 3528 def test_reopen_missing_pool(self): 3529 pool = b"nonexistent" 3530 with self.assertRaises(lzc_exc.PoolNotFound): 3531 lzc.lzc_reopen(pool) 3532 3533 def test_reopen_pool_no_restart(self): 3534 pool = ZFSTest.pool.getRoot().getName() 3535 lzc.lzc_reopen(pool, False) 3536 3537 def test_channel_program_missing_pool(self): 3538 pool = b"nonexistent" 3539 with self.assertRaises(lzc_exc.PoolNotFound): 3540 lzc.lzc_channel_program(pool, b"return {}") 3541 3542 def test_channel_program_timeout(self): 3543 pool = ZFSTest.pool.getRoot().getName() 3544 zcp = b""" 3545for i = 1,10000 do 3546 zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) 3547end 3548""" 3549 with self.assertRaises(lzc_exc.ZCPTimeout): 3550 lzc.lzc_channel_program(pool, zcp, instrlimit=1) 3551 3552 def test_channel_program_memory_limit(self): 3553 pool = ZFSTest.pool.getRoot().getName() 3554 zcp = b""" 3555for i = 1,10000 do 3556 zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) 3557end 3558""" 3559 with self.assertRaises(lzc_exc.ZCPSpaceError): 3560 lzc.lzc_channel_program(pool, zcp, memlimit=1) 3561 3562 def test_channel_program_invalid_limits(self): 3563 pool = ZFSTest.pool.getRoot().getName() 3564 zcp = b""" 3565return {} 3566""" 3567 with self.assertRaises(lzc_exc.ZCPLimitInvalid): 3568 lzc.lzc_channel_program(pool, zcp, instrlimit=0) 3569 with self.assertRaises(lzc_exc.ZCPLimitInvalid): 3570 lzc.lzc_channel_program(pool, zcp, memlimit=0) 3571 3572 def test_channel_program_syntax_error(self): 3573 pool = ZFSTest.pool.getRoot().getName() 3574 zcp = b""" 3575inv+val:id 3576""" 3577 with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx: 3578 lzc.lzc_channel_program(pool, zcp) 3579 self.assertTrue(b"syntax error" in ctx.exception.details) 3580 3581 def test_channel_program_sync_snapshot(self): 3582 pool = ZFSTest.pool.getRoot().getName() 3583 snapname = ZFSTest.pool.makeName(b"@zcp") 3584 zcp = b""" 3585zfs.sync.snapshot('""" + snapname + b"""') 3586""" 3587 lzc.lzc_channel_program(pool, zcp) 3588 self.assertExists(snapname) 3589 3590 def test_channel_program_runtime_error(self): 3591 pool = ZFSTest.pool.getRoot().getName() 3592 3593 # failing an assertion raises a runtime error 3594 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: 3595 lzc.lzc_channel_program(pool, b"assert(1 == 2)") 3596 self.assertTrue( 3597 b"assertion failed" in ctx.exception.details) 3598 # invoking the error() function raises a runtime error 3599 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: 3600 lzc.lzc_channel_program(pool, b"error()") 3601 3602 def test_channel_program_nosync_runtime_error(self): 3603 pool = ZFSTest.pool.getRoot().getName() 3604 zcp = b""" 3605zfs.sync.snapshot('""" + pool + b"""@zcp') 3606""" 3607 # lzc_channel_program_nosync() allows only "read-only" operations 3608 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: 3609 lzc.lzc_channel_program_nosync(pool, zcp) 3610 self.assertTrue( 3611 b"running functions from the zfs.sync" in ctx.exception.details) 3612 3613 def test_change_key_new(self): 3614 with encrypted_filesystem() as (fs, _): 3615 lzc.lzc_change_key( 3616 fs, 'new_key', 3617 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, 3618 key=os.urandom(lzc.WRAPPING_KEY_LEN)) 3619 3620 def test_change_key_missing_fs(self): 3621 name = b"nonexistent" 3622 3623 with self.assertRaises(lzc_exc.FilesystemNotFound): 3624 lzc.lzc_change_key( 3625 name, 'new_key', 3626 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, 3627 key=os.urandom(lzc.WRAPPING_KEY_LEN)) 3628 3629 def test_change_key_not_loaded(self): 3630 with encrypted_filesystem() as (fs, _): 3631 lzc.lzc_unload_key(fs) 3632 with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): 3633 lzc.lzc_change_key( 3634 fs, 'new_key', 3635 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, 3636 key=os.urandom(lzc.WRAPPING_KEY_LEN)) 3637 3638 def test_change_key_invalid_property(self): 3639 with encrypted_filesystem() as (fs, _): 3640 with self.assertRaises(lzc_exc.PropertyInvalid): 3641 lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"}) 3642 3643 def test_change_key_invalid_crypt_command(self): 3644 with encrypted_filesystem() as (fs, _): 3645 with self.assertRaises(lzc_exc.UnknownCryptCommand): 3646 lzc.lzc_change_key(fs, 'duplicate_key') 3647 3648 def test_load_key(self): 3649 with encrypted_filesystem() as (fs, key): 3650 lzc.lzc_unload_key(fs) 3651 lzc.lzc_load_key(fs, False, key) 3652 3653 def test_load_key_invalid(self): 3654 with encrypted_filesystem() as (fs, key): 3655 lzc.lzc_unload_key(fs) 3656 with self.assertRaises(lzc_exc.EncryptionKeyInvalid): 3657 lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN)) 3658 3659 def test_load_key_already_loaded(self): 3660 with encrypted_filesystem() as (fs, key): 3661 lzc.lzc_unload_key(fs) 3662 lzc.lzc_load_key(fs, False, key) 3663 with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded): 3664 lzc.lzc_load_key(fs, False, key) 3665 3666 def test_load_key_missing_fs(self): 3667 name = b"nonexistent" 3668 3669 with self.assertRaises(lzc_exc.FilesystemNotFound): 3670 lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN)) 3671 3672 def test_unload_key(self): 3673 with encrypted_filesystem() as (fs, _): 3674 lzc.lzc_unload_key(fs) 3675 3676 def test_unload_key_missing_fs(self): 3677 name = b"nonexistent" 3678 3679 with self.assertRaises(lzc_exc.FilesystemNotFound): 3680 lzc.lzc_unload_key(name) 3681 3682 def test_unload_key_busy(self): 3683 with encrypted_filesystem() as (fs, _): 3684 with zfs_mount(fs): 3685 with self.assertRaises(lzc_exc.DatasetBusy): 3686 lzc.lzc_unload_key(fs) 3687 3688 def test_unload_key_not_loaded(self): 3689 with encrypted_filesystem() as (fs, _): 3690 lzc.lzc_unload_key(fs) 3691 with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): 3692 lzc.lzc_unload_key(fs) 3693 3694 def test_checkpoint(self): 3695 pool = ZFSTest.pool.getRoot().getName() 3696 3697 lzc.lzc_pool_checkpoint(pool) 3698 3699 def test_checkpoint_missing_pool(self): 3700 pool = b"nonexistent" 3701 3702 with self.assertRaises(lzc_exc.PoolNotFound): 3703 lzc.lzc_pool_checkpoint(pool) 3704 3705 def test_checkpoint_already_exists(self): 3706 pool = ZFSTest.pool.getRoot().getName() 3707 3708 lzc.lzc_pool_checkpoint(pool) 3709 with self.assertRaises(lzc_exc.CheckpointExists): 3710 lzc.lzc_pool_checkpoint(pool) 3711 3712 def test_checkpoint_discard(self): 3713 pool = ZFSTest.pool.getRoot().getName() 3714 3715 lzc.lzc_pool_checkpoint(pool) 3716 lzc.lzc_pool_checkpoint_discard(pool) 3717 3718 def test_checkpoint_discard_missing_pool(self): 3719 pool = b"nonexistent" 3720 3721 with self.assertRaises(lzc_exc.PoolNotFound): 3722 lzc.lzc_pool_checkpoint_discard(pool) 3723 3724 def test_checkpoint_discard_missing_checkpoint(self): 3725 pool = ZFSTest.pool.getRoot().getName() 3726 3727 with self.assertRaises(lzc_exc.CheckpointNotFound): 3728 lzc.lzc_pool_checkpoint_discard(pool) 3729 3730 @needs_support(lzc.lzc_rename) 3731 def test_rename(self): 3732 src = ZFSTest.pool.makeName(b"source") 3733 tgt = ZFSTest.pool.makeName(b"target") 3734 3735 lzc.lzc_create(src) 3736 lzc.lzc_rename(src, tgt) 3737 self.assertNotExists(src) 3738 self.assertExists(tgt) 3739 3740 @needs_support(lzc.lzc_rename) 3741 def test_rename_nonexistent(self): 3742 src = ZFSTest.pool.makeName(b"source") 3743 tgt = ZFSTest.pool.makeName(b"target") 3744 3745 with self.assertRaises(lzc_exc.FilesystemNotFound): 3746 lzc.lzc_rename(src, tgt) 3747 3748 @needs_support(lzc.lzc_rename) 3749 def test_rename_existing_target(self): 3750 src = ZFSTest.pool.makeName(b"source") 3751 tgt = ZFSTest.pool.makeName(b"target") 3752 3753 lzc.lzc_create(src) 3754 lzc.lzc_create(tgt) 3755 with self.assertRaises(lzc_exc.FilesystemExists): 3756 lzc.lzc_rename(src, tgt) 3757 3758 @needs_support(lzc.lzc_rename) 3759 def test_rename_nonexistent_target_parent(self): 3760 src = ZFSTest.pool.makeName(b"source") 3761 tgt = ZFSTest.pool.makeName(b"parent/target") 3762 3763 lzc.lzc_create(src) 3764 with self.assertRaises(lzc_exc.FilesystemNotFound): 3765 lzc.lzc_rename(src, tgt) 3766 3767 @needs_support(lzc.lzc_rename) 3768 def test_rename_parent_is_zvol(self): 3769 src = ZFSTest.pool.makeName(b"source") 3770 zvol = ZFSTest.pool.makeName(b"parent") 3771 tgt = zvol + b"/target" 3772 props = {b"volsize": 1024 * 1024} 3773 3774 lzc.lzc_create(src) 3775 lzc.lzc_create(zvol, ds_type='zvol', props=props) 3776 with self.assertRaises(lzc_exc.WrongParent): 3777 lzc.lzc_rename(src, tgt) 3778 3779 @needs_support(lzc.lzc_destroy) 3780 def test_destroy(self): 3781 fs = ZFSTest.pool.makeName(b"test-fs") 3782 3783 lzc.lzc_create(fs) 3784 lzc.lzc_destroy(fs) 3785 self.assertNotExists(fs) 3786 3787 @needs_support(lzc.lzc_destroy) 3788 def test_destroy_nonexistent(self): 3789 fs = ZFSTest.pool.makeName(b"test-fs") 3790 3791 with self.assertRaises(lzc_exc.FilesystemNotFound): 3792 lzc.lzc_destroy(fs) 3793 3794 3795class _TempPool(object): 3796 SNAPSHOTS = [b'snap', b'snap1', b'snap2'] 3797 BOOKMARKS = [b'bmark', b'bmark1', b'bmark2'] 3798 3799 _cachefile_suffix = ".cachefile" 3800 3801 # XXX Whether to do a sloppy but much faster cleanup 3802 # or a proper but slower one. 3803 _recreate_pools = True 3804 3805 def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]): 3806 self._filesystems = filesystems 3807 self._readonly = readonly 3808 if sys.version_info < (3, 0): 3809 self._pool_name = b'pool.' + bytes(uuid.uuid4()) 3810 else: 3811 self._pool_name = b'pool.' + bytes(str(uuid.uuid4()), 3812 encoding='utf-8') 3813 self._root = _Filesystem(self._pool_name) 3814 (fd, self._pool_file_path) = tempfile.mkstemp( 3815 suffix='.zpool', prefix='tmp-') 3816 if readonly: 3817 cachefile = self._pool_file_path + _TempPool._cachefile_suffix 3818 else: 3819 cachefile = 'none' 3820 self._zpool_create = [ 3821 'zpool', 'create', '-o', 'cachefile=' + cachefile, 3822 '-O', 'mountpoint=legacy', '-O', 'compression=off', 3823 self._pool_name, self._pool_file_path] 3824 try: 3825 os.ftruncate(fd, size) 3826 os.close(fd) 3827 3828 subprocess.check_output( 3829 self._zpool_create, stderr=subprocess.STDOUT) 3830 3831 for fs in filesystems: 3832 lzc.lzc_create(self.makeName(fs)) 3833 3834 self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks') 3835 3836 if readonly: 3837 # To make a pool read-only it must exported and re-imported 3838 # with readonly option. 3839 # The most deterministic way to re-import the pool is by using 3840 # a cache file. 3841 # But the cache file has to be stashed away before the pool is 3842 # exported, because otherwise the pool is removed from the 3843 # cache. 3844 shutil.copyfile(cachefile, cachefile + '.tmp') 3845 subprocess.check_output( 3846 ['zpool', 'export', '-f', self._pool_name], 3847 stderr=subprocess.STDOUT) 3848 os.rename(cachefile + '.tmp', cachefile) 3849 subprocess.check_output( 3850 ['zpool', 'import', '-f', '-N', '-c', cachefile, 3851 '-o', 'readonly=on', self._pool_name], 3852 stderr=subprocess.STDOUT) 3853 os.remove(cachefile) 3854 3855 except subprocess.CalledProcessError as e: 3856 self.cleanUp() 3857 if b'permission denied' in e.output: 3858 raise unittest.SkipTest( 3859 'insufficient privileges to run libzfs_core tests') 3860 print('command failed: ', e.output) 3861 raise 3862 except Exception: 3863 self.cleanUp() 3864 raise 3865 3866 def reset(self): 3867 if self._readonly: 3868 return 3869 3870 if not self.__class__._recreate_pools: 3871 snaps = [] 3872 for fs in [''] + self._filesystems: 3873 for snap in self.__class__.SNAPSHOTS: 3874 snaps.append(self.makeName(fs + '@' + snap)) 3875 self.getRoot().visitSnaps(lambda snap: snaps.append(snap)) 3876 lzc.lzc_destroy_snaps(snaps, defer=False) 3877 3878 if self._bmarks_supported: 3879 bmarks = [] 3880 for fs in [''] + self._filesystems: 3881 for bmark in self.__class__.BOOKMARKS: 3882 bmarks.append(self.makeName(fs + '#' + bmark)) 3883 self.getRoot().visitBookmarks( 3884 lambda bmark: bmarks.append(bmark)) 3885 lzc.lzc_destroy_bookmarks(bmarks) 3886 self.getRoot().reset() 3887 return 3888 3889 # On the CI builders this may fail with "pool is busy" 3890 # Retry 5 times before raising an error 3891 retry = 0 3892 while True: 3893 try: 3894 subprocess.check_output( 3895 ['zpool', 'destroy', '-f', self._pool_name], 3896 stderr=subprocess.STDOUT) 3897 subprocess.check_output( 3898 self._zpool_create, stderr=subprocess.STDOUT) 3899 break 3900 except subprocess.CalledProcessError as e: 3901 if b'pool is busy' in e.output and retry < 5: 3902 retry += 1 3903 time.sleep(1) 3904 continue 3905 else: 3906 print('command failed: ', e.output) 3907 raise 3908 for fs in self._filesystems: 3909 lzc.lzc_create(self.makeName(fs)) 3910 self.getRoot().reset() 3911 3912 def cleanUp(self): 3913 try: 3914 subprocess.check_output( 3915 ['zpool', 'destroy', '-f', self._pool_name], 3916 stderr=subprocess.STDOUT) 3917 except Exception: 3918 pass 3919 try: 3920 os.remove(self._pool_file_path) 3921 except Exception: 3922 pass 3923 try: 3924 os.remove(self._pool_file_path + _TempPool._cachefile_suffix) 3925 except Exception: 3926 pass 3927 try: 3928 os.remove( 3929 self._pool_file_path + _TempPool._cachefile_suffix + '.tmp') 3930 except Exception: 3931 pass 3932 3933 def makeName(self, relative=None): 3934 if not relative: 3935 return self._pool_name 3936 if relative.startswith((b'@', b'#')): 3937 return self._pool_name + relative 3938 return self._pool_name + b'/' + relative 3939 3940 def makeTooLongName(self, prefix=None): 3941 if not prefix: 3942 prefix = b'x' 3943 prefix = self.makeName(prefix) 3944 pad_len = lzc.MAXNAMELEN + 1 - len(prefix) 3945 if pad_len > 0: 3946 return prefix + b'x' * pad_len 3947 else: 3948 return prefix 3949 3950 def makeTooLongComponent(self, prefix=None): 3951 padding = b'x' * (lzc.MAXNAMELEN + 1) 3952 if not prefix: 3953 prefix = padding 3954 else: 3955 prefix = prefix + padding 3956 return self.makeName(prefix) 3957 3958 def getRoot(self): 3959 return self._root 3960 3961 def getFilesystem(self, fsname): 3962 return _Filesystem(self._pool_name + b'/' + fsname) 3963 3964 def isPoolFeatureAvailable(self, feature): 3965 output = subprocess.check_output( 3966 ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) 3967 output = output.strip() 3968 return output != '' 3969 3970 def isPoolFeatureEnabled(self, feature): 3971 output = subprocess.check_output( 3972 ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) 3973 output = output.split()[2] 3974 return output in [b'active', b'enabled'] 3975 3976 3977class _Filesystem(object): 3978 3979 def __init__(self, name): 3980 self._name = name 3981 self.reset() 3982 3983 def getName(self): 3984 return self._name 3985 3986 def reset(self): 3987 self._children = [] 3988 self._fs_id = 0 3989 self._snap_id = 0 3990 self._bmark_id = 0 3991 3992 def getFilesystem(self): 3993 self._fs_id += 1 3994 fsname = self._name + b'/fs' + str(self._fs_id).encode() 3995 fs = _Filesystem(fsname) 3996 self._children.append(fs) 3997 return fs 3998 3999 def getProperty(self, propname, received=False): 4000 if received: 4001 output = subprocess.check_output( 4002 ['zfs', 'get', '-pH', '-o', 'received', propname, self._name]) 4003 else: 4004 output = subprocess.check_output( 4005 ['zfs', 'get', '-pH', '-o', 'value', propname, self._name]) 4006 return output.strip() 4007 4008 def _makeSnapName(self, i): 4009 return self._name + b'@snap' + str(i).encode() 4010 4011 def getSnap(self): 4012 self._snap_id += 1 4013 return self._makeSnapName(self._snap_id) 4014 4015 def _makeBookmarkName(self, i): 4016 return self._name + b'#bmark' + bytes(i) 4017 4018 def getBookmark(self): 4019 self._bmark_id += 1 4020 return self._makeBookmarkName(self._bmark_id) 4021 4022 def _makeTooLongName(self, too_long_component): 4023 if too_long_component: 4024 return b'x' * (lzc.MAXNAMELEN + 1) 4025 4026 # Note that another character is used for one of '/', '@', '#'. 4027 comp_len = lzc.MAXNAMELEN - len(self._name) 4028 if comp_len > 0: 4029 return b'x' * comp_len 4030 else: 4031 return b'x' 4032 4033 def getTooLongFilesystemName(self, too_long_component): 4034 return self._name + b'/' + self._makeTooLongName(too_long_component) 4035 4036 def getTooLongSnap(self, too_long_component): 4037 return self._name + b'@' + self._makeTooLongName(too_long_component) 4038 4039 def getTooLongBookmark(self, too_long_component): 4040 return self._name + b'#' + self._makeTooLongName(too_long_component) 4041 4042 def _visitFilesystems(self, visitor): 4043 for child in self._children: 4044 child._visitFilesystems(visitor) 4045 visitor(self) 4046 4047 def visitFilesystems(self, visitor): 4048 def _fsVisitor(fs): 4049 visitor(fs._name) 4050 4051 self._visitFilesystems(_fsVisitor) 4052 4053 def visitSnaps(self, visitor): 4054 def _snapVisitor(fs): 4055 for i in range(1, fs._snap_id + 1): 4056 visitor(fs._makeSnapName(i)) 4057 4058 self._visitFilesystems(_snapVisitor) 4059 4060 def visitBookmarks(self, visitor): 4061 def _bmarkVisitor(fs): 4062 for i in range(1, fs._bmark_id + 1): 4063 visitor(fs._makeBookmarkName(i)) 4064 4065 self._visitFilesystems(_bmarkVisitor) 4066 4067 4068# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 4069