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