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