]>
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 | Helper routines for converting ``errno`` style error codes from C functions | |
19 | to Python exceptions defined by `libzfs_core` API. | |
20 | ||
21 | The conversion heavily depends on the context of the error: the attempted | |
22 | operation and the input parameters. For this reason, there is a conversion | |
23 | routine for each `libzfs_core` interface function. The conversion routines | |
24 | have the return code as a parameter as well as all the parameters of the | |
25 | corresponding interface functions. | |
26 | ||
27 | The parameters and exceptions are documented in the `libzfs_core` interfaces. | |
28 | """ | |
29 | ||
30 | import errno | |
31 | import re | |
32 | import string | |
33 | from . import exceptions as lzc_exc | |
c962fd6c | 34 | from ._constants import ( |
35 | MAXNAMELEN, | |
36 | ZFS_ERR_CHECKPOINT_EXISTS, | |
37 | ZFS_ERR_DISCARDING_CHECKPOINT, | |
38 | ZFS_ERR_NO_CHECKPOINT, | |
39 | ZFS_ERR_DEVRM_IN_PROGRESS, | |
40 | ZFS_ERR_VDEV_TOO_BIG | |
41 | ) | |
6abf9225 AG |
42 | |
43 | ||
44 | def lzc_create_translate_error(ret, name, ds_type, props): | |
45 | if ret == 0: | |
46 | return | |
47 | if ret == errno.EINVAL: | |
85ce3f4f | 48 | # XXX: should raise lzc_exc.WrongParent if parent is ZVOL |
6abf9225 AG |
49 | _validate_fs_name(name) |
50 | raise lzc_exc.PropertyInvalid(name) | |
6abf9225 AG |
51 | if ret == errno.EEXIST: |
52 | raise lzc_exc.FilesystemExists(name) | |
53 | if ret == errno.ENOENT: | |
54 | raise lzc_exc.ParentNotFound(name) | |
55 | raise _generic_exception(ret, name, "Failed to create filesystem") | |
56 | ||
57 | ||
58 | def lzc_clone_translate_error(ret, name, origin, props): | |
59 | if ret == 0: | |
60 | return | |
61 | if ret == errno.EINVAL: | |
62 | _validate_fs_name(name) | |
63 | _validate_snap_name(origin) | |
85ce3f4f | 64 | raise lzc_exc.PropertyInvalid(name) |
65 | if ret == errno.EXDEV: | |
66 | raise lzc_exc.PoolsDiffer(name) | |
6abf9225 AG |
67 | if ret == errno.EEXIST: |
68 | raise lzc_exc.FilesystemExists(name) | |
69 | if ret == errno.ENOENT: | |
70 | if not _is_valid_snap_name(origin): | |
71 | raise lzc_exc.SnapshotNameInvalid(origin) | |
72 | raise lzc_exc.DatasetNotFound(name) | |
73 | raise _generic_exception(ret, name, "Failed to create clone") | |
74 | ||
75 | ||
76 | def lzc_rollback_translate_error(ret, name): | |
77 | if ret == 0: | |
78 | return | |
85ce3f4f | 79 | if ret == errno.ESRCH: |
80 | raise lzc_exc.SnapshotNotFound(name) | |
6abf9225 AG |
81 | if ret == errno.EINVAL: |
82 | _validate_fs_name(name) | |
85ce3f4f | 83 | raise lzc_exc.NameInvalid(name) |
6abf9225 AG |
84 | if ret == errno.ENOENT: |
85 | if not _is_valid_fs_name(name): | |
86 | raise lzc_exc.NameInvalid(name) | |
87 | else: | |
88 | raise lzc_exc.FilesystemNotFound(name) | |
89 | raise _generic_exception(ret, name, "Failed to rollback") | |
90 | ||
85ce3f4f | 91 | |
6abf9225 | 92 | def lzc_rollback_to_translate_error(ret, name, snap): |
6abf9225 AG |
93 | if ret == errno.EEXIST: |
94 | raise lzc_exc.SnapshotNotLatest(snap) | |
85ce3f4f | 95 | else: |
96 | lzc_rollback_translate_error(ret, name) | |
97 | ||
6abf9225 AG |
98 | |
99 | def lzc_snapshot_translate_errors(ret, errlist, snaps, props): | |
100 | if ret == 0: | |
101 | return | |
102 | ||
103 | def _map(ret, name): | |
104 | if ret == errno.EXDEV: | |
105 | pool_names = map(_pool_name, snaps) | |
106 | same_pool = all(x == pool_names[0] for x in pool_names) | |
107 | if same_pool: | |
108 | return lzc_exc.DuplicateSnapshots(name) | |
109 | else: | |
110 | return lzc_exc.PoolsDiffer(name) | |
111 | elif ret == errno.EINVAL: | |
112 | if any(not _is_valid_snap_name(s) for s in snaps): | |
113 | return lzc_exc.NameInvalid(name) | |
114 | elif any(len(s) > MAXNAMELEN for s in snaps): | |
115 | return lzc_exc.NameTooLong(name) | |
116 | else: | |
117 | return lzc_exc.PropertyInvalid(name) | |
118 | ||
119 | if ret == errno.EEXIST: | |
120 | return lzc_exc.SnapshotExists(name) | |
121 | if ret == errno.ENOENT: | |
122 | return lzc_exc.FilesystemNotFound(name) | |
123 | return _generic_exception(ret, name, "Failed to create snapshot") | |
124 | ||
125 | _handle_err_list(ret, errlist, snaps, lzc_exc.SnapshotFailure, _map) | |
126 | ||
127 | ||
128 | def lzc_destroy_snaps_translate_errors(ret, errlist, snaps, defer): | |
129 | if ret == 0: | |
130 | return | |
131 | ||
132 | def _map(ret, name): | |
133 | if ret == errno.EEXIST: | |
134 | return lzc_exc.SnapshotIsCloned(name) | |
135 | if ret == errno.ENOENT: | |
136 | return lzc_exc.PoolNotFound(name) | |
137 | if ret == errno.EBUSY: | |
138 | return lzc_exc.SnapshotIsHeld(name) | |
139 | return _generic_exception(ret, name, "Failed to destroy snapshot") | |
140 | ||
85ce3f4f | 141 | _handle_err_list( |
142 | ret, errlist, snaps, lzc_exc.SnapshotDestructionFailure, _map) | |
6abf9225 AG |
143 | |
144 | ||
145 | def lzc_bookmark_translate_errors(ret, errlist, bookmarks): | |
146 | if ret == 0: | |
147 | return | |
148 | ||
149 | def _map(ret, name): | |
150 | if ret == errno.EINVAL: | |
151 | if name: | |
152 | snap = bookmarks[name] | |
153 | pool_names = map(_pool_name, bookmarks.keys()) | |
154 | if not _is_valid_bmark_name(name): | |
155 | return lzc_exc.BookmarkNameInvalid(name) | |
156 | elif not _is_valid_snap_name(snap): | |
157 | return lzc_exc.SnapshotNameInvalid(snap) | |
158 | elif _fs_name(name) != _fs_name(snap): | |
159 | return lzc_exc.BookmarkMismatch(name) | |
160 | elif any(x != _pool_name(name) for x in pool_names): | |
161 | return lzc_exc.PoolsDiffer(name) | |
162 | else: | |
85ce3f4f | 163 | invalid_names = [ |
164 | b for b in bookmarks.keys() if not _is_valid_bmark_name(b)] | |
6abf9225 AG |
165 | if invalid_names: |
166 | return lzc_exc.BookmarkNameInvalid(invalid_names[0]) | |
167 | if ret == errno.EEXIST: | |
168 | return lzc_exc.BookmarkExists(name) | |
169 | if ret == errno.ENOENT: | |
170 | return lzc_exc.SnapshotNotFound(name) | |
171 | if ret == errno.ENOTSUP: | |
172 | return lzc_exc.BookmarkNotSupported(name) | |
173 | return _generic_exception(ret, name, "Failed to create bookmark") | |
174 | ||
85ce3f4f | 175 | _handle_err_list( |
176 | ret, errlist, bookmarks.keys(), lzc_exc.BookmarkFailure, _map) | |
6abf9225 AG |
177 | |
178 | ||
179 | def lzc_get_bookmarks_translate_error(ret, fsname, props): | |
180 | if ret == 0: | |
181 | return | |
182 | if ret == errno.ENOENT: | |
183 | raise lzc_exc.FilesystemNotFound(fsname) | |
184 | raise _generic_exception(ret, fsname, "Failed to list bookmarks") | |
185 | ||
186 | ||
187 | def lzc_destroy_bookmarks_translate_errors(ret, errlist, bookmarks): | |
188 | if ret == 0: | |
189 | return | |
190 | ||
191 | def _map(ret, name): | |
192 | if ret == errno.EINVAL: | |
193 | return lzc_exc.NameInvalid(name) | |
194 | return _generic_exception(ret, name, "Failed to destroy bookmark") | |
195 | ||
85ce3f4f | 196 | _handle_err_list( |
197 | ret, errlist, bookmarks, lzc_exc.BookmarkDestructionFailure, _map) | |
6abf9225 AG |
198 | |
199 | ||
200 | def lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap): | |
201 | if ret == 0: | |
202 | return | |
203 | if ret == errno.EXDEV and firstsnap is not None: | |
204 | if _pool_name(firstsnap) != _pool_name(lastsnap): | |
205 | raise lzc_exc.PoolsDiffer(lastsnap) | |
206 | else: | |
207 | raise lzc_exc.SnapshotMismatch(lastsnap) | |
208 | if ret == errno.EINVAL: | |
209 | if not _is_valid_snap_name(firstsnap): | |
210 | raise lzc_exc.NameInvalid(firstsnap) | |
211 | elif not _is_valid_snap_name(lastsnap): | |
212 | raise lzc_exc.NameInvalid(lastsnap) | |
213 | elif len(firstsnap) > MAXNAMELEN: | |
214 | raise lzc_exc.NameTooLong(firstsnap) | |
215 | elif len(lastsnap) > MAXNAMELEN: | |
216 | raise lzc_exc.NameTooLong(lastsnap) | |
217 | elif _pool_name(firstsnap) != _pool_name(lastsnap): | |
218 | raise lzc_exc.PoolsDiffer(lastsnap) | |
219 | else: | |
220 | raise lzc_exc.SnapshotMismatch(lastsnap) | |
221 | if ret == errno.ENOENT: | |
222 | raise lzc_exc.SnapshotNotFound(lastsnap) | |
85ce3f4f | 223 | raise _generic_exception( |
224 | ret, lastsnap, "Failed to calculate space used by range of snapshots") | |
6abf9225 AG |
225 | |
226 | ||
227 | def lzc_hold_translate_errors(ret, errlist, holds, fd): | |
228 | if ret == 0: | |
229 | return | |
230 | ||
231 | def _map(ret, name): | |
232 | if ret == errno.EXDEV: | |
233 | return lzc_exc.PoolsDiffer(name) | |
234 | elif ret == errno.EINVAL: | |
235 | if name: | |
236 | pool_names = map(_pool_name, holds.keys()) | |
237 | if not _is_valid_snap_name(name): | |
238 | return lzc_exc.NameInvalid(name) | |
239 | elif len(name) > MAXNAMELEN: | |
240 | return lzc_exc.NameTooLong(name) | |
241 | elif any(x != _pool_name(name) for x in pool_names): | |
242 | return lzc_exc.PoolsDiffer(name) | |
243 | else: | |
85ce3f4f | 244 | invalid_names = [ |
245 | b for b in holds.keys() if not _is_valid_snap_name(b)] | |
6abf9225 AG |
246 | if invalid_names: |
247 | return lzc_exc.NameInvalid(invalid_names[0]) | |
248 | fs_name = None | |
249 | hold_name = None | |
250 | pool_name = None | |
251 | if name is not None: | |
252 | fs_name = _fs_name(name) | |
253 | pool_name = _pool_name(name) | |
254 | hold_name = holds[name] | |
255 | if ret == errno.ENOENT: | |
256 | return lzc_exc.FilesystemNotFound(fs_name) | |
257 | if ret == errno.EEXIST: | |
258 | return lzc_exc.HoldExists(name) | |
259 | if ret == errno.E2BIG: | |
260 | return lzc_exc.NameTooLong(hold_name) | |
261 | if ret == errno.ENOTSUP: | |
262 | return lzc_exc.FeatureNotSupported(pool_name) | |
263 | return _generic_exception(ret, name, "Failed to hold snapshot") | |
264 | ||
265 | if ret == errno.EBADF: | |
266 | raise lzc_exc.BadHoldCleanupFD() | |
267 | _handle_err_list(ret, errlist, holds.keys(), lzc_exc.HoldFailure, _map) | |
268 | ||
269 | ||
270 | def lzc_release_translate_errors(ret, errlist, holds): | |
271 | if ret == 0: | |
272 | return | |
273 | for _, hold_list in holds.iteritems(): | |
274 | if not isinstance(hold_list, list): | |
275 | raise lzc_exc.TypeError('holds must be in a list') | |
276 | ||
277 | def _map(ret, name): | |
278 | if ret == errno.EXDEV: | |
279 | return lzc_exc.PoolsDiffer(name) | |
280 | elif ret == errno.EINVAL: | |
281 | if name: | |
282 | pool_names = map(_pool_name, holds.keys()) | |
283 | if not _is_valid_snap_name(name): | |
284 | return lzc_exc.NameInvalid(name) | |
285 | elif len(name) > MAXNAMELEN: | |
286 | return lzc_exc.NameTooLong(name) | |
287 | elif any(x != _pool_name(name) for x in pool_names): | |
288 | return lzc_exc.PoolsDiffer(name) | |
289 | else: | |
85ce3f4f | 290 | invalid_names = [ |
291 | b for b in holds.keys() if not _is_valid_snap_name(b)] | |
6abf9225 AG |
292 | if invalid_names: |
293 | return lzc_exc.NameInvalid(invalid_names[0]) | |
294 | elif ret == errno.ENOENT: | |
295 | return lzc_exc.HoldNotFound(name) | |
296 | elif ret == errno.E2BIG: | |
297 | tag_list = holds[name] | |
298 | too_long_tags = [t for t in tag_list if len(t) > MAXNAMELEN] | |
299 | return lzc_exc.NameTooLong(too_long_tags[0]) | |
300 | elif ret == errno.ENOTSUP: | |
301 | pool_name = None | |
302 | if name is not None: | |
303 | pool_name = _pool_name(name) | |
304 | return lzc_exc.FeatureNotSupported(pool_name) | |
305 | else: | |
85ce3f4f | 306 | return _generic_exception( |
307 | ret, name, "Failed to release snapshot hold") | |
6abf9225 | 308 | |
85ce3f4f | 309 | _handle_err_list( |
310 | ret, errlist, holds.keys(), lzc_exc.HoldReleaseFailure, _map) | |
6abf9225 AG |
311 | |
312 | ||
313 | def lzc_get_holds_translate_error(ret, snapname): | |
314 | if ret == 0: | |
315 | return | |
316 | if ret == errno.EINVAL: | |
317 | _validate_snap_name(snapname) | |
318 | if ret == errno.ENOENT: | |
319 | raise lzc_exc.SnapshotNotFound(snapname) | |
320 | if ret == errno.ENOTSUP: | |
321 | raise lzc_exc.FeatureNotSupported(_pool_name(snapname)) | |
322 | raise _generic_exception(ret, snapname, "Failed to get holds on snapshot") | |
323 | ||
324 | ||
325 | def lzc_send_translate_error(ret, snapname, fromsnap, fd, flags): | |
326 | if ret == 0: | |
327 | return | |
328 | if ret == errno.EXDEV and fromsnap is not None: | |
329 | if _pool_name(fromsnap) != _pool_name(snapname): | |
330 | raise lzc_exc.PoolsDiffer(snapname) | |
331 | else: | |
332 | raise lzc_exc.SnapshotMismatch(snapname) | |
333 | elif ret == errno.EINVAL: | |
334 | if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and | |
335 | not _is_valid_bmark_name(fromsnap)): | |
336 | raise lzc_exc.NameInvalid(fromsnap) | |
85ce3f4f | 337 | elif (not _is_valid_snap_name(snapname) and |
338 | not _is_valid_fs_name(snapname)): | |
6abf9225 AG |
339 | raise lzc_exc.NameInvalid(snapname) |
340 | elif fromsnap is not None and len(fromsnap) > MAXNAMELEN: | |
341 | raise lzc_exc.NameTooLong(fromsnap) | |
342 | elif len(snapname) > MAXNAMELEN: | |
343 | raise lzc_exc.NameTooLong(snapname) | |
85ce3f4f | 344 | elif (fromsnap is not None and |
345 | _pool_name(fromsnap) != _pool_name(snapname)): | |
6abf9225 AG |
346 | raise lzc_exc.PoolsDiffer(snapname) |
347 | elif ret == errno.ENOENT: | |
348 | if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and | |
349 | not _is_valid_bmark_name(fromsnap)): | |
350 | raise lzc_exc.NameInvalid(fromsnap) | |
351 | raise lzc_exc.SnapshotNotFound(snapname) | |
352 | elif ret == errno.ENAMETOOLONG: | |
353 | if fromsnap is not None and len(fromsnap) > MAXNAMELEN: | |
354 | raise lzc_exc.NameTooLong(fromsnap) | |
355 | else: | |
356 | raise lzc_exc.NameTooLong(snapname) | |
357 | raise lzc_exc.StreamIOError(ret) | |
358 | ||
359 | ||
360 | def lzc_send_space_translate_error(ret, snapname, fromsnap): | |
361 | if ret == 0: | |
362 | return | |
363 | if ret == errno.EXDEV and fromsnap is not None: | |
364 | if _pool_name(fromsnap) != _pool_name(snapname): | |
365 | raise lzc_exc.PoolsDiffer(snapname) | |
366 | else: | |
367 | raise lzc_exc.SnapshotMismatch(snapname) | |
368 | elif ret == errno.EINVAL: | |
369 | if fromsnap is not None and not _is_valid_snap_name(fromsnap): | |
370 | raise lzc_exc.NameInvalid(fromsnap) | |
371 | elif not _is_valid_snap_name(snapname): | |
372 | raise lzc_exc.NameInvalid(snapname) | |
373 | elif fromsnap is not None and len(fromsnap) > MAXNAMELEN: | |
374 | raise lzc_exc.NameTooLong(fromsnap) | |
375 | elif len(snapname) > MAXNAMELEN: | |
376 | raise lzc_exc.NameTooLong(snapname) | |
85ce3f4f | 377 | elif (fromsnap is not None and |
378 | _pool_name(fromsnap) != _pool_name(snapname)): | |
6abf9225 AG |
379 | raise lzc_exc.PoolsDiffer(snapname) |
380 | elif ret == errno.ENOENT and fromsnap is not None: | |
381 | if not _is_valid_snap_name(fromsnap): | |
382 | raise lzc_exc.NameInvalid(fromsnap) | |
383 | if ret == errno.ENOENT: | |
384 | raise lzc_exc.SnapshotNotFound(snapname) | |
85ce3f4f | 385 | raise _generic_exception( |
386 | ret, snapname, "Failed to estimate backup stream size") | |
6abf9225 AG |
387 | |
388 | ||
85ce3f4f | 389 | def lzc_receive_translate_errors( |
390 | ret, snapname, fd, force, raw, resumable, embedded, origin, properrs | |
391 | ): | |
6abf9225 | 392 | if ret == 0: |
85ce3f4f | 393 | if properrs is not None and len(properrs) > 0: |
394 | def _map(ret, name): | |
395 | if ret == errno.EINVAL: | |
396 | return lzc_exc.PropertyInvalid(name) | |
397 | return _generic_exception(ret, name, "Failed to set property") | |
398 | _handle_err_list( | |
399 | errno.EINVAL, properrs, [snapname], | |
400 | lzc_exc.ReceivePropertyFailure, _map) | |
401 | else: | |
402 | return | |
6abf9225 | 403 | if ret == errno.EINVAL: |
85ce3f4f | 404 | if (not _is_valid_snap_name(snapname) and |
405 | not _is_valid_fs_name(snapname)): | |
6abf9225 AG |
406 | raise lzc_exc.NameInvalid(snapname) |
407 | elif len(snapname) > MAXNAMELEN: | |
408 | raise lzc_exc.NameTooLong(snapname) | |
409 | elif origin is not None and not _is_valid_snap_name(origin): | |
410 | raise lzc_exc.NameInvalid(origin) | |
85ce3f4f | 411 | elif resumable: |
412 | raise lzc_exc.StreamFeatureInvalid() | |
413 | elif embedded and not raw: | |
414 | raise lzc_exc.StreamFeatureIncompatible() | |
6abf9225 AG |
415 | else: |
416 | raise lzc_exc.BadStream() | |
417 | if ret == errno.ENOENT: | |
418 | if not _is_valid_snap_name(snapname): | |
419 | raise lzc_exc.NameInvalid(snapname) | |
420 | else: | |
421 | raise lzc_exc.DatasetNotFound(snapname) | |
422 | if ret == errno.EEXIST: | |
423 | raise lzc_exc.DatasetExists(snapname) | |
424 | if ret == errno.ENOTSUP: | |
425 | raise lzc_exc.StreamFeatureNotSupported() | |
426 | if ret == errno.ENODEV: | |
427 | raise lzc_exc.StreamMismatch(_fs_name(snapname)) | |
428 | if ret == errno.ETXTBSY: | |
429 | raise lzc_exc.DestinationModified(_fs_name(snapname)) | |
430 | if ret == errno.EBUSY: | |
431 | raise lzc_exc.DatasetBusy(_fs_name(snapname)) | |
432 | if ret == errno.ENOSPC: | |
433 | raise lzc_exc.NoSpace(_fs_name(snapname)) | |
434 | if ret == errno.EDQUOT: | |
435 | raise lzc_exc.QuotaExceeded(_fs_name(snapname)) | |
436 | if ret == errno.ENAMETOOLONG: | |
437 | raise lzc_exc.NameTooLong(snapname) | |
438 | if ret == errno.EROFS: | |
439 | raise lzc_exc.ReadOnlyPool(_pool_name(snapname)) | |
440 | if ret == errno.EAGAIN: | |
441 | raise lzc_exc.SuspendedPool(_pool_name(snapname)) | |
85ce3f4f | 442 | if ret == errno.EBADE: # ECKSUM |
443 | raise lzc_exc.BadStream() | |
6abf9225 AG |
444 | |
445 | raise lzc_exc.StreamIOError(ret) | |
446 | ||
447 | ||
448 | def lzc_promote_translate_error(ret, name): | |
449 | if ret == 0: | |
450 | return | |
451 | if ret == errno.EINVAL: | |
452 | _validate_fs_name(name) | |
453 | raise lzc_exc.NotClone(name) | |
454 | if ret == errno.ENOTSOCK: | |
455 | raise lzc_exc.NotClone(name) | |
456 | if ret == errno.ENOENT: | |
457 | raise lzc_exc.FilesystemNotFound(name) | |
458 | if ret == errno.EEXIST: | |
459 | raise lzc_exc.SnapshotExists(name) | |
460 | raise _generic_exception(ret, name, "Failed to promote dataset") | |
461 | ||
462 | ||
85ce3f4f | 463 | def lzc_change_key_translate_error(ret, name): |
464 | if ret == 0: | |
465 | return | |
466 | if ret == errno.EINVAL: | |
467 | _validate_fs_name(name) | |
468 | raise lzc_exc.PropertyInvalid(name) | |
469 | if ret == errno.ENOENT: | |
470 | raise lzc_exc.FilesystemNotFound(name) | |
471 | if ret == errno.EACCES: | |
472 | raise lzc_exc.EncryptionKeyNotLoaded() | |
473 | raise _generic_exception(ret, name, "Failed to change encryption key") | |
474 | ||
475 | ||
476 | def lzc_load_key_translate_error(ret, name, noop): | |
477 | if ret == 0: | |
478 | return | |
479 | if ret == errno.EINVAL: | |
480 | _validate_fs_name(name) | |
481 | raise lzc_exc.PropertyInvalid(name) | |
482 | if ret == errno.ENOENT: | |
483 | raise lzc_exc.FilesystemNotFound(name) | |
484 | if ret == errno.EACCES: | |
485 | raise lzc_exc.EncryptionKeyInvalid() | |
486 | if ret == errno.EEXIST: | |
487 | raise lzc_exc.EncryptionKeyAlreadyLoaded() | |
488 | if noop: | |
489 | raise _generic_exception(ret, name, "Failed to load encryption key") | |
490 | else: | |
491 | raise _generic_exception(ret, name, "Failed to verify encryption key") | |
492 | ||
493 | ||
494 | def lzc_unload_key_translate_error(ret, name): | |
495 | if ret == 0: | |
496 | return | |
497 | if ret == errno.EINVAL: | |
498 | _validate_fs_name(name) | |
499 | raise lzc_exc.PropertyInvalid(name) | |
500 | if ret == errno.ENOENT: | |
501 | raise lzc_exc.FilesystemNotFound(name) | |
502 | if ret == errno.EACCES: | |
503 | raise lzc_exc.EncryptionKeyNotLoaded() | |
504 | raise _generic_exception(ret, name, "Failed to unload encryption key") | |
505 | ||
506 | ||
507 | def lzc_sync_translate_error(ret, name): | |
508 | if ret == 0: | |
509 | return | |
510 | if ret == errno.ENOENT: | |
511 | raise lzc_exc.PoolNotFound(name) | |
512 | raise _generic_exception(ret, name, "Failed to sync pool") | |
513 | ||
514 | ||
515 | def lzc_reopen_translate_error(ret, name): | |
516 | if ret == 0: | |
517 | return | |
518 | if ret == errno.ENOENT: | |
519 | raise lzc_exc.PoolNotFound(name) | |
520 | raise _generic_exception(ret, name, "Failed to reopen pool") | |
521 | ||
522 | ||
523 | def lzc_channel_program_translate_error(ret, name, error): | |
524 | if ret == 0: | |
525 | return | |
526 | if ret == errno.ENOENT: | |
527 | raise lzc_exc.PoolNotFound(name) | |
528 | if ret == errno.ETIME: | |
529 | raise lzc_exc.ZCPTimeout() | |
530 | if ret == errno.ENOMEM: | |
531 | raise lzc_exc.ZCPMemoryError() | |
532 | if ret == errno.ENOSPC: | |
533 | raise lzc_exc.ZCPSpaceError() | |
534 | if ret == errno.EPERM: | |
535 | raise lzc_exc.ZCPPermissionError() | |
536 | if ret == errno.ECHRNG: | |
537 | raise lzc_exc.ZCPRuntimeError(error) | |
538 | if ret == errno.EINVAL: | |
539 | if error is None: | |
540 | raise lzc_exc.ZCPLimitInvalid() | |
541 | else: | |
542 | raise lzc_exc.ZCPSyntaxError(error) | |
543 | raise _generic_exception(ret, name, "Failed to execute channel program") | |
544 | ||
545 | ||
546 | def lzc_remap_translate_error(ret, name): | |
547 | if ret == 0: | |
548 | return | |
549 | if ret == errno.ENOENT: | |
550 | raise lzc_exc.DatasetNotFound(name) | |
551 | if ret == errno.EINVAL: | |
552 | _validate_fs_name(name) | |
553 | if ret == errno.ENOTSUP: | |
554 | return lzc_exc.FeatureNotSupported(name) | |
555 | raise _generic_exception(ret, name, "Failed to remap dataset") | |
556 | ||
557 | ||
c962fd6c | 558 | def lzc_pool_checkpoint_translate_error(ret, name, discard=False): |
559 | if ret == 0: | |
560 | return | |
561 | if ret == errno.ENOENT: | |
562 | raise lzc_exc.PoolNotFound(name) | |
563 | if ret == ZFS_ERR_CHECKPOINT_EXISTS: | |
564 | raise lzc_exc.CheckpointExists() | |
565 | if ret == ZFS_ERR_NO_CHECKPOINT: | |
566 | raise lzc_exc.CheckpointNotFound() | |
567 | if ret == ZFS_ERR_DISCARDING_CHECKPOINT: | |
568 | raise lzc_exc.CheckpointDiscarding() | |
569 | if ret == ZFS_ERR_DEVRM_IN_PROGRESS: | |
570 | raise lzc_exc.DeviceRemovalRunning() | |
571 | if ret == ZFS_ERR_VDEV_TOO_BIG: | |
572 | raise lzc_exc.DeviceTooBig() | |
573 | if discard: | |
574 | raise _generic_exception( | |
575 | ret, name, "Failed to discard pool checkpoint") | |
576 | else: | |
577 | raise _generic_exception(ret, name, "Failed to create pool checkpoint") | |
578 | ||
579 | ||
580 | def lzc_pool_checkpoint_discard_translate_error(ret, name): | |
581 | lzc_pool_checkpoint_translate_error(ret, name, discard=True) | |
582 | ||
583 | ||
6abf9225 AG |
584 | def lzc_rename_translate_error(ret, source, target): |
585 | if ret == 0: | |
586 | return | |
587 | if ret == errno.EINVAL: | |
588 | _validate_fs_name(source) | |
589 | _validate_fs_name(target) | |
590 | if _pool_name(source) != _pool_name(target): | |
591 | raise lzc_exc.PoolsDiffer(source) | |
592 | if ret == errno.EEXIST: | |
593 | raise lzc_exc.FilesystemExists(target) | |
594 | if ret == errno.ENOENT: | |
595 | raise lzc_exc.FilesystemNotFound(source) | |
596 | raise _generic_exception(ret, source, "Failed to rename dataset") | |
597 | ||
598 | ||
599 | def lzc_destroy_translate_error(ret, name): | |
600 | if ret == 0: | |
601 | return | |
602 | if ret == errno.EINVAL: | |
603 | _validate_fs_name(name) | |
604 | if ret == errno.ENOENT: | |
605 | raise lzc_exc.FilesystemNotFound(name) | |
606 | raise _generic_exception(ret, name, "Failed to destroy dataset") | |
607 | ||
608 | ||
609 | def lzc_inherit_prop_translate_error(ret, name, prop): | |
610 | if ret == 0: | |
611 | return | |
612 | if ret == errno.EINVAL: | |
613 | _validate_fs_name(name) | |
614 | raise lzc_exc.PropertyInvalid(prop) | |
615 | if ret == errno.ENOENT: | |
616 | raise lzc_exc.DatasetNotFound(name) | |
617 | raise _generic_exception(ret, name, "Failed to inherit a property") | |
618 | ||
619 | ||
620 | def lzc_set_prop_translate_error(ret, name, prop, val): | |
621 | if ret == 0: | |
622 | return | |
623 | if ret == errno.EINVAL: | |
624 | _validate_fs_or_snap_name(name) | |
625 | raise lzc_exc.PropertyInvalid(prop) | |
626 | if ret == errno.ENOENT: | |
627 | raise lzc_exc.DatasetNotFound(name) | |
628 | raise _generic_exception(ret, name, "Failed to set a property") | |
629 | ||
630 | ||
631 | def lzc_get_props_translate_error(ret, name): | |
632 | if ret == 0: | |
633 | return | |
634 | if ret == errno.EINVAL: | |
635 | _validate_fs_or_snap_name(name) | |
636 | if ret == errno.ENOENT: | |
637 | raise lzc_exc.DatasetNotFound(name) | |
638 | raise _generic_exception(ret, name, "Failed to get properties") | |
639 | ||
640 | ||
641 | def lzc_list_children_translate_error(ret, name): | |
642 | if ret == 0: | |
643 | return | |
644 | if ret == errno.EINVAL: | |
645 | _validate_fs_name(name) | |
646 | raise _generic_exception(ret, name, "Error while iterating children") | |
647 | ||
648 | ||
649 | def lzc_list_snaps_translate_error(ret, name): | |
650 | if ret == 0: | |
651 | return | |
652 | if ret == errno.EINVAL: | |
653 | _validate_fs_name(name) | |
654 | raise _generic_exception(ret, name, "Error while iterating snapshots") | |
655 | ||
656 | ||
657 | def lzc_list_translate_error(ret, name, opts): | |
658 | if ret == 0: | |
659 | return | |
660 | if ret == errno.ENOENT: | |
661 | raise lzc_exc.DatasetNotFound(name) | |
662 | if ret == errno.EINVAL: | |
663 | _validate_fs_or_snap_name(name) | |
664 | raise _generic_exception(ret, name, "Error obtaining a list") | |
665 | ||
666 | ||
667 | def _handle_err_list(ret, errlist, names, exception, mapper): | |
668 | ''' | |
669 | Convert one or more errors from an operation into the requested exception. | |
670 | ||
671 | :param int ret: the overall return code. | |
85ce3f4f | 672 | :param errlist: the dictionary that maps entity names to their specific |
673 | error codes. | |
6abf9225 | 674 | :type errlist: dict of bytes:int |
85ce3f4f | 675 | :param names: the list of all names of the entities on which the operation |
676 | was attempted. | |
677 | :param type exception: the type of the exception to raise if an error | |
678 | occurred. The exception should be a subclass of | |
679 | ``MultipleOperationsFailure``. | |
680 | :param function mapper: the function that maps an error code and a name to | |
681 | a Python exception. | |
6abf9225 AG |
682 | |
683 | Unless ``ret`` is zero this function will raise the ``exception``. | |
85ce3f4f | 684 | If the ``errlist`` is not empty, then the compound exception will contain |
685 | a list of exceptions corresponding to each individual error code in the | |
686 | ``errlist``. | |
687 | Otherwise, the ``exception`` will contain a list with a single exception | |
688 | corresponding to the ``ret`` value. If the ``names`` list contains only one | |
689 | element, that is, the operation was attempted on a single entity, then the | |
690 | name of that entity is passed to the ``mapper``. | |
691 | If the operation was attempted on multiple entities, but the ``errlist`` | |
692 | is empty, then we can not know which entity caused the error and, thus, | |
693 | ``None`` is used as a name to signify that fact. | |
6abf9225 AG |
694 | |
695 | .. note:: | |
85ce3f4f | 696 | Note that the ``errlist`` can contain a special element with a key of |
697 | "N_MORE_ERRORS". | |
698 | That element means that there were too many errors to place on the | |
699 | ``errlist``. | |
700 | Those errors are suppressed and only their count is provided as a | |
701 | value of the special ``N_MORE_ERRORS`` element. | |
6abf9225 AG |
702 | ''' |
703 | if ret == 0: | |
704 | return | |
705 | ||
706 | if len(errlist) == 0: | |
707 | suppressed_count = 0 | |
708 | if len(names) == 1: | |
709 | name = names[0] | |
710 | else: | |
711 | name = None | |
712 | errors = [mapper(ret, name)] | |
713 | else: | |
714 | errors = [] | |
715 | suppressed_count = errlist.pop('N_MORE_ERRORS', 0) | |
716 | for name, err in errlist.iteritems(): | |
717 | errors.append(mapper(err, name)) | |
718 | ||
719 | raise exception(errors, suppressed_count) | |
720 | ||
721 | ||
722 | def _pool_name(name): | |
723 | ''' | |
724 | Extract a pool name from the given dataset or bookmark name. | |
725 | ||
726 | '/' separates dataset name components. | |
727 | '@' separates a snapshot name from the rest of the dataset name. | |
728 | '#' separates a bookmark name from the rest of the dataset name. | |
729 | ''' | |
730 | return re.split('[/@#]', name, 1)[0] | |
731 | ||
732 | ||
733 | def _fs_name(name): | |
734 | ''' | |
735 | Extract a dataset name from the given snapshot or bookmark name. | |
736 | ||
737 | '@' separates a snapshot name from the rest of the dataset name. | |
738 | '#' separates a bookmark name from the rest of the dataset name. | |
739 | ''' | |
740 | return re.split('[@#]', name, 1)[0] | |
741 | ||
742 | ||
743 | def _is_valid_name_component(component): | |
744 | allowed = string.ascii_letters + string.digits + '-_.: ' | |
745 | return component and all(x in allowed for x in component) | |
746 | ||
747 | ||
748 | def _is_valid_fs_name(name): | |
749 | return name and all(_is_valid_name_component(c) for c in name.split('/')) | |
750 | ||
751 | ||
752 | def _is_valid_snap_name(name): | |
753 | parts = name.split('@') | |
754 | return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and | |
755 | _is_valid_name_component(parts[1])) | |
756 | ||
757 | ||
758 | def _is_valid_bmark_name(name): | |
759 | parts = name.split('#') | |
760 | return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and | |
761 | _is_valid_name_component(parts[1])) | |
762 | ||
763 | ||
764 | def _validate_fs_name(name): | |
765 | if not _is_valid_fs_name(name): | |
766 | raise lzc_exc.FilesystemNameInvalid(name) | |
767 | elif len(name) > MAXNAMELEN: | |
768 | raise lzc_exc.NameTooLong(name) | |
769 | ||
770 | ||
771 | def _validate_snap_name(name): | |
772 | if not _is_valid_snap_name(name): | |
773 | raise lzc_exc.SnapshotNameInvalid(name) | |
774 | elif len(name) > MAXNAMELEN: | |
775 | raise lzc_exc.NameTooLong(name) | |
776 | ||
777 | ||
778 | def _validate_bmark_name(name): | |
779 | if not _is_valid_bmark_name(name): | |
780 | raise lzc_exc.BookmarkNameInvalid(name) | |
781 | elif len(name) > MAXNAMELEN: | |
782 | raise lzc_exc.NameTooLong(name) | |
783 | ||
784 | ||
785 | def _validate_fs_or_snap_name(name): | |
786 | if not _is_valid_fs_name(name) and not _is_valid_snap_name(name): | |
787 | raise lzc_exc.NameInvalid(name) | |
788 | elif len(name) > MAXNAMELEN: | |
789 | raise lzc_exc.NameTooLong(name) | |
790 | ||
791 | ||
792 | def _generic_exception(err, name, message): | |
793 | if err in _error_to_exception: | |
794 | return _error_to_exception[err](name) | |
795 | else: | |
796 | return lzc_exc.ZFSGenericError(err, message, name) | |
797 | ||
85ce3f4f | 798 | |
6abf9225 AG |
799 | _error_to_exception = {e.errno: e for e in [ |
800 | lzc_exc.ZIOError, | |
801 | lzc_exc.NoSpace, | |
802 | lzc_exc.QuotaExceeded, | |
803 | lzc_exc.DatasetBusy, | |
804 | lzc_exc.NameTooLong, | |
805 | lzc_exc.ReadOnlyPool, | |
806 | lzc_exc.SuspendedPool, | |
807 | lzc_exc.PoolsDiffer, | |
808 | lzc_exc.PropertyNotSupported, | |
809 | ]} | |
810 | ||
811 | ||
812 | # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 |