]> git.proxmox.com Git - mirror_zfs.git/blob - contrib/pyzfs/libzfs_core/test/test_libzfs_core.py
Remove code for zfs remap
[mirror_zfs.git] / contrib / pyzfs / libzfs_core / test / test_libzfs_core.py
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 """
18 Tests for `libzfs_core` operations.
19
20 These are mostly functional and conformance tests that validate
21 that the operations produce expected effects or fail with expected
22 exceptions.
23 """
24 from __future__ import absolute_import, division, print_function
25
26 import unittest
27 import contextlib
28 import errno
29 import filecmp
30 import os
31 import platform
32 import resource
33 import shutil
34 import stat
35 import subprocess
36 import sys
37 import tempfile
38 import time
39 import uuid
40 import itertools
41 import zlib
42 from .. import _libzfs_core as lzc
43 from .. import exceptions as lzc_exc
44 from .._nvlist import packed_nvlist_out
45
46
47 def _print(*args):
48 for arg in args:
49 print(arg, end=' ')
50 print()
51
52
53 @contextlib.contextmanager
54 def 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
65 def _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
102 def _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
115 def _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
122 def _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
131 if platform.system() == 'SunOS':
132 zfs_mount = _zfs_mount_illumos
133 else:
134 zfs_mount = _zfs_mount
135
136
137 @contextlib.contextmanager
138 def 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
147 def 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
156 def dev_null():
157 with os_open('/dev/null', os.O_WRONLY) as fd:
158 yield fd
159
160
161 @contextlib.contextmanager
162 def dev_zero():
163 with os_open('/dev/zero', os.O_RDONLY) as fd:
164 yield fd
165
166
167 @contextlib.contextmanager
168 def 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
177 def 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
194 def 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
209 def 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
225 def 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
237 def skipIfFeatureAvailable(feature, message):
238 return runtimeSkipIf(
239 lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature),
240 message)
241
242
243 def skipUnlessFeatureEnabled(feature, message):
244 return runtimeSkipIf(
245 lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature),
246 message)
247
248
249 def skipUnlessBookmarksSupported(f):
250 return skipUnlessFeatureEnabled(
251 'bookmarks', 'bookmarks are not enabled')(f)
252
253
254 def snap_always_unmounted_before_destruction():
255 # Apparently ZoL automatically unmounts the snapshot
256 # only if it is mounted at its default .zfs/snapshot
257 # mountpoint.
258 return (
259 platform.system() != 'Linux', 'snapshot is not auto-unmounted')
260
261
262 def 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
269 def needs_support(function):
270 return unittest.skipUnless(
271 lzc.is_supported(function),
272 '{} not available'.format(function.__name__))
273
274
275 class 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
1036 lzc.lzc_snapshot(snaps)
1037 lzc.lzc_bookmark(bmark_dict)
1038 lzc.lzc_destroy_snaps(snaps, defer=False)
1039
1040 @skipUnlessBookmarksSupported
1041 def test_bookmarks_empty(self):
1042 lzc.lzc_bookmark({})
1043
1044 @skipUnlessBookmarksSupported
1045 def test_bookmarks_mismatching_name(self):
1046 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1047 bmarks = [ZFSTest.pool.makeName(b'fs2#bmark1')]
1048 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1049
1050 lzc.lzc_snapshot(snaps)
1051 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1052 lzc.lzc_bookmark(bmark_dict)
1053
1054 for e in ctx.exception.errors:
1055 self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1056
1057 @skipUnlessBookmarksSupported
1058 def test_bookmarks_invalid_name(self):
1059 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1060 bmarks = [ZFSTest.pool.makeName(b'fs1#bmark!')]
1061 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1062
1063 lzc.lzc_snapshot(snaps)
1064 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1065 lzc.lzc_bookmark(bmark_dict)
1066
1067 for e in ctx.exception.errors:
1068 self.assertIsInstance(e, lzc_exc.NameInvalid)
1069
1070 @skipUnlessBookmarksSupported
1071 def test_bookmarks_invalid_name_2(self):
1072 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1073 bmarks = [ZFSTest.pool.makeName(b'fs1@bmark')]
1074 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1075
1076 lzc.lzc_snapshot(snaps)
1077 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1078 lzc.lzc_bookmark(bmark_dict)
1079
1080 for e in ctx.exception.errors:
1081 self.assertIsInstance(e, lzc_exc.NameInvalid)
1082
1083 @skipUnlessBookmarksSupported
1084 def test_bookmarks_too_long_name(self):
1085 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1086 bmarks = [ZFSTest.pool.makeTooLongName(b'fs1#')]
1087 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1088
1089 lzc.lzc_snapshot(snaps)
1090 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1091 lzc.lzc_bookmark(bmark_dict)
1092
1093 for e in ctx.exception.errors:
1094 self.assertIsInstance(e, lzc_exc.NameTooLong)
1095
1096 @skipUnlessBookmarksSupported
1097 def test_bookmarks_too_long_name_2(self):
1098 snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1099 bmarks = [ZFSTest.pool.makeTooLongComponent(b'fs1#')]
1100 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1101
1102 lzc.lzc_snapshot(snaps)
1103 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1104 lzc.lzc_bookmark(bmark_dict)
1105
1106 for e in ctx.exception.errors:
1107 self.assertIsInstance(e, lzc_exc.NameTooLong)
1108
1109 @skipUnlessBookmarksSupported
1110 def test_bookmarks_mismatching_names(self):
1111 snaps = [ZFSTest.pool.makeName(
1112 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1113 bmarks = [ZFSTest.pool.makeName(
1114 b'fs2#bmark1'), ZFSTest.pool.makeName(b'fs1#bmark1')]
1115 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1116
1117 lzc.lzc_snapshot(snaps)
1118 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1119 lzc.lzc_bookmark(bmark_dict)
1120
1121 for e in ctx.exception.errors:
1122 self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1123
1124 @skipUnlessBookmarksSupported
1125 def test_bookmarks_partially_mismatching_names(self):
1126 snaps = [ZFSTest.pool.makeName(
1127 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1128 bmarks = [ZFSTest.pool.makeName(
1129 b'fs2#bmark'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1130 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1131
1132 lzc.lzc_snapshot(snaps)
1133 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1134 lzc.lzc_bookmark(bmark_dict)
1135
1136 for e in ctx.exception.errors:
1137 self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1138
1139 @skipUnlessBookmarksSupported
1140 def test_bookmarks_cross_pool(self):
1141 snaps = [ZFSTest.pool.makeName(
1142 b'fs1@snap1'), ZFSTest.misc_pool.makeName(b'@snap1')]
1143 bmarks = [ZFSTest.pool.makeName(
1144 b'fs1#bmark1'), ZFSTest.misc_pool.makeName(b'#bmark1')]
1145 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1146
1147 lzc.lzc_snapshot(snaps[0:1])
1148 lzc.lzc_snapshot(snaps[1:2])
1149 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1150 lzc.lzc_bookmark(bmark_dict)
1151
1152 for e in ctx.exception.errors:
1153 self.assertIsInstance(e, lzc_exc.PoolsDiffer)
1154
1155 @skipUnlessBookmarksSupported
1156 def test_bookmarks_missing_snap(self):
1157 snaps = [ZFSTest.pool.makeName(
1158 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1159 bmarks = [ZFSTest.pool.makeName(
1160 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1161 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1162
1163 lzc.lzc_snapshot(snaps[0:1])
1164 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1165 lzc.lzc_bookmark(bmark_dict)
1166
1167 for e in ctx.exception.errors:
1168 self.assertIsInstance(e, lzc_exc.SnapshotNotFound)
1169
1170 @skipUnlessBookmarksSupported
1171 def test_bookmarks_missing_snaps(self):
1172 snaps = [ZFSTest.pool.makeName(
1173 b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1174 bmarks = [ZFSTest.pool.makeName(
1175 b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1176 bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1177
1178 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1179 lzc.lzc_bookmark(bmark_dict)
1180
1181 for e in ctx.exception.errors:
1182 self.assertIsInstance(e, lzc_exc.SnapshotNotFound)
1183
1184 @skipUnlessBookmarksSupported
1185 def test_bookmarks_for_the_same_snap(self):
1186 snap = ZFSTest.pool.makeName(b'fs1@snap1')
1187 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1188 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1189 bmark_dict = {bmark1: snap, bmark2: snap}
1190
1191 lzc.lzc_snapshot([snap])
1192 lzc.lzc_bookmark(bmark_dict)
1193
1194 @skipUnlessBookmarksSupported
1195 def test_bookmarks_for_the_same_snap_2(self):
1196 snap = ZFSTest.pool.makeName(b'fs1@snap1')
1197 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1198 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1199 bmark_dict1 = {bmark1: snap}
1200 bmark_dict2 = {bmark2: snap}
1201
1202 lzc.lzc_snapshot([snap])
1203 lzc.lzc_bookmark(bmark_dict1)
1204 lzc.lzc_bookmark(bmark_dict2)
1205
1206 @skipUnlessBookmarksSupported
1207 def test_bookmarks_duplicate_name(self):
1208 snap1 = ZFSTest.pool.makeName(b'fs1@snap1')
1209 snap2 = ZFSTest.pool.makeName(b'fs1@snap2')
1210 bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1211 bmark_dict1 = {bmark: snap1}
1212 bmark_dict2 = {bmark: snap2}
1213
1214 lzc.lzc_snapshot([snap1])
1215 lzc.lzc_snapshot([snap2])
1216 lzc.lzc_bookmark(bmark_dict1)
1217 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1218 lzc.lzc_bookmark(bmark_dict2)
1219
1220 for e in ctx.exception.errors:
1221 self.assertIsInstance(e, lzc_exc.BookmarkExists)
1222
1223 @skipUnlessBookmarksSupported
1224 def test_get_bookmarks(self):
1225 snap1 = ZFSTest.pool.makeName(b'fs1@snap1')
1226 snap2 = ZFSTest.pool.makeName(b'fs1@snap2')
1227 bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1228 bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1229 bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1230 bmark_dict1 = {bmark1: snap1, bmark2: snap2}
1231 bmark_dict2 = {bmark: snap2}
1232
1233 lzc.lzc_snapshot([snap1])
1234 lzc.lzc_snapshot([snap2])
1235 lzc.lzc_bookmark(bmark_dict1)
1236 lzc.lzc_bookmark(bmark_dict2)
1237 lzc.lzc_destroy_snaps([snap1, snap2], defer=False)
1238
1239 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1240 self.assertEqual(len(bmarks), 3)
1241 for b in b'bmark', b'bmark1', b'bmark2':
1242 self.assertIn(b, bmarks)
1243 self.assertIsInstance(bmarks[b], dict)
1244 self.assertEqual(len(bmarks[b]), 0)
1245
1246 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'),
1247 [b'guid', b'createtxg', b'creation'])
1248 self.assertEqual(len(bmarks), 3)
1249 for b in b'bmark', b'bmark1', b'bmark2':
1250 self.assertIn(b, bmarks)
1251 self.assertIsInstance(bmarks[b], dict)
1252 self.assertEqual(len(bmarks[b]), 3)
1253
1254 @skipUnlessBookmarksSupported
1255 def test_get_bookmarks_invalid_property(self):
1256 snap = ZFSTest.pool.makeName(b'fs1@snap')
1257 bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1258 bmark_dict = {bmark: snap}
1259
1260 lzc.lzc_snapshot([snap])
1261 lzc.lzc_bookmark(bmark_dict)
1262
1263 bmarks = lzc.lzc_get_bookmarks(
1264 ZFSTest.pool.makeName(b'fs1'), [b'badprop'])
1265 self.assertEqual(len(bmarks), 1)
1266 for b in (b'bmark', ):
1267 self.assertIn(b, bmarks)
1268 self.assertIsInstance(bmarks[b], dict)
1269 self.assertEqual(len(bmarks[b]), 0)
1270
1271 @skipUnlessBookmarksSupported
1272 def test_get_bookmarks_nonexistent_fs(self):
1273 with self.assertRaises(lzc_exc.FilesystemNotFound):
1274 lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'nonexistent'))
1275
1276 @skipUnlessBookmarksSupported
1277 def test_destroy_bookmarks(self):
1278 snap = ZFSTest.pool.makeName(b'fs1@snap')
1279 bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1280 bmark_dict = {bmark: snap}
1281
1282 lzc.lzc_snapshot([snap])
1283 lzc.lzc_bookmark(bmark_dict)
1284
1285 lzc.lzc_destroy_bookmarks(
1286 [bmark, ZFSTest.pool.makeName(b'fs1#nonexistent')])
1287 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1288 self.assertEqual(len(bmarks), 0)
1289
1290 @skipUnlessBookmarksSupported
1291 def test_destroy_bookmarks_invalid_name(self):
1292 snap = ZFSTest.pool.makeName(b'fs1@snap')
1293 bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1294 bmark_dict = {bmark: snap}
1295
1296 lzc.lzc_snapshot([snap])
1297 lzc.lzc_bookmark(bmark_dict)
1298
1299 with self.assertRaises(lzc_exc.BookmarkDestructionFailure) as ctx:
1300 lzc.lzc_destroy_bookmarks(
1301 [bmark, ZFSTest.pool.makeName(b'fs1/nonexistent')])
1302 for e in ctx.exception.errors:
1303 self.assertIsInstance(e, lzc_exc.NameInvalid)
1304
1305 bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1306 self.assertEqual(len(bmarks), 1)
1307 self.assertIn(b'bmark', bmarks)
1308
1309 @skipUnlessBookmarksSupported
1310 def test_destroy_bookmark_nonexistent_fs(self):
1311 lzc.lzc_destroy_bookmarks(
1312 [ZFSTest.pool.makeName(b'nonexistent#bmark')])
1313
1314 @skipUnlessBookmarksSupported
1315 def test_destroy_bookmarks_empty(self):
1316 lzc.lzc_bookmark({})
1317
1318 def test_snaprange_space(self):
1319 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1320 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1321 snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1322
1323 lzc.lzc_snapshot([snap1])
1324 lzc.lzc_snapshot([snap2])
1325 lzc.lzc_snapshot([snap3])
1326
1327 space = lzc.lzc_snaprange_space(snap1, snap2)
1328 self.assertIsInstance(space, (int, int))
1329 space = lzc.lzc_snaprange_space(snap2, snap3)
1330 self.assertIsInstance(space, (int, int))
1331 space = lzc.lzc_snaprange_space(snap1, snap3)
1332 self.assertIsInstance(space, (int, int))
1333
1334 def test_snaprange_space_2(self):
1335 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1336 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1337 snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1338
1339 lzc.lzc_snapshot([snap1])
1340 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1341 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1342 for i in range(1024):
1343 f.write(b'x' * 1024)
1344 f.flush()
1345 lzc.lzc_snapshot([snap2])
1346 lzc.lzc_snapshot([snap3])
1347
1348 space = lzc.lzc_snaprange_space(snap1, snap2)
1349 self.assertGreater(space, 1024 * 1024)
1350 space = lzc.lzc_snaprange_space(snap2, snap3)
1351 self.assertGreater(space, 1024 * 1024)
1352 space = lzc.lzc_snaprange_space(snap1, snap3)
1353 self.assertGreater(space, 1024 * 1024)
1354
1355 def test_snaprange_space_same_snap(self):
1356 snap = ZFSTest.pool.makeName(b"fs1@snap")
1357
1358 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1359 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1360 for i in range(1024):
1361 f.write(b'x' * 1024)
1362 f.flush()
1363 lzc.lzc_snapshot([snap])
1364
1365 space = lzc.lzc_snaprange_space(snap, snap)
1366 self.assertGreater(space, 1024 * 1024)
1367 self.assertAlmostEqual(space, 1024 * 1024, delta=1024 * 1024 // 20)
1368
1369 def test_snaprange_space_wrong_order(self):
1370 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1371 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1372
1373 lzc.lzc_snapshot([snap1])
1374 lzc.lzc_snapshot([snap2])
1375
1376 with self.assertRaises(lzc_exc.SnapshotMismatch):
1377 lzc.lzc_snaprange_space(snap2, snap1)
1378
1379 def test_snaprange_space_unrelated(self):
1380 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1381 snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1382
1383 lzc.lzc_snapshot([snap1])
1384 lzc.lzc_snapshot([snap2])
1385
1386 with self.assertRaises(lzc_exc.SnapshotMismatch):
1387 lzc.lzc_snaprange_space(snap1, snap2)
1388
1389 def test_snaprange_space_across_pools(self):
1390 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1391 snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1392
1393 lzc.lzc_snapshot([snap1])
1394 lzc.lzc_snapshot([snap2])
1395
1396 with self.assertRaises(lzc_exc.PoolsDiffer):
1397 lzc.lzc_snaprange_space(snap1, snap2)
1398
1399 def test_snaprange_space_nonexistent(self):
1400 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1401 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1402
1403 lzc.lzc_snapshot([snap1])
1404
1405 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1406 lzc.lzc_snaprange_space(snap1, snap2)
1407 self.assertEqual(ctx.exception.name, snap2)
1408
1409 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1410 lzc.lzc_snaprange_space(snap2, snap1)
1411 self.assertEqual(ctx.exception.name, snap1)
1412
1413 def test_snaprange_space_invalid_name(self):
1414 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1415 snap2 = ZFSTest.pool.makeName(b"fs1@sn#p")
1416
1417 lzc.lzc_snapshot([snap1])
1418
1419 with self.assertRaises(lzc_exc.NameInvalid):
1420 lzc.lzc_snaprange_space(snap1, snap2)
1421
1422 def test_snaprange_space_not_snap(self):
1423 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1424 snap2 = ZFSTest.pool.makeName(b"fs1")
1425
1426 lzc.lzc_snapshot([snap1])
1427
1428 with self.assertRaises(lzc_exc.NameInvalid):
1429 lzc.lzc_snaprange_space(snap1, snap2)
1430 with self.assertRaises(lzc_exc.NameInvalid):
1431 lzc.lzc_snaprange_space(snap2, snap1)
1432
1433 def test_snaprange_space_not_snap_2(self):
1434 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1435 snap2 = ZFSTest.pool.makeName(b"fs1#bmark")
1436
1437 lzc.lzc_snapshot([snap1])
1438
1439 with self.assertRaises(lzc_exc.NameInvalid):
1440 lzc.lzc_snaprange_space(snap1, snap2)
1441 with self.assertRaises(lzc_exc.NameInvalid):
1442 lzc.lzc_snaprange_space(snap2, snap1)
1443
1444 def test_send_space(self):
1445 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1446 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1447 snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1448
1449 lzc.lzc_snapshot([snap1])
1450 lzc.lzc_snapshot([snap2])
1451 lzc.lzc_snapshot([snap3])
1452
1453 space = lzc.lzc_send_space(snap2, snap1)
1454 self.assertIsInstance(space, (int, int))
1455 space = lzc.lzc_send_space(snap3, snap2)
1456 self.assertIsInstance(space, (int, int))
1457 space = lzc.lzc_send_space(snap3, snap1)
1458 self.assertIsInstance(space, (int, int))
1459 space = lzc.lzc_send_space(snap1)
1460 self.assertIsInstance(space, (int, int))
1461 space = lzc.lzc_send_space(snap2)
1462 self.assertIsInstance(space, (int, int))
1463 space = lzc.lzc_send_space(snap3)
1464 self.assertIsInstance(space, (int, int))
1465
1466 def test_send_space_2(self):
1467 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1468 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1469 snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1470
1471 lzc.lzc_snapshot([snap1])
1472 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1473 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1474 for i in range(1024):
1475 f.write(b'x' * 1024)
1476 f.flush()
1477 lzc.lzc_snapshot([snap2])
1478 lzc.lzc_snapshot([snap3])
1479
1480 space = lzc.lzc_send_space(snap2, snap1)
1481 self.assertGreater(space, 1024 * 1024)
1482
1483 space = lzc.lzc_send_space(snap3, snap2)
1484
1485 space = lzc.lzc_send_space(snap3, snap1)
1486
1487 space_empty = lzc.lzc_send_space(snap1)
1488
1489 space = lzc.lzc_send_space(snap2)
1490 self.assertGreater(space, 1024 * 1024)
1491
1492 space = lzc.lzc_send_space(snap3)
1493 self.assertEqual(space, space_empty)
1494
1495 def test_send_space_same_snap(self):
1496 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1497 lzc.lzc_snapshot([snap1])
1498 with self.assertRaises(lzc_exc.SnapshotMismatch):
1499 lzc.lzc_send_space(snap1, snap1)
1500
1501 def test_send_space_wrong_order(self):
1502 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1503 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1504
1505 lzc.lzc_snapshot([snap1])
1506 lzc.lzc_snapshot([snap2])
1507
1508 with self.assertRaises(lzc_exc.SnapshotMismatch):
1509 lzc.lzc_send_space(snap1, snap2)
1510
1511 def test_send_space_unrelated(self):
1512 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1513 snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1514
1515 lzc.lzc_snapshot([snap1])
1516 lzc.lzc_snapshot([snap2])
1517
1518 with self.assertRaises(lzc_exc.SnapshotMismatch):
1519 lzc.lzc_send_space(snap1, snap2)
1520
1521 def test_send_space_across_pools(self):
1522 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1523 snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1524
1525 lzc.lzc_snapshot([snap1])
1526 lzc.lzc_snapshot([snap2])
1527
1528 with self.assertRaises(lzc_exc.PoolsDiffer):
1529 lzc.lzc_send_space(snap1, snap2)
1530
1531 def test_send_space_nonexistent(self):
1532 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1533 snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1534
1535 lzc.lzc_snapshot([snap1])
1536
1537 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1538 lzc.lzc_send_space(snap1, snap2)
1539 self.assertEqual(ctx.exception.name, snap1)
1540
1541 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1542 lzc.lzc_send_space(snap2, snap1)
1543 self.assertEqual(ctx.exception.name, snap2)
1544
1545 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1546 lzc.lzc_send_space(snap2)
1547 self.assertEqual(ctx.exception.name, snap2)
1548
1549 def test_send_space_invalid_name(self):
1550 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1551 snap2 = ZFSTest.pool.makeName(b"fs1@sn!p")
1552
1553 lzc.lzc_snapshot([snap1])
1554
1555 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1556 lzc.lzc_send_space(snap2, snap1)
1557 self.assertEqual(ctx.exception.name, snap2)
1558 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1559 lzc.lzc_send_space(snap2)
1560 self.assertEqual(ctx.exception.name, snap2)
1561 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1562 lzc.lzc_send_space(snap1, snap2)
1563 self.assertEqual(ctx.exception.name, snap2)
1564
1565 def test_send_space_not_snap(self):
1566 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1567 snap2 = ZFSTest.pool.makeName(b"fs1")
1568
1569 lzc.lzc_snapshot([snap1])
1570
1571 with self.assertRaises(lzc_exc.NameInvalid):
1572 lzc.lzc_send_space(snap1, snap2)
1573 with self.assertRaises(lzc_exc.NameInvalid):
1574 lzc.lzc_send_space(snap2, snap1)
1575 with self.assertRaises(lzc_exc.NameInvalid):
1576 lzc.lzc_send_space(snap2)
1577
1578 def test_send_space_not_snap_2(self):
1579 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1580 snap2 = ZFSTest.pool.makeName(b"fs1#bmark")
1581
1582 lzc.lzc_snapshot([snap1])
1583
1584 with self.assertRaises(lzc_exc.NameInvalid):
1585 lzc.lzc_send_space(snap2, snap1)
1586 with self.assertRaises(lzc_exc.NameInvalid):
1587 lzc.lzc_send_space(snap2)
1588
1589 def test_send_full(self):
1590 snap = ZFSTest.pool.makeName(b"fs1@snap")
1591
1592 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1593 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1594 for i in range(1024):
1595 f.write(b'x' * 1024)
1596 f.flush()
1597 lzc.lzc_snapshot([snap])
1598
1599 with tempfile.TemporaryFile(suffix='.zstream') as output:
1600 estimate = lzc.lzc_send_space(snap)
1601
1602 fd = output.fileno()
1603 lzc.lzc_send(snap, None, fd)
1604 st = os.fstat(fd)
1605 # 5%, arbitrary.
1606 self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20)
1607
1608 def test_send_incremental(self):
1609 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1610 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1611
1612 lzc.lzc_snapshot([snap1])
1613 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1614 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1615 for i in range(1024):
1616 f.write(b'x' * 1024)
1617 f.flush()
1618 lzc.lzc_snapshot([snap2])
1619
1620 with tempfile.TemporaryFile(suffix='.zstream') as output:
1621 estimate = lzc.lzc_send_space(snap2, snap1)
1622
1623 fd = output.fileno()
1624 lzc.lzc_send(snap2, snap1, fd)
1625 st = os.fstat(fd)
1626 # 5%, arbitrary.
1627 self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20)
1628
1629 def test_send_flags(self):
1630 flags = ['embedded_data', 'large_blocks', 'compress', 'raw']
1631 snap = ZFSTest.pool.makeName(b"fs1@snap")
1632 lzc.lzc_snapshot([snap])
1633
1634 for c in range(len(flags)):
1635 for flag in itertools.permutations(flags, c + 1):
1636 with dev_null() as fd:
1637 lzc.lzc_send(snap, None, fd, list(flag))
1638
1639 def test_send_unknown_flags(self):
1640 snap = ZFSTest.pool.makeName(b"fs1@snap")
1641 lzc.lzc_snapshot([snap])
1642 with dev_null() as fd:
1643 with self.assertRaises(lzc_exc.UnknownStreamFeature):
1644 lzc.lzc_send(snap, None, fd, ['embedded_data', 'UNKNOWN'])
1645
1646 def test_send_same_snap(self):
1647 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1648 lzc.lzc_snapshot([snap1])
1649 with tempfile.TemporaryFile(suffix='.zstream') as output:
1650 fd = output.fileno()
1651 with self.assertRaises(lzc_exc.SnapshotMismatch):
1652 lzc.lzc_send(snap1, snap1, fd)
1653
1654 def test_send_wrong_order(self):
1655 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1656 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1657
1658 lzc.lzc_snapshot([snap1])
1659 lzc.lzc_snapshot([snap2])
1660
1661 with tempfile.TemporaryFile(suffix='.zstream') as output:
1662 fd = output.fileno()
1663 with self.assertRaises(lzc_exc.SnapshotMismatch):
1664 lzc.lzc_send(snap1, snap2, fd)
1665
1666 def test_send_unrelated(self):
1667 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1668 snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1669
1670 lzc.lzc_snapshot([snap1])
1671 lzc.lzc_snapshot([snap2])
1672
1673 with tempfile.TemporaryFile(suffix='.zstream') as output:
1674 fd = output.fileno()
1675 with self.assertRaises(lzc_exc.SnapshotMismatch):
1676 lzc.lzc_send(snap1, snap2, fd)
1677
1678 def test_send_across_pools(self):
1679 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1680 snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1681
1682 lzc.lzc_snapshot([snap1])
1683 lzc.lzc_snapshot([snap2])
1684
1685 with tempfile.TemporaryFile(suffix='.zstream') as output:
1686 fd = output.fileno()
1687 with self.assertRaises(lzc_exc.PoolsDiffer):
1688 lzc.lzc_send(snap1, snap2, fd)
1689
1690 def test_send_nonexistent(self):
1691 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1692 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1693
1694 lzc.lzc_snapshot([snap1])
1695
1696 with tempfile.TemporaryFile(suffix='.zstream') as output:
1697 fd = output.fileno()
1698 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1699 lzc.lzc_send(snap1, snap2, fd)
1700 self.assertEqual(ctx.exception.name, snap1)
1701
1702 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1703 lzc.lzc_send(snap2, snap1, fd)
1704 self.assertEqual(ctx.exception.name, snap2)
1705
1706 with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1707 lzc.lzc_send(snap2, None, fd)
1708 self.assertEqual(ctx.exception.name, snap2)
1709
1710 def test_send_invalid_name(self):
1711 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1712 snap2 = ZFSTest.pool.makeName(b"fs1@sn!p")
1713
1714 lzc.lzc_snapshot([snap1])
1715
1716 with tempfile.TemporaryFile(suffix='.zstream') as output:
1717 fd = output.fileno()
1718 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1719 lzc.lzc_send(snap2, snap1, fd)
1720 self.assertEqual(ctx.exception.name, snap2)
1721 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1722 lzc.lzc_send(snap2, None, fd)
1723 self.assertEqual(ctx.exception.name, snap2)
1724 with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1725 lzc.lzc_send(snap1, snap2, fd)
1726 self.assertEqual(ctx.exception.name, snap2)
1727
1728 # XXX Although undocumented the API allows to create an incremental
1729 # or full stream for a filesystem as if a temporary unnamed snapshot
1730 # is taken at some time after the call is made and before the stream
1731 # starts being produced.
1732 def test_send_filesystem(self):
1733 snap = ZFSTest.pool.makeName(b"fs1@snap1")
1734 fs = ZFSTest.pool.makeName(b"fs1")
1735
1736 lzc.lzc_snapshot([snap])
1737
1738 with tempfile.TemporaryFile(suffix='.zstream') as output:
1739 fd = output.fileno()
1740 lzc.lzc_send(fs, snap, fd)
1741 lzc.lzc_send(fs, None, fd)
1742
1743 def test_send_from_filesystem(self):
1744 snap = ZFSTest.pool.makeName(b"fs1@snap1")
1745 fs = ZFSTest.pool.makeName(b"fs1")
1746
1747 lzc.lzc_snapshot([snap])
1748
1749 with tempfile.TemporaryFile(suffix='.zstream') as output:
1750 fd = output.fileno()
1751 with self.assertRaises(lzc_exc.NameInvalid):
1752 lzc.lzc_send(snap, fs, fd)
1753
1754 @skipUnlessBookmarksSupported
1755 def test_send_bookmark(self):
1756 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1757 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1758 bmark = ZFSTest.pool.makeName(b"fs1#bmark")
1759
1760 lzc.lzc_snapshot([snap1])
1761 lzc.lzc_snapshot([snap2])
1762 lzc.lzc_bookmark({bmark: snap2})
1763 lzc.lzc_destroy_snaps([snap2], defer=False)
1764
1765 with tempfile.TemporaryFile(suffix='.zstream') as output:
1766 fd = output.fileno()
1767 with self.assertRaises(lzc_exc.NameInvalid):
1768 lzc.lzc_send(bmark, snap1, fd)
1769 with self.assertRaises(lzc_exc.NameInvalid):
1770 lzc.lzc_send(bmark, None, fd)
1771
1772 @skipUnlessBookmarksSupported
1773 def test_send_from_bookmark(self):
1774 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1775 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1776 bmark = ZFSTest.pool.makeName(b"fs1#bmark")
1777
1778 lzc.lzc_snapshot([snap1])
1779 lzc.lzc_snapshot([snap2])
1780 lzc.lzc_bookmark({bmark: snap1})
1781 lzc.lzc_destroy_snaps([snap1], defer=False)
1782
1783 with tempfile.TemporaryFile(suffix='.zstream') as output:
1784 fd = output.fileno()
1785 lzc.lzc_send(snap2, bmark, fd)
1786
1787 def test_send_bad_fd(self):
1788 snap = ZFSTest.pool.makeName(b"fs1@snap")
1789 lzc.lzc_snapshot([snap])
1790
1791 with tempfile.TemporaryFile() as tmp:
1792 bad_fd = tmp.fileno()
1793
1794 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1795 lzc.lzc_send(snap, None, bad_fd)
1796 self.assertEqual(ctx.exception.errno, errno.EBADF)
1797
1798 def test_send_bad_fd_2(self):
1799 snap = ZFSTest.pool.makeName(b"fs1@snap")
1800 lzc.lzc_snapshot([snap])
1801
1802 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1803 lzc.lzc_send(snap, None, -2)
1804 self.assertEqual(ctx.exception.errno, errno.EBADF)
1805
1806 def test_send_bad_fd_3(self):
1807 snap = ZFSTest.pool.makeName(b"fs1@snap")
1808 lzc.lzc_snapshot([snap])
1809
1810 with tempfile.TemporaryFile() as tmp:
1811 bad_fd = tmp.fileno()
1812
1813 (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
1814 bad_fd = hard + 1
1815 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1816 lzc.lzc_send(snap, None, bad_fd)
1817 self.assertEqual(ctx.exception.errno, errno.EBADF)
1818
1819 def test_send_to_broken_pipe(self):
1820 snap = ZFSTest.pool.makeName(b"fs1@snap")
1821 lzc.lzc_snapshot([snap])
1822
1823 if sys.version_info < (3, 0):
1824 proc = subprocess.Popen(['true'], stdin=subprocess.PIPE)
1825 proc.wait()
1826 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1827 lzc.lzc_send(snap, None, proc.stdin.fileno())
1828 self.assertEqual(ctx.exception.errno, errno.EPIPE)
1829 else:
1830 with subprocess.Popen(['true'], stdin=subprocess.PIPE) as proc:
1831 proc.wait()
1832 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1833 lzc.lzc_send(snap, None, proc.stdin.fileno())
1834 self.assertEqual(ctx.exception.errno, errno.EPIPE)
1835
1836 def test_send_to_broken_pipe_2(self):
1837 snap = ZFSTest.pool.makeName(b"fs1@snap")
1838 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1839 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1840 for i in range(1024):
1841 f.write(b'x' * 1024)
1842 f.flush()
1843 lzc.lzc_snapshot([snap])
1844
1845 if sys.version_info < (3, 0):
1846 p = subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE)
1847 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1848 lzc.lzc_send(snap, None, p.stdin.fileno())
1849 self.assertTrue(ctx.exception.errno == errno.EPIPE or
1850 ctx.exception.errno == errno.EINTR)
1851 else:
1852 with subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) as p:
1853 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1854 lzc.lzc_send(snap, None, p.stdin.fileno())
1855 self.assertTrue(ctx.exception.errno == errno.EPIPE or
1856 ctx.exception.errno == errno.EINTR)
1857
1858 def test_send_to_ro_file(self):
1859 snap = ZFSTest.pool.makeName(b"fs1@snap")
1860 lzc.lzc_snapshot([snap])
1861
1862 with tempfile.NamedTemporaryFile(
1863 suffix='.zstream', delete=False) as output:
1864 # tempfile always opens a temporary file in read-write mode
1865 # regardless of the specified mode, so we have to open it again.
1866 os.chmod(output.name, stat.S_IRUSR)
1867 fd = os.open(output.name, os.O_RDONLY)
1868 with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1869 lzc.lzc_send(snap, None, fd)
1870 os.close(fd)
1871 self.assertEqual(ctx.exception.errno, errno.EBADF)
1872
1873 def test_recv_full(self):
1874 src = ZFSTest.pool.makeName(b"fs1@snap")
1875 dst = ZFSTest.pool.makeName(b"fs2/received-1@snap")
1876
1877 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
1878 lzc.lzc_snapshot([src])
1879
1880 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1881 lzc.lzc_send(src, None, stream.fileno())
1882 stream.seek(0)
1883 lzc.lzc_receive(dst, stream.fileno())
1884
1885 name = os.path.basename(name)
1886 with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2:
1887 self.assertTrue(
1888 filecmp.cmp(
1889 os.path.join(mnt1, name), os.path.join(mnt2, name), False))
1890
1891 def test_recv_incremental(self):
1892 src1 = ZFSTest.pool.makeName(b"fs1@snap1")
1893 src2 = ZFSTest.pool.makeName(b"fs1@snap2")
1894 dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1")
1895 dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2")
1896
1897 lzc.lzc_snapshot([src1])
1898 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
1899 lzc.lzc_snapshot([src2])
1900
1901 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1902 lzc.lzc_send(src1, None, stream.fileno())
1903 stream.seek(0)
1904 lzc.lzc_receive(dst1, stream.fileno())
1905 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1906 lzc.lzc_send(src2, src1, stream.fileno())
1907 stream.seek(0)
1908 lzc.lzc_receive(dst2, stream.fileno())
1909
1910 name = os.path.basename(name)
1911 with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2:
1912 self.assertTrue(
1913 filecmp.cmp(
1914 os.path.join(mnt1, name), os.path.join(mnt2, name), False))
1915
1916 # This test case fails unless unless a patch from
1917 # https://clusterhq.atlassian.net/browse/ZFS-20
1918 # is applied to libzfs_core, otherwise it succeeds.
1919 @unittest.skip("fails with unpatched libzfs_core")
1920 def test_recv_without_explicit_snap_name(self):
1921 srcfs = ZFSTest.pool.makeName(b"fs1")
1922 src1 = srcfs + b"@snap1"
1923 src2 = srcfs + b"@snap2"
1924 dstfs = ZFSTest.pool.makeName(b"fs2/received-100")
1925 dst1 = dstfs + b'@snap1'
1926 dst2 = dstfs + b'@snap2'
1927
1928 with streams(srcfs, src1, src2) as (_, (full, incr)):
1929 lzc.lzc_receive(dstfs, full.fileno())
1930 lzc.lzc_receive(dstfs, incr.fileno())
1931 self.assertExists(dst1)
1932 self.assertExists(dst2)
1933
1934 def test_recv_clone(self):
1935 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin")
1936 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone")
1937 clone_snap = clone + b"@snap"
1938 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap")
1939 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap")
1940
1941 lzc.lzc_snapshot([orig_src])
1942 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1943 lzc.lzc_send(orig_src, None, stream.fileno())
1944 stream.seek(0)
1945 lzc.lzc_receive(orig_dst, stream.fileno())
1946
1947 lzc.lzc_clone(clone, orig_src)
1948 lzc.lzc_snapshot([clone_snap])
1949 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1950 lzc.lzc_send(clone_snap, orig_src, stream.fileno())
1951 stream.seek(0)
1952 lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst)
1953
1954 def test_recv_full_already_existing_empty_fs(self):
1955 src = ZFSTest.pool.makeName(b"fs1@snap")
1956 dstfs = ZFSTest.pool.makeName(b"fs2/received-3")
1957 dst = dstfs + b'@snap'
1958
1959 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
1960 lzc.lzc_snapshot([src])
1961 lzc.lzc_create(dstfs)
1962 with tempfile.TemporaryFile(suffix='.zstream') as stream:
1963 lzc.lzc_send(src, None, stream.fileno())
1964 stream.seek(0)
1965 with self.assertRaises((
1966 lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
1967 lzc.lzc_receive(dst, stream.fileno())
1968
1969 def test_recv_full_into_root_empty_pool(self):
1970 empty_pool = None
1971 try:
1972 srcfs = ZFSTest.pool.makeName(b"fs1")
1973 empty_pool = _TempPool()
1974 dst = empty_pool.makeName(b'@snap')
1975
1976 with streams(srcfs, b"snap", None) as (_, (stream, _)):
1977 with self.assertRaises((
1978 lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
1979 lzc.lzc_receive(dst, stream.fileno())
1980 finally:
1981 if empty_pool is not None:
1982 empty_pool.cleanUp()
1983
1984 def test_recv_full_into_ro_pool(self):
1985 srcfs = ZFSTest.pool.makeName(b"fs1")
1986 dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap')
1987
1988 with streams(srcfs, b"snap", None) as (_, (stream, _)):
1989 with self.assertRaises(lzc_exc.ReadOnlyPool):
1990 lzc.lzc_receive(dst, stream.fileno())
1991
1992 def test_recv_full_already_existing_modified_fs(self):
1993 src = ZFSTest.pool.makeName(b"fs1@snap")
1994 dstfs = ZFSTest.pool.makeName(b"fs2/received-5")
1995 dst = dstfs + b'@snap'
1996
1997 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
1998 lzc.lzc_snapshot([src])
1999 lzc.lzc_create(dstfs)
2000 with temp_file_in_fs(dstfs):
2001 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2002 lzc.lzc_send(src, None, stream.fileno())
2003 stream.seek(0)
2004 with self.assertRaises((
2005 lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
2006 lzc.lzc_receive(dst, stream.fileno())
2007
2008 def test_recv_full_already_existing_with_snapshots(self):
2009 src = ZFSTest.pool.makeName(b"fs1@snap")
2010 dstfs = ZFSTest.pool.makeName(b"fs2/received-4")
2011 dst = dstfs + b'@snap'
2012
2013 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2014 lzc.lzc_snapshot([src])
2015 lzc.lzc_create(dstfs)
2016 lzc.lzc_snapshot([dstfs + b"@snap1"])
2017 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2018 lzc.lzc_send(src, None, stream.fileno())
2019 stream.seek(0)
2020 with self.assertRaises((
2021 lzc_exc.StreamMismatch, lzc_exc.DatasetExists)):
2022 lzc.lzc_receive(dst, stream.fileno())
2023
2024 def test_recv_full_already_existing_snapshot(self):
2025 src = ZFSTest.pool.makeName(b"fs1@snap")
2026 dstfs = ZFSTest.pool.makeName(b"fs2/received-6")
2027 dst = dstfs + b'@snap'
2028
2029 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2030 lzc.lzc_snapshot([src])
2031 lzc.lzc_create(dstfs)
2032 lzc.lzc_snapshot([dst])
2033 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2034 lzc.lzc_send(src, None, stream.fileno())
2035 stream.seek(0)
2036 with self.assertRaises(lzc_exc.DatasetExists):
2037 lzc.lzc_receive(dst, stream.fileno())
2038
2039 def test_recv_full_missing_parent_fs(self):
2040 src = ZFSTest.pool.makeName(b"fs1@snap")
2041 dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap")
2042
2043 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2044 lzc.lzc_snapshot([src])
2045 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2046 lzc.lzc_send(src, None, stream.fileno())
2047 stream.seek(0)
2048 with self.assertRaises(lzc_exc.DatasetNotFound):
2049 lzc.lzc_receive(dst, stream.fileno())
2050
2051 def test_recv_full_but_specify_origin(self):
2052 srcfs = ZFSTest.pool.makeName(b"fs1")
2053 src = srcfs + b"@snap"
2054 dstfs = ZFSTest.pool.makeName(b"fs2/received-30")
2055 dst = dstfs + b'@snap'
2056 origin1 = ZFSTest.pool.makeName(b"fs2@snap1")
2057 origin2 = ZFSTest.pool.makeName(b"fs2@snap2")
2058
2059 lzc.lzc_snapshot([origin1])
2060 with streams(srcfs, src, None) as (_, (stream, _)):
2061 lzc.lzc_receive(dst, stream.fileno(), origin=origin1)
2062 origin = ZFSTest.pool.getFilesystem(
2063 b"fs2/received-30").getProperty('origin')
2064 self.assertEqual(origin, origin1)
2065 stream.seek(0)
2066 # because origin snap does not exist can't receive as a clone of it
2067 with self.assertRaises((
2068 lzc_exc.DatasetNotFound,
2069 lzc_exc.BadStream)):
2070 lzc.lzc_receive(dst, stream.fileno(), origin=origin2)
2071
2072 def test_recv_full_existing_empty_fs_and_origin(self):
2073 srcfs = ZFSTest.pool.makeName(b"fs1")
2074 src = srcfs + b"@snap"
2075 dstfs = ZFSTest.pool.makeName(b"fs2/received-31")
2076 dst = dstfs + b'@snap'
2077 origin = dstfs + b'@dummy'
2078
2079 lzc.lzc_create(dstfs)
2080 with streams(srcfs, src, None) as (_, (stream, _)):
2081 # because the destination fs already exists and has no snaps
2082 with self.assertRaises((
2083 lzc_exc.DestinationModified,
2084 lzc_exc.DatasetExists,
2085 lzc_exc.BadStream)):
2086 lzc.lzc_receive(dst, stream.fileno(), origin=origin)
2087 lzc.lzc_snapshot([origin])
2088 stream.seek(0)
2089 # because the destination fs already exists and has the snap
2090 with self.assertRaises((
2091 lzc_exc.StreamMismatch,
2092 lzc_exc.DatasetExists,
2093 lzc_exc.BadStream)):
2094 lzc.lzc_receive(dst, stream.fileno(), origin=origin)
2095
2096 def test_recv_incremental_mounted_fs(self):
2097 srcfs = ZFSTest.pool.makeName(b"fs1")
2098 src1 = srcfs + b"@snap1"
2099 src2 = srcfs + b"@snap2"
2100 dstfs = ZFSTest.pool.makeName(b"fs2/received-7")
2101 dst1 = dstfs + b'@snap1'
2102 dst2 = dstfs + b'@snap2'
2103
2104 with streams(srcfs, src1, src2) as (_, (full, incr)):
2105 lzc.lzc_receive(dst1, full.fileno())
2106 with zfs_mount(dstfs):
2107 lzc.lzc_receive(dst2, incr.fileno())
2108
2109 def test_recv_incremental_modified_fs(self):
2110 srcfs = ZFSTest.pool.makeName(b"fs1")
2111 src1 = srcfs + b"@snap1"
2112 src2 = srcfs + b"@snap2"
2113 dstfs = ZFSTest.pool.makeName(b"fs2/received-15")
2114 dst1 = dstfs + b'@snap1'
2115 dst2 = dstfs + b'@snap2'
2116
2117 with streams(srcfs, src1, src2) as (_, (full, incr)):
2118 lzc.lzc_receive(dst1, full.fileno())
2119 with temp_file_in_fs(dstfs):
2120 with self.assertRaises(lzc_exc.DestinationModified):
2121 lzc.lzc_receive(dst2, incr.fileno())
2122
2123 def test_recv_incremental_snapname_used(self):
2124 srcfs = ZFSTest.pool.makeName(b"fs1")
2125 src1 = srcfs + b"@snap1"
2126 src2 = srcfs + b"@snap2"
2127 dstfs = ZFSTest.pool.makeName(b"fs2/received-8")
2128 dst1 = dstfs + b'@snap1'
2129 dst2 = dstfs + b'@snap2'
2130
2131 with streams(srcfs, src1, src2) as (_, (full, incr)):
2132 lzc.lzc_receive(dst1, full.fileno())
2133 lzc.lzc_snapshot([dst2])
2134 with self.assertRaises(lzc_exc.DatasetExists):
2135 lzc.lzc_receive(dst2, incr.fileno())
2136
2137 def test_recv_incremental_more_recent_snap_with_no_changes(self):
2138 srcfs = ZFSTest.pool.makeName(b"fs1")
2139 src1 = srcfs + b"@snap1"
2140 src2 = srcfs + b"@snap2"
2141 dstfs = ZFSTest.pool.makeName(b"fs2/received-9")
2142 dst1 = dstfs + b'@snap1'
2143 dst2 = dstfs + b'@snap2'
2144 dst_snap = dstfs + b'@snap'
2145
2146 with streams(srcfs, src1, src2) as (_, (full, incr)):
2147 lzc.lzc_receive(dst1, full.fileno())
2148 lzc.lzc_snapshot([dst_snap])
2149 lzc.lzc_receive(dst2, incr.fileno())
2150
2151 def test_recv_incremental_non_clone_but_set_origin(self):
2152 srcfs = ZFSTest.pool.makeName(b"fs1")
2153 src1 = srcfs + b"@snap1"
2154 src2 = srcfs + b"@snap2"
2155 dstfs = ZFSTest.pool.makeName(b"fs2/received-20")
2156 dst1 = dstfs + b'@snap1'
2157 dst2 = dstfs + b'@snap2'
2158 dst_snap = dstfs + b'@snap'
2159
2160 with streams(srcfs, src1, src2) as (_, (full, incr)):
2161 lzc.lzc_receive(dst1, full.fileno())
2162 lzc.lzc_snapshot([dst_snap])
2163 # becase cannot receive incremental and set origin on a non-clone
2164 with self.assertRaises(lzc_exc.BadStream):
2165 lzc.lzc_receive(dst2, incr.fileno(), origin=dst1)
2166
2167 def test_recv_incremental_non_clone_but_set_random_origin(self):
2168 srcfs = ZFSTest.pool.makeName(b"fs1")
2169 src1 = srcfs + b"@snap1"
2170 src2 = srcfs + b"@snap2"
2171 dstfs = ZFSTest.pool.makeName(b"fs2/received-21")
2172 dst1 = dstfs + b'@snap1'
2173 dst2 = dstfs + b'@snap2'
2174 dst_snap = dstfs + b'@snap'
2175
2176 with streams(srcfs, src1, src2) as (_, (full, incr)):
2177 lzc.lzc_receive(dst1, full.fileno())
2178 lzc.lzc_snapshot([dst_snap])
2179 # because origin snap does not exist can't receive as a clone of it
2180 with self.assertRaises((
2181 lzc_exc.DatasetNotFound,
2182 lzc_exc.BadStream)):
2183 lzc.lzc_receive(
2184 dst2, incr.fileno(),
2185 origin=ZFSTest.pool.makeName(b"fs2/fs@snap"))
2186
2187 def test_recv_incremental_more_recent_snap(self):
2188 srcfs = ZFSTest.pool.makeName(b"fs1")
2189 src1 = srcfs + b"@snap1"
2190 src2 = srcfs + b"@snap2"
2191 dstfs = ZFSTest.pool.makeName(b"fs2/received-10")
2192 dst1 = dstfs + b'@snap1'
2193 dst2 = dstfs + b'@snap2'
2194 dst_snap = dstfs + b'@snap'
2195
2196 with streams(srcfs, src1, src2) as (_, (full, incr)):
2197 lzc.lzc_receive(dst1, full.fileno())
2198 with temp_file_in_fs(dstfs):
2199 lzc.lzc_snapshot([dst_snap])
2200 with self.assertRaises(lzc_exc.DestinationModified):
2201 lzc.lzc_receive(dst2, incr.fileno())
2202
2203 def test_recv_incremental_duplicate(self):
2204 srcfs = ZFSTest.pool.makeName(b"fs1")
2205 src1 = srcfs + b"@snap1"
2206 src2 = srcfs + b"@snap2"
2207 dstfs = ZFSTest.pool.makeName(b"fs2/received-11")
2208 dst1 = dstfs + b'@snap1'
2209 dst2 = dstfs + b'@snap2'
2210 dst_snap = dstfs + b'@snap'
2211
2212 with streams(srcfs, src1, src2) as (_, (full, incr)):
2213 lzc.lzc_receive(dst1, full.fileno())
2214 lzc.lzc_receive(dst2, incr.fileno())
2215 incr.seek(0)
2216 with self.assertRaises(lzc_exc.DestinationModified):
2217 lzc.lzc_receive(dst_snap, incr.fileno())
2218
2219 def test_recv_incremental_unrelated_fs(self):
2220 srcfs = ZFSTest.pool.makeName(b"fs1")
2221 src1 = srcfs + b"@snap1"
2222 src2 = srcfs + b"@snap2"
2223 dstfs = ZFSTest.pool.makeName(b"fs2/received-12")
2224 dst_snap = dstfs + b'@snap'
2225
2226 with streams(srcfs, src1, src2) as (_, (_, incr)):
2227 lzc.lzc_create(dstfs)
2228 with self.assertRaises(lzc_exc.StreamMismatch):
2229 lzc.lzc_receive(dst_snap, incr.fileno())
2230
2231 def test_recv_incremental_nonexistent_fs(self):
2232 srcfs = ZFSTest.pool.makeName(b"fs1")
2233 src1 = srcfs + b"@snap1"
2234 src2 = srcfs + b"@snap2"
2235 dstfs = ZFSTest.pool.makeName(b"fs2/received-13")
2236 dst_snap = dstfs + b'@snap'
2237
2238 with streams(srcfs, src1, src2) as (_, (_, incr)):
2239 with self.assertRaises(lzc_exc.DatasetNotFound):
2240 lzc.lzc_receive(dst_snap, incr.fileno())
2241
2242 def test_recv_incremental_same_fs(self):
2243 srcfs = ZFSTest.pool.makeName(b"fs1")
2244 src1 = srcfs + b"@snap1"
2245 src2 = srcfs + b"@snap2"
2246 src_snap = srcfs + b'@snap'
2247
2248 with streams(srcfs, src1, src2) as (_, (_, incr)):
2249 with self.assertRaises(lzc_exc.DestinationModified):
2250 lzc.lzc_receive(src_snap, incr.fileno())
2251
2252 def test_recv_clone_without_specifying_origin(self):
2253 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2")
2254 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2")
2255 clone_snap = clone + b"@snap"
2256 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap")
2257 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap")
2258
2259 lzc.lzc_snapshot([orig_src])
2260 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2261 lzc.lzc_send(orig_src, None, stream.fileno())
2262 stream.seek(0)
2263 lzc.lzc_receive(orig_dst, stream.fileno())
2264
2265 lzc.lzc_clone(clone, orig_src)
2266 lzc.lzc_snapshot([clone_snap])
2267 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2268 lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2269 stream.seek(0)
2270 with self.assertRaises(lzc_exc.BadStream):
2271 lzc.lzc_receive(clone_dst, stream.fileno())
2272
2273 def test_recv_clone_invalid_origin(self):
2274 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3")
2275 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3")
2276 clone_snap = clone + b"@snap"
2277 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap")
2278 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap")
2279
2280 lzc.lzc_snapshot([orig_src])
2281 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2282 lzc.lzc_send(orig_src, None, stream.fileno())
2283 stream.seek(0)
2284 lzc.lzc_receive(orig_dst, stream.fileno())
2285
2286 lzc.lzc_clone(clone, orig_src)
2287 lzc.lzc_snapshot([clone_snap])
2288 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2289 lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2290 stream.seek(0)
2291 with self.assertRaises(lzc_exc.NameInvalid):
2292 lzc.lzc_receive(
2293 clone_dst, stream.fileno(),
2294 origin=ZFSTest.pool.makeName(b"fs1/fs"))
2295
2296 def test_recv_clone_wrong_origin(self):
2297 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4")
2298 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4")
2299 clone_snap = clone + b"@snap"
2300 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap")
2301 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap")
2302 wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap")
2303
2304 lzc.lzc_snapshot([orig_src])
2305 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2306 lzc.lzc_send(orig_src, None, stream.fileno())
2307 stream.seek(0)
2308 lzc.lzc_receive(orig_dst, stream.fileno())
2309
2310 lzc.lzc_clone(clone, orig_src)
2311 lzc.lzc_snapshot([clone_snap])
2312 lzc.lzc_snapshot([wrong_origin])
2313 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2314 lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2315 stream.seek(0)
2316 with self.assertRaises(lzc_exc.StreamMismatch):
2317 lzc.lzc_receive(
2318 clone_dst, stream.fileno(), origin=wrong_origin)
2319
2320 def test_recv_clone_nonexistent_origin(self):
2321 orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5")
2322 clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5")
2323 clone_snap = clone + b"@snap"
2324 orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap")
2325 clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap")
2326 wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap")
2327
2328 lzc.lzc_snapshot([orig_src])
2329 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2330 lzc.lzc_send(orig_src, None, stream.fileno())
2331 stream.seek(0)
2332 lzc.lzc_receive(orig_dst, stream.fileno())
2333
2334 lzc.lzc_clone(clone, orig_src)
2335 lzc.lzc_snapshot([clone_snap])
2336 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2337 lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2338 stream.seek(0)
2339 with self.assertRaises(lzc_exc.DatasetNotFound):
2340 lzc.lzc_receive(
2341 clone_dst, stream.fileno(), origin=wrong_origin)
2342
2343 def test_force_recv_full_existing_fs(self):
2344 src = ZFSTest.pool.makeName(b"fs1@snap")
2345 dstfs = ZFSTest.pool.makeName(b"fs2/received-50")
2346 dst = dstfs + b'@snap'
2347
2348 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2349 lzc.lzc_snapshot([src])
2350
2351 lzc.lzc_create(dstfs)
2352 with temp_file_in_fs(dstfs):
2353 pass # enough to taint the fs
2354
2355 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2356 lzc.lzc_send(src, None, stream.fileno())
2357 stream.seek(0)
2358 lzc.lzc_receive(dst, stream.fileno(), force=True)
2359
2360 def test_force_recv_full_existing_modified_mounted_fs(self):
2361 src = ZFSTest.pool.makeName(b"fs1@snap")
2362 dstfs = ZFSTest.pool.makeName(b"fs2/received-53")
2363 dst = dstfs + b'@snap'
2364
2365 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2366 lzc.lzc_snapshot([src])
2367
2368 lzc.lzc_create(dstfs)
2369
2370 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2371 lzc.lzc_send(src, None, stream.fileno())
2372 stream.seek(0)
2373 with zfs_mount(dstfs) as mntdir:
2374 f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False)
2375 for i in range(1024):
2376 f.write(b'x' * 1024)
2377 lzc.lzc_receive(dst, stream.fileno(), force=True)
2378 # The temporary file dissappears and any access, even close(),
2379 # results in EIO.
2380 self.assertFalse(os.path.exists(f.name))
2381 with self.assertRaises(IOError):
2382 f.close()
2383
2384 # This test-case expects the behavior that should be there,
2385 # at the moment it may fail with DatasetExists or StreamMismatch
2386 # depending on the implementation.
2387 def test_force_recv_full_already_existing_with_snapshots(self):
2388 src = ZFSTest.pool.makeName(b"fs1@snap")
2389 dstfs = ZFSTest.pool.makeName(b"fs2/received-51")
2390 dst = dstfs + b'@snap'
2391
2392 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2393 lzc.lzc_snapshot([src])
2394
2395 lzc.lzc_create(dstfs)
2396 with temp_file_in_fs(dstfs):
2397 pass # enough to taint the fs
2398 lzc.lzc_snapshot([dstfs + b"@snap1"])
2399
2400 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2401 lzc.lzc_send(src, None, stream.fileno())
2402 stream.seek(0)
2403 lzc.lzc_receive(dst, stream.fileno(), force=True)
2404
2405 def test_force_recv_full_already_existing_with_same_snap(self):
2406 src = ZFSTest.pool.makeName(b"fs1@snap")
2407 dstfs = ZFSTest.pool.makeName(b"fs2/received-52")
2408 dst = dstfs + b'@snap'
2409
2410 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2411 lzc.lzc_snapshot([src])
2412
2413 lzc.lzc_create(dstfs)
2414 with temp_file_in_fs(dstfs):
2415 pass # enough to taint the fs
2416 lzc.lzc_snapshot([dst])
2417
2418 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2419 lzc.lzc_send(src, None, stream.fileno())
2420 stream.seek(0)
2421 with self.assertRaises(lzc_exc.DatasetExists):
2422 lzc.lzc_receive(dst, stream.fileno(), force=True)
2423
2424 def test_force_recv_full_missing_parent_fs(self):
2425 src = ZFSTest.pool.makeName(b"fs1@snap")
2426 dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap")
2427
2428 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2429 lzc.lzc_snapshot([src])
2430 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2431 lzc.lzc_send(src, None, stream.fileno())
2432 stream.seek(0)
2433 with self.assertRaises(lzc_exc.DatasetNotFound):
2434 lzc.lzc_receive(dst, stream.fileno(), force=True)
2435
2436 def test_force_recv_incremental_modified_fs(self):
2437 srcfs = ZFSTest.pool.makeName(b"fs1")
2438 src1 = srcfs + b"@snap1"
2439 src2 = srcfs + b"@snap2"
2440 dstfs = ZFSTest.pool.makeName(b"fs2/received-60")
2441 dst1 = dstfs + b'@snap1'
2442 dst2 = dstfs + b'@snap2'
2443
2444 with streams(srcfs, src1, src2) as (_, (full, incr)):
2445 lzc.lzc_receive(dst1, full.fileno())
2446 with temp_file_in_fs(dstfs):
2447 pass # enough to taint the fs
2448 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2449
2450 def test_force_recv_incremental_modified_mounted_fs(self):
2451 srcfs = ZFSTest.pool.makeName(b"fs1")
2452 src1 = srcfs + b"@snap1"
2453 src2 = srcfs + b"@snap2"
2454 dstfs = ZFSTest.pool.makeName(b"fs2/received-64")
2455 dst1 = dstfs + b'@snap1'
2456 dst2 = dstfs + b'@snap2'
2457
2458 with streams(srcfs, src1, src2) as (_, (full, incr)):
2459 lzc.lzc_receive(dst1, full.fileno())
2460 with zfs_mount(dstfs) as mntdir:
2461 f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False)
2462 for i in range(1024):
2463 f.write(b'x' * 1024)
2464 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2465 # The temporary file dissappears and any access, even close(),
2466 # results in EIO.
2467 self.assertFalse(os.path.exists(f.name))
2468 with self.assertRaises(IOError):
2469 f.close()
2470
2471 def test_force_recv_incremental_modified_fs_plus_later_snap(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-61")
2476 dst1 = dstfs + b'@snap1'
2477 dst2 = dstfs + b'@snap2'
2478 dst3 = dstfs + b'@snap'
2479
2480 with streams(srcfs, src1, src2) as (_, (full, incr)):
2481 lzc.lzc_receive(dst1, full.fileno())
2482 with temp_file_in_fs(dstfs):
2483 pass # enough to taint the fs
2484 lzc.lzc_snapshot([dst3])
2485 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2486 self.assertExists(dst1)
2487 self.assertExists(dst2)
2488 self.assertNotExists(dst3)
2489
2490 def test_force_recv_incremental_modified_fs_plus_same_name_snap(self):
2491 srcfs = ZFSTest.pool.makeName(b"fs1")
2492 src1 = srcfs + b"@snap1"
2493 src2 = srcfs + b"@snap2"
2494 dstfs = ZFSTest.pool.makeName(b"fs2/received-62")
2495 dst1 = dstfs + b'@snap1'
2496 dst2 = dstfs + b'@snap2'
2497
2498 with streams(srcfs, src1, src2) as (_, (full, incr)):
2499 lzc.lzc_receive(dst1, full.fileno())
2500 with temp_file_in_fs(dstfs):
2501 pass # enough to taint the fs
2502 lzc.lzc_snapshot([dst2])
2503 with self.assertRaises(lzc_exc.DatasetExists):
2504 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2505
2506 def test_force_recv_incremental_modified_fs_plus_held_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-63")
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 with cleanup_fd() as cfd:
2521 lzc.lzc_hold({dst3: b'tag'}, cfd)
2522 with self.assertRaises(lzc_exc.DatasetBusy):
2523 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2524 self.assertExists(dst1)
2525 self.assertNotExists(dst2)
2526 self.assertExists(dst3)
2527
2528 def test_force_recv_incremental_modified_fs_plus_cloned_snap(self):
2529 srcfs = ZFSTest.pool.makeName(b"fs1")
2530 src1 = srcfs + b"@snap1"
2531 src2 = srcfs + b"@snap2"
2532 dstfs = ZFSTest.pool.makeName(b"fs2/received-70")
2533 dst1 = dstfs + b'@snap1'
2534 dst2 = dstfs + b'@snap2'
2535 dst3 = dstfs + b'@snap'
2536 cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70")
2537
2538 with streams(srcfs, src1, src2) as (_, (full, incr)):
2539 lzc.lzc_receive(dst1, full.fileno())
2540 with temp_file_in_fs(dstfs):
2541 pass # enough to taint the fs
2542 lzc.lzc_snapshot([dst3])
2543 lzc.lzc_clone(cloned, dst3)
2544 with self.assertRaises(lzc_exc.DatasetExists):
2545 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2546 self.assertExists(dst1)
2547 self.assertNotExists(dst2)
2548 self.assertExists(dst3)
2549
2550 def test_recv_incremental_into_cloned_fs(self):
2551 srcfs = ZFSTest.pool.makeName(b"fs1")
2552 src1 = srcfs + b"@snap1"
2553 src2 = srcfs + b"@snap2"
2554 dstfs = ZFSTest.pool.makeName(b"fs2/received-71")
2555 dst1 = dstfs + b'@snap1'
2556 cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71")
2557 dst2 = cloned + b'@snap'
2558
2559 with streams(srcfs, src1, src2) as (_, (full, incr)):
2560 lzc.lzc_receive(dst1, full.fileno())
2561 lzc.lzc_clone(cloned, dst1)
2562 # test both graceful and with-force attempts
2563 with self.assertRaises(lzc_exc.StreamMismatch):
2564 lzc.lzc_receive(dst2, incr.fileno())
2565 incr.seek(0)
2566 with self.assertRaises(lzc_exc.StreamMismatch):
2567 lzc.lzc_receive(dst2, incr.fileno(), force=True)
2568 self.assertExists(dst1)
2569 self.assertNotExists(dst2)
2570
2571 def test_recv_with_header_full(self):
2572 src = ZFSTest.pool.makeName(b"fs1@snap")
2573 dst = ZFSTest.pool.makeName(b"fs2/received")
2574
2575 with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
2576 lzc.lzc_snapshot([src])
2577
2578 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2579 lzc.lzc_send(src, None, stream.fileno())
2580 stream.seek(0)
2581
2582 (header, c_header) = lzc.receive_header(stream.fileno())
2583 self.assertEqual(src, header['drr_toname'])
2584 snap = header['drr_toname'].split(b'@', 1)[1]
2585 lzc.lzc_receive_with_header(
2586 dst + b'@' + snap, stream.fileno(), c_header)
2587
2588 name = os.path.basename(name)
2589 with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2:
2590 self.assertTrue(
2591 filecmp.cmp(
2592 os.path.join(mnt1, name), os.path.join(mnt2, name), False))
2593
2594 def test_recv_fs_below_zvol(self):
2595 send = ZFSTest.pool.makeName(b"fs1@snap")
2596 zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2597 dest = zvol + b"/fs@snap"
2598 props = {b"volsize": 1024 * 1024}
2599
2600 lzc.lzc_snapshot([send])
2601 lzc.lzc_create(zvol, ds_type='zvol', props=props)
2602 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2603 lzc.lzc_send(send, None, stream.fileno())
2604 stream.seek(0)
2605 with self.assertRaises(lzc_exc.WrongParent):
2606 lzc.lzc_receive(dest, stream.fileno())
2607
2608 def test_recv_zvol_over_fs_with_children(self):
2609 parent = ZFSTest.pool.makeName(b"fs1")
2610 child = parent + b"subfs"
2611 zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2612 send = zvol + b"@snap"
2613 props = {b"volsize": 1024 * 1024}
2614
2615 lzc.lzc_create(child)
2616 lzc.lzc_create(zvol, ds_type='zvol', props=props)
2617 lzc.lzc_snapshot([send])
2618 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2619 lzc.lzc_send(send, None, stream.fileno())
2620 stream.seek(0)
2621 with self.assertRaises(lzc_exc.WrongParent):
2622 lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True)
2623
2624 def test_recv_zvol_overwrite_rootds(self):
2625 zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2626 snap = zvol + b"@snap"
2627 rootds = ZFSTest.pool.getRoot().getName()
2628 props = {b"volsize": 1024 * 1024}
2629
2630 lzc.lzc_create(zvol, ds_type='zvol', props=props)
2631 lzc.lzc_snapshot([snap])
2632 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2633 lzc.lzc_send(snap, None, stream.fileno())
2634 stream.seek(0)
2635 with self.assertRaises(lzc_exc.WrongParent):
2636 lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True)
2637
2638 def test_send_full_across_clone_branch_point(self):
2639 origfs = ZFSTest.pool.makeName(b"fs2")
2640
2641 (_, (fromsnap, origsnap, _)) = make_snapshots(
2642 origfs, b"snap1", b"send-origin-20", None)
2643
2644 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20")
2645 lzc.lzc_clone(clonefs, origsnap)
2646
2647 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2648
2649 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2650 lzc.lzc_send(tosnap, None, stream.fileno())
2651
2652 def test_send_incr_across_clone_branch_point(self):
2653 origfs = ZFSTest.pool.makeName(b"fs2")
2654
2655 (_, (fromsnap, origsnap, _)) = make_snapshots(
2656 origfs, b"snap1", b"send-origin-21", None)
2657
2658 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21")
2659 lzc.lzc_clone(clonefs, origsnap)
2660
2661 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2662
2663 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2664 lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2665
2666 def test_send_resume_token_full(self):
2667 src = ZFSTest.pool.makeName(b"fs1@snap")
2668 dstfs = ZFSTest.pool.getFilesystem(b"fs2/received")
2669 dst = dstfs.getSnap()
2670
2671 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
2672 for i in range(1, 10):
2673 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
2674 f.write(b'x' * 1024 * i)
2675 f.flush()
2676 lzc.lzc_snapshot([src])
2677
2678 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2679 lzc.lzc_send(src, None, stream.fileno())
2680 stream.seek(0)
2681 stream.truncate(1024 * 3)
2682 with self.assertRaises(lzc_exc.BadStream):
2683 lzc.lzc_receive_resumable(dst, stream.fileno())
2684 # Resume token code from zfs_send_resume_token_to_nvlist()
2685 # XXX: if used more than twice move this code into an external func
2686 # format: <version>-<cksum>-<packed-size>-<compressed-payload>
2687 token = dstfs.getProperty("receive_resume_token")
2688 self.assertNotEqual(token, b'-')
2689 tokens = token.split(b'-')
2690 self.assertEqual(len(tokens), 4)
2691 version = tokens[0]
2692 packed_size = int(tokens[2], 16)
2693 compressed_nvs = tokens[3]
2694 # Validate resume token
2695 self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION
2696 if sys.version_info < (3, 0):
2697 payload = (
2698 zlib.decompress(str(bytearray.fromhex(compressed_nvs)))
2699 )
2700 else:
2701 payload = (
2702 zlib.decompress(bytearray.fromhex(compressed_nvs.decode()))
2703 )
2704 self.assertEqual(len(payload), packed_size)
2705 # Unpack
2706 resume_values = packed_nvlist_out(payload, packed_size)
2707 resumeobj = resume_values.get(b'object')
2708 resumeoff = resume_values.get(b'offset')
2709 with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream:
2710 lzc.lzc_send_resume(
2711 src, None, rstream.fileno(), None, resumeobj, resumeoff)
2712 rstream.seek(0)
2713 lzc.lzc_receive_resumable(dst, rstream.fileno())
2714
2715 def test_send_resume_token_incremental(self):
2716 snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
2717 snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
2718 dstfs = ZFSTest.pool.getFilesystem(b"fs2/received")
2719 dst1 = dstfs.getSnap()
2720 dst2 = dstfs.getSnap()
2721
2722 lzc.lzc_snapshot([snap1])
2723 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2724 lzc.lzc_send(snap1, None, stream.fileno())
2725 stream.seek(0)
2726 lzc.lzc_receive(dst1, stream.fileno())
2727
2728 with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
2729 for i in range(1, 10):
2730 with tempfile.NamedTemporaryFile(dir=mntdir) as f:
2731 f.write(b'x' * 1024 * i)
2732 f.flush()
2733 lzc.lzc_snapshot([snap2])
2734
2735 with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2736 lzc.lzc_send(snap2, snap1, stream.fileno())
2737 stream.seek(0)
2738 stream.truncate(1024 * 3)
2739 with self.assertRaises(lzc_exc.BadStream):
2740 lzc.lzc_receive_resumable(dst2, stream.fileno())
2741 # Resume token code from zfs_send_resume_token_to_nvlist()
2742 # format: <version>-<cksum>-<packed-size>-<compressed-payload>
2743 token = dstfs.getProperty("receive_resume_token")
2744 self.assertNotEqual(token, '-')
2745 tokens = token.split(b'-')
2746 self.assertEqual(len(tokens), 4)
2747 version = tokens[0]
2748 packed_size = int(tokens[2], 16)
2749 compressed_nvs = tokens[3]
2750 # Validate resume token
2751 self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION
2752 if sys.version_info < (3, 0):
2753 payload = (
2754 zlib.decompress(str(bytearray.fromhex(compressed_nvs)))
2755 )
2756 else:
2757 payload = (
2758 zlib.decompress(bytearray.fromhex(compressed_nvs.decode()))
2759 )
2760 self.assertEqual(len(payload), packed_size)
2761 # Unpack
2762 resume_values = packed_nvlist_out(payload, packed_size)
2763 resumeobj = resume_values.get(b'object')
2764 resumeoff = resume_values.get(b'offset')
2765 with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream:
2766 lzc.lzc_send_resume(
2767 snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff)
2768 rstream.seek(0)
2769 lzc.lzc_receive_resumable(dst2, rstream.fileno())
2770
2771 def test_recv_full_across_clone_branch_point(self):
2772 origfs = ZFSTest.pool.makeName(b"fs2")
2773
2774 (_, (fromsnap, origsnap, _)) = make_snapshots(
2775 origfs, b"snap1", b"send-origin-30", None)
2776
2777 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30")
2778 lzc.lzc_clone(clonefs, origsnap)
2779
2780 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2781
2782 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30")
2783 recvsnap = recvfs + b"@snap"
2784 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2785 lzc.lzc_send(tosnap, None, stream.fileno())
2786 stream.seek(0)
2787 lzc.lzc_receive(recvsnap, stream.fileno())
2788
2789 def test_recv_one(self):
2790 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2791 tosnap = ZFSTest.pool.makeName(b"recv@snap1")
2792
2793 lzc.lzc_snapshot([fromsnap])
2794 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2795 lzc.lzc_send(fromsnap, None, stream.fileno())
2796 stream.seek(0)
2797 (header, c_header) = lzc.receive_header(stream.fileno())
2798 lzc.lzc_receive_one(tosnap, stream.fileno(), c_header)
2799
2800 def test_recv_one_size(self):
2801 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2802 tosnap = ZFSTest.pool.makeName(b"recv@snap1")
2803
2804 lzc.lzc_snapshot([fromsnap])
2805 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2806 lzc.lzc_send(fromsnap, None, stream.fileno())
2807 size = os.fstat(stream.fileno()).st_size
2808 stream.seek(0)
2809 (header, c_header) = lzc.receive_header(stream.fileno())
2810 (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header)
2811 self.assertAlmostEqual(read, size, delta=read * 0.05)
2812
2813 def test_recv_one_props(self):
2814 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2815 fs = ZFSTest.pool.getFilesystem(b"recv")
2816 tosnap = fs.getName() + b"@snap1"
2817 props = {
2818 b"compression": 0x01,
2819 b"ns:prop": b"val"
2820 }
2821
2822 lzc.lzc_snapshot([fromsnap])
2823 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2824 lzc.lzc_send(fromsnap, None, stream.fileno())
2825 stream.seek(0)
2826 (header, c_header) = lzc.receive_header(stream.fileno())
2827 lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props)
2828 self.assertExists(tosnap)
2829 self.assertEqual(fs.getProperty("compression", "received"), b"on")
2830 self.assertEqual(fs.getProperty("ns:prop", "received"), b"val")
2831
2832 def test_recv_one_invalid_prop(self):
2833 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2834 fs = ZFSTest.pool.getFilesystem(b"recv")
2835 tosnap = fs.getName() + b"@snap1"
2836 props = {
2837 b"exec": 0xff,
2838 b"atime": 0x00
2839 }
2840
2841 lzc.lzc_snapshot([fromsnap])
2842 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2843 lzc.lzc_send(fromsnap, None, stream.fileno())
2844 stream.seek(0)
2845 (header, c_header) = lzc.receive_header(stream.fileno())
2846 with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx:
2847 lzc.lzc_receive_one(
2848 tosnap, stream.fileno(), c_header, props=props)
2849 self.assertExists(tosnap)
2850 self.assertEqual(fs.getProperty("atime", "received"), b"off")
2851 for e in ctx.exception.errors:
2852 self.assertIsInstance(e, lzc_exc.PropertyInvalid)
2853 self.assertEqual(e.name, b"exec")
2854
2855 def test_recv_with_cmdprops(self):
2856 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2857 fs = ZFSTest.pool.getFilesystem(b"recv")
2858 tosnap = fs.getName() + b"@snap1"
2859 props = {}
2860 cmdprops = {
2861 b"compression": 0x01,
2862 b"ns:prop": b"val"
2863 }
2864
2865 lzc.lzc_snapshot([fromsnap])
2866 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2867 lzc.lzc_send(fromsnap, None, stream.fileno())
2868 stream.seek(0)
2869 (header, c_header) = lzc.receive_header(stream.fileno())
2870 lzc.lzc_receive_with_cmdprops(
2871 tosnap, stream.fileno(), c_header, props=props,
2872 cmdprops=cmdprops)
2873 self.assertExists(tosnap)
2874 self.assertEqual(fs.getProperty("compression"), b"on")
2875 self.assertEqual(fs.getProperty("ns:prop"), b"val")
2876
2877 def test_recv_with_cmdprops_and_recvprops(self):
2878 fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2879 fs = ZFSTest.pool.getFilesystem(b"recv")
2880 tosnap = fs.getName() + b"@snap1"
2881 props = {
2882 b"atime": 0x01,
2883 b"exec": 0x00,
2884 b"ns:prop": b"abc"
2885 }
2886 cmdprops = {
2887 b"compression": 0x01,
2888 b"ns:prop": b"def",
2889 b"exec": None,
2890 }
2891
2892 lzc.lzc_snapshot([fromsnap])
2893 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2894 lzc.lzc_send(fromsnap, None, stream.fileno())
2895 stream.seek(0)
2896 (header, c_header) = lzc.receive_header(stream.fileno())
2897 lzc.lzc_receive_with_cmdprops(
2898 tosnap, stream.fileno(), c_header, props=props,
2899 cmdprops=cmdprops)
2900 self.assertExists(tosnap)
2901 self.assertEqual(fs.getProperty("atime", True), b"on")
2902 self.assertEqual(fs.getProperty("exec", True), b"off")
2903 self.assertEqual(fs.getProperty("ns:prop", True), b"abc")
2904 self.assertEqual(fs.getProperty("compression"), b"on")
2905 self.assertEqual(fs.getProperty("ns:prop"), b"def")
2906 self.assertEqual(fs.getProperty("exec"), b"on")
2907
2908 def test_recv_incr_across_clone_branch_point_no_origin(self):
2909 origfs = ZFSTest.pool.makeName(b"fs2")
2910
2911 (_, (fromsnap, origsnap, _)) = make_snapshots(
2912 origfs, b"snap1", b"send-origin-32", None)
2913
2914 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32")
2915 lzc.lzc_clone(clonefs, origsnap)
2916
2917 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2918
2919 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32")
2920 recvsnap1 = recvfs + b"@snap1"
2921 recvsnap2 = recvfs + b"@snap2"
2922 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2923 lzc.lzc_send(fromsnap, None, stream.fileno())
2924 stream.seek(0)
2925 lzc.lzc_receive(recvsnap1, stream.fileno())
2926 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2927 lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2928 stream.seek(0)
2929 with self.assertRaises(lzc_exc.BadStream):
2930 lzc.lzc_receive(recvsnap2, stream.fileno())
2931
2932 def test_recv_incr_across_clone_branch_point(self):
2933 origfs = ZFSTest.pool.makeName(b"fs2")
2934
2935 (_, (fromsnap, origsnap, _)) = make_snapshots(
2936 origfs, b"snap1", b"send-origin-31", None)
2937
2938 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31")
2939 lzc.lzc_clone(clonefs, origsnap)
2940
2941 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2942
2943 recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31")
2944 recvsnap1 = recvfs + b"@snap1"
2945 recvsnap2 = recvfs + b"@snap2"
2946 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2947 lzc.lzc_send(fromsnap, None, stream.fileno())
2948 stream.seek(0)
2949 lzc.lzc_receive(recvsnap1, stream.fileno())
2950 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2951 lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2952 stream.seek(0)
2953 with self.assertRaises(lzc_exc.BadStream):
2954 lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1)
2955
2956 def test_recv_incr_across_clone_branch_point_new_fs(self):
2957 origfs = ZFSTest.pool.makeName(b"fs2")
2958
2959 (_, (fromsnap, origsnap, _)) = make_snapshots(
2960 origfs, b"snap1", b"send-origin-33", None)
2961
2962 clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33")
2963 lzc.lzc_clone(clonefs, origsnap)
2964
2965 (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2966
2967 recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33")
2968 recvsnap1 = recvfs1 + b"@snap"
2969 recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2")
2970 recvsnap2 = recvfs2 + b"@snap"
2971 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2972 lzc.lzc_send(fromsnap, None, stream.fileno())
2973 stream.seek(0)
2974 lzc.lzc_receive(recvsnap1, stream.fileno())
2975 with tempfile.TemporaryFile(suffix='.zstream') as stream:
2976 lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2977 stream.seek(0)
2978 lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1)
2979
2980 def test_recv_bad_stream(self):
2981 dstfs = ZFSTest.pool.makeName(b"fs2/received")
2982 dst_snap = dstfs + b'@snap'
2983
2984 with dev_zero() as fd:
2985 with self.assertRaises(lzc_exc.BadStream):
2986 lzc.lzc_receive(dst_snap, fd)
2987
2988 @needs_support(lzc.lzc_promote)
2989 def test_promote(self):
2990 origfs = ZFSTest.pool.makeName(b"fs2")
2991 snap = b"@promote-snap-1"
2992 origsnap = origfs + snap
2993 lzc.lzc_snap([origsnap])
2994
2995 clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1")
2996 lzc.lzc_clone(clonefs, origsnap)
2997
2998 lzc.lzc_promote(clonefs)
2999 # the snapshot now should belong to the promoted fs
3000 self.assertExists(clonefs + snap)
3001
3002 @needs_support(lzc.lzc_promote)
3003 def test_promote_too_long_snapname(self):
3004 # origfs name must be shorter than clonefs name
3005 origfs = ZFSTest.pool.makeName(b"fs2")
3006 clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2")
3007 snapprefix = b"@promote-snap-2-"
3008 pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix)
3009 snap = snapprefix + b'x' * pad_len
3010 origsnap = origfs + snap
3011
3012 lzc.lzc_snap([origsnap])
3013 lzc.lzc_clone(clonefs, origsnap)
3014
3015 # This may fail on older buggy systems.
3016 # See: https://www.illumos.org/issues/5909
3017 with self.assertRaises(lzc_exc.NameTooLong):
3018 lzc.lzc_promote(clonefs)
3019
3020 @needs_support(lzc.lzc_promote)
3021 def test_promote_not_cloned(self):
3022 fs = ZFSTest.pool.makeName(b"fs2")
3023 with self.assertRaises(lzc_exc.NotClone):
3024 lzc.lzc_promote(fs)
3025
3026 @unittest.skipIf(*illumos_bug_6379())
3027 def test_hold_bad_fd(self):
3028 snap = ZFSTest.pool.getRoot().getSnap()
3029 lzc.lzc_snapshot([snap])
3030
3031 with tempfile.TemporaryFile() as tmp:
3032 bad_fd = tmp.fileno()
3033
3034 with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3035 lzc.lzc_hold({snap: b'tag'}, bad_fd)
3036
3037 @unittest.skipIf(*illumos_bug_6379())
3038 def test_hold_bad_fd_2(self):
3039 snap = ZFSTest.pool.getRoot().getSnap()
3040 lzc.lzc_snapshot([snap])
3041
3042 with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3043 lzc.lzc_hold({snap: b'tag'}, -2)
3044
3045 @unittest.skipIf(*illumos_bug_6379())
3046 def test_hold_bad_fd_3(self):
3047 snap = ZFSTest.pool.getRoot().getSnap()
3048 lzc.lzc_snapshot([snap])
3049
3050 (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
3051 bad_fd = hard + 1
3052 with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3053 lzc.lzc_hold({snap: b'tag'}, bad_fd)
3054
3055 @unittest.skipIf(*illumos_bug_6379())
3056 def test_hold_wrong_fd(self):
3057 snap = ZFSTest.pool.getRoot().getSnap()
3058 lzc.lzc_snapshot([snap])
3059
3060 with tempfile.TemporaryFile() as tmp:
3061 fd = tmp.fileno()
3062 with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3063 lzc.lzc_hold({snap: b'tag'}, fd)
3064
3065 def test_hold_fd(self):
3066 snap = ZFSTest.pool.getRoot().getSnap()
3067 lzc.lzc_snapshot([snap])
3068
3069 with cleanup_fd() as fd:
3070 lzc.lzc_hold({snap: b'tag'}, fd)
3071
3072 def test_hold_empty(self):
3073 with cleanup_fd() as fd:
3074 lzc.lzc_hold({}, fd)
3075
3076 def test_hold_empty_2(self):
3077 lzc.lzc_hold({})
3078
3079 def test_hold_vs_snap_destroy(self):
3080 snap = ZFSTest.pool.getRoot().getSnap()
3081 lzc.lzc_snapshot([snap])
3082
3083 with cleanup_fd() as fd:
3084 lzc.lzc_hold({snap: b'tag'}, fd)
3085
3086 with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
3087 lzc.lzc_destroy_snaps([snap], defer=False)
3088 for e in ctx.exception.errors:
3089 self.assertIsInstance(e, lzc_exc.SnapshotIsHeld)
3090
3091 lzc.lzc_destroy_snaps([snap], defer=True)
3092 self.assertExists(snap)
3093
3094 # after automatic hold cleanup and deferred destruction
3095 self.assertNotExists(snap)
3096
3097 def test_hold_many_tags(self):
3098 snap = ZFSTest.pool.getRoot().getSnap()
3099 lzc.lzc_snapshot([snap])
3100
3101 with cleanup_fd() as fd:
3102 lzc.lzc_hold({snap: b'tag1'}, fd)
3103 lzc.lzc_hold({snap: b'tag2'}, fd)
3104
3105 def test_hold_many_snaps(self):
3106 snap1 = ZFSTest.pool.getRoot().getSnap()
3107 snap2 = ZFSTest.pool.getRoot().getSnap()
3108 lzc.lzc_snapshot([snap1])
3109 lzc.lzc_snapshot([snap2])
3110
3111 with cleanup_fd() as fd:
3112 lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3113
3114 def test_hold_many_with_one_missing(self):
3115 snap1 = ZFSTest.pool.getRoot().getSnap()
3116 snap2 = ZFSTest.pool.getRoot().getSnap()
3117 lzc.lzc_snapshot([snap1])
3118
3119 with cleanup_fd() as fd:
3120 missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3121 self.assertEqual(len(missing), 1)
3122 self.assertEqual(missing[0], snap2)
3123
3124 def test_hold_many_with_all_missing(self):
3125 snap1 = ZFSTest.pool.getRoot().getSnap()
3126 snap2 = ZFSTest.pool.getRoot().getSnap()
3127
3128 with cleanup_fd() as fd:
3129 missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3130 self.assertEqual(len(missing), 2)
3131 self.assertEqual(sorted(missing), sorted([snap1, snap2]))
3132
3133 def test_hold_missing_fs(self):
3134 # XXX skip pre-created filesystems
3135 ZFSTest.pool.getRoot().getFilesystem()
3136 ZFSTest.pool.getRoot().getFilesystem()
3137 ZFSTest.pool.getRoot().getFilesystem()
3138 ZFSTest.pool.getRoot().getFilesystem()
3139 ZFSTest.pool.getRoot().getFilesystem()
3140 snap = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3141
3142 snaps = lzc.lzc_hold({snap: b'tag'})
3143 self.assertEqual([snap], snaps)
3144
3145 def test_hold_missing_fs_auto_cleanup(self):
3146 # XXX skip pre-created filesystems
3147 ZFSTest.pool.getRoot().getFilesystem()
3148 ZFSTest.pool.getRoot().getFilesystem()
3149 ZFSTest.pool.getRoot().getFilesystem()
3150 ZFSTest.pool.getRoot().getFilesystem()
3151 ZFSTest.pool.getRoot().getFilesystem()
3152 snap = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3153
3154 with cleanup_fd() as fd:
3155 snaps = lzc.lzc_hold({snap: b'tag'}, fd)
3156 self.assertEqual([snap], snaps)
3157
3158 def test_hold_duplicate(self):
3159 snap = ZFSTest.pool.getRoot().getSnap()
3160 lzc.lzc_snapshot([snap])
3161
3162 with cleanup_fd() as fd:
3163 lzc.lzc_hold({snap: b'tag'}, fd)
3164 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3165 lzc.lzc_hold({snap: b'tag'}, fd)
3166 for e in ctx.exception.errors:
3167 self.assertIsInstance(e, lzc_exc.HoldExists)
3168
3169 def test_hold_across_pools(self):
3170 snap1 = ZFSTest.pool.getRoot().getSnap()
3171 snap2 = ZFSTest.misc_pool.getRoot().getSnap()
3172 lzc.lzc_snapshot([snap1])
3173 lzc.lzc_snapshot([snap2])
3174
3175 with cleanup_fd() as fd:
3176 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3177 lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3178 for e in ctx.exception.errors:
3179 self.assertIsInstance(e, lzc_exc.PoolsDiffer)
3180
3181 def test_hold_too_long_tag(self):
3182 snap = ZFSTest.pool.getRoot().getSnap()
3183 tag = b't' * 256
3184 lzc.lzc_snapshot([snap])
3185
3186 with cleanup_fd() as fd:
3187 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3188 lzc.lzc_hold({snap: tag}, fd)
3189 for e in ctx.exception.errors:
3190 self.assertIsInstance(e, lzc_exc.NameTooLong)
3191 self.assertEqual(e.name, tag)
3192
3193 # Apparently the full snapshot name is not checked for length
3194 # and this snapshot is treated as simply missing.
3195 @unittest.expectedFailure
3196 def test_hold_too_long_snap_name(self):
3197 snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3198 with cleanup_fd() as 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.NameTooLong)
3203 self.assertEqual(e.name, snap)
3204
3205 def test_hold_too_long_snap_name_2(self):
3206 snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3207 with cleanup_fd() as fd:
3208 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3209 lzc.lzc_hold({snap: b'tag'}, fd)
3210 for e in ctx.exception.errors:
3211 self.assertIsInstance(e, lzc_exc.NameTooLong)
3212 self.assertEqual(e.name, snap)
3213
3214 def test_hold_invalid_snap_name(self):
3215 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3216 with cleanup_fd() as fd:
3217 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3218 lzc.lzc_hold({snap: b'tag'}, fd)
3219 for e in ctx.exception.errors:
3220 self.assertIsInstance(e, lzc_exc.NameInvalid)
3221 self.assertEqual(e.name, snap)
3222
3223 def test_hold_invalid_snap_name_2(self):
3224 snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3225 with cleanup_fd() as fd:
3226 with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3227 lzc.lzc_hold({snap: b'tag'}, fd)
3228 for e in ctx.exception.errors:
3229 self.assertIsInstance(e, lzc_exc.NameInvalid)
3230 self.assertEqual(e.name, snap)
3231
3232 def test_get_holds(self):
3233 snap = ZFSTest.pool.getRoot().getSnap()
3234 lzc.lzc_snapshot([snap])
3235
3236 with cleanup_fd() as fd:
3237 lzc.lzc_hold({snap: b'tag1'}, fd)
3238 lzc.lzc_hold({snap: b'tag2'}, fd)
3239
3240 holds = lzc.lzc_get_holds(snap)
3241 self.assertEqual(len(holds), 2)
3242 self.assertIn(b'tag1', holds)
3243 self.assertIn(b'tag2', holds)
3244 self.assertIsInstance(holds[b'tag1'], (int, int))
3245
3246 def test_get_holds_after_auto_cleanup(self):
3247 snap = ZFSTest.pool.getRoot().getSnap()
3248 lzc.lzc_snapshot([snap])
3249
3250 with cleanup_fd() as fd:
3251 lzc.lzc_hold({snap: b'tag1'}, fd)
3252 lzc.lzc_hold({snap: b'tag2'}, fd)
3253
3254 holds = lzc.lzc_get_holds(snap)
3255 self.assertEqual(len(holds), 0)
3256 self.assertIsInstance(holds, dict)
3257
3258 def test_get_holds_nonexistent_snap(self):
3259 snap = ZFSTest.pool.getRoot().getSnap()
3260 with self.assertRaises(lzc_exc.SnapshotNotFound):
3261 lzc.lzc_get_holds(snap)
3262
3263 def test_get_holds_too_long_snap_name(self):
3264 snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3265 with self.assertRaises(lzc_exc.NameTooLong):
3266 lzc.lzc_get_holds(snap)
3267
3268 def test_get_holds_too_long_snap_name_2(self):
3269 snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3270 with self.assertRaises(lzc_exc.NameTooLong):
3271 lzc.lzc_get_holds(snap)
3272
3273 def test_get_holds_invalid_snap_name(self):
3274 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3275 with self.assertRaises(lzc_exc.NameInvalid):
3276 lzc.lzc_get_holds(snap)
3277
3278 # A filesystem-like snapshot name is not recognized as
3279 # an invalid name.
3280 @unittest.expectedFailure
3281 def test_get_holds_invalid_snap_name_2(self):
3282 snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3283 with self.assertRaises(lzc_exc.NameInvalid):
3284 lzc.lzc_get_holds(snap)
3285
3286 def test_release_hold(self):
3287 snap = ZFSTest.pool.getRoot().getSnap()
3288 lzc.lzc_snapshot([snap])
3289
3290 lzc.lzc_hold({snap: b'tag'})
3291 ret = lzc.lzc_release({snap: [b'tag']})
3292 self.assertEqual(len(ret), 0)
3293
3294 def test_release_hold_empty(self):
3295 ret = lzc.lzc_release({})
3296 self.assertEqual(len(ret), 0)
3297
3298 def test_release_hold_complex(self):
3299 snap1 = ZFSTest.pool.getRoot().getSnap()
3300 snap2 = ZFSTest.pool.getRoot().getSnap()
3301 snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3302 lzc.lzc_snapshot([snap1])
3303 lzc.lzc_snapshot([snap2, snap3])
3304
3305 lzc.lzc_hold({snap1: b'tag1'})
3306 lzc.lzc_hold({snap1: b'tag2'})
3307 lzc.lzc_hold({snap2: b'tag'})
3308 lzc.lzc_hold({snap3: b'tag1'})
3309 lzc.lzc_hold({snap3: b'tag2'})
3310
3311 holds = lzc.lzc_get_holds(snap1)
3312 self.assertEqual(len(holds), 2)
3313 holds = lzc.lzc_get_holds(snap2)
3314 self.assertEqual(len(holds), 1)
3315 holds = lzc.lzc_get_holds(snap3)
3316 self.assertEqual(len(holds), 2)
3317
3318 release = {
3319 snap1: [b'tag1', b'tag2'],
3320 snap2: [b'tag'],
3321 snap3: [b'tag2'],
3322 }
3323 ret = lzc.lzc_release(release)
3324 self.assertEqual(len(ret), 0)
3325
3326 holds = lzc.lzc_get_holds(snap1)
3327 self.assertEqual(len(holds), 0)
3328 holds = lzc.lzc_get_holds(snap2)
3329 self.assertEqual(len(holds), 0)
3330 holds = lzc.lzc_get_holds(snap3)
3331 self.assertEqual(len(holds), 1)
3332
3333 ret = lzc.lzc_release({snap3: [b'tag1']})
3334 self.assertEqual(len(ret), 0)
3335 holds = lzc.lzc_get_holds(snap3)
3336 self.assertEqual(len(holds), 0)
3337
3338 def test_release_hold_before_auto_cleanup(self):
3339 snap = ZFSTest.pool.getRoot().getSnap()
3340 lzc.lzc_snapshot([snap])
3341
3342 with cleanup_fd() as fd:
3343 lzc.lzc_hold({snap: b'tag'}, fd)
3344 ret = lzc.lzc_release({snap: [b'tag']})
3345 self.assertEqual(len(ret), 0)
3346
3347 def test_release_hold_and_snap_destruction(self):
3348 snap = ZFSTest.pool.getRoot().getSnap()
3349 lzc.lzc_snapshot([snap])
3350
3351 with cleanup_fd() as fd:
3352 lzc.lzc_hold({snap: b'tag1'}, fd)
3353 lzc.lzc_hold({snap: b'tag2'}, fd)
3354
3355 lzc.lzc_destroy_snaps([snap], defer=True)
3356 self.assertExists(snap)
3357
3358 lzc.lzc_release({snap: [b'tag1']})
3359 self.assertExists(snap)
3360
3361 lzc.lzc_release({snap: [b'tag2']})
3362 self.assertNotExists(snap)
3363
3364 def test_release_hold_and_multiple_snap_destruction(self):
3365 snap = ZFSTest.pool.getRoot().getSnap()
3366 lzc.lzc_snapshot([snap])
3367
3368 with cleanup_fd() as fd:
3369 lzc.lzc_hold({snap: b'tag'}, fd)
3370
3371 lzc.lzc_destroy_snaps([snap], defer=True)
3372 self.assertExists(snap)
3373
3374 lzc.lzc_destroy_snaps([snap], defer=True)
3375 self.assertExists(snap)
3376
3377 lzc.lzc_release({snap: [b'tag']})
3378 self.assertNotExists(snap)
3379
3380 def test_release_hold_missing_tag(self):
3381 snap = ZFSTest.pool.getRoot().getSnap()
3382 lzc.lzc_snapshot([snap])
3383
3384 ret = lzc.lzc_release({snap: [b'tag']})
3385 self.assertEqual(len(ret), 1)
3386 self.assertEqual(ret[0], snap + b'#tag')
3387
3388 def test_release_hold_missing_snap(self):
3389 snap = ZFSTest.pool.getRoot().getSnap()
3390
3391 ret = lzc.lzc_release({snap: [b'tag']})
3392 self.assertEqual(len(ret), 1)
3393 self.assertEqual(ret[0], snap)
3394
3395 def test_release_hold_missing_snap_2(self):
3396 snap = ZFSTest.pool.getRoot().getSnap()
3397
3398 ret = lzc.lzc_release({snap: [b'tag', b'another']})
3399 self.assertEqual(len(ret), 1)
3400 self.assertEqual(ret[0], snap)
3401
3402 def test_release_hold_across_pools(self):
3403 snap1 = ZFSTest.pool.getRoot().getSnap()
3404 snap2 = ZFSTest.misc_pool.getRoot().getSnap()
3405 lzc.lzc_snapshot([snap1])
3406 lzc.lzc_snapshot([snap2])
3407
3408 with cleanup_fd() as fd:
3409 lzc.lzc_hold({snap1: b'tag'}, fd)
3410 lzc.lzc_hold({snap2: b'tag'}, fd)
3411 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3412 lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']})
3413 for e in ctx.exception.errors:
3414 self.assertIsInstance(e, lzc_exc.PoolsDiffer)
3415
3416 # Apparently the tag name is not verified,
3417 # only its existence is checked.
3418 @unittest.expectedFailure
3419 def test_release_hold_too_long_tag(self):
3420 snap = ZFSTest.pool.getRoot().getSnap()
3421 tag = b't' * 256
3422 lzc.lzc_snapshot([snap])
3423
3424 with self.assertRaises(lzc_exc.HoldReleaseFailure):
3425 lzc.lzc_release({snap: [tag]})
3426
3427 # Apparently the full snapshot name is not checked for length
3428 # and this snapshot is treated as simply missing.
3429 @unittest.expectedFailure
3430 def test_release_hold_too_long_snap_name(self):
3431 snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3432
3433 with self.assertRaises(lzc_exc.HoldReleaseFailure):
3434 lzc.lzc_release({snap: [b'tag']})
3435
3436 def test_release_hold_too_long_snap_name_2(self):
3437 snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3438 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3439 lzc.lzc_release({snap: [b'tag']})
3440 for e in ctx.exception.errors:
3441 self.assertIsInstance(e, lzc_exc.NameTooLong)
3442 self.assertEqual(e.name, snap)
3443
3444 def test_release_hold_invalid_snap_name(self):
3445 snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3446 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3447 lzc.lzc_release({snap: [b'tag']})
3448 for e in ctx.exception.errors:
3449 self.assertIsInstance(e, lzc_exc.NameInvalid)
3450 self.assertEqual(e.name, snap)
3451
3452 def test_release_hold_invalid_snap_name_2(self):
3453 snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3454 with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3455 lzc.lzc_release({snap: [b'tag']})
3456 for e in ctx.exception.errors:
3457 self.assertIsInstance(e, lzc_exc.NameInvalid)
3458 self.assertEqual(e.name, snap)
3459
3460 def test_sync_missing_pool(self):
3461 pool = b"nonexistent"
3462 with self.assertRaises(lzc_exc.PoolNotFound):
3463 lzc.lzc_sync(pool)
3464
3465 def test_sync_pool_forced(self):
3466 pool = ZFSTest.pool.getRoot().getName()
3467 lzc.lzc_sync(pool, True)
3468
3469 def test_reopen_missing_pool(self):
3470 pool = b"nonexistent"
3471 with self.assertRaises(lzc_exc.PoolNotFound):
3472 lzc.lzc_reopen(pool)
3473
3474 def test_reopen_pool_no_restart(self):
3475 pool = ZFSTest.pool.getRoot().getName()
3476 lzc.lzc_reopen(pool, False)
3477
3478 def test_channel_program_missing_pool(self):
3479 pool = b"nonexistent"
3480 with self.assertRaises(lzc_exc.PoolNotFound):
3481 lzc.lzc_channel_program(pool, b"return {}")
3482
3483 def test_channel_program_timeout(self):
3484 pool = ZFSTest.pool.getRoot().getName()
3485 zcp = b"""
3486 for i = 1,10000 do
3487 zfs.sync.snapshot('""" + pool + b"""@zcp' .. i)
3488 end
3489 """
3490 with self.assertRaises(lzc_exc.ZCPTimeout):
3491 lzc.lzc_channel_program(pool, zcp, instrlimit=1)
3492
3493 def test_channel_program_memory_limit(self):
3494 pool = ZFSTest.pool.getRoot().getName()
3495 zcp = b"""
3496 for i = 1,10000 do
3497 zfs.sync.snapshot('""" + pool + b"""@zcp' .. i)
3498 end
3499 """
3500 with self.assertRaises(lzc_exc.ZCPSpaceError):
3501 lzc.lzc_channel_program(pool, zcp, memlimit=1)
3502
3503 def test_channel_program_invalid_limits(self):
3504 pool = ZFSTest.pool.getRoot().getName()
3505 zcp = b"""
3506 return {}
3507 """
3508 with self.assertRaises(lzc_exc.ZCPLimitInvalid):
3509 lzc.lzc_channel_program(pool, zcp, instrlimit=0)
3510 with self.assertRaises(lzc_exc.ZCPLimitInvalid):
3511 lzc.lzc_channel_program(pool, zcp, memlimit=0)
3512
3513 def test_channel_program_syntax_error(self):
3514 pool = ZFSTest.pool.getRoot().getName()
3515 zcp = b"""
3516 inv+val:id
3517 """
3518 with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx:
3519 lzc.lzc_channel_program(pool, zcp)
3520 self.assertTrue(b"syntax error" in ctx.exception.details)
3521
3522 def test_channel_program_sync_snapshot(self):
3523 pool = ZFSTest.pool.getRoot().getName()
3524 snapname = ZFSTest.pool.makeName(b"@zcp")
3525 zcp = b"""
3526 zfs.sync.snapshot('""" + snapname + b"""')
3527 """
3528 lzc.lzc_channel_program(pool, zcp)
3529 self.assertExists(snapname)
3530
3531 def test_channel_program_runtime_error(self):
3532 pool = ZFSTest.pool.getRoot().getName()
3533
3534 # failing an assertion raises a runtime error
3535 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3536 lzc.lzc_channel_program(pool, b"assert(1 == 2)")
3537 self.assertTrue(
3538 b"assertion failed" in ctx.exception.details)
3539 # invoking the error() function raises a runtime error
3540 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3541 lzc.lzc_channel_program(pool, b"error()")
3542
3543 def test_channel_program_nosync_runtime_error(self):
3544 pool = ZFSTest.pool.getRoot().getName()
3545 zcp = b"""
3546 zfs.sync.snapshot('""" + pool + b"""@zcp')
3547 """
3548 # lzc_channel_program_nosync() allows only "read-only" operations
3549 with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3550 lzc.lzc_channel_program_nosync(pool, zcp)
3551 self.assertTrue(
3552 b"running functions from the zfs.sync" in ctx.exception.details)
3553
3554 def test_change_key_new(self):
3555 with encrypted_filesystem() as (fs, _):
3556 lzc.lzc_change_key(
3557 fs, 'new_key',
3558 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3559 key=os.urandom(lzc.WRAPPING_KEY_LEN))
3560
3561 def test_change_key_missing_fs(self):
3562 name = b"nonexistent"
3563
3564 with self.assertRaises(lzc_exc.FilesystemNotFound):
3565 lzc.lzc_change_key(
3566 name, 'new_key',
3567 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3568 key=os.urandom(lzc.WRAPPING_KEY_LEN))
3569
3570 def test_change_key_not_loaded(self):
3571 with encrypted_filesystem() as (fs, _):
3572 lzc.lzc_unload_key(fs)
3573 with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded):
3574 lzc.lzc_change_key(
3575 fs, 'new_key',
3576 props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3577 key=os.urandom(lzc.WRAPPING_KEY_LEN))
3578
3579 def test_change_key_invalid_property(self):
3580 with encrypted_filesystem() as (fs, _):
3581 with self.assertRaises(lzc_exc.PropertyInvalid):
3582 lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"})
3583
3584 def test_change_key_invalid_crypt_command(self):
3585 with encrypted_filesystem() as (fs, _):
3586 with self.assertRaises(lzc_exc.UnknownCryptCommand):
3587 lzc.lzc_change_key(fs, 'duplicate_key')
3588
3589 def test_load_key(self):
3590 with encrypted_filesystem() as (fs, key):
3591 lzc.lzc_unload_key(fs)
3592 lzc.lzc_load_key(fs, False, key)
3593
3594 def test_load_key_invalid(self):
3595 with encrypted_filesystem() as (fs, key):
3596 lzc.lzc_unload_key(fs)
3597 with self.assertRaises(lzc_exc.EncryptionKeyInvalid):
3598 lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN))
3599
3600 def test_load_key_already_loaded(self):
3601 with encrypted_filesystem() as (fs, key):
3602 lzc.lzc_unload_key(fs)
3603 lzc.lzc_load_key(fs, False, key)
3604 with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded):
3605 lzc.lzc_load_key(fs, False, key)
3606
3607 def test_load_key_missing_fs(self):
3608 name = b"nonexistent"
3609
3610 with self.assertRaises(lzc_exc.FilesystemNotFound):
3611 lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN))
3612
3613 def test_unload_key(self):
3614 with encrypted_filesystem() as (fs, _):
3615 lzc.lzc_unload_key(fs)
3616
3617 def test_unload_key_missing_fs(self):
3618 name = b"nonexistent"
3619
3620 with self.assertRaises(lzc_exc.FilesystemNotFound):
3621 lzc.lzc_unload_key(name)
3622
3623 def test_unload_key_busy(self):
3624 with encrypted_filesystem() as (fs, _):
3625 with zfs_mount(fs):
3626 with self.assertRaises(lzc_exc.DatasetBusy):
3627 lzc.lzc_unload_key(fs)
3628
3629 def test_unload_key_not_loaded(self):
3630 with encrypted_filesystem() as (fs, _):
3631 lzc.lzc_unload_key(fs)
3632 with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded):
3633 lzc.lzc_unload_key(fs)
3634
3635 def test_checkpoint(self):
3636 pool = ZFSTest.pool.getRoot().getName()
3637
3638 lzc.lzc_pool_checkpoint(pool)
3639
3640 def test_checkpoint_missing_pool(self):
3641 pool = b"nonexistent"
3642
3643 with self.assertRaises(lzc_exc.PoolNotFound):
3644 lzc.lzc_pool_checkpoint(pool)
3645
3646 def test_checkpoint_already_exists(self):
3647 pool = ZFSTest.pool.getRoot().getName()
3648
3649 lzc.lzc_pool_checkpoint(pool)
3650 with self.assertRaises(lzc_exc.CheckpointExists):
3651 lzc.lzc_pool_checkpoint(pool)
3652
3653 def test_checkpoint_discard(self):
3654 pool = ZFSTest.pool.getRoot().getName()
3655
3656 lzc.lzc_pool_checkpoint(pool)
3657 lzc.lzc_pool_checkpoint_discard(pool)
3658
3659 def test_checkpoint_discard_missing_pool(self):
3660 pool = b"nonexistent"
3661
3662 with self.assertRaises(lzc_exc.PoolNotFound):
3663 lzc.lzc_pool_checkpoint_discard(pool)
3664
3665 def test_checkpoint_discard_missing_checkpoint(self):
3666 pool = ZFSTest.pool.getRoot().getName()
3667
3668 with self.assertRaises(lzc_exc.CheckpointNotFound):
3669 lzc.lzc_pool_checkpoint_discard(pool)
3670
3671 @needs_support(lzc.lzc_list_children)
3672 def test_list_children(self):
3673 name = ZFSTest.pool.makeName(b"fs1/fs")
3674 names = [ZFSTest.pool.makeName(b"fs1/fs/test1"),
3675 ZFSTest.pool.makeName(b"fs1/fs/test2"),
3676 ZFSTest.pool.makeName(b"fs1/fs/test3"), ]
3677 # and one snap to see that it is not listed
3678 snap = ZFSTest.pool.makeName(b"fs1/fs@test")
3679
3680 for fs in names:
3681 lzc.lzc_create(fs)
3682 lzc.lzc_snapshot([snap])
3683
3684 children = list(lzc.lzc_list_children(name))
3685 self.assertItemsEqual(children, names)
3686
3687 @needs_support(lzc.lzc_list_children)
3688 def test_list_children_nonexistent(self):
3689 fs = ZFSTest.pool.makeName(b"nonexistent")
3690
3691 with self.assertRaises(lzc_exc.DatasetNotFound):
3692 list(lzc.lzc_list_children(fs))
3693
3694 @needs_support(lzc.lzc_list_children)
3695 def test_list_children_of_snap(self):
3696 snap = ZFSTest.pool.makeName(b"@newsnap")
3697
3698 lzc.lzc_snapshot([snap])
3699 children = list(lzc.lzc_list_children(snap))
3700 self.assertEqual(children, [])
3701
3702 @needs_support(lzc.lzc_list_snaps)
3703 def test_list_snaps(self):
3704 name = ZFSTest.pool.makeName(b"fs1/fs")
3705 names = [ZFSTest.pool.makeName(b"fs1/fs@test1"),
3706 ZFSTest.pool.makeName(b"fs1/fs@test2"),
3707 ZFSTest.pool.makeName(b"fs1/fs@test3"), ]
3708 # and one filesystem to see that it is not listed
3709 fs = ZFSTest.pool.makeName(b"fs1/fs/test")
3710
3711 for snap in names:
3712 lzc.lzc_snapshot([snap])
3713 lzc.lzc_create(fs)
3714
3715 snaps = list(lzc.lzc_list_snaps(name))
3716 self.assertItemsEqual(snaps, names)
3717
3718 @needs_support(lzc.lzc_list_snaps)
3719 def test_list_snaps_nonexistent(self):
3720 fs = ZFSTest.pool.makeName(b"nonexistent")
3721
3722 with self.assertRaises(lzc_exc.DatasetNotFound):
3723 list(lzc.lzc_list_snaps(fs))
3724
3725 @needs_support(lzc.lzc_list_snaps)
3726 def test_list_snaps_of_snap(self):
3727 snap = ZFSTest.pool.makeName(b"@newsnap")
3728
3729 lzc.lzc_snapshot([snap])
3730 snaps = list(lzc.lzc_list_snaps(snap))
3731 self.assertEqual(snaps, [])
3732
3733 @needs_support(lzc.lzc_get_props)
3734 def test_get_fs_props(self):
3735 fs = ZFSTest.pool.makeName(b"new")
3736 props = {b"user:foo": b"bar"}
3737
3738 lzc.lzc_create(fs, props=props)
3739 actual_props = lzc.lzc_get_props(fs)
3740 self.assertDictContainsSubset(props, actual_props)
3741
3742 @needs_support(lzc.lzc_get_props)
3743 def test_get_fs_props_with_child(self):
3744 parent = ZFSTest.pool.makeName(b"parent")
3745 child = ZFSTest.pool.makeName(b"parent/child")
3746 parent_props = {b"user:foo": b"parent"}
3747 child_props = {b"user:foo": b"child"}
3748
3749 lzc.lzc_create(parent, props=parent_props)
3750 lzc.lzc_create(child, props=child_props)
3751 actual_parent_props = lzc.lzc_get_props(parent)
3752 actual_child_props = lzc.lzc_get_props(child)
3753 self.assertDictContainsSubset(parent_props, actual_parent_props)
3754 self.assertDictContainsSubset(child_props, actual_child_props)
3755
3756 @needs_support(lzc.lzc_get_props)
3757 def test_get_snap_props(self):
3758 snapname = ZFSTest.pool.makeName(b"@snap")
3759 snaps = [snapname]
3760 props = {b"user:foo": b"bar"}
3761
3762 lzc.lzc_snapshot(snaps, props)
3763 actual_props = lzc.lzc_get_props(snapname)
3764 self.assertDictContainsSubset(props, actual_props)
3765
3766 @needs_support(lzc.lzc_get_props)
3767 def test_get_props_nonexistent(self):
3768 fs = ZFSTest.pool.makeName(b"nonexistent")
3769
3770 with self.assertRaises(lzc_exc.DatasetNotFound):
3771 lzc.lzc_get_props(fs)
3772
3773 @needs_support(lzc.lzc_get_props)
3774 def test_get_mountpoint_none(self):
3775 '''
3776 If the *mountpoint* property is set to none, then its
3777 value is returned as `bytes` "none".
3778 Also, a child filesystem inherits that value.
3779 '''
3780 fs = ZFSTest.pool.makeName(b"new")
3781 child = ZFSTest.pool.makeName(b"new/child")
3782 props = {b"mountpoint": b"none"}
3783
3784 lzc.lzc_create(fs, props=props)
3785 lzc.lzc_create(child)
3786 actual_props = lzc.lzc_get_props(fs)
3787 self.assertDictContainsSubset(props, actual_props)
3788 # check that mountpoint value is correctly inherited
3789 child_props = lzc.lzc_get_props(child)
3790 self.assertDictContainsSubset(props, child_props)
3791
3792 @needs_support(lzc.lzc_get_props)
3793 def test_get_mountpoint_legacy(self):
3794 '''
3795 If the *mountpoint* property is set to legacy, then its
3796 value is returned as `bytes` "legacy".
3797 Also, a child filesystem inherits that value.
3798 '''
3799 fs = ZFSTest.pool.makeName(b"new")
3800 child = ZFSTest.pool.makeName(b"new/child")
3801 props = {b"mountpoint": b"legacy"}
3802
3803 lzc.lzc_create(fs, props=props)
3804 lzc.lzc_create(child)
3805 actual_props = lzc.lzc_get_props(fs)
3806 self.assertDictContainsSubset(props, actual_props)
3807 # check that mountpoint value is correctly inherited
3808 child_props = lzc.lzc_get_props(child)
3809 self.assertDictContainsSubset(props, child_props)
3810
3811 @needs_support(lzc.lzc_get_props)
3812 def test_get_mountpoint_path(self):
3813 '''
3814 If the *mountpoint* property is set to a path and the property
3815 is not explicitly set on a child filesystem, then its
3816 value is that of the parent filesystem with the child's
3817 name appended using the '/' separator.
3818 '''
3819 fs = ZFSTest.pool.makeName(b"new")
3820 child = ZFSTest.pool.makeName(b"new/child")
3821 props = {b"mountpoint": b"/mnt"}
3822
3823 lzc.lzc_create(fs, props=props)
3824 lzc.lzc_create(child)
3825 actual_props = lzc.lzc_get_props(fs)
3826 self.assertDictContainsSubset(props, actual_props)
3827 # check that mountpoint value is correctly inherited
3828 child_props = lzc.lzc_get_props(child)
3829 self.assertDictContainsSubset(
3830 {b"mountpoint": b"/mnt/child"}, child_props)
3831
3832 @needs_support(lzc.lzc_get_props)
3833 def test_get_snap_clones(self):
3834 fs = ZFSTest.pool.makeName(b"new")
3835 snap = ZFSTest.pool.makeName(b"@snap")
3836 clone1 = ZFSTest.pool.makeName(b"clone1")
3837 clone2 = ZFSTest.pool.makeName(b"clone2")
3838
3839 lzc.lzc_create(fs)
3840 lzc.lzc_snapshot([snap])
3841 lzc.lzc_clone(clone1, snap)
3842 lzc.lzc_clone(clone2, snap)
3843
3844 clones_prop = lzc.lzc_get_props(snap)["clones"]
3845 self.assertItemsEqual(clones_prop, [clone1, clone2])
3846
3847 @needs_support(lzc.lzc_rename)
3848 def test_rename(self):
3849 src = ZFSTest.pool.makeName(b"source")
3850 tgt = ZFSTest.pool.makeName(b"target")
3851
3852 lzc.lzc_create(src)
3853 lzc.lzc_rename(src, tgt)
3854 self.assertNotExists(src)
3855 self.assertExists(tgt)
3856
3857 @needs_support(lzc.lzc_rename)
3858 def test_rename_nonexistent(self):
3859 src = ZFSTest.pool.makeName(b"source")
3860 tgt = ZFSTest.pool.makeName(b"target")
3861
3862 with self.assertRaises(lzc_exc.FilesystemNotFound):
3863 lzc.lzc_rename(src, tgt)
3864
3865 @needs_support(lzc.lzc_rename)
3866 def test_rename_existing_target(self):
3867 src = ZFSTest.pool.makeName(b"source")
3868 tgt = ZFSTest.pool.makeName(b"target")
3869
3870 lzc.lzc_create(src)
3871 lzc.lzc_create(tgt)
3872 with self.assertRaises(lzc_exc.FilesystemExists):
3873 lzc.lzc_rename(src, tgt)
3874
3875 @needs_support(lzc.lzc_rename)
3876 def test_rename_nonexistent_target_parent(self):
3877 src = ZFSTest.pool.makeName(b"source")
3878 tgt = ZFSTest.pool.makeName(b"parent/target")
3879
3880 lzc.lzc_create(src)
3881 with self.assertRaises(lzc_exc.FilesystemNotFound):
3882 lzc.lzc_rename(src, tgt)
3883
3884 @needs_support(lzc.lzc_rename)
3885 def test_rename_parent_is_zvol(self):
3886 src = ZFSTest.pool.makeName(b"source")
3887 zvol = ZFSTest.pool.makeName(b"parent")
3888 tgt = zvol + b"/target"
3889 props = {b"volsize": 1024 * 1024}
3890
3891 lzc.lzc_create(src)
3892 lzc.lzc_create(zvol, ds_type='zvol', props=props)
3893 with self.assertRaises(lzc_exc.WrongParent):
3894 lzc.lzc_rename(src, tgt)
3895
3896 @needs_support(lzc.lzc_destroy)
3897 def test_destroy(self):
3898 fs = ZFSTest.pool.makeName(b"test-fs")
3899
3900 lzc.lzc_create(fs)
3901 lzc.lzc_destroy(fs)
3902 self.assertNotExists(fs)
3903
3904 @needs_support(lzc.lzc_destroy)
3905 def test_destroy_nonexistent(self):
3906 fs = ZFSTest.pool.makeName(b"test-fs")
3907
3908 with self.assertRaises(lzc_exc.FilesystemNotFound):
3909 lzc.lzc_destroy(fs)
3910
3911 @needs_support(lzc.lzc_inherit_prop)
3912 def test_inherit_prop(self):
3913 parent = ZFSTest.pool.makeName(b"parent")
3914 child = ZFSTest.pool.makeName(b"parent/child")
3915 the_prop = b"user:foo"
3916 parent_props = {the_prop: b"parent"}
3917 child_props = {the_prop: b"child"}
3918
3919 lzc.lzc_create(parent, props=parent_props)
3920 lzc.lzc_create(child, props=child_props)
3921 lzc.lzc_inherit_prop(child, the_prop)
3922 actual_props = lzc.lzc_get_props(child)
3923 self.assertDictContainsSubset(parent_props, actual_props)
3924
3925 @needs_support(lzc.lzc_inherit_prop)
3926 def test_inherit_missing_prop(self):
3927 parent = ZFSTest.pool.makeName(b"parent")
3928 child = ZFSTest.pool.makeName(b"parent/child")
3929 the_prop = "user:foo"
3930 child_props = {the_prop: "child"}
3931
3932 lzc.lzc_create(parent)
3933 lzc.lzc_create(child, props=child_props)
3934 lzc.lzc_inherit_prop(child, the_prop)
3935 actual_props = lzc.lzc_get_props(child)
3936 self.assertNotIn(the_prop, actual_props)
3937
3938 @needs_support(lzc.lzc_inherit_prop)
3939 def test_inherit_readonly_prop(self):
3940 parent = ZFSTest.pool.makeName(b"parent")
3941 child = ZFSTest.pool.makeName(b"parent/child")
3942 the_prop = b"createtxg"
3943
3944 lzc.lzc_create(parent)
3945 lzc.lzc_create(child)
3946 with self.assertRaises(lzc_exc.PropertyInvalid):
3947 lzc.lzc_inherit_prop(child, the_prop)
3948
3949 @needs_support(lzc.lzc_inherit_prop)
3950 def test_inherit_unknown_prop(self):
3951 parent = ZFSTest.pool.makeName(b"parent")
3952 child = ZFSTest.pool.makeName(b"parent/child")
3953 the_prop = b"nosuchprop"
3954
3955 lzc.lzc_create(parent)
3956 lzc.lzc_create(child)
3957 with self.assertRaises(lzc_exc.PropertyInvalid):
3958 lzc.lzc_inherit_prop(child, the_prop)
3959
3960 @needs_support(lzc.lzc_inherit_prop)
3961 def test_inherit_prop_on_snap(self):
3962 fs = ZFSTest.pool.makeName(b"new")
3963 snapname = ZFSTest.pool.makeName(b"new@snap")
3964 prop = b"user:foo"
3965 fs_val = b"fs"
3966 snap_val = b"snap"
3967
3968 lzc.lzc_create(fs, props={prop: fs_val})
3969 lzc.lzc_snapshot([snapname], props={prop: snap_val})
3970
3971 actual_props = lzc.lzc_get_props(snapname)
3972 self.assertDictContainsSubset({prop: snap_val}, actual_props)
3973
3974 lzc.lzc_inherit_prop(snapname, prop)
3975 actual_props = lzc.lzc_get_props(snapname)
3976 self.assertDictContainsSubset({prop: fs_val}, actual_props)
3977
3978 @needs_support(lzc.lzc_set_prop)
3979 def test_set_fs_prop(self):
3980 fs = ZFSTest.pool.makeName(b"new")
3981 prop = b"user:foo"
3982 val = b"bar"
3983
3984 lzc.lzc_create(fs)
3985 lzc.lzc_set_prop(fs, prop, val)
3986 actual_props = lzc.lzc_get_props(fs)
3987 self.assertDictContainsSubset({prop: val}, actual_props)
3988
3989 @needs_support(lzc.lzc_set_prop)
3990 def test_set_snap_prop(self):
3991 snapname = ZFSTest.pool.makeName(b"@snap")
3992 prop = b"user:foo"
3993 val = b"bar"
3994
3995 lzc.lzc_snapshot([snapname])
3996 lzc.lzc_set_prop(snapname, prop, val)
3997 actual_props = lzc.lzc_get_props(snapname)
3998 self.assertDictContainsSubset({prop: val}, actual_props)
3999
4000 @needs_support(lzc.lzc_set_prop)
4001 def test_set_prop_nonexistent(self):
4002 fs = ZFSTest.pool.makeName(b"nonexistent")
4003 prop = b"user:foo"
4004 val = b"bar"
4005
4006 with self.assertRaises(lzc_exc.DatasetNotFound):
4007 lzc.lzc_set_prop(fs, prop, val)
4008
4009 @needs_support(lzc.lzc_set_prop)
4010 def test_set_sys_prop(self):
4011 fs = ZFSTest.pool.makeName(b"new")
4012 prop = b"recordsize"
4013 val = 4096
4014
4015 lzc.lzc_create(fs)
4016 lzc.lzc_set_prop(fs, prop, val)
4017 actual_props = lzc.lzc_get_props(fs)
4018 self.assertDictContainsSubset({prop: val}, actual_props)
4019
4020 @needs_support(lzc.lzc_set_prop)
4021 def test_set_invalid_prop(self):
4022 fs = ZFSTest.pool.makeName(b"new")
4023 prop = b"nosuchprop"
4024 val = 0
4025
4026 lzc.lzc_create(fs)
4027 with self.assertRaises(lzc_exc.PropertyInvalid):
4028 lzc.lzc_set_prop(fs, prop, val)
4029
4030 @needs_support(lzc.lzc_set_prop)
4031 def test_set_invalid_value_prop(self):
4032 fs = ZFSTest.pool.makeName(b"new")
4033 prop = b"atime"
4034 val = 100
4035
4036 lzc.lzc_create(fs)
4037 with self.assertRaises(lzc_exc.PropertyInvalid):
4038 lzc.lzc_set_prop(fs, prop, val)
4039
4040 @needs_support(lzc.lzc_set_prop)
4041 def test_set_invalid_value_prop_2(self):
4042 fs = ZFSTest.pool.makeName(b"new")
4043 prop = b"readonly"
4044 val = 100
4045
4046 lzc.lzc_create(fs)
4047 with self.assertRaises(lzc_exc.PropertyInvalid):
4048 lzc.lzc_set_prop(fs, prop, val)
4049
4050 @needs_support(lzc.lzc_set_prop)
4051 def test_set_prop_too_small_quota(self):
4052 fs = ZFSTest.pool.makeName(b"new")
4053 prop = b"refquota"
4054 val = 1
4055
4056 lzc.lzc_create(fs)
4057 with self.assertRaises(lzc_exc.NoSpace):
4058 lzc.lzc_set_prop(fs, prop, val)
4059
4060 @needs_support(lzc.lzc_set_prop)
4061 def test_set_readonly_prop(self):
4062 fs = ZFSTest.pool.makeName(b"new")
4063 prop = b"creation"
4064 val = 0
4065
4066 lzc.lzc_create(fs)
4067 lzc.lzc_set_prop(fs, prop, val)
4068 actual_props = lzc.lzc_get_props(fs)
4069 # the change is silently ignored
4070 self.assertTrue(actual_props[prop] != val)
4071
4072
4073 class _TempPool(object):
4074 SNAPSHOTS = [b'snap', b'snap1', b'snap2']
4075 BOOKMARKS = [b'bmark', b'bmark1', b'bmark2']
4076
4077 _cachefile_suffix = ".cachefile"
4078
4079 # XXX Whether to do a sloppy but much faster cleanup
4080 # or a proper but slower one.
4081 _recreate_pools = True
4082
4083 def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]):
4084 self._filesystems = filesystems
4085 self._readonly = readonly
4086 if sys.version_info < (3, 0):
4087 self._pool_name = b'pool.' + bytes(uuid.uuid4())
4088 else:
4089 self._pool_name = b'pool.' + bytes(str(uuid.uuid4()),
4090 encoding='utf-8')
4091 self._root = _Filesystem(self._pool_name)
4092 (fd, self._pool_file_path) = tempfile.mkstemp(
4093 suffix='.zpool', prefix='tmp-')
4094 if readonly:
4095 cachefile = self._pool_file_path + _TempPool._cachefile_suffix
4096 else:
4097 cachefile = 'none'
4098 self._zpool_create = [
4099 'zpool', 'create', '-o', 'cachefile=' + cachefile,
4100 '-O', 'mountpoint=legacy', self._pool_name, self._pool_file_path]
4101 try:
4102 os.ftruncate(fd, size)
4103 os.close(fd)
4104
4105 subprocess.check_output(
4106 self._zpool_create, stderr=subprocess.STDOUT)
4107
4108 for fs in filesystems:
4109 lzc.lzc_create(self.makeName(fs))
4110
4111 self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks')
4112
4113 if readonly:
4114 # To make a pool read-only it must exported and re-imported
4115 # with readonly option.
4116 # The most deterministic way to re-import the pool is by using
4117 # a cache file.
4118 # But the cache file has to be stashed away before the pool is
4119 # exported, because otherwise the pool is removed from the
4120 # cache.
4121 shutil.copyfile(cachefile, cachefile + '.tmp')
4122 subprocess.check_output(
4123 ['zpool', 'export', '-f', self._pool_name],
4124 stderr=subprocess.STDOUT)
4125 os.rename(cachefile + '.tmp', cachefile)
4126 subprocess.check_output(
4127 ['zpool', 'import', '-f', '-N', '-c', cachefile,
4128 '-o', 'readonly=on', self._pool_name],
4129 stderr=subprocess.STDOUT)
4130 os.remove(cachefile)
4131
4132 except subprocess.CalledProcessError as e:
4133 self.cleanUp()
4134 if b'permission denied' in e.output:
4135 raise unittest.SkipTest(
4136 'insufficient privileges to run libzfs_core tests')
4137 print('command failed: ', e.output)
4138 raise
4139 except Exception:
4140 self.cleanUp()
4141 raise
4142
4143 def reset(self):
4144 if self._readonly:
4145 return
4146
4147 if not self.__class__._recreate_pools:
4148 snaps = []
4149 for fs in [''] + self._filesystems:
4150 for snap in self.__class__.SNAPSHOTS:
4151 snaps.append(self.makeName(fs + '@' + snap))
4152 self.getRoot().visitSnaps(lambda snap: snaps.append(snap))
4153 lzc.lzc_destroy_snaps(snaps, defer=False)
4154
4155 if self._bmarks_supported:
4156 bmarks = []
4157 for fs in [''] + self._filesystems:
4158 for bmark in self.__class__.BOOKMARKS:
4159 bmarks.append(self.makeName(fs + '#' + bmark))
4160 self.getRoot().visitBookmarks(
4161 lambda bmark: bmarks.append(bmark))
4162 lzc.lzc_destroy_bookmarks(bmarks)
4163 self.getRoot().reset()
4164 return
4165
4166 # On the Buildbot builders this may fail with "pool is busy"
4167 # Retry 5 times before raising an error
4168 retry = 0
4169 while True:
4170 try:
4171 subprocess.check_output(
4172 ['zpool', 'destroy', '-f', self._pool_name],
4173 stderr=subprocess.STDOUT)
4174 subprocess.check_output(
4175 self._zpool_create, stderr=subprocess.STDOUT)
4176 break
4177 except subprocess.CalledProcessError as e:
4178 if b'pool is busy' in e.output and retry < 5:
4179 retry += 1
4180 time.sleep(1)
4181 continue
4182 else:
4183 print('command failed: ', e.output)
4184 raise
4185 for fs in self._filesystems:
4186 lzc.lzc_create(self.makeName(fs))
4187 self.getRoot().reset()
4188
4189 def cleanUp(self):
4190 try:
4191 subprocess.check_output(
4192 ['zpool', 'destroy', '-f', self._pool_name],
4193 stderr=subprocess.STDOUT)
4194 except Exception:
4195 pass
4196 try:
4197 os.remove(self._pool_file_path)
4198 except Exception:
4199 pass
4200 try:
4201 os.remove(self._pool_file_path + _TempPool._cachefile_suffix)
4202 except Exception:
4203 pass
4204 try:
4205 os.remove(
4206 self._pool_file_path + _TempPool._cachefile_suffix + '.tmp')
4207 except Exception:
4208 pass
4209
4210 def makeName(self, relative=None):
4211 if not relative:
4212 return self._pool_name
4213 if relative.startswith((b'@', b'#')):
4214 return self._pool_name + relative
4215 return self._pool_name + b'/' + relative
4216
4217 def makeTooLongName(self, prefix=None):
4218 if not prefix:
4219 prefix = b'x'
4220 prefix = self.makeName(prefix)
4221 pad_len = lzc.MAXNAMELEN + 1 - len(prefix)
4222 if pad_len > 0:
4223 return prefix + b'x' * pad_len
4224 else:
4225 return prefix
4226
4227 def makeTooLongComponent(self, prefix=None):
4228 padding = b'x' * (lzc.MAXNAMELEN + 1)
4229 if not prefix:
4230 prefix = padding
4231 else:
4232 prefix = prefix + padding
4233 return self.makeName(prefix)
4234
4235 def getRoot(self):
4236 return self._root
4237
4238 def getFilesystem(self, fsname):
4239 return _Filesystem(self._pool_name + b'/' + fsname)
4240
4241 def isPoolFeatureAvailable(self, feature):
4242 output = subprocess.check_output(
4243 ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name])
4244 output = output.strip()
4245 return output != ''
4246
4247 def isPoolFeatureEnabled(self, feature):
4248 output = subprocess.check_output(
4249 ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name])
4250 output = output.split()[2]
4251 return output in [b'active', b'enabled']
4252
4253
4254 class _Filesystem(object):
4255
4256 def __init__(self, name):
4257 self._name = name
4258 self.reset()
4259
4260 def getName(self):
4261 return self._name
4262
4263 def reset(self):
4264 self._children = []
4265 self._fs_id = 0
4266 self._snap_id = 0
4267 self._bmark_id = 0
4268
4269 def getFilesystem(self):
4270 self._fs_id += 1
4271 fsname = self._name + b'/fs' + str(self._fs_id).encode()
4272 fs = _Filesystem(fsname)
4273 self._children.append(fs)
4274 return fs
4275
4276 def getProperty(self, propname, received=False):
4277 if received:
4278 output = subprocess.check_output(
4279 ['zfs', 'get', '-pH', '-o', 'received', propname, self._name])
4280 else:
4281 output = subprocess.check_output(
4282 ['zfs', 'get', '-pH', '-o', 'value', propname, self._name])
4283 return output.strip()
4284
4285 def _makeSnapName(self, i):
4286 return self._name + b'@snap' + str(i).encode()
4287
4288 def getSnap(self):
4289 self._snap_id += 1
4290 return self._makeSnapName(self._snap_id)
4291
4292 def _makeBookmarkName(self, i):
4293 return self._name + b'#bmark' + bytes(i)
4294
4295 def getBookmark(self):
4296 self._bmark_id += 1
4297 return self._makeBookmarkName(self._bmark_id)
4298
4299 def _makeTooLongName(self, too_long_component):
4300 if too_long_component:
4301 return b'x' * (lzc.MAXNAMELEN + 1)
4302
4303 # Note that another character is used for one of '/', '@', '#'.
4304 comp_len = lzc.MAXNAMELEN - len(self._name)
4305 if comp_len > 0:
4306 return b'x' * comp_len
4307 else:
4308 return b'x'
4309
4310 def getTooLongFilesystemName(self, too_long_component):
4311 return self._name + b'/' + self._makeTooLongName(too_long_component)
4312
4313 def getTooLongSnap(self, too_long_component):
4314 return self._name + b'@' + self._makeTooLongName(too_long_component)
4315
4316 def getTooLongBookmark(self, too_long_component):
4317 return self._name + b'#' + self._makeTooLongName(too_long_component)
4318
4319 def _visitFilesystems(self, visitor):
4320 for child in self._children:
4321 child._visitFilesystems(visitor)
4322 visitor(self)
4323
4324 def visitFilesystems(self, visitor):
4325 def _fsVisitor(fs):
4326 visitor(fs._name)
4327
4328 self._visitFilesystems(_fsVisitor)
4329
4330 def visitSnaps(self, visitor):
4331 def _snapVisitor(fs):
4332 for i in range(1, fs._snap_id + 1):
4333 visitor(fs._makeSnapName(i))
4334
4335 self._visitFilesystems(_snapVisitor)
4336
4337 def visitBookmarks(self, visitor):
4338 def _bmarkVisitor(fs):
4339 for i in range(1, fs._bmark_id + 1):
4340 visitor(fs._makeBookmarkName(i))
4341
4342 self._visitFilesystems(_bmarkVisitor)
4343
4344
4345 # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4