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