]>
Commit | Line | Data |
---|---|---|
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 | """ | |
18 | Tests for `libzfs_core` operations. | |
19 | ||
20 | These are mostly functional and conformance tests that validate | |
21 | that the operations produce expected effects or fail with expected | |
22 | exceptions. | |
23 | """ | |
9de8c0cd | 24 | from __future__ import absolute_import, division, print_function |
6abf9225 AG |
25 | |
26 | import unittest | |
27 | import contextlib | |
28 | import errno | |
29 | import filecmp | |
30 | import os | |
31 | import platform | |
32 | import resource | |
33 | import shutil | |
34 | import stat | |
35 | import subprocess | |
4b1c4062 | 36 | import sys |
6abf9225 AG |
37 | import tempfile |
38 | import time | |
39 | import uuid | |
85ce3f4f | 40 | import itertools |
41 | import zlib | |
6abf9225 AG |
42 | from .. import _libzfs_core as lzc |
43 | from .. import exceptions as lzc_exc | |
85ce3f4f | 44 | from .._nvlist import packed_nvlist_out |
6abf9225 AG |
45 | |
46 | ||
47 | def _print(*args): | |
48 | for arg in args: | |
9de8c0cd AR |
49 | print(arg, end=' ') |
50 | print() | |
6abf9225 AG |
51 | |
52 | ||
53 | @contextlib.contextmanager | |
54 | def suppress(exceptions=None): | |
55 | try: | |
56 | yield | |
57 | except BaseException as e: | |
58 | if exceptions is None or isinstance(e, exceptions): | |
59 | pass | |
60 | else: | |
61 | raise | |
62 | ||
63 | ||
64 | @contextlib.contextmanager | |
65 | def _zfs_mount(fs): | |
66 | mntdir = tempfile.mkdtemp() | |
67 | if platform.system() == 'SunOS': | |
68 | mount_cmd = ['mount', '-F', 'zfs', fs, mntdir] | |
69 | else: | |
70 | mount_cmd = ['mount', '-t', 'zfs', fs, mntdir] | |
71 | unmount_cmd = ['umount', '-f', mntdir] | |
72 | ||
73 | try: | |
74 | subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT) | |
75 | try: | |
76 | yield mntdir | |
77 | finally: | |
78 | with suppress(): | |
79 | subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT) | |
80 | except subprocess.CalledProcessError as e: | |
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 | |
102 | def _illumos_mount_fs(fs): | |
103 | if fs in _mnttab: | |
104 | yield _mnttab[fs] | |
105 | else: | |
106 | with _zfs_mount(fs) as mntdir: | |
107 | _mnttab[fs] = mntdir | |
108 | try: | |
109 | yield mntdir | |
110 | finally: | |
111 | _mnttab.pop(fs, None) | |
112 | ||
113 | ||
114 | @contextlib.contextmanager | |
115 | def _illumos_mount_snap(fs): | |
116 | (base, snap) = fs.split('@', 1) | |
117 | with _illumos_mount_fs(base) as mntdir: | |
118 | yield os.path.join(mntdir, '.zfs', 'snapshot', snap) | |
119 | ||
120 | ||
121 | @contextlib.contextmanager | |
122 | def _zfs_mount_illumos(fs): | |
123 | if '@' not in fs: | |
124 | with _illumos_mount_fs(fs) as mntdir: | |
125 | yield mntdir | |
126 | else: | |
127 | with _illumos_mount_snap(fs) as mntdir: | |
128 | yield mntdir | |
129 | ||
130 | ||
131 | if platform.system() == 'SunOS': | |
132 | zfs_mount = _zfs_mount_illumos | |
133 | else: | |
134 | zfs_mount = _zfs_mount | |
135 | ||
136 | ||
137 | @contextlib.contextmanager | |
138 | def cleanup_fd(): | |
139 | fd = os.open('/dev/zfs', os.O_EXCL) | |
140 | try: | |
141 | yield fd | |
142 | finally: | |
143 | os.close(fd) | |
144 | ||
145 | ||
146 | @contextlib.contextmanager | |
147 | def os_open(name, mode): | |
148 | fd = os.open(name, mode) | |
149 | try: | |
150 | yield fd | |
151 | finally: | |
152 | os.close(fd) | |
153 | ||
154 | ||
155 | @contextlib.contextmanager | |
156 | def dev_null(): | |
f8c4d63a AR |
157 | with tempfile.TemporaryFile(suffix='.zstream') as fd: |
158 | yield fd.fileno() | |
6abf9225 AG |
159 | |
160 | ||
161 | @contextlib.contextmanager | |
162 | def dev_zero(): | |
163 | with os_open('/dev/zero', os.O_RDONLY) as fd: | |
164 | yield fd | |
165 | ||
166 | ||
167 | @contextlib.contextmanager | |
168 | def temp_file_in_fs(fs): | |
169 | with zfs_mount(fs) as mntdir: | |
170 | with tempfile.NamedTemporaryFile(dir=mntdir) as f: | |
171 | for i in range(1024): | |
4b1c4062 | 172 | f.write(b'x' * 1024) |
6abf9225 AG |
173 | f.flush() |
174 | yield f.name | |
175 | ||
176 | ||
177 | def make_snapshots(fs, before, modified, after): | |
178 | def _maybe_snap(snap): | |
179 | if snap is not None: | |
180 | if not snap.startswith(fs): | |
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 | |
194 | def 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 |
209 | def 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 |
225 | def runtimeSkipIf(check_method, message): |
226 | def _decorator(f): | |
227 | def _f(_self, *args, **kwargs): | |
228 | if check_method(_self): | |
229 | return _self.skipTest(message) | |
230 | else: | |
231 | return f(_self, *args, **kwargs) | |
232 | _f.__name__ = f.__name__ | |
233 | return _f | |
234 | return _decorator | |
235 | ||
236 | ||
237 | def skipIfFeatureAvailable(feature, message): | |
85ce3f4f | 238 | return runtimeSkipIf( |
239 | lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature), | |
240 | message) | |
6abf9225 AG |
241 | |
242 | ||
243 | def skipUnlessFeatureEnabled(feature, message): | |
85ce3f4f | 244 | return runtimeSkipIf( |
245 | lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature), | |
246 | message) | |
6abf9225 AG |
247 | |
248 | ||
249 | def skipUnlessBookmarksSupported(f): | |
85ce3f4f | 250 | return skipUnlessFeatureEnabled( |
251 | 'bookmarks', 'bookmarks are not enabled')(f) | |
6abf9225 AG |
252 | |
253 | ||
254 | def snap_always_unmounted_before_destruction(): | |
1966e959 | 255 | # Apparently OpenZFS automatically unmounts the snapshot |
6abf9225 | 256 | # only if it is mounted at its default .zfs/snapshot |
1966e959 | 257 | # mountpoint under Linux. |
85ce3f4f | 258 | return ( |
259 | platform.system() != 'Linux', 'snapshot is not auto-unmounted') | |
6abf9225 AG |
260 | |
261 | ||
262 | def 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 | ||
269 | def needs_support(function): | |
85ce3f4f | 270 | return unittest.skipUnless( |
271 | lzc.is_supported(function), | |
272 | '{} not available'.format(function.__name__)) | |
6abf9225 AG |
273 | |
274 | ||
275 | class 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 | |
be1e69f3 | 1065 | def test_bookmarks_foreign_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) | |
6ef2151c AZ |
1906 | os.unlink(output.name) |
1907 | ||
9de8c0cd | 1908 | self.assertEqual(ctx.exception.errno, errno.EBADF) |
6abf9225 AG |
1909 | |
1910 | def test_recv_full(self): | |
4b1c4062 BB |
1911 | src = ZFSTest.pool.makeName(b"fs1@snap") |
1912 | dst = ZFSTest.pool.makeName(b"fs2/received-1@snap") | |
6abf9225 | 1913 | |
4b1c4062 | 1914 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: |
6abf9225 AG |
1915 | lzc.lzc_snapshot([src]) |
1916 | ||
d8d418ff | 1917 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
1918 | lzc.lzc_send(src, None, stream.fileno()) |
1919 | stream.seek(0) | |
1920 | lzc.lzc_receive(dst, stream.fileno()) | |
1921 | ||
1922 | name = os.path.basename(name) | |
1923 | with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: | |
1924 | self.assertTrue( | |
85ce3f4f | 1925 | filecmp.cmp( |
1926 | os.path.join(mnt1, name), os.path.join(mnt2, name), False)) | |
6abf9225 AG |
1927 | |
1928 | def test_recv_incremental(self): | |
4b1c4062 BB |
1929 | src1 = ZFSTest.pool.makeName(b"fs1@snap1") |
1930 | src2 = ZFSTest.pool.makeName(b"fs1@snap2") | |
1931 | dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1") | |
1932 | dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2") | |
6abf9225 AG |
1933 | |
1934 | lzc.lzc_snapshot([src1]) | |
4b1c4062 | 1935 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: |
6abf9225 AG |
1936 | lzc.lzc_snapshot([src2]) |
1937 | ||
d8d418ff | 1938 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
1939 | lzc.lzc_send(src1, None, stream.fileno()) |
1940 | stream.seek(0) | |
1941 | lzc.lzc_receive(dst1, stream.fileno()) | |
d8d418ff | 1942 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
1943 | lzc.lzc_send(src2, src1, stream.fileno()) |
1944 | stream.seek(0) | |
1945 | lzc.lzc_receive(dst2, stream.fileno()) | |
1946 | ||
1947 | name = os.path.basename(name) | |
1948 | with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2: | |
1949 | self.assertTrue( | |
85ce3f4f | 1950 | filecmp.cmp( |
1951 | os.path.join(mnt1, name), os.path.join(mnt2, name), False)) | |
1952 | ||
cd6b910b | 1953 | # This test case fails unless a patch from |
85ce3f4f | 1954 | # https://clusterhq.atlassian.net/browse/ZFS-20 |
1955 | # is applied to libzfs_core, otherwise it succeeds. | |
1956 | @unittest.skip("fails with unpatched libzfs_core") | |
1957 | def test_recv_without_explicit_snap_name(self): | |
4b1c4062 BB |
1958 | srcfs = ZFSTest.pool.makeName(b"fs1") |
1959 | src1 = srcfs + b"@snap1" | |
1960 | src2 = srcfs + b"@snap2" | |
1961 | dstfs = ZFSTest.pool.makeName(b"fs2/received-100") | |
1962 | dst1 = dstfs + b'@snap1' | |
1963 | dst2 = dstfs + b'@snap2' | |
85ce3f4f | 1964 | |
1965 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
1966 | lzc.lzc_receive(dstfs, full.fileno()) | |
1967 | lzc.lzc_receive(dstfs, incr.fileno()) | |
1968 | self.assertExists(dst1) | |
1969 | self.assertExists(dst2) | |
6abf9225 AG |
1970 | |
1971 | def test_recv_clone(self): | |
4b1c4062 BB |
1972 | orig_src = ZFSTest.pool.makeName(b"fs2@send-origin") |
1973 | clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone") | |
1974 | clone_snap = clone + b"@snap" | |
1975 | orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap") | |
1976 | clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap") | |
6abf9225 AG |
1977 | |
1978 | lzc.lzc_snapshot([orig_src]) | |
d8d418ff | 1979 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
1980 | lzc.lzc_send(orig_src, None, stream.fileno()) |
1981 | stream.seek(0) | |
1982 | lzc.lzc_receive(orig_dst, stream.fileno()) | |
1983 | ||
1984 | lzc.lzc_clone(clone, orig_src) | |
1985 | lzc.lzc_snapshot([clone_snap]) | |
d8d418ff | 1986 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
1987 | lzc.lzc_send(clone_snap, orig_src, stream.fileno()) |
1988 | stream.seek(0) | |
1989 | lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst) | |
1990 | ||
1991 | def test_recv_full_already_existing_empty_fs(self): | |
4b1c4062 BB |
1992 | src = ZFSTest.pool.makeName(b"fs1@snap") |
1993 | dstfs = ZFSTest.pool.makeName(b"fs2/received-3") | |
1994 | dst = dstfs + b'@snap' | |
6abf9225 | 1995 | |
4b1c4062 | 1996 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
1997 | lzc.lzc_snapshot([src]) |
1998 | lzc.lzc_create(dstfs) | |
d8d418ff | 1999 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2000 | lzc.lzc_send(src, None, stream.fileno()) |
2001 | stream.seek(0) | |
85ce3f4f | 2002 | with self.assertRaises(( |
2003 | lzc_exc.DestinationModified, lzc_exc.DatasetExists)): | |
6abf9225 AG |
2004 | lzc.lzc_receive(dst, stream.fileno()) |
2005 | ||
2006 | def test_recv_full_into_root_empty_pool(self): | |
2007 | empty_pool = None | |
2008 | try: | |
4b1c4062 | 2009 | srcfs = ZFSTest.pool.makeName(b"fs1") |
6abf9225 | 2010 | empty_pool = _TempPool() |
4b1c4062 | 2011 | dst = empty_pool.makeName(b'@snap') |
6abf9225 | 2012 | |
4b1c4062 | 2013 | with streams(srcfs, b"snap", None) as (_, (stream, _)): |
85ce3f4f | 2014 | with self.assertRaises(( |
2015 | lzc_exc.DestinationModified, lzc_exc.DatasetExists)): | |
6abf9225 AG |
2016 | lzc.lzc_receive(dst, stream.fileno()) |
2017 | finally: | |
2018 | if empty_pool is not None: | |
2019 | empty_pool.cleanUp() | |
2020 | ||
2021 | def test_recv_full_into_ro_pool(self): | |
4b1c4062 BB |
2022 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2023 | dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap') | |
6abf9225 | 2024 | |
4b1c4062 | 2025 | with streams(srcfs, b"snap", None) as (_, (stream, _)): |
6abf9225 AG |
2026 | with self.assertRaises(lzc_exc.ReadOnlyPool): |
2027 | lzc.lzc_receive(dst, stream.fileno()) | |
2028 | ||
2029 | def test_recv_full_already_existing_modified_fs(self): | |
4b1c4062 BB |
2030 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2031 | dstfs = ZFSTest.pool.makeName(b"fs2/received-5") | |
2032 | dst = dstfs + b'@snap' | |
6abf9225 | 2033 | |
4b1c4062 | 2034 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2035 | lzc.lzc_snapshot([src]) |
2036 | lzc.lzc_create(dstfs) | |
2037 | with temp_file_in_fs(dstfs): | |
d8d418ff | 2038 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2039 | lzc.lzc_send(src, None, stream.fileno()) |
2040 | stream.seek(0) | |
85ce3f4f | 2041 | with self.assertRaises(( |
2042 | lzc_exc.DestinationModified, lzc_exc.DatasetExists)): | |
6abf9225 AG |
2043 | lzc.lzc_receive(dst, stream.fileno()) |
2044 | ||
2045 | def test_recv_full_already_existing_with_snapshots(self): | |
4b1c4062 BB |
2046 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2047 | dstfs = ZFSTest.pool.makeName(b"fs2/received-4") | |
2048 | dst = dstfs + b'@snap' | |
6abf9225 | 2049 | |
4b1c4062 | 2050 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2051 | lzc.lzc_snapshot([src]) |
2052 | lzc.lzc_create(dstfs) | |
4b1c4062 | 2053 | lzc.lzc_snapshot([dstfs + b"@snap1"]) |
d8d418ff | 2054 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2055 | lzc.lzc_send(src, None, stream.fileno()) |
2056 | stream.seek(0) | |
85ce3f4f | 2057 | with self.assertRaises(( |
2058 | lzc_exc.StreamMismatch, lzc_exc.DatasetExists)): | |
6abf9225 AG |
2059 | lzc.lzc_receive(dst, stream.fileno()) |
2060 | ||
2061 | def test_recv_full_already_existing_snapshot(self): | |
4b1c4062 BB |
2062 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2063 | dstfs = ZFSTest.pool.makeName(b"fs2/received-6") | |
2064 | dst = dstfs + b'@snap' | |
6abf9225 | 2065 | |
4b1c4062 | 2066 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2067 | lzc.lzc_snapshot([src]) |
2068 | lzc.lzc_create(dstfs) | |
2069 | lzc.lzc_snapshot([dst]) | |
d8d418ff | 2070 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2071 | lzc.lzc_send(src, None, stream.fileno()) |
2072 | stream.seek(0) | |
2073 | with self.assertRaises(lzc_exc.DatasetExists): | |
2074 | lzc.lzc_receive(dst, stream.fileno()) | |
2075 | ||
2076 | def test_recv_full_missing_parent_fs(self): | |
4b1c4062 BB |
2077 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2078 | dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") | |
6abf9225 | 2079 | |
4b1c4062 | 2080 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 | 2081 | lzc.lzc_snapshot([src]) |
d8d418ff | 2082 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2083 | lzc.lzc_send(src, None, stream.fileno()) |
2084 | stream.seek(0) | |
2085 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
2086 | lzc.lzc_receive(dst, stream.fileno()) | |
2087 | ||
6abf9225 | 2088 | def test_recv_full_but_specify_origin(self): |
4b1c4062 BB |
2089 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2090 | src = srcfs + b"@snap" | |
2091 | dstfs = ZFSTest.pool.makeName(b"fs2/received-30") | |
2092 | dst = dstfs + b'@snap' | |
2093 | origin1 = ZFSTest.pool.makeName(b"fs2@snap1") | |
2094 | origin2 = ZFSTest.pool.makeName(b"fs2@snap2") | |
6abf9225 AG |
2095 | |
2096 | lzc.lzc_snapshot([origin1]) | |
2097 | with streams(srcfs, src, None) as (_, (stream, _)): | |
85ce3f4f | 2098 | lzc.lzc_receive(dst, stream.fileno(), origin=origin1) |
4b1c4062 BB |
2099 | origin = ZFSTest.pool.getFilesystem( |
2100 | b"fs2/received-30").getProperty('origin') | |
9de8c0cd | 2101 | self.assertEqual(origin, origin1) |
6abf9225 | 2102 | stream.seek(0) |
85ce3f4f | 2103 | # because origin snap does not exist can't receive as a clone of it |
2104 | with self.assertRaises(( | |
2105 | lzc_exc.DatasetNotFound, | |
2106 | lzc_exc.BadStream)): | |
6abf9225 AG |
2107 | lzc.lzc_receive(dst, stream.fileno(), origin=origin2) |
2108 | ||
6abf9225 | 2109 | def test_recv_full_existing_empty_fs_and_origin(self): |
4b1c4062 BB |
2110 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2111 | src = srcfs + b"@snap" | |
2112 | dstfs = ZFSTest.pool.makeName(b"fs2/received-31") | |
2113 | dst = dstfs + b'@snap' | |
2114 | origin = dstfs + b'@dummy' | |
6abf9225 AG |
2115 | |
2116 | lzc.lzc_create(dstfs) | |
2117 | with streams(srcfs, src, None) as (_, (stream, _)): | |
2118 | # because the destination fs already exists and has no snaps | |
85ce3f4f | 2119 | with self.assertRaises(( |
2120 | lzc_exc.DestinationModified, | |
2121 | lzc_exc.DatasetExists, | |
2122 | lzc_exc.BadStream)): | |
6abf9225 AG |
2123 | lzc.lzc_receive(dst, stream.fileno(), origin=origin) |
2124 | lzc.lzc_snapshot([origin]) | |
2125 | stream.seek(0) | |
2126 | # because the destination fs already exists and has the snap | |
85ce3f4f | 2127 | with self.assertRaises(( |
2128 | lzc_exc.StreamMismatch, | |
2129 | lzc_exc.DatasetExists, | |
2130 | lzc_exc.BadStream)): | |
6abf9225 AG |
2131 | lzc.lzc_receive(dst, stream.fileno(), origin=origin) |
2132 | ||
2133 | def test_recv_incremental_mounted_fs(self): | |
4b1c4062 BB |
2134 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2135 | src1 = srcfs + b"@snap1" | |
2136 | src2 = srcfs + b"@snap2" | |
2137 | dstfs = ZFSTest.pool.makeName(b"fs2/received-7") | |
2138 | dst1 = dstfs + b'@snap1' | |
2139 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2140 | |
2141 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2142 | lzc.lzc_receive(dst1, full.fileno()) | |
2143 | with zfs_mount(dstfs): | |
2144 | lzc.lzc_receive(dst2, incr.fileno()) | |
2145 | ||
2146 | def test_recv_incremental_modified_fs(self): | |
4b1c4062 BB |
2147 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2148 | src1 = srcfs + b"@snap1" | |
2149 | src2 = srcfs + b"@snap2" | |
2150 | dstfs = ZFSTest.pool.makeName(b"fs2/received-15") | |
2151 | dst1 = dstfs + b'@snap1' | |
2152 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2153 | |
2154 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2155 | lzc.lzc_receive(dst1, full.fileno()) | |
2156 | with temp_file_in_fs(dstfs): | |
2157 | with self.assertRaises(lzc_exc.DestinationModified): | |
2158 | lzc.lzc_receive(dst2, incr.fileno()) | |
2159 | ||
2160 | def test_recv_incremental_snapname_used(self): | |
4b1c4062 BB |
2161 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2162 | src1 = srcfs + b"@snap1" | |
2163 | src2 = srcfs + b"@snap2" | |
2164 | dstfs = ZFSTest.pool.makeName(b"fs2/received-8") | |
2165 | dst1 = dstfs + b'@snap1' | |
2166 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2167 | |
2168 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2169 | lzc.lzc_receive(dst1, full.fileno()) | |
2170 | lzc.lzc_snapshot([dst2]) | |
2171 | with self.assertRaises(lzc_exc.DatasetExists): | |
2172 | lzc.lzc_receive(dst2, incr.fileno()) | |
2173 | ||
2174 | def test_recv_incremental_more_recent_snap_with_no_changes(self): | |
4b1c4062 BB |
2175 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2176 | src1 = srcfs + b"@snap1" | |
2177 | src2 = srcfs + b"@snap2" | |
2178 | dstfs = ZFSTest.pool.makeName(b"fs2/received-9") | |
2179 | dst1 = dstfs + b'@snap1' | |
2180 | dst2 = dstfs + b'@snap2' | |
2181 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2182 | |
2183 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2184 | lzc.lzc_receive(dst1, full.fileno()) | |
2185 | lzc.lzc_snapshot([dst_snap]) | |
2186 | lzc.lzc_receive(dst2, incr.fileno()) | |
2187 | ||
6abf9225 | 2188 | def test_recv_incremental_non_clone_but_set_origin(self): |
4b1c4062 BB |
2189 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2190 | src1 = srcfs + b"@snap1" | |
2191 | src2 = srcfs + b"@snap2" | |
2192 | dstfs = ZFSTest.pool.makeName(b"fs2/received-20") | |
2193 | dst1 = dstfs + b'@snap1' | |
2194 | dst2 = dstfs + b'@snap2' | |
2195 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2196 | |
2197 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2198 | lzc.lzc_receive(dst1, full.fileno()) | |
2199 | lzc.lzc_snapshot([dst_snap]) | |
cd6b910b | 2200 | # because cannot receive incremental and set origin on a non-clone |
85ce3f4f | 2201 | with self.assertRaises(lzc_exc.BadStream): |
2202 | lzc.lzc_receive(dst2, incr.fileno(), origin=dst1) | |
6abf9225 | 2203 | |
6abf9225 | 2204 | def test_recv_incremental_non_clone_but_set_random_origin(self): |
4b1c4062 BB |
2205 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2206 | src1 = srcfs + b"@snap1" | |
2207 | src2 = srcfs + b"@snap2" | |
2208 | dstfs = ZFSTest.pool.makeName(b"fs2/received-21") | |
2209 | dst1 = dstfs + b'@snap1' | |
2210 | dst2 = dstfs + b'@snap2' | |
2211 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2212 | |
2213 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2214 | lzc.lzc_receive(dst1, full.fileno()) | |
2215 | lzc.lzc_snapshot([dst_snap]) | |
85ce3f4f | 2216 | # because origin snap does not exist can't receive as a clone of it |
2217 | with self.assertRaises(( | |
2218 | lzc_exc.DatasetNotFound, | |
2219 | lzc_exc.BadStream)): | |
2220 | lzc.lzc_receive( | |
2221 | dst2, incr.fileno(), | |
4b1c4062 | 2222 | origin=ZFSTest.pool.makeName(b"fs2/fs@snap")) |
6abf9225 AG |
2223 | |
2224 | def test_recv_incremental_more_recent_snap(self): | |
4b1c4062 BB |
2225 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2226 | src1 = srcfs + b"@snap1" | |
2227 | src2 = srcfs + b"@snap2" | |
2228 | dstfs = ZFSTest.pool.makeName(b"fs2/received-10") | |
2229 | dst1 = dstfs + b'@snap1' | |
2230 | dst2 = dstfs + b'@snap2' | |
2231 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2232 | |
2233 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2234 | lzc.lzc_receive(dst1, full.fileno()) | |
2235 | with temp_file_in_fs(dstfs): | |
2236 | lzc.lzc_snapshot([dst_snap]) | |
2237 | with self.assertRaises(lzc_exc.DestinationModified): | |
2238 | lzc.lzc_receive(dst2, incr.fileno()) | |
2239 | ||
2240 | def test_recv_incremental_duplicate(self): | |
4b1c4062 BB |
2241 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2242 | src1 = srcfs + b"@snap1" | |
2243 | src2 = srcfs + b"@snap2" | |
2244 | dstfs = ZFSTest.pool.makeName(b"fs2/received-11") | |
2245 | dst1 = dstfs + b'@snap1' | |
2246 | dst2 = dstfs + b'@snap2' | |
2247 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2248 | |
2249 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2250 | lzc.lzc_receive(dst1, full.fileno()) | |
2251 | lzc.lzc_receive(dst2, incr.fileno()) | |
2252 | incr.seek(0) | |
2253 | with self.assertRaises(lzc_exc.DestinationModified): | |
2254 | lzc.lzc_receive(dst_snap, incr.fileno()) | |
2255 | ||
2256 | def test_recv_incremental_unrelated_fs(self): | |
4b1c4062 BB |
2257 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2258 | src1 = srcfs + b"@snap1" | |
2259 | src2 = srcfs + b"@snap2" | |
2260 | dstfs = ZFSTest.pool.makeName(b"fs2/received-12") | |
2261 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2262 | |
2263 | with streams(srcfs, src1, src2) as (_, (_, incr)): | |
2264 | lzc.lzc_create(dstfs) | |
2265 | with self.assertRaises(lzc_exc.StreamMismatch): | |
2266 | lzc.lzc_receive(dst_snap, incr.fileno()) | |
2267 | ||
2268 | def test_recv_incremental_nonexistent_fs(self): | |
4b1c4062 BB |
2269 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2270 | src1 = srcfs + b"@snap1" | |
2271 | src2 = srcfs + b"@snap2" | |
2272 | dstfs = ZFSTest.pool.makeName(b"fs2/received-13") | |
2273 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
2274 | |
2275 | with streams(srcfs, src1, src2) as (_, (_, incr)): | |
2276 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
2277 | lzc.lzc_receive(dst_snap, incr.fileno()) | |
2278 | ||
2279 | def test_recv_incremental_same_fs(self): | |
4b1c4062 BB |
2280 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2281 | src1 = srcfs + b"@snap1" | |
2282 | src2 = srcfs + b"@snap2" | |
2283 | src_snap = srcfs + b'@snap' | |
6abf9225 AG |
2284 | |
2285 | with streams(srcfs, src1, src2) as (_, (_, incr)): | |
2286 | with self.assertRaises(lzc_exc.DestinationModified): | |
2287 | lzc.lzc_receive(src_snap, incr.fileno()) | |
2288 | ||
2289 | def test_recv_clone_without_specifying_origin(self): | |
4b1c4062 BB |
2290 | orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2") |
2291 | clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2") | |
2292 | clone_snap = clone + b"@snap" | |
2293 | orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap") | |
2294 | clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap") | |
6abf9225 AG |
2295 | |
2296 | lzc.lzc_snapshot([orig_src]) | |
d8d418ff | 2297 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2298 | lzc.lzc_send(orig_src, None, stream.fileno()) |
2299 | stream.seek(0) | |
2300 | lzc.lzc_receive(orig_dst, stream.fileno()) | |
2301 | ||
2302 | lzc.lzc_clone(clone, orig_src) | |
2303 | lzc.lzc_snapshot([clone_snap]) | |
d8d418ff | 2304 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2305 | lzc.lzc_send(clone_snap, orig_src, stream.fileno()) |
2306 | stream.seek(0) | |
2307 | with self.assertRaises(lzc_exc.BadStream): | |
2308 | lzc.lzc_receive(clone_dst, stream.fileno()) | |
2309 | ||
2310 | def test_recv_clone_invalid_origin(self): | |
4b1c4062 BB |
2311 | orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3") |
2312 | clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3") | |
2313 | clone_snap = clone + b"@snap" | |
2314 | orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap") | |
2315 | clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap") | |
6abf9225 AG |
2316 | |
2317 | lzc.lzc_snapshot([orig_src]) | |
d8d418ff | 2318 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2319 | lzc.lzc_send(orig_src, None, stream.fileno()) |
2320 | stream.seek(0) | |
2321 | lzc.lzc_receive(orig_dst, stream.fileno()) | |
2322 | ||
2323 | lzc.lzc_clone(clone, orig_src) | |
2324 | lzc.lzc_snapshot([clone_snap]) | |
d8d418ff | 2325 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2326 | lzc.lzc_send(clone_snap, orig_src, stream.fileno()) |
2327 | stream.seek(0) | |
2328 | with self.assertRaises(lzc_exc.NameInvalid): | |
2329 | lzc.lzc_receive( | |
85ce3f4f | 2330 | clone_dst, stream.fileno(), |
4b1c4062 | 2331 | origin=ZFSTest.pool.makeName(b"fs1/fs")) |
6abf9225 AG |
2332 | |
2333 | def test_recv_clone_wrong_origin(self): | |
4b1c4062 BB |
2334 | orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4") |
2335 | clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4") | |
2336 | clone_snap = clone + b"@snap" | |
2337 | orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap") | |
2338 | clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap") | |
2339 | wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") | |
6abf9225 AG |
2340 | |
2341 | lzc.lzc_snapshot([orig_src]) | |
d8d418ff | 2342 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2343 | lzc.lzc_send(orig_src, None, stream.fileno()) |
2344 | stream.seek(0) | |
2345 | lzc.lzc_receive(orig_dst, stream.fileno()) | |
2346 | ||
2347 | lzc.lzc_clone(clone, orig_src) | |
2348 | lzc.lzc_snapshot([clone_snap]) | |
2349 | lzc.lzc_snapshot([wrong_origin]) | |
d8d418ff | 2350 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2351 | lzc.lzc_send(clone_snap, orig_src, stream.fileno()) |
2352 | stream.seek(0) | |
2353 | with self.assertRaises(lzc_exc.StreamMismatch): | |
2354 | lzc.lzc_receive( | |
2355 | clone_dst, stream.fileno(), origin=wrong_origin) | |
2356 | ||
2357 | def test_recv_clone_nonexistent_origin(self): | |
4b1c4062 BB |
2358 | orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5") |
2359 | clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5") | |
2360 | clone_snap = clone + b"@snap" | |
2361 | orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap") | |
2362 | clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap") | |
2363 | wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") | |
6abf9225 AG |
2364 | |
2365 | lzc.lzc_snapshot([orig_src]) | |
d8d418ff | 2366 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2367 | lzc.lzc_send(orig_src, None, stream.fileno()) |
2368 | stream.seek(0) | |
2369 | lzc.lzc_receive(orig_dst, stream.fileno()) | |
2370 | ||
2371 | lzc.lzc_clone(clone, orig_src) | |
2372 | lzc.lzc_snapshot([clone_snap]) | |
d8d418ff | 2373 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2374 | lzc.lzc_send(clone_snap, orig_src, stream.fileno()) |
2375 | stream.seek(0) | |
2376 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
2377 | lzc.lzc_receive( | |
2378 | clone_dst, stream.fileno(), origin=wrong_origin) | |
2379 | ||
2380 | def test_force_recv_full_existing_fs(self): | |
4b1c4062 BB |
2381 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2382 | dstfs = ZFSTest.pool.makeName(b"fs2/received-50") | |
2383 | dst = dstfs + b'@snap' | |
6abf9225 | 2384 | |
4b1c4062 | 2385 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2386 | lzc.lzc_snapshot([src]) |
2387 | ||
2388 | lzc.lzc_create(dstfs) | |
2389 | with temp_file_in_fs(dstfs): | |
2390 | pass # enough to taint the fs | |
2391 | ||
d8d418ff | 2392 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2393 | lzc.lzc_send(src, None, stream.fileno()) |
2394 | stream.seek(0) | |
2395 | lzc.lzc_receive(dst, stream.fileno(), force=True) | |
2396 | ||
2397 | def test_force_recv_full_existing_modified_mounted_fs(self): | |
4b1c4062 BB |
2398 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2399 | dstfs = ZFSTest.pool.makeName(b"fs2/received-53") | |
2400 | dst = dstfs + b'@snap' | |
6abf9225 | 2401 | |
4b1c4062 | 2402 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2403 | lzc.lzc_snapshot([src]) |
2404 | ||
2405 | lzc.lzc_create(dstfs) | |
2406 | ||
d8d418ff | 2407 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2408 | lzc.lzc_send(src, None, stream.fileno()) |
2409 | stream.seek(0) | |
2410 | with zfs_mount(dstfs) as mntdir: | |
2411 | f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) | |
2412 | for i in range(1024): | |
4b1c4062 | 2413 | f.write(b'x' * 1024) |
6abf9225 | 2414 | lzc.lzc_receive(dst, stream.fileno(), force=True) |
cd6b910b | 2415 | # The temporary file disappears and any access, even close(), |
6abf9225 AG |
2416 | # results in EIO. |
2417 | self.assertFalse(os.path.exists(f.name)) | |
2418 | with self.assertRaises(IOError): | |
2419 | f.close() | |
2420 | ||
2421 | # This test-case expects the behavior that should be there, | |
2422 | # at the moment it may fail with DatasetExists or StreamMismatch | |
2423 | # depending on the implementation. | |
2424 | def test_force_recv_full_already_existing_with_snapshots(self): | |
4b1c4062 BB |
2425 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2426 | dstfs = ZFSTest.pool.makeName(b"fs2/received-51") | |
2427 | dst = dstfs + b'@snap' | |
6abf9225 | 2428 | |
4b1c4062 | 2429 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2430 | lzc.lzc_snapshot([src]) |
2431 | ||
2432 | lzc.lzc_create(dstfs) | |
2433 | with temp_file_in_fs(dstfs): | |
2434 | pass # enough to taint the fs | |
4b1c4062 | 2435 | lzc.lzc_snapshot([dstfs + b"@snap1"]) |
6abf9225 | 2436 | |
d8d418ff | 2437 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2438 | lzc.lzc_send(src, None, stream.fileno()) |
2439 | stream.seek(0) | |
2440 | lzc.lzc_receive(dst, stream.fileno(), force=True) | |
2441 | ||
2442 | def test_force_recv_full_already_existing_with_same_snap(self): | |
4b1c4062 BB |
2443 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2444 | dstfs = ZFSTest.pool.makeName(b"fs2/received-52") | |
2445 | dst = dstfs + b'@snap' | |
6abf9225 | 2446 | |
4b1c4062 | 2447 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 AG |
2448 | lzc.lzc_snapshot([src]) |
2449 | ||
2450 | lzc.lzc_create(dstfs) | |
2451 | with temp_file_in_fs(dstfs): | |
2452 | pass # enough to taint the fs | |
2453 | lzc.lzc_snapshot([dst]) | |
2454 | ||
d8d418ff | 2455 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2456 | lzc.lzc_send(src, None, stream.fileno()) |
2457 | stream.seek(0) | |
2458 | with self.assertRaises(lzc_exc.DatasetExists): | |
2459 | lzc.lzc_receive(dst, stream.fileno(), force=True) | |
2460 | ||
2461 | def test_force_recv_full_missing_parent_fs(self): | |
4b1c4062 BB |
2462 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2463 | dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") | |
6abf9225 | 2464 | |
4b1c4062 | 2465 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): |
6abf9225 | 2466 | lzc.lzc_snapshot([src]) |
d8d418ff | 2467 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2468 | lzc.lzc_send(src, None, stream.fileno()) |
2469 | stream.seek(0) | |
2470 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
2471 | lzc.lzc_receive(dst, stream.fileno(), force=True) | |
2472 | ||
2473 | def test_force_recv_incremental_modified_fs(self): | |
4b1c4062 BB |
2474 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2475 | src1 = srcfs + b"@snap1" | |
2476 | src2 = srcfs + b"@snap2" | |
2477 | dstfs = ZFSTest.pool.makeName(b"fs2/received-60") | |
2478 | dst1 = dstfs + b'@snap1' | |
2479 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2480 | |
2481 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2482 | lzc.lzc_receive(dst1, full.fileno()) | |
2483 | with temp_file_in_fs(dstfs): | |
2484 | pass # enough to taint the fs | |
2485 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2486 | ||
2487 | def test_force_recv_incremental_modified_mounted_fs(self): | |
4b1c4062 BB |
2488 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2489 | src1 = srcfs + b"@snap1" | |
2490 | src2 = srcfs + b"@snap2" | |
2491 | dstfs = ZFSTest.pool.makeName(b"fs2/received-64") | |
2492 | dst1 = dstfs + b'@snap1' | |
2493 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2494 | |
2495 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2496 | lzc.lzc_receive(dst1, full.fileno()) | |
2497 | with zfs_mount(dstfs) as mntdir: | |
2498 | f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) | |
2499 | for i in range(1024): | |
4b1c4062 | 2500 | f.write(b'x' * 1024) |
6abf9225 | 2501 | lzc.lzc_receive(dst2, incr.fileno(), force=True) |
cd6b910b | 2502 | # The temporary file disappears and any access, even close(), |
6abf9225 AG |
2503 | # results in EIO. |
2504 | self.assertFalse(os.path.exists(f.name)) | |
2505 | with self.assertRaises(IOError): | |
2506 | f.close() | |
2507 | ||
2508 | def test_force_recv_incremental_modified_fs_plus_later_snap(self): | |
4b1c4062 BB |
2509 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2510 | src1 = srcfs + b"@snap1" | |
2511 | src2 = srcfs + b"@snap2" | |
2512 | dstfs = ZFSTest.pool.makeName(b"fs2/received-61") | |
2513 | dst1 = dstfs + b'@snap1' | |
2514 | dst2 = dstfs + b'@snap2' | |
2515 | dst3 = dstfs + b'@snap' | |
6abf9225 AG |
2516 | |
2517 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2518 | lzc.lzc_receive(dst1, full.fileno()) | |
2519 | with temp_file_in_fs(dstfs): | |
2520 | pass # enough to taint the fs | |
2521 | lzc.lzc_snapshot([dst3]) | |
2522 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2523 | self.assertExists(dst1) | |
2524 | self.assertExists(dst2) | |
2525 | self.assertNotExists(dst3) | |
2526 | ||
2527 | def test_force_recv_incremental_modified_fs_plus_same_name_snap(self): | |
4b1c4062 BB |
2528 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2529 | src1 = srcfs + b"@snap1" | |
2530 | src2 = srcfs + b"@snap2" | |
2531 | dstfs = ZFSTest.pool.makeName(b"fs2/received-62") | |
2532 | dst1 = dstfs + b'@snap1' | |
2533 | dst2 = dstfs + b'@snap2' | |
6abf9225 AG |
2534 | |
2535 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2536 | lzc.lzc_receive(dst1, full.fileno()) | |
2537 | with temp_file_in_fs(dstfs): | |
2538 | pass # enough to taint the fs | |
2539 | lzc.lzc_snapshot([dst2]) | |
2540 | with self.assertRaises(lzc_exc.DatasetExists): | |
2541 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2542 | ||
2543 | def test_force_recv_incremental_modified_fs_plus_held_snap(self): | |
4b1c4062 BB |
2544 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2545 | src1 = srcfs + b"@snap1" | |
2546 | src2 = srcfs + b"@snap2" | |
2547 | dstfs = ZFSTest.pool.makeName(b"fs2/received-63") | |
2548 | dst1 = dstfs + b'@snap1' | |
2549 | dst2 = dstfs + b'@snap2' | |
2550 | dst3 = dstfs + b'@snap' | |
6abf9225 AG |
2551 | |
2552 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2553 | lzc.lzc_receive(dst1, full.fileno()) | |
2554 | with temp_file_in_fs(dstfs): | |
2555 | pass # enough to taint the fs | |
2556 | lzc.lzc_snapshot([dst3]) | |
2557 | with cleanup_fd() as cfd: | |
4b1c4062 | 2558 | lzc.lzc_hold({dst3: b'tag'}, cfd) |
6abf9225 AG |
2559 | with self.assertRaises(lzc_exc.DatasetBusy): |
2560 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2561 | self.assertExists(dst1) | |
2562 | self.assertNotExists(dst2) | |
2563 | self.assertExists(dst3) | |
2564 | ||
2565 | def test_force_recv_incremental_modified_fs_plus_cloned_snap(self): | |
4b1c4062 BB |
2566 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2567 | src1 = srcfs + b"@snap1" | |
2568 | src2 = srcfs + b"@snap2" | |
2569 | dstfs = ZFSTest.pool.makeName(b"fs2/received-70") | |
2570 | dst1 = dstfs + b'@snap1' | |
2571 | dst2 = dstfs + b'@snap2' | |
2572 | dst3 = dstfs + b'@snap' | |
2573 | cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70") | |
6abf9225 AG |
2574 | |
2575 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2576 | lzc.lzc_receive(dst1, full.fileno()) | |
2577 | with temp_file_in_fs(dstfs): | |
2578 | pass # enough to taint the fs | |
2579 | lzc.lzc_snapshot([dst3]) | |
2580 | lzc.lzc_clone(cloned, dst3) | |
2581 | with self.assertRaises(lzc_exc.DatasetExists): | |
2582 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2583 | self.assertExists(dst1) | |
2584 | self.assertNotExists(dst2) | |
2585 | self.assertExists(dst3) | |
2586 | ||
6abf9225 | 2587 | def test_recv_incremental_into_cloned_fs(self): |
4b1c4062 BB |
2588 | srcfs = ZFSTest.pool.makeName(b"fs1") |
2589 | src1 = srcfs + b"@snap1" | |
2590 | src2 = srcfs + b"@snap2" | |
2591 | dstfs = ZFSTest.pool.makeName(b"fs2/received-71") | |
2592 | dst1 = dstfs + b'@snap1' | |
2593 | cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71") | |
2594 | dst2 = cloned + b'@snap' | |
6abf9225 AG |
2595 | |
2596 | with streams(srcfs, src1, src2) as (_, (full, incr)): | |
2597 | lzc.lzc_receive(dst1, full.fileno()) | |
2598 | lzc.lzc_clone(cloned, dst1) | |
2599 | # test both graceful and with-force attempts | |
2600 | with self.assertRaises(lzc_exc.StreamMismatch): | |
2601 | lzc.lzc_receive(dst2, incr.fileno()) | |
2602 | incr.seek(0) | |
2603 | with self.assertRaises(lzc_exc.StreamMismatch): | |
2604 | lzc.lzc_receive(dst2, incr.fileno(), force=True) | |
2605 | self.assertExists(dst1) | |
2606 | self.assertNotExists(dst2) | |
2607 | ||
85ce3f4f | 2608 | def test_recv_with_header_full(self): |
4b1c4062 BB |
2609 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2610 | dst = ZFSTest.pool.makeName(b"fs2/received") | |
85ce3f4f | 2611 | |
4b1c4062 | 2612 | with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: |
85ce3f4f | 2613 | lzc.lzc_snapshot([src]) |
2614 | ||
d8d418ff | 2615 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2616 | lzc.lzc_send(src, None, stream.fileno()) |
2617 | stream.seek(0) | |
2618 | ||
2619 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2620 | self.assertEqual(src, header['drr_toname']) | |
4b1c4062 | 2621 | snap = header['drr_toname'].split(b'@', 1)[1] |
85ce3f4f | 2622 | lzc.lzc_receive_with_header( |
4b1c4062 | 2623 | dst + b'@' + snap, stream.fileno(), c_header) |
85ce3f4f | 2624 | |
2625 | name = os.path.basename(name) | |
2626 | with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: | |
2627 | self.assertTrue( | |
2628 | filecmp.cmp( | |
2629 | os.path.join(mnt1, name), os.path.join(mnt2, name), False)) | |
2630 | ||
d8d418ff | 2631 | def test_recv_fs_below_zvol(self): |
2632 | send = ZFSTest.pool.makeName(b"fs1@snap") | |
2633 | zvol = ZFSTest.pool.makeName(b"fs1/zvol") | |
2634 | dest = zvol + b"/fs@snap" | |
2635 | props = {b"volsize": 1024 * 1024} | |
2636 | ||
2637 | lzc.lzc_snapshot([send]) | |
2638 | lzc.lzc_create(zvol, ds_type='zvol', props=props) | |
2639 | with tempfile.TemporaryFile(suffix='.zstream') as stream: | |
2640 | lzc.lzc_send(send, None, stream.fileno()) | |
2641 | stream.seek(0) | |
2642 | with self.assertRaises(lzc_exc.WrongParent): | |
2643 | lzc.lzc_receive(dest, stream.fileno()) | |
2644 | ||
2645 | def test_recv_zvol_over_fs_with_children(self): | |
2646 | parent = ZFSTest.pool.makeName(b"fs1") | |
2647 | child = parent + b"subfs" | |
2648 | zvol = ZFSTest.pool.makeName(b"fs1/zvol") | |
2649 | send = zvol + b"@snap" | |
2650 | props = {b"volsize": 1024 * 1024} | |
2651 | ||
2652 | lzc.lzc_create(child) | |
2653 | lzc.lzc_create(zvol, ds_type='zvol', props=props) | |
2654 | lzc.lzc_snapshot([send]) | |
2655 | with tempfile.TemporaryFile(suffix='.zstream') as stream: | |
2656 | lzc.lzc_send(send, None, stream.fileno()) | |
2657 | stream.seek(0) | |
2658 | with self.assertRaises(lzc_exc.WrongParent): | |
2659 | lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True) | |
2660 | ||
2661 | def test_recv_zvol_overwrite_rootds(self): | |
2662 | zvol = ZFSTest.pool.makeName(b"fs1/zvol") | |
2663 | snap = zvol + b"@snap" | |
2664 | rootds = ZFSTest.pool.getRoot().getName() | |
2665 | props = {b"volsize": 1024 * 1024} | |
2666 | ||
2667 | lzc.lzc_create(zvol, ds_type='zvol', props=props) | |
2668 | lzc.lzc_snapshot([snap]) | |
2669 | with tempfile.TemporaryFile(suffix='.zstream') as stream: | |
2670 | lzc.lzc_send(snap, None, stream.fileno()) | |
2671 | stream.seek(0) | |
2672 | with self.assertRaises(lzc_exc.WrongParent): | |
2673 | lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True) | |
2674 | ||
6abf9225 | 2675 | def test_send_full_across_clone_branch_point(self): |
4b1c4062 | 2676 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
2677 | |
2678 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 2679 | origfs, b"snap1", b"send-origin-20", None) |
6abf9225 | 2680 | |
4b1c4062 | 2681 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20") |
6abf9225 AG |
2682 | lzc.lzc_clone(clonefs, origsnap) |
2683 | ||
4b1c4062 | 2684 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 2685 | |
d8d418ff | 2686 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2687 | lzc.lzc_send(tosnap, None, stream.fileno()) |
2688 | ||
2689 | def test_send_incr_across_clone_branch_point(self): | |
4b1c4062 | 2690 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
2691 | |
2692 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 2693 | origfs, b"snap1", b"send-origin-21", None) |
6abf9225 | 2694 | |
4b1c4062 | 2695 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21") |
6abf9225 AG |
2696 | lzc.lzc_clone(clonefs, origsnap) |
2697 | ||
4b1c4062 | 2698 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 2699 | |
d8d418ff | 2700 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2701 | lzc.lzc_send(tosnap, fromsnap, stream.fileno()) |
2702 | ||
85ce3f4f | 2703 | def test_send_resume_token_full(self): |
4b1c4062 BB |
2704 | src = ZFSTest.pool.makeName(b"fs1@snap") |
2705 | dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") | |
85ce3f4f | 2706 | dst = dstfs.getSnap() |
2707 | ||
4b1c4062 | 2708 | with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: |
85ce3f4f | 2709 | for i in range(1, 10): |
2710 | with tempfile.NamedTemporaryFile(dir=mntdir) as f: | |
4b1c4062 | 2711 | f.write(b'x' * 1024 * i) |
85ce3f4f | 2712 | f.flush() |
2713 | lzc.lzc_snapshot([src]) | |
2714 | ||
d8d418ff | 2715 | with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2716 | lzc.lzc_send(src, None, stream.fileno()) |
2717 | stream.seek(0) | |
2718 | stream.truncate(1024 * 3) | |
7145123b | 2719 | with self.assertRaises(lzc_exc.StreamTruncated): |
85ce3f4f | 2720 | lzc.lzc_receive_resumable(dst, stream.fileno()) |
2721 | # Resume token code from zfs_send_resume_token_to_nvlist() | |
2722 | # XXX: if used more than twice move this code into an external func | |
2723 | # format: <version>-<cksum>-<packed-size>-<compressed-payload> | |
2724 | token = dstfs.getProperty("receive_resume_token") | |
4b1c4062 BB |
2725 | self.assertNotEqual(token, b'-') |
2726 | tokens = token.split(b'-') | |
85ce3f4f | 2727 | self.assertEqual(len(tokens), 4) |
2728 | version = tokens[0] | |
2729 | packed_size = int(tokens[2], 16) | |
2730 | compressed_nvs = tokens[3] | |
2731 | # Validate resume token | |
4b1c4062 BB |
2732 | self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION |
2733 | if sys.version_info < (3, 0): | |
2734 | payload = ( | |
2735 | zlib.decompress(str(bytearray.fromhex(compressed_nvs))) | |
2736 | ) | |
2737 | else: | |
2738 | payload = ( | |
2739 | zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) | |
2740 | ) | |
85ce3f4f | 2741 | self.assertEqual(len(payload), packed_size) |
2742 | # Unpack | |
2743 | resume_values = packed_nvlist_out(payload, packed_size) | |
4b1c4062 BB |
2744 | resumeobj = resume_values.get(b'object') |
2745 | resumeoff = resume_values.get(b'offset') | |
d8d418ff | 2746 | with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: |
85ce3f4f | 2747 | lzc.lzc_send_resume( |
2748 | src, None, rstream.fileno(), None, resumeobj, resumeoff) | |
2749 | rstream.seek(0) | |
2750 | lzc.lzc_receive_resumable(dst, rstream.fileno()) | |
2751 | ||
2752 | def test_send_resume_token_incremental(self): | |
4b1c4062 BB |
2753 | snap1 = ZFSTest.pool.makeName(b"fs1@snap1") |
2754 | snap2 = ZFSTest.pool.makeName(b"fs1@snap2") | |
2755 | dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") | |
85ce3f4f | 2756 | dst1 = dstfs.getSnap() |
2757 | dst2 = dstfs.getSnap() | |
2758 | ||
2759 | lzc.lzc_snapshot([snap1]) | |
d8d418ff | 2760 | with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2761 | lzc.lzc_send(snap1, None, stream.fileno()) |
2762 | stream.seek(0) | |
2763 | lzc.lzc_receive(dst1, stream.fileno()) | |
2764 | ||
4b1c4062 | 2765 | with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: |
85ce3f4f | 2766 | for i in range(1, 10): |
2767 | with tempfile.NamedTemporaryFile(dir=mntdir) as f: | |
4b1c4062 | 2768 | f.write(b'x' * 1024 * i) |
85ce3f4f | 2769 | f.flush() |
2770 | lzc.lzc_snapshot([snap2]) | |
2771 | ||
d8d418ff | 2772 | with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2773 | lzc.lzc_send(snap2, snap1, stream.fileno()) |
2774 | stream.seek(0) | |
2775 | stream.truncate(1024 * 3) | |
7145123b | 2776 | with self.assertRaises(lzc_exc.StreamTruncated): |
85ce3f4f | 2777 | lzc.lzc_receive_resumable(dst2, stream.fileno()) |
2778 | # Resume token code from zfs_send_resume_token_to_nvlist() | |
2779 | # format: <version>-<cksum>-<packed-size>-<compressed-payload> | |
2780 | token = dstfs.getProperty("receive_resume_token") | |
2781 | self.assertNotEqual(token, '-') | |
4b1c4062 | 2782 | tokens = token.split(b'-') |
85ce3f4f | 2783 | self.assertEqual(len(tokens), 4) |
2784 | version = tokens[0] | |
2785 | packed_size = int(tokens[2], 16) | |
2786 | compressed_nvs = tokens[3] | |
2787 | # Validate resume token | |
4b1c4062 BB |
2788 | self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION |
2789 | if sys.version_info < (3, 0): | |
2790 | payload = ( | |
2791 | zlib.decompress(str(bytearray.fromhex(compressed_nvs))) | |
2792 | ) | |
2793 | else: | |
2794 | payload = ( | |
2795 | zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) | |
2796 | ) | |
85ce3f4f | 2797 | self.assertEqual(len(payload), packed_size) |
2798 | # Unpack | |
2799 | resume_values = packed_nvlist_out(payload, packed_size) | |
4b1c4062 BB |
2800 | resumeobj = resume_values.get(b'object') |
2801 | resumeoff = resume_values.get(b'offset') | |
d8d418ff | 2802 | with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: |
85ce3f4f | 2803 | lzc.lzc_send_resume( |
2804 | snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff) | |
2805 | rstream.seek(0) | |
2806 | lzc.lzc_receive_resumable(dst2, rstream.fileno()) | |
2807 | ||
6abf9225 | 2808 | def test_recv_full_across_clone_branch_point(self): |
4b1c4062 | 2809 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
2810 | |
2811 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 2812 | origfs, b"snap1", b"send-origin-30", None) |
6abf9225 | 2813 | |
4b1c4062 | 2814 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30") |
6abf9225 AG |
2815 | lzc.lzc_clone(clonefs, origsnap) |
2816 | ||
4b1c4062 | 2817 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 2818 | |
4b1c4062 BB |
2819 | recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30") |
2820 | recvsnap = recvfs + b"@snap" | |
d8d418ff | 2821 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2822 | lzc.lzc_send(tosnap, None, stream.fileno()) |
2823 | stream.seek(0) | |
2824 | lzc.lzc_receive(recvsnap, stream.fileno()) | |
2825 | ||
85ce3f4f | 2826 | def test_recv_one(self): |
4b1c4062 BB |
2827 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2828 | tosnap = ZFSTest.pool.makeName(b"recv@snap1") | |
85ce3f4f | 2829 | |
2830 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2831 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2832 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2833 | stream.seek(0) | |
2834 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2835 | lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) | |
2836 | ||
2837 | def test_recv_one_size(self): | |
4b1c4062 BB |
2838 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2839 | tosnap = ZFSTest.pool.makeName(b"recv@snap1") | |
85ce3f4f | 2840 | |
2841 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2842 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2843 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2844 | size = os.fstat(stream.fileno()).st_size | |
2845 | stream.seek(0) | |
2846 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2847 | (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) | |
2848 | self.assertAlmostEqual(read, size, delta=read * 0.05) | |
2849 | ||
2850 | def test_recv_one_props(self): | |
4b1c4062 BB |
2851 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2852 | fs = ZFSTest.pool.getFilesystem(b"recv") | |
2853 | tosnap = fs.getName() + b"@snap1" | |
85ce3f4f | 2854 | props = { |
4b1c4062 BB |
2855 | b"compression": 0x01, |
2856 | b"ns:prop": b"val" | |
85ce3f4f | 2857 | } |
2858 | ||
2859 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2860 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2861 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2862 | stream.seek(0) | |
2863 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2864 | lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props) | |
2865 | self.assertExists(tosnap) | |
4b1c4062 BB |
2866 | self.assertEqual(fs.getProperty("compression", "received"), b"on") |
2867 | self.assertEqual(fs.getProperty("ns:prop", "received"), b"val") | |
85ce3f4f | 2868 | |
2869 | def test_recv_one_invalid_prop(self): | |
4b1c4062 BB |
2870 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2871 | fs = ZFSTest.pool.getFilesystem(b"recv") | |
2872 | tosnap = fs.getName() + b"@snap1" | |
85ce3f4f | 2873 | props = { |
4b1c4062 BB |
2874 | b"exec": 0xff, |
2875 | b"atime": 0x00 | |
85ce3f4f | 2876 | } |
2877 | ||
2878 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2879 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2880 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2881 | stream.seek(0) | |
2882 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2883 | with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx: | |
2884 | lzc.lzc_receive_one( | |
2885 | tosnap, stream.fileno(), c_header, props=props) | |
2886 | self.assertExists(tosnap) | |
4b1c4062 | 2887 | self.assertEqual(fs.getProperty("atime", "received"), b"off") |
85ce3f4f | 2888 | for e in ctx.exception.errors: |
2889 | self.assertIsInstance(e, lzc_exc.PropertyInvalid) | |
4b1c4062 | 2890 | self.assertEqual(e.name, b"exec") |
85ce3f4f | 2891 | |
2892 | def test_recv_with_cmdprops(self): | |
4b1c4062 BB |
2893 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2894 | fs = ZFSTest.pool.getFilesystem(b"recv") | |
2895 | tosnap = fs.getName() + b"@snap1" | |
85ce3f4f | 2896 | props = {} |
2897 | cmdprops = { | |
4b1c4062 BB |
2898 | b"compression": 0x01, |
2899 | b"ns:prop": b"val" | |
85ce3f4f | 2900 | } |
2901 | ||
2902 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2903 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2904 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2905 | stream.seek(0) | |
2906 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2907 | lzc.lzc_receive_with_cmdprops( | |
2908 | tosnap, stream.fileno(), c_header, props=props, | |
2909 | cmdprops=cmdprops) | |
2910 | self.assertExists(tosnap) | |
4b1c4062 BB |
2911 | self.assertEqual(fs.getProperty("compression"), b"on") |
2912 | self.assertEqual(fs.getProperty("ns:prop"), b"val") | |
85ce3f4f | 2913 | |
e8cf3a4f AP |
2914 | def test_recv_with_heal(self): |
2915 | snap = ZFSTest.pool.makeName(b"fs1@snap1") | |
2916 | fs = ZFSTest.pool.getFilesystem(b"fs1") | |
2917 | props = {} | |
2918 | cmdprops = { | |
2919 | b"compression": 0x01, | |
2920 | b"ns:prop": b"val" | |
2921 | } | |
2922 | ||
2923 | lzc.lzc_snapshot([snap]) | |
2924 | with tempfile.TemporaryFile(suffix='.zstream') as stream: | |
2925 | lzc.lzc_send(snap, None, stream.fileno()) | |
2926 | stream.seek(0) | |
2927 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2928 | lzc.lzc_receive_with_heal( | |
2929 | snap, stream.fileno(), c_header, props=props, | |
2930 | cmdprops=cmdprops) | |
2931 | self.assertExists(snap) | |
2932 | self.assertEqual(fs.getProperty("compression"), b"on") | |
2933 | self.assertEqual(fs.getProperty("ns:prop"), b"val") | |
2934 | ||
85ce3f4f | 2935 | def test_recv_with_cmdprops_and_recvprops(self): |
4b1c4062 BB |
2936 | fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") |
2937 | fs = ZFSTest.pool.getFilesystem(b"recv") | |
2938 | tosnap = fs.getName() + b"@snap1" | |
85ce3f4f | 2939 | props = { |
4b1c4062 BB |
2940 | b"atime": 0x01, |
2941 | b"exec": 0x00, | |
2942 | b"ns:prop": b"abc" | |
85ce3f4f | 2943 | } |
2944 | cmdprops = { | |
4b1c4062 BB |
2945 | b"compression": 0x01, |
2946 | b"ns:prop": b"def", | |
2947 | b"exec": None, | |
85ce3f4f | 2948 | } |
2949 | ||
2950 | lzc.lzc_snapshot([fromsnap]) | |
d8d418ff | 2951 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
85ce3f4f | 2952 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2953 | stream.seek(0) | |
2954 | (header, c_header) = lzc.receive_header(stream.fileno()) | |
2955 | lzc.lzc_receive_with_cmdprops( | |
2956 | tosnap, stream.fileno(), c_header, props=props, | |
2957 | cmdprops=cmdprops) | |
2958 | self.assertExists(tosnap) | |
4b1c4062 BB |
2959 | self.assertEqual(fs.getProperty("atime", True), b"on") |
2960 | self.assertEqual(fs.getProperty("exec", True), b"off") | |
2961 | self.assertEqual(fs.getProperty("ns:prop", True), b"abc") | |
2962 | self.assertEqual(fs.getProperty("compression"), b"on") | |
2963 | self.assertEqual(fs.getProperty("ns:prop"), b"def") | |
2964 | self.assertEqual(fs.getProperty("exec"), b"on") | |
85ce3f4f | 2965 | |
2966 | def test_recv_incr_across_clone_branch_point_no_origin(self): | |
4b1c4062 | 2967 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
2968 | |
2969 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 2970 | origfs, b"snap1", b"send-origin-32", None) |
6abf9225 | 2971 | |
4b1c4062 | 2972 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32") |
6abf9225 AG |
2973 | lzc.lzc_clone(clonefs, origsnap) |
2974 | ||
4b1c4062 | 2975 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 2976 | |
4b1c4062 BB |
2977 | recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32") |
2978 | recvsnap1 = recvfs + b"@snap1" | |
2979 | recvsnap2 = recvfs + b"@snap2" | |
d8d418ff | 2980 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2981 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
2982 | stream.seek(0) | |
2983 | lzc.lzc_receive(recvsnap1, stream.fileno()) | |
d8d418ff | 2984 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
2985 | lzc.lzc_send(tosnap, fromsnap, stream.fileno()) |
2986 | stream.seek(0) | |
2987 | with self.assertRaises(lzc_exc.BadStream): | |
2988 | lzc.lzc_receive(recvsnap2, stream.fileno()) | |
2989 | ||
2990 | def test_recv_incr_across_clone_branch_point(self): | |
4b1c4062 | 2991 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
2992 | |
2993 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 2994 | origfs, b"snap1", b"send-origin-31", None) |
6abf9225 | 2995 | |
4b1c4062 | 2996 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31") |
6abf9225 AG |
2997 | lzc.lzc_clone(clonefs, origsnap) |
2998 | ||
4b1c4062 | 2999 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 3000 | |
4b1c4062 BB |
3001 | recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31") |
3002 | recvsnap1 = recvfs + b"@snap1" | |
3003 | recvsnap2 = recvfs + b"@snap2" | |
d8d418ff | 3004 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
3005 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
3006 | stream.seek(0) | |
3007 | lzc.lzc_receive(recvsnap1, stream.fileno()) | |
d8d418ff | 3008 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
3009 | lzc.lzc_send(tosnap, fromsnap, stream.fileno()) |
3010 | stream.seek(0) | |
3011 | with self.assertRaises(lzc_exc.BadStream): | |
3012 | lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) | |
3013 | ||
85ce3f4f | 3014 | def test_recv_incr_across_clone_branch_point_new_fs(self): |
4b1c4062 | 3015 | origfs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
3016 | |
3017 | (_, (fromsnap, origsnap, _)) = make_snapshots( | |
4b1c4062 | 3018 | origfs, b"snap1", b"send-origin-33", None) |
6abf9225 | 3019 | |
4b1c4062 | 3020 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33") |
6abf9225 AG |
3021 | lzc.lzc_clone(clonefs, origsnap) |
3022 | ||
4b1c4062 | 3023 | (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) |
6abf9225 | 3024 | |
4b1c4062 BB |
3025 | recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33") |
3026 | recvsnap1 = recvfs1 + b"@snap" | |
3027 | recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2") | |
3028 | recvsnap2 = recvfs2 + b"@snap" | |
d8d418ff | 3029 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
3030 | lzc.lzc_send(fromsnap, None, stream.fileno()) |
3031 | stream.seek(0) | |
3032 | lzc.lzc_receive(recvsnap1, stream.fileno()) | |
d8d418ff | 3033 | with tempfile.TemporaryFile(suffix='.zstream') as stream: |
6abf9225 AG |
3034 | lzc.lzc_send(tosnap, fromsnap, stream.fileno()) |
3035 | stream.seek(0) | |
3036 | lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) | |
3037 | ||
3038 | def test_recv_bad_stream(self): | |
4b1c4062 BB |
3039 | dstfs = ZFSTest.pool.makeName(b"fs2/received") |
3040 | dst_snap = dstfs + b'@snap' | |
6abf9225 AG |
3041 | |
3042 | with dev_zero() as fd: | |
3043 | with self.assertRaises(lzc_exc.BadStream): | |
3044 | lzc.lzc_receive(dst_snap, fd) | |
3045 | ||
3046 | @needs_support(lzc.lzc_promote) | |
3047 | def test_promote(self): | |
4b1c4062 BB |
3048 | origfs = ZFSTest.pool.makeName(b"fs2") |
3049 | snap = b"@promote-snap-1" | |
6abf9225 AG |
3050 | origsnap = origfs + snap |
3051 | lzc.lzc_snap([origsnap]) | |
3052 | ||
4b1c4062 | 3053 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1") |
6abf9225 AG |
3054 | lzc.lzc_clone(clonefs, origsnap) |
3055 | ||
3056 | lzc.lzc_promote(clonefs) | |
3057 | # the snapshot now should belong to the promoted fs | |
3058 | self.assertExists(clonefs + snap) | |
3059 | ||
3060 | @needs_support(lzc.lzc_promote) | |
3061 | def test_promote_too_long_snapname(self): | |
3062 | # origfs name must be shorter than clonefs name | |
4b1c4062 BB |
3063 | origfs = ZFSTest.pool.makeName(b"fs2") |
3064 | clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2") | |
3065 | snapprefix = b"@promote-snap-2-" | |
6abf9225 | 3066 | pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix) |
4b1c4062 | 3067 | snap = snapprefix + b'x' * pad_len |
6abf9225 AG |
3068 | origsnap = origfs + snap |
3069 | ||
3070 | lzc.lzc_snap([origsnap]) | |
3071 | lzc.lzc_clone(clonefs, origsnap) | |
3072 | ||
3073 | # This may fail on older buggy systems. | |
3074 | # See: https://www.illumos.org/issues/5909 | |
3075 | with self.assertRaises(lzc_exc.NameTooLong): | |
3076 | lzc.lzc_promote(clonefs) | |
3077 | ||
3078 | @needs_support(lzc.lzc_promote) | |
3079 | def test_promote_not_cloned(self): | |
4b1c4062 | 3080 | fs = ZFSTest.pool.makeName(b"fs2") |
6abf9225 AG |
3081 | with self.assertRaises(lzc_exc.NotClone): |
3082 | lzc.lzc_promote(fs) | |
3083 | ||
3084 | @unittest.skipIf(*illumos_bug_6379()) | |
3085 | def test_hold_bad_fd(self): | |
3086 | snap = ZFSTest.pool.getRoot().getSnap() | |
3087 | lzc.lzc_snapshot([snap]) | |
3088 | ||
3089 | with tempfile.TemporaryFile() as tmp: | |
3090 | bad_fd = tmp.fileno() | |
3091 | ||
3092 | with self.assertRaises(lzc_exc.BadHoldCleanupFD): | |
4b1c4062 | 3093 | lzc.lzc_hold({snap: b'tag'}, bad_fd) |
6abf9225 AG |
3094 | |
3095 | @unittest.skipIf(*illumos_bug_6379()) | |
3096 | def test_hold_bad_fd_2(self): | |
3097 | snap = ZFSTest.pool.getRoot().getSnap() | |
3098 | lzc.lzc_snapshot([snap]) | |
3099 | ||
3100 | with self.assertRaises(lzc_exc.BadHoldCleanupFD): | |
4b1c4062 | 3101 | lzc.lzc_hold({snap: b'tag'}, -2) |
6abf9225 AG |
3102 | |
3103 | @unittest.skipIf(*illumos_bug_6379()) | |
3104 | def test_hold_bad_fd_3(self): | |
3105 | snap = ZFSTest.pool.getRoot().getSnap() | |
3106 | lzc.lzc_snapshot([snap]) | |
3107 | ||
3108 | (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) | |
3109 | bad_fd = hard + 1 | |
3110 | with self.assertRaises(lzc_exc.BadHoldCleanupFD): | |
4b1c4062 | 3111 | lzc.lzc_hold({snap: b'tag'}, bad_fd) |
6abf9225 AG |
3112 | |
3113 | @unittest.skipIf(*illumos_bug_6379()) | |
3114 | def test_hold_wrong_fd(self): | |
3115 | snap = ZFSTest.pool.getRoot().getSnap() | |
3116 | lzc.lzc_snapshot([snap]) | |
3117 | ||
3118 | with tempfile.TemporaryFile() as tmp: | |
3119 | fd = tmp.fileno() | |
3120 | with self.assertRaises(lzc_exc.BadHoldCleanupFD): | |
4b1c4062 | 3121 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3122 | |
3123 | def test_hold_fd(self): | |
3124 | snap = ZFSTest.pool.getRoot().getSnap() | |
3125 | lzc.lzc_snapshot([snap]) | |
3126 | ||
3127 | with cleanup_fd() as fd: | |
4b1c4062 | 3128 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3129 | |
3130 | def test_hold_empty(self): | |
3131 | with cleanup_fd() as fd: | |
3132 | lzc.lzc_hold({}, fd) | |
3133 | ||
3134 | def test_hold_empty_2(self): | |
3135 | lzc.lzc_hold({}) | |
3136 | ||
3137 | def test_hold_vs_snap_destroy(self): | |
3138 | snap = ZFSTest.pool.getRoot().getSnap() | |
3139 | lzc.lzc_snapshot([snap]) | |
3140 | ||
3141 | with cleanup_fd() as fd: | |
4b1c4062 | 3142 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3143 | |
3144 | with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: | |
3145 | lzc.lzc_destroy_snaps([snap], defer=False) | |
3146 | for e in ctx.exception.errors: | |
3147 | self.assertIsInstance(e, lzc_exc.SnapshotIsHeld) | |
3148 | ||
3149 | lzc.lzc_destroy_snaps([snap], defer=True) | |
3150 | self.assertExists(snap) | |
3151 | ||
3152 | # after automatic hold cleanup and deferred destruction | |
3153 | self.assertNotExists(snap) | |
3154 | ||
3155 | def test_hold_many_tags(self): | |
3156 | snap = ZFSTest.pool.getRoot().getSnap() | |
3157 | lzc.lzc_snapshot([snap]) | |
3158 | ||
3159 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3160 | lzc.lzc_hold({snap: b'tag1'}, fd) |
3161 | lzc.lzc_hold({snap: b'tag2'}, fd) | |
6abf9225 AG |
3162 | |
3163 | def test_hold_many_snaps(self): | |
3164 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3165 | snap2 = ZFSTest.pool.getRoot().getSnap() | |
3166 | lzc.lzc_snapshot([snap1]) | |
3167 | lzc.lzc_snapshot([snap2]) | |
3168 | ||
3169 | with cleanup_fd() as fd: | |
4b1c4062 | 3170 | lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) |
6abf9225 AG |
3171 | |
3172 | def test_hold_many_with_one_missing(self): | |
3173 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3174 | snap2 = ZFSTest.pool.getRoot().getSnap() | |
3175 | lzc.lzc_snapshot([snap1]) | |
3176 | ||
3177 | with cleanup_fd() as fd: | |
4b1c4062 | 3178 | missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) |
6abf9225 AG |
3179 | self.assertEqual(len(missing), 1) |
3180 | self.assertEqual(missing[0], snap2) | |
3181 | ||
3182 | def test_hold_many_with_all_missing(self): | |
3183 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3184 | snap2 = ZFSTest.pool.getRoot().getSnap() | |
3185 | ||
3186 | with cleanup_fd() as fd: | |
4b1c4062 | 3187 | missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) |
6abf9225 AG |
3188 | self.assertEqual(len(missing), 2) |
3189 | self.assertEqual(sorted(missing), sorted([snap1, snap2])) | |
3190 | ||
6abf9225 AG |
3191 | def test_hold_missing_fs(self): |
3192 | # XXX skip pre-created filesystems | |
3193 | ZFSTest.pool.getRoot().getFilesystem() | |
3194 | ZFSTest.pool.getRoot().getFilesystem() | |
3195 | ZFSTest.pool.getRoot().getFilesystem() | |
3196 | ZFSTest.pool.getRoot().getFilesystem() | |
3197 | ZFSTest.pool.getRoot().getFilesystem() | |
3198 | snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() | |
3199 | ||
4b1c4062 | 3200 | snaps = lzc.lzc_hold({snap: b'tag'}) |
9de8c0cd | 3201 | self.assertEqual([snap], snaps) |
6abf9225 | 3202 | |
6abf9225 AG |
3203 | def test_hold_missing_fs_auto_cleanup(self): |
3204 | # XXX skip pre-created filesystems | |
3205 | ZFSTest.pool.getRoot().getFilesystem() | |
3206 | ZFSTest.pool.getRoot().getFilesystem() | |
3207 | ZFSTest.pool.getRoot().getFilesystem() | |
3208 | ZFSTest.pool.getRoot().getFilesystem() | |
3209 | ZFSTest.pool.getRoot().getFilesystem() | |
3210 | snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() | |
3211 | ||
3212 | with cleanup_fd() as fd: | |
4b1c4062 | 3213 | snaps = lzc.lzc_hold({snap: b'tag'}, fd) |
9de8c0cd | 3214 | self.assertEqual([snap], snaps) |
6abf9225 AG |
3215 | |
3216 | def test_hold_duplicate(self): | |
3217 | snap = ZFSTest.pool.getRoot().getSnap() | |
3218 | lzc.lzc_snapshot([snap]) | |
3219 | ||
3220 | with cleanup_fd() as fd: | |
4b1c4062 | 3221 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 | 3222 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: |
4b1c4062 | 3223 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3224 | for e in ctx.exception.errors: |
3225 | self.assertIsInstance(e, lzc_exc.HoldExists) | |
3226 | ||
3227 | def test_hold_across_pools(self): | |
3228 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3229 | snap2 = ZFSTest.misc_pool.getRoot().getSnap() | |
3230 | lzc.lzc_snapshot([snap1]) | |
3231 | lzc.lzc_snapshot([snap2]) | |
3232 | ||
3233 | with cleanup_fd() as fd: | |
3234 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
4b1c4062 | 3235 | lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) |
6abf9225 AG |
3236 | for e in ctx.exception.errors: |
3237 | self.assertIsInstance(e, lzc_exc.PoolsDiffer) | |
3238 | ||
3239 | def test_hold_too_long_tag(self): | |
3240 | snap = ZFSTest.pool.getRoot().getSnap() | |
4b1c4062 | 3241 | tag = b't' * 256 |
6abf9225 AG |
3242 | lzc.lzc_snapshot([snap]) |
3243 | ||
3244 | with cleanup_fd() as fd: | |
3245 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
3246 | lzc.lzc_hold({snap: tag}, fd) | |
3247 | for e in ctx.exception.errors: | |
3248 | self.assertIsInstance(e, lzc_exc.NameTooLong) | |
9de8c0cd | 3249 | self.assertEqual(e.name, tag) |
6abf9225 AG |
3250 | |
3251 | # Apparently the full snapshot name is not checked for length | |
3252 | # and this snapshot is treated as simply missing. | |
3253 | @unittest.expectedFailure | |
3254 | def test_hold_too_long_snap_name(self): | |
3255 | snap = ZFSTest.pool.getRoot().getTooLongSnap(False) | |
3256 | with cleanup_fd() as fd: | |
3257 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
4b1c4062 | 3258 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3259 | for e in ctx.exception.errors: |
3260 | self.assertIsInstance(e, lzc_exc.NameTooLong) | |
9de8c0cd | 3261 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3262 | |
3263 | def test_hold_too_long_snap_name_2(self): | |
3264 | snap = ZFSTest.pool.getRoot().getTooLongSnap(True) | |
3265 | with cleanup_fd() as fd: | |
3266 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
4b1c4062 | 3267 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3268 | for e in ctx.exception.errors: |
3269 | self.assertIsInstance(e, lzc_exc.NameTooLong) | |
9de8c0cd | 3270 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3271 | |
3272 | def test_hold_invalid_snap_name(self): | |
4b1c4062 | 3273 | snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' |
6abf9225 AG |
3274 | with cleanup_fd() as fd: |
3275 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
4b1c4062 | 3276 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3277 | for e in ctx.exception.errors: |
3278 | self.assertIsInstance(e, lzc_exc.NameInvalid) | |
9de8c0cd | 3279 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3280 | |
3281 | def test_hold_invalid_snap_name_2(self): | |
3282 | snap = ZFSTest.pool.getRoot().getFilesystem().getName() | |
3283 | with cleanup_fd() as fd: | |
3284 | with self.assertRaises(lzc_exc.HoldFailure) as ctx: | |
4b1c4062 | 3285 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3286 | for e in ctx.exception.errors: |
3287 | self.assertIsInstance(e, lzc_exc.NameInvalid) | |
9de8c0cd | 3288 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3289 | |
3290 | def test_get_holds(self): | |
3291 | snap = ZFSTest.pool.getRoot().getSnap() | |
3292 | lzc.lzc_snapshot([snap]) | |
3293 | ||
3294 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3295 | lzc.lzc_hold({snap: b'tag1'}, fd) |
3296 | lzc.lzc_hold({snap: b'tag2'}, fd) | |
6abf9225 AG |
3297 | |
3298 | holds = lzc.lzc_get_holds(snap) | |
9de8c0cd | 3299 | self.assertEqual(len(holds), 2) |
4b1c4062 BB |
3300 | self.assertIn(b'tag1', holds) |
3301 | self.assertIn(b'tag2', holds) | |
3302 | self.assertIsInstance(holds[b'tag1'], (int, int)) | |
6abf9225 AG |
3303 | |
3304 | def test_get_holds_after_auto_cleanup(self): | |
3305 | snap = ZFSTest.pool.getRoot().getSnap() | |
3306 | lzc.lzc_snapshot([snap]) | |
3307 | ||
3308 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3309 | lzc.lzc_hold({snap: b'tag1'}, fd) |
3310 | lzc.lzc_hold({snap: b'tag2'}, fd) | |
6abf9225 AG |
3311 | |
3312 | holds = lzc.lzc_get_holds(snap) | |
9de8c0cd | 3313 | self.assertEqual(len(holds), 0) |
6abf9225 AG |
3314 | self.assertIsInstance(holds, dict) |
3315 | ||
3316 | def test_get_holds_nonexistent_snap(self): | |
3317 | snap = ZFSTest.pool.getRoot().getSnap() | |
3318 | with self.assertRaises(lzc_exc.SnapshotNotFound): | |
3319 | lzc.lzc_get_holds(snap) | |
3320 | ||
3321 | def test_get_holds_too_long_snap_name(self): | |
3322 | snap = ZFSTest.pool.getRoot().getTooLongSnap(False) | |
3323 | with self.assertRaises(lzc_exc.NameTooLong): | |
3324 | lzc.lzc_get_holds(snap) | |
3325 | ||
3326 | def test_get_holds_too_long_snap_name_2(self): | |
3327 | snap = ZFSTest.pool.getRoot().getTooLongSnap(True) | |
3328 | with self.assertRaises(lzc_exc.NameTooLong): | |
3329 | lzc.lzc_get_holds(snap) | |
3330 | ||
3331 | def test_get_holds_invalid_snap_name(self): | |
4b1c4062 | 3332 | snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' |
6abf9225 AG |
3333 | with self.assertRaises(lzc_exc.NameInvalid): |
3334 | lzc.lzc_get_holds(snap) | |
3335 | ||
3336 | # A filesystem-like snapshot name is not recognized as | |
3337 | # an invalid name. | |
3338 | @unittest.expectedFailure | |
3339 | def test_get_holds_invalid_snap_name_2(self): | |
3340 | snap = ZFSTest.pool.getRoot().getFilesystem().getName() | |
3341 | with self.assertRaises(lzc_exc.NameInvalid): | |
3342 | lzc.lzc_get_holds(snap) | |
3343 | ||
3344 | def test_release_hold(self): | |
3345 | snap = ZFSTest.pool.getRoot().getSnap() | |
3346 | lzc.lzc_snapshot([snap]) | |
3347 | ||
4b1c4062 BB |
3348 | lzc.lzc_hold({snap: b'tag'}) |
3349 | ret = lzc.lzc_release({snap: [b'tag']}) | |
9de8c0cd | 3350 | self.assertEqual(len(ret), 0) |
6abf9225 AG |
3351 | |
3352 | def test_release_hold_empty(self): | |
3353 | ret = lzc.lzc_release({}) | |
9de8c0cd | 3354 | self.assertEqual(len(ret), 0) |
6abf9225 AG |
3355 | |
3356 | def test_release_hold_complex(self): | |
3357 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3358 | snap2 = ZFSTest.pool.getRoot().getSnap() | |
3359 | snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap() | |
3360 | lzc.lzc_snapshot([snap1]) | |
3361 | lzc.lzc_snapshot([snap2, snap3]) | |
3362 | ||
4b1c4062 BB |
3363 | lzc.lzc_hold({snap1: b'tag1'}) |
3364 | lzc.lzc_hold({snap1: b'tag2'}) | |
3365 | lzc.lzc_hold({snap2: b'tag'}) | |
3366 | lzc.lzc_hold({snap3: b'tag1'}) | |
3367 | lzc.lzc_hold({snap3: b'tag2'}) | |
6abf9225 AG |
3368 | |
3369 | holds = lzc.lzc_get_holds(snap1) | |
9de8c0cd | 3370 | self.assertEqual(len(holds), 2) |
6abf9225 | 3371 | holds = lzc.lzc_get_holds(snap2) |
9de8c0cd | 3372 | self.assertEqual(len(holds), 1) |
6abf9225 | 3373 | holds = lzc.lzc_get_holds(snap3) |
9de8c0cd | 3374 | self.assertEqual(len(holds), 2) |
6abf9225 AG |
3375 | |
3376 | release = { | |
4b1c4062 BB |
3377 | snap1: [b'tag1', b'tag2'], |
3378 | snap2: [b'tag'], | |
3379 | snap3: [b'tag2'], | |
6abf9225 AG |
3380 | } |
3381 | ret = lzc.lzc_release(release) | |
9de8c0cd | 3382 | self.assertEqual(len(ret), 0) |
6abf9225 AG |
3383 | |
3384 | holds = lzc.lzc_get_holds(snap1) | |
9de8c0cd | 3385 | self.assertEqual(len(holds), 0) |
6abf9225 | 3386 | holds = lzc.lzc_get_holds(snap2) |
9de8c0cd | 3387 | self.assertEqual(len(holds), 0) |
6abf9225 | 3388 | holds = lzc.lzc_get_holds(snap3) |
9de8c0cd | 3389 | self.assertEqual(len(holds), 1) |
6abf9225 | 3390 | |
4b1c4062 | 3391 | ret = lzc.lzc_release({snap3: [b'tag1']}) |
9de8c0cd | 3392 | self.assertEqual(len(ret), 0) |
6abf9225 | 3393 | holds = lzc.lzc_get_holds(snap3) |
9de8c0cd | 3394 | self.assertEqual(len(holds), 0) |
6abf9225 AG |
3395 | |
3396 | def test_release_hold_before_auto_cleanup(self): | |
3397 | snap = ZFSTest.pool.getRoot().getSnap() | |
3398 | lzc.lzc_snapshot([snap]) | |
3399 | ||
3400 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3401 | lzc.lzc_hold({snap: b'tag'}, fd) |
3402 | ret = lzc.lzc_release({snap: [b'tag']}) | |
9de8c0cd | 3403 | self.assertEqual(len(ret), 0) |
6abf9225 AG |
3404 | |
3405 | def test_release_hold_and_snap_destruction(self): | |
3406 | snap = ZFSTest.pool.getRoot().getSnap() | |
3407 | lzc.lzc_snapshot([snap]) | |
3408 | ||
3409 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3410 | lzc.lzc_hold({snap: b'tag1'}, fd) |
3411 | lzc.lzc_hold({snap: b'tag2'}, fd) | |
6abf9225 AG |
3412 | |
3413 | lzc.lzc_destroy_snaps([snap], defer=True) | |
3414 | self.assertExists(snap) | |
3415 | ||
4b1c4062 | 3416 | lzc.lzc_release({snap: [b'tag1']}) |
6abf9225 AG |
3417 | self.assertExists(snap) |
3418 | ||
4b1c4062 | 3419 | lzc.lzc_release({snap: [b'tag2']}) |
6abf9225 AG |
3420 | self.assertNotExists(snap) |
3421 | ||
3422 | def test_release_hold_and_multiple_snap_destruction(self): | |
3423 | snap = ZFSTest.pool.getRoot().getSnap() | |
3424 | lzc.lzc_snapshot([snap]) | |
3425 | ||
3426 | with cleanup_fd() as fd: | |
4b1c4062 | 3427 | lzc.lzc_hold({snap: b'tag'}, fd) |
6abf9225 AG |
3428 | |
3429 | lzc.lzc_destroy_snaps([snap], defer=True) | |
3430 | self.assertExists(snap) | |
3431 | ||
3432 | lzc.lzc_destroy_snaps([snap], defer=True) | |
3433 | self.assertExists(snap) | |
3434 | ||
4b1c4062 | 3435 | lzc.lzc_release({snap: [b'tag']}) |
6abf9225 AG |
3436 | self.assertNotExists(snap) |
3437 | ||
3438 | def test_release_hold_missing_tag(self): | |
3439 | snap = ZFSTest.pool.getRoot().getSnap() | |
3440 | lzc.lzc_snapshot([snap]) | |
3441 | ||
4b1c4062 | 3442 | ret = lzc.lzc_release({snap: [b'tag']}) |
9de8c0cd | 3443 | self.assertEqual(len(ret), 1) |
4b1c4062 | 3444 | self.assertEqual(ret[0], snap + b'#tag') |
6abf9225 AG |
3445 | |
3446 | def test_release_hold_missing_snap(self): | |
3447 | snap = ZFSTest.pool.getRoot().getSnap() | |
3448 | ||
4b1c4062 | 3449 | ret = lzc.lzc_release({snap: [b'tag']}) |
9de8c0cd AR |
3450 | self.assertEqual(len(ret), 1) |
3451 | self.assertEqual(ret[0], snap) | |
6abf9225 AG |
3452 | |
3453 | def test_release_hold_missing_snap_2(self): | |
3454 | snap = ZFSTest.pool.getRoot().getSnap() | |
3455 | ||
4b1c4062 | 3456 | ret = lzc.lzc_release({snap: [b'tag', b'another']}) |
9de8c0cd AR |
3457 | self.assertEqual(len(ret), 1) |
3458 | self.assertEqual(ret[0], snap) | |
6abf9225 AG |
3459 | |
3460 | def test_release_hold_across_pools(self): | |
3461 | snap1 = ZFSTest.pool.getRoot().getSnap() | |
3462 | snap2 = ZFSTest.misc_pool.getRoot().getSnap() | |
3463 | lzc.lzc_snapshot([snap1]) | |
3464 | lzc.lzc_snapshot([snap2]) | |
3465 | ||
3466 | with cleanup_fd() as fd: | |
4b1c4062 BB |
3467 | lzc.lzc_hold({snap1: b'tag'}, fd) |
3468 | lzc.lzc_hold({snap2: b'tag'}, fd) | |
6abf9225 | 3469 | with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: |
4b1c4062 | 3470 | lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']}) |
6abf9225 AG |
3471 | for e in ctx.exception.errors: |
3472 | self.assertIsInstance(e, lzc_exc.PoolsDiffer) | |
3473 | ||
3474 | # Apparently the tag name is not verified, | |
3475 | # only its existence is checked. | |
3476 | @unittest.expectedFailure | |
3477 | def test_release_hold_too_long_tag(self): | |
3478 | snap = ZFSTest.pool.getRoot().getSnap() | |
4b1c4062 | 3479 | tag = b't' * 256 |
6abf9225 AG |
3480 | lzc.lzc_snapshot([snap]) |
3481 | ||
3482 | with self.assertRaises(lzc_exc.HoldReleaseFailure): | |
3483 | lzc.lzc_release({snap: [tag]}) | |
3484 | ||
3485 | # Apparently the full snapshot name is not checked for length | |
3486 | # and this snapshot is treated as simply missing. | |
3487 | @unittest.expectedFailure | |
3488 | def test_release_hold_too_long_snap_name(self): | |
3489 | snap = ZFSTest.pool.getRoot().getTooLongSnap(False) | |
3490 | ||
3491 | with self.assertRaises(lzc_exc.HoldReleaseFailure): | |
4b1c4062 | 3492 | lzc.lzc_release({snap: [b'tag']}) |
6abf9225 AG |
3493 | |
3494 | def test_release_hold_too_long_snap_name_2(self): | |
3495 | snap = ZFSTest.pool.getRoot().getTooLongSnap(True) | |
3496 | with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: | |
4b1c4062 | 3497 | lzc.lzc_release({snap: [b'tag']}) |
6abf9225 AG |
3498 | for e in ctx.exception.errors: |
3499 | self.assertIsInstance(e, lzc_exc.NameTooLong) | |
9de8c0cd | 3500 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3501 | |
3502 | def test_release_hold_invalid_snap_name(self): | |
4b1c4062 | 3503 | snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' |
6abf9225 | 3504 | with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: |
4b1c4062 | 3505 | lzc.lzc_release({snap: [b'tag']}) |
6abf9225 AG |
3506 | for e in ctx.exception.errors: |
3507 | self.assertIsInstance(e, lzc_exc.NameInvalid) | |
9de8c0cd | 3508 | self.assertEqual(e.name, snap) |
6abf9225 AG |
3509 | |
3510 | def test_release_hold_invalid_snap_name_2(self): | |
3511 | snap = ZFSTest.pool.getRoot().getFilesystem().getName() | |
3512 | with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: | |
4b1c4062 | 3513 | lzc.lzc_release({snap: [b'tag']}) |
6abf9225 AG |
3514 | for e in ctx.exception.errors: |
3515 | self.assertIsInstance(e, lzc_exc.NameInvalid) | |
9de8c0cd | 3516 | self.assertEqual(e.name, snap) |
6abf9225 | 3517 | |
85ce3f4f | 3518 | def test_sync_missing_pool(self): |
4b1c4062 | 3519 | pool = b"nonexistent" |
85ce3f4f | 3520 | with self.assertRaises(lzc_exc.PoolNotFound): |
3521 | lzc.lzc_sync(pool) | |
3522 | ||
3523 | def test_sync_pool_forced(self): | |
3524 | pool = ZFSTest.pool.getRoot().getName() | |
3525 | lzc.lzc_sync(pool, True) | |
3526 | ||
3527 | def test_reopen_missing_pool(self): | |
4b1c4062 | 3528 | pool = b"nonexistent" |
85ce3f4f | 3529 | with self.assertRaises(lzc_exc.PoolNotFound): |
3530 | lzc.lzc_reopen(pool) | |
3531 | ||
3532 | def test_reopen_pool_no_restart(self): | |
3533 | pool = ZFSTest.pool.getRoot().getName() | |
3534 | lzc.lzc_reopen(pool, False) | |
3535 | ||
3536 | def test_channel_program_missing_pool(self): | |
4b1c4062 | 3537 | pool = b"nonexistent" |
85ce3f4f | 3538 | with self.assertRaises(lzc_exc.PoolNotFound): |
4b1c4062 | 3539 | lzc.lzc_channel_program(pool, b"return {}") |
85ce3f4f | 3540 | |
3541 | def test_channel_program_timeout(self): | |
3542 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 | 3543 | zcp = b""" |
85ce3f4f | 3544 | for i = 1,10000 do |
4b1c4062 | 3545 | zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) |
85ce3f4f | 3546 | end |
3547 | """ | |
3548 | with self.assertRaises(lzc_exc.ZCPTimeout): | |
3549 | lzc.lzc_channel_program(pool, zcp, instrlimit=1) | |
3550 | ||
3551 | def test_channel_program_memory_limit(self): | |
3552 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 | 3553 | zcp = b""" |
85ce3f4f | 3554 | for i = 1,10000 do |
4b1c4062 | 3555 | zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) |
85ce3f4f | 3556 | end |
3557 | """ | |
3558 | with self.assertRaises(lzc_exc.ZCPSpaceError): | |
3559 | lzc.lzc_channel_program(pool, zcp, memlimit=1) | |
3560 | ||
3561 | def test_channel_program_invalid_limits(self): | |
3562 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 | 3563 | zcp = b""" |
85ce3f4f | 3564 | return {} |
3565 | """ | |
3566 | with self.assertRaises(lzc_exc.ZCPLimitInvalid): | |
3567 | lzc.lzc_channel_program(pool, zcp, instrlimit=0) | |
3568 | with self.assertRaises(lzc_exc.ZCPLimitInvalid): | |
3569 | lzc.lzc_channel_program(pool, zcp, memlimit=0) | |
3570 | ||
3571 | def test_channel_program_syntax_error(self): | |
3572 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 | 3573 | zcp = b""" |
85ce3f4f | 3574 | inv+val:id |
3575 | """ | |
3576 | with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx: | |
3577 | lzc.lzc_channel_program(pool, zcp) | |
4b1c4062 | 3578 | self.assertTrue(b"syntax error" in ctx.exception.details) |
85ce3f4f | 3579 | |
3580 | def test_channel_program_sync_snapshot(self): | |
3581 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 BB |
3582 | snapname = ZFSTest.pool.makeName(b"@zcp") |
3583 | zcp = b""" | |
3584 | zfs.sync.snapshot('""" + snapname + b"""') | |
85ce3f4f | 3585 | """ |
3586 | lzc.lzc_channel_program(pool, zcp) | |
3587 | self.assertExists(snapname) | |
3588 | ||
3589 | def test_channel_program_runtime_error(self): | |
3590 | pool = ZFSTest.pool.getRoot().getName() | |
3591 | ||
3592 | # failing an assertion raises a runtime error | |
3593 | with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: | |
4b1c4062 | 3594 | lzc.lzc_channel_program(pool, b"assert(1 == 2)") |
85ce3f4f | 3595 | self.assertTrue( |
4b1c4062 | 3596 | b"assertion failed" in ctx.exception.details) |
85ce3f4f | 3597 | # invoking the error() function raises a runtime error |
3598 | with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: | |
4b1c4062 | 3599 | lzc.lzc_channel_program(pool, b"error()") |
85ce3f4f | 3600 | |
3601 | def test_channel_program_nosync_runtime_error(self): | |
3602 | pool = ZFSTest.pool.getRoot().getName() | |
4b1c4062 BB |
3603 | zcp = b""" |
3604 | zfs.sync.snapshot('""" + pool + b"""@zcp') | |
85ce3f4f | 3605 | """ |
3606 | # lzc_channel_program_nosync() allows only "read-only" operations | |
3607 | with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: | |
3608 | lzc.lzc_channel_program_nosync(pool, zcp) | |
3609 | self.assertTrue( | |
4b1c4062 | 3610 | b"running functions from the zfs.sync" in ctx.exception.details) |
85ce3f4f | 3611 | |
3612 | def test_change_key_new(self): | |
3613 | with encrypted_filesystem() as (fs, _): | |
3614 | lzc.lzc_change_key( | |
3615 | fs, 'new_key', | |
4b1c4062 | 3616 | props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, |
85ce3f4f | 3617 | key=os.urandom(lzc.WRAPPING_KEY_LEN)) |
3618 | ||
3619 | def test_change_key_missing_fs(self): | |
4b1c4062 | 3620 | name = b"nonexistent" |
85ce3f4f | 3621 | |
3622 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3623 | lzc.lzc_change_key( | |
3624 | name, 'new_key', | |
4b1c4062 | 3625 | props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, |
85ce3f4f | 3626 | key=os.urandom(lzc.WRAPPING_KEY_LEN)) |
3627 | ||
3628 | def test_change_key_not_loaded(self): | |
3629 | with encrypted_filesystem() as (fs, _): | |
3630 | lzc.lzc_unload_key(fs) | |
3631 | with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): | |
3632 | lzc.lzc_change_key( | |
3633 | fs, 'new_key', | |
4b1c4062 | 3634 | props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, |
85ce3f4f | 3635 | key=os.urandom(lzc.WRAPPING_KEY_LEN)) |
3636 | ||
3637 | def test_change_key_invalid_property(self): | |
3638 | with encrypted_filesystem() as (fs, _): | |
3639 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4b1c4062 | 3640 | lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"}) |
85ce3f4f | 3641 | |
3642 | def test_change_key_invalid_crypt_command(self): | |
3643 | with encrypted_filesystem() as (fs, _): | |
3644 | with self.assertRaises(lzc_exc.UnknownCryptCommand): | |
3645 | lzc.lzc_change_key(fs, 'duplicate_key') | |
3646 | ||
3647 | def test_load_key(self): | |
3648 | with encrypted_filesystem() as (fs, key): | |
3649 | lzc.lzc_unload_key(fs) | |
3650 | lzc.lzc_load_key(fs, False, key) | |
3651 | ||
3652 | def test_load_key_invalid(self): | |
3653 | with encrypted_filesystem() as (fs, key): | |
3654 | lzc.lzc_unload_key(fs) | |
3655 | with self.assertRaises(lzc_exc.EncryptionKeyInvalid): | |
3656 | lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN)) | |
3657 | ||
3658 | def test_load_key_already_loaded(self): | |
3659 | with encrypted_filesystem() as (fs, key): | |
3660 | lzc.lzc_unload_key(fs) | |
3661 | lzc.lzc_load_key(fs, False, key) | |
3662 | with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded): | |
3663 | lzc.lzc_load_key(fs, False, key) | |
3664 | ||
3665 | def test_load_key_missing_fs(self): | |
4b1c4062 | 3666 | name = b"nonexistent" |
85ce3f4f | 3667 | |
3668 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3669 | lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN)) | |
3670 | ||
3671 | def test_unload_key(self): | |
3672 | with encrypted_filesystem() as (fs, _): | |
3673 | lzc.lzc_unload_key(fs) | |
3674 | ||
3675 | def test_unload_key_missing_fs(self): | |
4b1c4062 | 3676 | name = b"nonexistent" |
85ce3f4f | 3677 | |
3678 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3679 | lzc.lzc_unload_key(name) | |
3680 | ||
3681 | def test_unload_key_busy(self): | |
3682 | with encrypted_filesystem() as (fs, _): | |
3683 | with zfs_mount(fs): | |
3684 | with self.assertRaises(lzc_exc.DatasetBusy): | |
3685 | lzc.lzc_unload_key(fs) | |
3686 | ||
3687 | def test_unload_key_not_loaded(self): | |
3688 | with encrypted_filesystem() as (fs, _): | |
3689 | lzc.lzc_unload_key(fs) | |
3690 | with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): | |
3691 | lzc.lzc_unload_key(fs) | |
3692 | ||
c962fd6c | 3693 | def test_checkpoint(self): |
3694 | pool = ZFSTest.pool.getRoot().getName() | |
3695 | ||
3696 | lzc.lzc_pool_checkpoint(pool) | |
3697 | ||
3698 | def test_checkpoint_missing_pool(self): | |
4b1c4062 | 3699 | pool = b"nonexistent" |
c962fd6c | 3700 | |
3701 | with self.assertRaises(lzc_exc.PoolNotFound): | |
3702 | lzc.lzc_pool_checkpoint(pool) | |
3703 | ||
3704 | def test_checkpoint_already_exists(self): | |
3705 | pool = ZFSTest.pool.getRoot().getName() | |
3706 | ||
3707 | lzc.lzc_pool_checkpoint(pool) | |
3708 | with self.assertRaises(lzc_exc.CheckpointExists): | |
3709 | lzc.lzc_pool_checkpoint(pool) | |
3710 | ||
3711 | def test_checkpoint_discard(self): | |
3712 | pool = ZFSTest.pool.getRoot().getName() | |
3713 | ||
3714 | lzc.lzc_pool_checkpoint(pool) | |
3715 | lzc.lzc_pool_checkpoint_discard(pool) | |
3716 | ||
3717 | def test_checkpoint_discard_missing_pool(self): | |
4b1c4062 | 3718 | pool = b"nonexistent" |
c962fd6c | 3719 | |
3720 | with self.assertRaises(lzc_exc.PoolNotFound): | |
3721 | lzc.lzc_pool_checkpoint_discard(pool) | |
3722 | ||
3723 | def test_checkpoint_discard_missing_checkpoint(self): | |
3724 | pool = ZFSTest.pool.getRoot().getName() | |
3725 | ||
3726 | with self.assertRaises(lzc_exc.CheckpointNotFound): | |
3727 | lzc.lzc_pool_checkpoint_discard(pool) | |
3728 | ||
6abf9225 AG |
3729 | @needs_support(lzc.lzc_list_children) |
3730 | def test_list_children(self): | |
4b1c4062 BB |
3731 | name = ZFSTest.pool.makeName(b"fs1/fs") |
3732 | names = [ZFSTest.pool.makeName(b"fs1/fs/test1"), | |
3733 | ZFSTest.pool.makeName(b"fs1/fs/test2"), | |
3734 | ZFSTest.pool.makeName(b"fs1/fs/test3"), ] | |
6abf9225 | 3735 | # and one snap to see that it is not listed |
4b1c4062 | 3736 | snap = ZFSTest.pool.makeName(b"fs1/fs@test") |
6abf9225 AG |
3737 | |
3738 | for fs in names: | |
3739 | lzc.lzc_create(fs) | |
3740 | lzc.lzc_snapshot([snap]) | |
3741 | ||
3742 | children = list(lzc.lzc_list_children(name)) | |
3743 | self.assertItemsEqual(children, names) | |
3744 | ||
3745 | @needs_support(lzc.lzc_list_children) | |
3746 | def test_list_children_nonexistent(self): | |
4b1c4062 | 3747 | fs = ZFSTest.pool.makeName(b"nonexistent") |
6abf9225 AG |
3748 | |
3749 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
3750 | list(lzc.lzc_list_children(fs)) | |
3751 | ||
3752 | @needs_support(lzc.lzc_list_children) | |
3753 | def test_list_children_of_snap(self): | |
4b1c4062 | 3754 | snap = ZFSTest.pool.makeName(b"@newsnap") |
6abf9225 AG |
3755 | |
3756 | lzc.lzc_snapshot([snap]) | |
3757 | children = list(lzc.lzc_list_children(snap)) | |
3758 | self.assertEqual(children, []) | |
3759 | ||
3760 | @needs_support(lzc.lzc_list_snaps) | |
3761 | def test_list_snaps(self): | |
4b1c4062 BB |
3762 | name = ZFSTest.pool.makeName(b"fs1/fs") |
3763 | names = [ZFSTest.pool.makeName(b"fs1/fs@test1"), | |
3764 | ZFSTest.pool.makeName(b"fs1/fs@test2"), | |
3765 | ZFSTest.pool.makeName(b"fs1/fs@test3"), ] | |
6abf9225 | 3766 | # and one filesystem to see that it is not listed |
4b1c4062 | 3767 | fs = ZFSTest.pool.makeName(b"fs1/fs/test") |
6abf9225 AG |
3768 | |
3769 | for snap in names: | |
3770 | lzc.lzc_snapshot([snap]) | |
3771 | lzc.lzc_create(fs) | |
3772 | ||
3773 | snaps = list(lzc.lzc_list_snaps(name)) | |
3774 | self.assertItemsEqual(snaps, names) | |
3775 | ||
3776 | @needs_support(lzc.lzc_list_snaps) | |
3777 | def test_list_snaps_nonexistent(self): | |
4b1c4062 | 3778 | fs = ZFSTest.pool.makeName(b"nonexistent") |
6abf9225 AG |
3779 | |
3780 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
3781 | list(lzc.lzc_list_snaps(fs)) | |
3782 | ||
3783 | @needs_support(lzc.lzc_list_snaps) | |
3784 | def test_list_snaps_of_snap(self): | |
4b1c4062 | 3785 | snap = ZFSTest.pool.makeName(b"@newsnap") |
6abf9225 AG |
3786 | |
3787 | lzc.lzc_snapshot([snap]) | |
3788 | snaps = list(lzc.lzc_list_snaps(snap)) | |
3789 | self.assertEqual(snaps, []) | |
3790 | ||
3791 | @needs_support(lzc.lzc_get_props) | |
3792 | def test_get_fs_props(self): | |
4b1c4062 BB |
3793 | fs = ZFSTest.pool.makeName(b"new") |
3794 | props = {b"user:foo": b"bar"} | |
6abf9225 AG |
3795 | |
3796 | lzc.lzc_create(fs, props=props) | |
3797 | actual_props = lzc.lzc_get_props(fs) | |
3798 | self.assertDictContainsSubset(props, actual_props) | |
3799 | ||
3800 | @needs_support(lzc.lzc_get_props) | |
3801 | def test_get_fs_props_with_child(self): | |
4b1c4062 BB |
3802 | parent = ZFSTest.pool.makeName(b"parent") |
3803 | child = ZFSTest.pool.makeName(b"parent/child") | |
3804 | parent_props = {b"user:foo": b"parent"} | |
3805 | child_props = {b"user:foo": b"child"} | |
6abf9225 AG |
3806 | |
3807 | lzc.lzc_create(parent, props=parent_props) | |
3808 | lzc.lzc_create(child, props=child_props) | |
3809 | actual_parent_props = lzc.lzc_get_props(parent) | |
3810 | actual_child_props = lzc.lzc_get_props(child) | |
3811 | self.assertDictContainsSubset(parent_props, actual_parent_props) | |
3812 | self.assertDictContainsSubset(child_props, actual_child_props) | |
3813 | ||
3814 | @needs_support(lzc.lzc_get_props) | |
3815 | def test_get_snap_props(self): | |
4b1c4062 | 3816 | snapname = ZFSTest.pool.makeName(b"@snap") |
6abf9225 | 3817 | snaps = [snapname] |
4b1c4062 | 3818 | props = {b"user:foo": b"bar"} |
6abf9225 AG |
3819 | |
3820 | lzc.lzc_snapshot(snaps, props) | |
3821 | actual_props = lzc.lzc_get_props(snapname) | |
3822 | self.assertDictContainsSubset(props, actual_props) | |
3823 | ||
3824 | @needs_support(lzc.lzc_get_props) | |
3825 | def test_get_props_nonexistent(self): | |
4b1c4062 | 3826 | fs = ZFSTest.pool.makeName(b"nonexistent") |
6abf9225 AG |
3827 | |
3828 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
3829 | lzc.lzc_get_props(fs) | |
3830 | ||
3831 | @needs_support(lzc.lzc_get_props) | |
3832 | def test_get_mountpoint_none(self): | |
3833 | ''' | |
3834 | If the *mountpoint* property is set to none, then its | |
3835 | value is returned as `bytes` "none". | |
3836 | Also, a child filesystem inherits that value. | |
3837 | ''' | |
4b1c4062 BB |
3838 | fs = ZFSTest.pool.makeName(b"new") |
3839 | child = ZFSTest.pool.makeName(b"new/child") | |
3840 | props = {b"mountpoint": b"none"} | |
6abf9225 AG |
3841 | |
3842 | lzc.lzc_create(fs, props=props) | |
3843 | lzc.lzc_create(child) | |
3844 | actual_props = lzc.lzc_get_props(fs) | |
3845 | self.assertDictContainsSubset(props, actual_props) | |
3846 | # check that mountpoint value is correctly inherited | |
3847 | child_props = lzc.lzc_get_props(child) | |
3848 | self.assertDictContainsSubset(props, child_props) | |
3849 | ||
3850 | @needs_support(lzc.lzc_get_props) | |
3851 | def test_get_mountpoint_legacy(self): | |
3852 | ''' | |
3853 | If the *mountpoint* property is set to legacy, then its | |
3854 | value is returned as `bytes` "legacy". | |
3855 | Also, a child filesystem inherits that value. | |
3856 | ''' | |
4b1c4062 BB |
3857 | fs = ZFSTest.pool.makeName(b"new") |
3858 | child = ZFSTest.pool.makeName(b"new/child") | |
3859 | props = {b"mountpoint": b"legacy"} | |
6abf9225 AG |
3860 | |
3861 | lzc.lzc_create(fs, props=props) | |
3862 | lzc.lzc_create(child) | |
3863 | actual_props = lzc.lzc_get_props(fs) | |
3864 | self.assertDictContainsSubset(props, actual_props) | |
3865 | # check that mountpoint value is correctly inherited | |
3866 | child_props = lzc.lzc_get_props(child) | |
3867 | self.assertDictContainsSubset(props, child_props) | |
3868 | ||
3869 | @needs_support(lzc.lzc_get_props) | |
3870 | def test_get_mountpoint_path(self): | |
3871 | ''' | |
3872 | If the *mountpoint* property is set to a path and the property | |
3873 | is not explicitly set on a child filesystem, then its | |
3874 | value is that of the parent filesystem with the child's | |
3875 | name appended using the '/' separator. | |
3876 | ''' | |
4b1c4062 BB |
3877 | fs = ZFSTest.pool.makeName(b"new") |
3878 | child = ZFSTest.pool.makeName(b"new/child") | |
3879 | props = {b"mountpoint": b"/mnt"} | |
6abf9225 AG |
3880 | |
3881 | lzc.lzc_create(fs, props=props) | |
3882 | lzc.lzc_create(child) | |
3883 | actual_props = lzc.lzc_get_props(fs) | |
3884 | self.assertDictContainsSubset(props, actual_props) | |
3885 | # check that mountpoint value is correctly inherited | |
3886 | child_props = lzc.lzc_get_props(child) | |
3887 | self.assertDictContainsSubset( | |
4b1c4062 | 3888 | {b"mountpoint": b"/mnt/child"}, child_props) |
6abf9225 AG |
3889 | |
3890 | @needs_support(lzc.lzc_get_props) | |
3891 | def test_get_snap_clones(self): | |
4b1c4062 BB |
3892 | fs = ZFSTest.pool.makeName(b"new") |
3893 | snap = ZFSTest.pool.makeName(b"@snap") | |
3894 | clone1 = ZFSTest.pool.makeName(b"clone1") | |
3895 | clone2 = ZFSTest.pool.makeName(b"clone2") | |
6abf9225 AG |
3896 | |
3897 | lzc.lzc_create(fs) | |
3898 | lzc.lzc_snapshot([snap]) | |
3899 | lzc.lzc_clone(clone1, snap) | |
3900 | lzc.lzc_clone(clone2, snap) | |
3901 | ||
3902 | clones_prop = lzc.lzc_get_props(snap)["clones"] | |
3903 | self.assertItemsEqual(clones_prop, [clone1, clone2]) | |
3904 | ||
3905 | @needs_support(lzc.lzc_rename) | |
3906 | def test_rename(self): | |
4b1c4062 BB |
3907 | src = ZFSTest.pool.makeName(b"source") |
3908 | tgt = ZFSTest.pool.makeName(b"target") | |
6abf9225 AG |
3909 | |
3910 | lzc.lzc_create(src) | |
3911 | lzc.lzc_rename(src, tgt) | |
3912 | self.assertNotExists(src) | |
3913 | self.assertExists(tgt) | |
3914 | ||
3915 | @needs_support(lzc.lzc_rename) | |
3916 | def test_rename_nonexistent(self): | |
4b1c4062 BB |
3917 | src = ZFSTest.pool.makeName(b"source") |
3918 | tgt = ZFSTest.pool.makeName(b"target") | |
6abf9225 AG |
3919 | |
3920 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3921 | lzc.lzc_rename(src, tgt) | |
3922 | ||
3923 | @needs_support(lzc.lzc_rename) | |
3924 | def test_rename_existing_target(self): | |
4b1c4062 BB |
3925 | src = ZFSTest.pool.makeName(b"source") |
3926 | tgt = ZFSTest.pool.makeName(b"target") | |
6abf9225 AG |
3927 | |
3928 | lzc.lzc_create(src) | |
3929 | lzc.lzc_create(tgt) | |
3930 | with self.assertRaises(lzc_exc.FilesystemExists): | |
3931 | lzc.lzc_rename(src, tgt) | |
3932 | ||
3933 | @needs_support(lzc.lzc_rename) | |
3934 | def test_rename_nonexistent_target_parent(self): | |
4b1c4062 BB |
3935 | src = ZFSTest.pool.makeName(b"source") |
3936 | tgt = ZFSTest.pool.makeName(b"parent/target") | |
6abf9225 AG |
3937 | |
3938 | lzc.lzc_create(src) | |
3939 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3940 | lzc.lzc_rename(src, tgt) | |
3941 | ||
d8d418ff | 3942 | @needs_support(lzc.lzc_rename) |
3943 | def test_rename_parent_is_zvol(self): | |
3944 | src = ZFSTest.pool.makeName(b"source") | |
3945 | zvol = ZFSTest.pool.makeName(b"parent") | |
3946 | tgt = zvol + b"/target" | |
3947 | props = {b"volsize": 1024 * 1024} | |
3948 | ||
3949 | lzc.lzc_create(src) | |
3950 | lzc.lzc_create(zvol, ds_type='zvol', props=props) | |
3951 | with self.assertRaises(lzc_exc.WrongParent): | |
3952 | lzc.lzc_rename(src, tgt) | |
3953 | ||
6abf9225 AG |
3954 | @needs_support(lzc.lzc_destroy) |
3955 | def test_destroy(self): | |
4b1c4062 | 3956 | fs = ZFSTest.pool.makeName(b"test-fs") |
6abf9225 AG |
3957 | |
3958 | lzc.lzc_create(fs) | |
3959 | lzc.lzc_destroy(fs) | |
3960 | self.assertNotExists(fs) | |
3961 | ||
3962 | @needs_support(lzc.lzc_destroy) | |
3963 | def test_destroy_nonexistent(self): | |
4b1c4062 | 3964 | fs = ZFSTest.pool.makeName(b"test-fs") |
6abf9225 AG |
3965 | |
3966 | with self.assertRaises(lzc_exc.FilesystemNotFound): | |
3967 | lzc.lzc_destroy(fs) | |
3968 | ||
3969 | @needs_support(lzc.lzc_inherit_prop) | |
3970 | def test_inherit_prop(self): | |
4b1c4062 BB |
3971 | parent = ZFSTest.pool.makeName(b"parent") |
3972 | child = ZFSTest.pool.makeName(b"parent/child") | |
3973 | the_prop = b"user:foo" | |
3974 | parent_props = {the_prop: b"parent"} | |
3975 | child_props = {the_prop: b"child"} | |
6abf9225 AG |
3976 | |
3977 | lzc.lzc_create(parent, props=parent_props) | |
3978 | lzc.lzc_create(child, props=child_props) | |
3979 | lzc.lzc_inherit_prop(child, the_prop) | |
3980 | actual_props = lzc.lzc_get_props(child) | |
3981 | self.assertDictContainsSubset(parent_props, actual_props) | |
3982 | ||
3983 | @needs_support(lzc.lzc_inherit_prop) | |
3984 | def test_inherit_missing_prop(self): | |
4b1c4062 BB |
3985 | parent = ZFSTest.pool.makeName(b"parent") |
3986 | child = ZFSTest.pool.makeName(b"parent/child") | |
6abf9225 AG |
3987 | the_prop = "user:foo" |
3988 | child_props = {the_prop: "child"} | |
3989 | ||
3990 | lzc.lzc_create(parent) | |
3991 | lzc.lzc_create(child, props=child_props) | |
3992 | lzc.lzc_inherit_prop(child, the_prop) | |
3993 | actual_props = lzc.lzc_get_props(child) | |
3994 | self.assertNotIn(the_prop, actual_props) | |
3995 | ||
3996 | @needs_support(lzc.lzc_inherit_prop) | |
3997 | def test_inherit_readonly_prop(self): | |
4b1c4062 BB |
3998 | parent = ZFSTest.pool.makeName(b"parent") |
3999 | child = ZFSTest.pool.makeName(b"parent/child") | |
4000 | the_prop = b"createtxg" | |
6abf9225 AG |
4001 | |
4002 | lzc.lzc_create(parent) | |
4003 | lzc.lzc_create(child) | |
4004 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4005 | lzc.lzc_inherit_prop(child, the_prop) | |
4006 | ||
4007 | @needs_support(lzc.lzc_inherit_prop) | |
4008 | def test_inherit_unknown_prop(self): | |
4b1c4062 BB |
4009 | parent = ZFSTest.pool.makeName(b"parent") |
4010 | child = ZFSTest.pool.makeName(b"parent/child") | |
4011 | the_prop = b"nosuchprop" | |
6abf9225 AG |
4012 | |
4013 | lzc.lzc_create(parent) | |
4014 | lzc.lzc_create(child) | |
4015 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4016 | lzc.lzc_inherit_prop(child, the_prop) | |
4017 | ||
4018 | @needs_support(lzc.lzc_inherit_prop) | |
4019 | def test_inherit_prop_on_snap(self): | |
4b1c4062 BB |
4020 | fs = ZFSTest.pool.makeName(b"new") |
4021 | snapname = ZFSTest.pool.makeName(b"new@snap") | |
4022 | prop = b"user:foo" | |
4023 | fs_val = b"fs" | |
4024 | snap_val = b"snap" | |
6abf9225 AG |
4025 | |
4026 | lzc.lzc_create(fs, props={prop: fs_val}) | |
4027 | lzc.lzc_snapshot([snapname], props={prop: snap_val}) | |
4028 | ||
4029 | actual_props = lzc.lzc_get_props(snapname) | |
4030 | self.assertDictContainsSubset({prop: snap_val}, actual_props) | |
4031 | ||
4032 | lzc.lzc_inherit_prop(snapname, prop) | |
4033 | actual_props = lzc.lzc_get_props(snapname) | |
4034 | self.assertDictContainsSubset({prop: fs_val}, actual_props) | |
4035 | ||
4036 | @needs_support(lzc.lzc_set_prop) | |
4037 | def test_set_fs_prop(self): | |
4b1c4062 BB |
4038 | fs = ZFSTest.pool.makeName(b"new") |
4039 | prop = b"user:foo" | |
4040 | val = b"bar" | |
6abf9225 AG |
4041 | |
4042 | lzc.lzc_create(fs) | |
4043 | lzc.lzc_set_prop(fs, prop, val) | |
4044 | actual_props = lzc.lzc_get_props(fs) | |
4045 | self.assertDictContainsSubset({prop: val}, actual_props) | |
4046 | ||
4047 | @needs_support(lzc.lzc_set_prop) | |
4048 | def test_set_snap_prop(self): | |
4b1c4062 BB |
4049 | snapname = ZFSTest.pool.makeName(b"@snap") |
4050 | prop = b"user:foo" | |
4051 | val = b"bar" | |
6abf9225 AG |
4052 | |
4053 | lzc.lzc_snapshot([snapname]) | |
4054 | lzc.lzc_set_prop(snapname, prop, val) | |
4055 | actual_props = lzc.lzc_get_props(snapname) | |
4056 | self.assertDictContainsSubset({prop: val}, actual_props) | |
4057 | ||
4058 | @needs_support(lzc.lzc_set_prop) | |
4059 | def test_set_prop_nonexistent(self): | |
4b1c4062 BB |
4060 | fs = ZFSTest.pool.makeName(b"nonexistent") |
4061 | prop = b"user:foo" | |
4062 | val = b"bar" | |
6abf9225 AG |
4063 | |
4064 | with self.assertRaises(lzc_exc.DatasetNotFound): | |
4065 | lzc.lzc_set_prop(fs, prop, val) | |
4066 | ||
4067 | @needs_support(lzc.lzc_set_prop) | |
4068 | def test_set_sys_prop(self): | |
4b1c4062 BB |
4069 | fs = ZFSTest.pool.makeName(b"new") |
4070 | prop = b"recordsize" | |
6abf9225 AG |
4071 | val = 4096 |
4072 | ||
4073 | lzc.lzc_create(fs) | |
4074 | lzc.lzc_set_prop(fs, prop, val) | |
4075 | actual_props = lzc.lzc_get_props(fs) | |
4076 | self.assertDictContainsSubset({prop: val}, actual_props) | |
4077 | ||
4078 | @needs_support(lzc.lzc_set_prop) | |
4079 | def test_set_invalid_prop(self): | |
4b1c4062 BB |
4080 | fs = ZFSTest.pool.makeName(b"new") |
4081 | prop = b"nosuchprop" | |
6abf9225 AG |
4082 | val = 0 |
4083 | ||
4084 | lzc.lzc_create(fs) | |
4085 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4086 | lzc.lzc_set_prop(fs, prop, val) | |
4087 | ||
4088 | @needs_support(lzc.lzc_set_prop) | |
4089 | def test_set_invalid_value_prop(self): | |
4b1c4062 BB |
4090 | fs = ZFSTest.pool.makeName(b"new") |
4091 | prop = b"atime" | |
6abf9225 AG |
4092 | val = 100 |
4093 | ||
4094 | lzc.lzc_create(fs) | |
4095 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4096 | lzc.lzc_set_prop(fs, prop, val) | |
4097 | ||
4098 | @needs_support(lzc.lzc_set_prop) | |
4099 | def test_set_invalid_value_prop_2(self): | |
4b1c4062 BB |
4100 | fs = ZFSTest.pool.makeName(b"new") |
4101 | prop = b"readonly" | |
6abf9225 AG |
4102 | val = 100 |
4103 | ||
4104 | lzc.lzc_create(fs) | |
4105 | with self.assertRaises(lzc_exc.PropertyInvalid): | |
4106 | lzc.lzc_set_prop(fs, prop, val) | |
4107 | ||
4108 | @needs_support(lzc.lzc_set_prop) | |
4109 | def test_set_prop_too_small_quota(self): | |
4b1c4062 BB |
4110 | fs = ZFSTest.pool.makeName(b"new") |
4111 | prop = b"refquota" | |
6abf9225 AG |
4112 | val = 1 |
4113 | ||
4114 | lzc.lzc_create(fs) | |
4115 | with self.assertRaises(lzc_exc.NoSpace): | |
4116 | lzc.lzc_set_prop(fs, prop, val) | |
4117 | ||
4118 | @needs_support(lzc.lzc_set_prop) | |
4119 | def test_set_readonly_prop(self): | |
4b1c4062 BB |
4120 | fs = ZFSTest.pool.makeName(b"new") |
4121 | prop = b"creation" | |
6abf9225 AG |
4122 | val = 0 |
4123 | ||
4124 | lzc.lzc_create(fs) | |
4125 | lzc.lzc_set_prop(fs, prop, val) | |
4126 | actual_props = lzc.lzc_get_props(fs) | |
4127 | # the change is silently ignored | |
4128 | self.assertTrue(actual_props[prop] != val) | |
4129 | ||
4130 | ||
4131 | class _TempPool(object): | |
4b1c4062 BB |
4132 | SNAPSHOTS = [b'snap', b'snap1', b'snap2'] |
4133 | BOOKMARKS = [b'bmark', b'bmark1', b'bmark2'] | |
6abf9225 AG |
4134 | |
4135 | _cachefile_suffix = ".cachefile" | |
4136 | ||
4137 | # XXX Whether to do a sloppy but much faster cleanup | |
4138 | # or a proper but slower one. | |
4139 | _recreate_pools = True | |
4140 | ||
4141 | def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]): | |
4142 | self._filesystems = filesystems | |
4143 | self._readonly = readonly | |
4b1c4062 BB |
4144 | if sys.version_info < (3, 0): |
4145 | self._pool_name = b'pool.' + bytes(uuid.uuid4()) | |
4146 | else: | |
4147 | self._pool_name = b'pool.' + bytes(str(uuid.uuid4()), | |
4148 | encoding='utf-8') | |
6abf9225 AG |
4149 | self._root = _Filesystem(self._pool_name) |
4150 | (fd, self._pool_file_path) = tempfile.mkstemp( | |
4151 | suffix='.zpool', prefix='tmp-') | |
4152 | if readonly: | |
4153 | cachefile = self._pool_file_path + _TempPool._cachefile_suffix | |
4154 | else: | |
4155 | cachefile = 'none' | |
85ce3f4f | 4156 | self._zpool_create = [ |
4157 | 'zpool', 'create', '-o', 'cachefile=' + cachefile, | |
56fa4aa9 RE |
4158 | '-O', 'mountpoint=legacy', '-O', 'compression=off', |
4159 | self._pool_name, self._pool_file_path] | |
6abf9225 AG |
4160 | try: |
4161 | os.ftruncate(fd, size) | |
4162 | os.close(fd) | |
4163 | ||
4164 | subprocess.check_output( | |
4165 | self._zpool_create, stderr=subprocess.STDOUT) | |
4166 | ||
4167 | for fs in filesystems: | |
4168 | lzc.lzc_create(self.makeName(fs)) | |
4169 | ||
4170 | self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks') | |
4171 | ||
4172 | if readonly: | |
85ce3f4f | 4173 | # To make a pool read-only it must exported and re-imported |
4174 | # with readonly option. | |
4175 | # The most deterministic way to re-import the pool is by using | |
4176 | # a cache file. | |
4177 | # But the cache file has to be stashed away before the pool is | |
4178 | # exported, because otherwise the pool is removed from the | |
4179 | # cache. | |
6abf9225 AG |
4180 | shutil.copyfile(cachefile, cachefile + '.tmp') |
4181 | subprocess.check_output( | |
85ce3f4f | 4182 | ['zpool', 'export', '-f', self._pool_name], |
4183 | stderr=subprocess.STDOUT) | |
6abf9225 | 4184 | os.rename(cachefile + '.tmp', cachefile) |
85ce3f4f | 4185 | subprocess.check_output( |
4186 | ['zpool', 'import', '-f', '-N', '-c', cachefile, | |
4187 | '-o', 'readonly=on', self._pool_name], | |
4188 | stderr=subprocess.STDOUT) | |
6abf9225 AG |
4189 | os.remove(cachefile) |
4190 | ||
4191 | except subprocess.CalledProcessError as e: | |
4192 | self.cleanUp() | |
4b1c4062 | 4193 | if b'permission denied' in e.output: |
6abf9225 AG |
4194 | raise unittest.SkipTest( |
4195 | 'insufficient privileges to run libzfs_core tests') | |
9de8c0cd | 4196 | print('command failed: ', e.output) |
6abf9225 AG |
4197 | raise |
4198 | except Exception: | |
4199 | self.cleanUp() | |
4200 | raise | |
4201 | ||
4202 | def reset(self): | |
4203 | if self._readonly: | |
4204 | return | |
4205 | ||
4206 | if not self.__class__._recreate_pools: | |
4207 | snaps = [] | |
4208 | for fs in [''] + self._filesystems: | |
4209 | for snap in self.__class__.SNAPSHOTS: | |
4210 | snaps.append(self.makeName(fs + '@' + snap)) | |
4211 | self.getRoot().visitSnaps(lambda snap: snaps.append(snap)) | |
4212 | lzc.lzc_destroy_snaps(snaps, defer=False) | |
4213 | ||
4214 | if self._bmarks_supported: | |
4215 | bmarks = [] | |
4216 | for fs in [''] + self._filesystems: | |
4217 | for bmark in self.__class__.BOOKMARKS: | |
4218 | bmarks.append(self.makeName(fs + '#' + bmark)) | |
4219 | self.getRoot().visitBookmarks( | |
4220 | lambda bmark: bmarks.append(bmark)) | |
4221 | lzc.lzc_destroy_bookmarks(bmarks) | |
4222 | self.getRoot().reset() | |
4223 | return | |
4224 | ||
85ce3f4f | 4225 | # On the Buildbot builders this may fail with "pool is busy" |
4226 | # Retry 5 times before raising an error | |
4227 | retry = 0 | |
4228 | while True: | |
4229 | try: | |
4230 | subprocess.check_output( | |
4231 | ['zpool', 'destroy', '-f', self._pool_name], | |
4232 | stderr=subprocess.STDOUT) | |
4233 | subprocess.check_output( | |
4234 | self._zpool_create, stderr=subprocess.STDOUT) | |
4235 | break | |
4236 | except subprocess.CalledProcessError as e: | |
4b1c4062 | 4237 | if b'pool is busy' in e.output and retry < 5: |
85ce3f4f | 4238 | retry += 1 |
4239 | time.sleep(1) | |
4240 | continue | |
4241 | else: | |
9de8c0cd | 4242 | print('command failed: ', e.output) |
85ce3f4f | 4243 | raise |
6abf9225 AG |
4244 | for fs in self._filesystems: |
4245 | lzc.lzc_create(self.makeName(fs)) | |
4246 | self.getRoot().reset() | |
4247 | ||
4248 | def cleanUp(self): | |
4249 | try: | |
4250 | subprocess.check_output( | |
85ce3f4f | 4251 | ['zpool', 'destroy', '-f', self._pool_name], |
4252 | stderr=subprocess.STDOUT) | |
6abf9225 AG |
4253 | except Exception: |
4254 | pass | |
4255 | try: | |
4256 | os.remove(self._pool_file_path) | |
4257 | except Exception: | |
4258 | pass | |
4259 | try: | |
4260 | os.remove(self._pool_file_path + _TempPool._cachefile_suffix) | |
4261 | except Exception: | |
4262 | pass | |
4263 | try: | |
4264 | os.remove( | |
4265 | self._pool_file_path + _TempPool._cachefile_suffix + '.tmp') | |
4266 | except Exception: | |
4267 | pass | |
4268 | ||
4269 | def makeName(self, relative=None): | |
4270 | if not relative: | |
4271 | return self._pool_name | |
4b1c4062 | 4272 | if relative.startswith((b'@', b'#')): |
6abf9225 | 4273 | return self._pool_name + relative |
4b1c4062 | 4274 | return self._pool_name + b'/' + relative |
6abf9225 AG |
4275 | |
4276 | def makeTooLongName(self, prefix=None): | |
4277 | if not prefix: | |
4b1c4062 | 4278 | prefix = b'x' |
6abf9225 AG |
4279 | prefix = self.makeName(prefix) |
4280 | pad_len = lzc.MAXNAMELEN + 1 - len(prefix) | |
4281 | if pad_len > 0: | |
4b1c4062 | 4282 | return prefix + b'x' * pad_len |
6abf9225 AG |
4283 | else: |
4284 | return prefix | |
4285 | ||
4286 | def makeTooLongComponent(self, prefix=None): | |
4b1c4062 | 4287 | padding = b'x' * (lzc.MAXNAMELEN + 1) |
6abf9225 AG |
4288 | if not prefix: |
4289 | prefix = padding | |
4290 | else: | |
4291 | prefix = prefix + padding | |
4292 | return self.makeName(prefix) | |
4293 | ||
4294 | def getRoot(self): | |
4295 | return self._root | |
4296 | ||
85ce3f4f | 4297 | def getFilesystem(self, fsname): |
4b1c4062 | 4298 | return _Filesystem(self._pool_name + b'/' + fsname) |
85ce3f4f | 4299 | |
6abf9225 AG |
4300 | def isPoolFeatureAvailable(self, feature): |
4301 | output = subprocess.check_output( | |
4302 | ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) | |
4303 | output = output.strip() | |
4304 | return output != '' | |
4305 | ||
4306 | def isPoolFeatureEnabled(self, feature): | |
4307 | output = subprocess.check_output( | |
4308 | ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) | |
4309 | output = output.split()[2] | |
4b1c4062 | 4310 | return output in [b'active', b'enabled'] |
6abf9225 AG |
4311 | |
4312 | ||
4313 | class _Filesystem(object): | |
4314 | ||
4315 | def __init__(self, name): | |
4316 | self._name = name | |
4317 | self.reset() | |
4318 | ||
4319 | def getName(self): | |
4320 | return self._name | |
4321 | ||
4322 | def reset(self): | |
4323 | self._children = [] | |
4324 | self._fs_id = 0 | |
4325 | self._snap_id = 0 | |
4326 | self._bmark_id = 0 | |
4327 | ||
4328 | def getFilesystem(self): | |
4329 | self._fs_id += 1 | |
4b1c4062 | 4330 | fsname = self._name + b'/fs' + str(self._fs_id).encode() |
6abf9225 AG |
4331 | fs = _Filesystem(fsname) |
4332 | self._children.append(fs) | |
4333 | return fs | |
4334 | ||
85ce3f4f | 4335 | def getProperty(self, propname, received=False): |
4336 | if received: | |
4337 | output = subprocess.check_output( | |
4338 | ['zfs', 'get', '-pH', '-o', 'received', propname, self._name]) | |
4339 | else: | |
4340 | output = subprocess.check_output( | |
4341 | ['zfs', 'get', '-pH', '-o', 'value', propname, self._name]) | |
4342 | return output.strip() | |
4343 | ||
6abf9225 | 4344 | def _makeSnapName(self, i): |
4b1c4062 | 4345 | return self._name + b'@snap' + str(i).encode() |
6abf9225 AG |
4346 | |
4347 | def getSnap(self): | |
4348 | self._snap_id += 1 | |
4349 | return self._makeSnapName(self._snap_id) | |
4350 | ||
4351 | def _makeBookmarkName(self, i): | |
4b1c4062 | 4352 | return self._name + b'#bmark' + bytes(i) |
6abf9225 AG |
4353 | |
4354 | def getBookmark(self): | |
4355 | self._bmark_id += 1 | |
4356 | return self._makeBookmarkName(self._bmark_id) | |
4357 | ||
4358 | def _makeTooLongName(self, too_long_component): | |
4359 | if too_long_component: | |
4b1c4062 | 4360 | return b'x' * (lzc.MAXNAMELEN + 1) |
6abf9225 AG |
4361 | |
4362 | # Note that another character is used for one of '/', '@', '#'. | |
4363 | comp_len = lzc.MAXNAMELEN - len(self._name) | |
4364 | if comp_len > 0: | |
4b1c4062 | 4365 | return b'x' * comp_len |
6abf9225 | 4366 | else: |
4b1c4062 | 4367 | return b'x' |
6abf9225 AG |
4368 | |
4369 | def getTooLongFilesystemName(self, too_long_component): | |
4b1c4062 | 4370 | return self._name + b'/' + self._makeTooLongName(too_long_component) |
6abf9225 AG |
4371 | |
4372 | def getTooLongSnap(self, too_long_component): | |
4b1c4062 | 4373 | return self._name + b'@' + self._makeTooLongName(too_long_component) |
6abf9225 AG |
4374 | |
4375 | def getTooLongBookmark(self, too_long_component): | |
4b1c4062 | 4376 | return self._name + b'#' + self._makeTooLongName(too_long_component) |
6abf9225 AG |
4377 | |
4378 | def _visitFilesystems(self, visitor): | |
4379 | for child in self._children: | |
4380 | child._visitFilesystems(visitor) | |
4381 | visitor(self) | |
4382 | ||
4383 | def visitFilesystems(self, visitor): | |
4384 | def _fsVisitor(fs): | |
4385 | visitor(fs._name) | |
4386 | ||
4387 | self._visitFilesystems(_fsVisitor) | |
4388 | ||
4389 | def visitSnaps(self, visitor): | |
4390 | def _snapVisitor(fs): | |
4391 | for i in range(1, fs._snap_id + 1): | |
4392 | visitor(fs._makeSnapName(i)) | |
4393 | ||
4394 | self._visitFilesystems(_snapVisitor) | |
4395 | ||
4396 | def visitBookmarks(self, visitor): | |
4397 | def _bmarkVisitor(fs): | |
4398 | for i in range(1, fs._bmark_id + 1): | |
4399 | visitor(fs._makeBookmarkName(i)) | |
4400 | ||
4401 | self._visitFilesystems(_bmarkVisitor) | |
4402 | ||
4403 | ||
4404 | # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 |