1#
2# Copyright 2015 ClusterHQ
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Exceptions that can be raised by libzfs_core operations.
19"""
20from __future__ import absolute_import, division, print_function
21
22import errno
23from ._constants import (
24    ECHRNG,
25    ECKSUM,
26    ETIME,
27    ZFS_ERR_CHECKPOINT_EXISTS,
28    ZFS_ERR_DISCARDING_CHECKPOINT,
29    ZFS_ERR_NO_CHECKPOINT,
30    ZFS_ERR_DEVRM_IN_PROGRESS,
31    ZFS_ERR_VDEV_TOO_BIG,
32    ZFS_ERR_WRONG_PARENT,
33    ZFS_ERR_RAIDZ_EXPAND_IN_PROGRESS,
34    zfs_errno
35)
36
37
38class ZFSError(Exception):
39    errno = None
40    message = None
41    name = None
42
43    def __str__(self):
44        if self.name is not None:
45            return "[Errno %d] %s: '%s'" % (
46                self.errno, self.message, self.name)
47        else:
48            return "[Errno %d] %s" % (self.errno, self.message)
49
50    def __repr__(self):
51        return "%s(%r, %r)" % (
52            self.__class__.__name__, self.errno, self.message)
53
54
55class ZFSGenericError(ZFSError):
56
57    def __init__(self, errno, name, message):
58        self.errno = errno
59        self.message = message
60        self.name = name
61
62
63class ZFSInitializationFailed(ZFSError):
64    message = "Failed to initialize libzfs_core"
65
66    def __init__(self, errno):
67        self.errno = errno
68
69
70class MultipleOperationsFailure(ZFSError):
71
72    def __init__(self, errors, suppressed_count):
73        # Use first of the individual error codes
74        # as an overall error code.  This is more consistent.
75        self.errno = errors[0].errno
76        self.errors = errors
77        # this many errors were encountered but not placed on the `errors` list
78        self.suppressed_count = suppressed_count
79
80    def __str__(self):
81        return "%s, %d errors included, %d suppressed" % (
82            ZFSError.__str__(self), len(self.errors), self.suppressed_count)
83
84    def __repr__(self):
85        return "%s(%r, %r, errors=%r, suppressed=%r)" % (
86            self.__class__.__name__, self.errno, self.message, self.errors,
87            self.suppressed_count)
88
89
90class DatasetNotFound(ZFSError):
91
92    """
93    This exception is raised when an operation failure can be caused by a
94    missing snapshot or a missing filesystem and it is impossible to
95    distinguish between the causes.
96    """
97    errno = errno.ENOENT
98    message = "Dataset not found"
99
100    def __init__(self, name):
101        self.name = name
102
103
104class DatasetExists(ZFSError):
105
106    """
107    This exception is raised when an operation failure can be caused by an
108    existing snapshot or filesystem and it is impossible to distinguish between
109    the causes.
110    """
111    errno = errno.EEXIST
112    message = "Dataset already exists"
113
114    def __init__(self, name):
115        self.name = name
116
117
118class NotClone(ZFSError):
119    errno = errno.EINVAL
120    message = "Filesystem is not a clone, can not promote"
121
122    def __init__(self, name):
123        self.name = name
124
125
126class FilesystemExists(DatasetExists):
127    message = "Filesystem already exists"
128
129    def __init__(self, name):
130        self.name = name
131
132
133class FilesystemNotFound(DatasetNotFound):
134    message = "Filesystem not found"
135
136    def __init__(self, name):
137        self.name = name
138
139
140class ParentNotFound(ZFSError):
141    errno = errno.ENOENT
142    message = "Parent not found"
143
144    def __init__(self, name):
145        self.name = name
146
147
148class WrongParent(ZFSError):
149    errno = ZFS_ERR_WRONG_PARENT
150    message = "Parent dataset is not a filesystem"
151
152    def __init__(self, name):
153        self.name = name
154
155
156class SnapshotExists(DatasetExists):
157    message = "Snapshot already exists"
158
159    def __init__(self, name):
160        self.name = name
161
162
163class SnapshotNotFound(DatasetNotFound):
164    message = "Snapshot not found"
165
166    def __init__(self, name):
167        self.name = name
168
169
170class SnapshotNotLatest(ZFSError):
171    errno = errno.EEXIST
172    message = "Snapshot is not the latest"
173
174    def __init__(self, name):
175        self.name = name
176
177
178class SnapshotIsCloned(ZFSError):
179    errno = errno.EEXIST
180    message = "Snapshot is cloned"
181
182    def __init__(self, name):
183        self.name = name
184
185
186class SnapshotIsHeld(ZFSError):
187    errno = errno.EBUSY
188    message = "Snapshot is held"
189
190    def __init__(self, name):
191        self.name = name
192
193
194class DuplicateSnapshots(ZFSError):
195    errno = errno.EXDEV
196    message = "Requested multiple snapshots of the same filesystem"
197
198    def __init__(self, name):
199        self.name = name
200
201
202class SnapshotFailure(MultipleOperationsFailure):
203    message = "Creation of snapshot(s) failed for one or more reasons"
204
205    def __init__(self, errors, suppressed_count):
206        super(SnapshotFailure, self).__init__(errors, suppressed_count)
207
208
209class SnapshotDestructionFailure(MultipleOperationsFailure):
210    message = "Destruction of snapshot(s) failed for one or more reasons"
211
212    def __init__(self, errors, suppressed_count):
213        super(SnapshotDestructionFailure, self).__init__(
214            errors, suppressed_count)
215
216
217class BookmarkExists(ZFSError):
218    errno = errno.EEXIST
219    message = "Bookmark already exists"
220
221    def __init__(self, name):
222        self.name = name
223
224
225class BookmarkNotFound(ZFSError):
226    errno = errno.ENOENT
227    message = "Bookmark not found"
228
229    def __init__(self, name):
230        self.name = name
231
232
233class BookmarkMismatch(ZFSError):
234    errno = errno.EINVAL
235    message = "source is not an ancestor of the new bookmark's dataset"
236
237    def __init__(self, name):
238        self.name = name
239
240
241class BookmarkSourceInvalid(ZFSError):
242    errno = errno.EINVAL
243    message = "Bookmark source is not a valid snapshot or existing bookmark"
244
245    def __init__(self, name):
246        self.name = name
247
248
249class BookmarkNotSupported(ZFSError):
250    errno = errno.ENOTSUP
251    message = "Bookmark feature is not supported"
252
253    def __init__(self, name):
254        self.name = name
255
256
257class BookmarkFailure(MultipleOperationsFailure):
258    message = "Creation of bookmark(s) failed for one or more reasons"
259
260    def __init__(self, errors, suppressed_count):
261        super(BookmarkFailure, self).__init__(errors, suppressed_count)
262
263
264class BookmarkDestructionFailure(MultipleOperationsFailure):
265    message = "Destruction of bookmark(s) failed for one or more reasons"
266
267    def __init__(self, errors, suppressed_count):
268        super(BookmarkDestructionFailure, self).__init__(
269            errors, suppressed_count)
270
271
272class BadHoldCleanupFD(ZFSError):
273    errno = errno.EBADF
274    message = "Bad file descriptor as cleanup file descriptor"
275
276
277class HoldExists(ZFSError):
278    errno = errno.EEXIST
279    message = "Hold with a given tag already exists on snapshot"
280
281    def __init__(self, name):
282        self.name = name
283
284
285class HoldNotFound(ZFSError):
286    errno = errno.ENOENT
287    message = "Hold with a given tag does not exist on snapshot"
288
289    def __init__(self, name):
290        self.name = name
291
292
293class HoldFailure(MultipleOperationsFailure):
294    message = "Placement of hold(s) failed for one or more reasons"
295
296    def __init__(self, errors, suppressed_count):
297        super(HoldFailure, self).__init__(errors, suppressed_count)
298
299
300class HoldReleaseFailure(MultipleOperationsFailure):
301    message = "Release of hold(s) failed for one or more reasons"
302
303    def __init__(self, errors, suppressed_count):
304        super(HoldReleaseFailure, self).__init__(errors, suppressed_count)
305
306
307class SnapshotMismatch(ZFSError):
308    errno = errno.ENODEV
309    message = "Snapshot is not descendant of source snapshot"
310
311    def __init__(self, name):
312        self.name = name
313
314
315class StreamMismatch(ZFSError):
316    errno = errno.ENODEV
317    message = "Stream is not applicable to destination dataset"
318
319    def __init__(self, name):
320        self.name = name
321
322
323class DestinationModified(ZFSError):
324    errno = errno.ETXTBSY
325    message = "Destination dataset has modifications that can not be undone"
326
327    def __init__(self, name):
328        self.name = name
329
330
331class BadStream(ZFSError):
332    errno = ECKSUM
333    message = "Bad backup stream"
334
335
336class StreamFeatureNotSupported(ZFSError):
337    errno = errno.ENOTSUP
338    message = "Stream contains unsupported feature"
339
340
341class UnknownStreamFeature(ZFSError):
342    errno = errno.ENOTSUP
343    message = "Unknown feature requested for stream"
344
345
346class StreamFeatureInvalid(ZFSError):
347    errno = errno.EINVAL
348    message = "Kernel modules must be upgraded to receive this stream"
349
350
351class StreamFeatureIncompatible(ZFSError):
352    errno = errno.EINVAL
353    message = "Incompatible embedded feature with encrypted receive"
354
355
356class StreamTruncated(ZFSError):
357    errno = zfs_errno.ZFS_ERR_STREAM_TRUNCATED
358    message = "incomplete stream"
359
360
361class ReceivePropertyFailure(MultipleOperationsFailure):
362    message = "Receiving of properties failed for one or more reasons"
363
364    def __init__(self, errors, suppressed_count):
365        super(ReceivePropertyFailure, self).__init__(errors, suppressed_count)
366
367
368class StreamIOError(ZFSError):
369    message = "I/O error while writing or reading stream"
370
371    def __init__(self, errno):
372        self.errno = errno
373
374
375class ZIOError(ZFSError):
376    errno = errno.EIO
377    message = "I/O error"
378
379    def __init__(self, name):
380        self.name = name
381
382
383class NoSpace(ZFSError):
384    errno = errno.ENOSPC
385    message = "No space left"
386
387    def __init__(self, name):
388        self.name = name
389
390
391class QuotaExceeded(ZFSError):
392    errno = errno.EDQUOT
393    message = "Quota exceeded"
394
395    def __init__(self, name):
396        self.name = name
397
398
399class DatasetBusy(ZFSError):
400    errno = errno.EBUSY
401    message = "Dataset is busy"
402
403    def __init__(self, name):
404        self.name = name
405
406
407class NameTooLong(ZFSError):
408    errno = errno.ENAMETOOLONG
409    message = "Dataset name is too long"
410
411    def __init__(self, name):
412        self.name = name
413
414
415class NameInvalid(ZFSError):
416    errno = errno.EINVAL
417    message = "Invalid name"
418
419    def __init__(self, name):
420        self.name = name
421
422
423class SnapshotNameInvalid(NameInvalid):
424    message = "Invalid name for snapshot"
425
426    def __init__(self, name):
427        self.name = name
428
429
430class FilesystemNameInvalid(NameInvalid):
431    message = "Invalid name for filesystem or volume"
432
433    def __init__(self, name):
434        self.name = name
435
436
437class BookmarkNameInvalid(NameInvalid):
438    message = "Invalid name for bookmark"
439
440    def __init__(self, name):
441        self.name = name
442
443
444class ReadOnlyPool(ZFSError):
445    errno = errno.EROFS
446    message = "Pool is read-only"
447
448    def __init__(self, name):
449        self.name = name
450
451
452class SuspendedPool(ZFSError):
453    errno = errno.EAGAIN
454    message = "Pool is suspended"
455
456    def __init__(self, name):
457        self.name = name
458
459
460class PoolNotFound(ZFSError):
461    errno = errno.EXDEV
462    message = "No such pool"
463
464    def __init__(self, name):
465        self.name = name
466
467
468class PoolsDiffer(ZFSError):
469    errno = errno.EXDEV
470    message = "Source and target belong to different pools"
471
472    def __init__(self, name):
473        self.name = name
474
475
476class FeatureNotSupported(ZFSError):
477    errno = errno.ENOTSUP
478    message = "Feature is not supported in this version"
479
480    def __init__(self, name):
481        self.name = name
482
483
484class PropertyNotSupported(ZFSError):
485    errno = errno.ENOTSUP
486    message = "Property is not supported in this version"
487
488    def __init__(self, name):
489        self.name = name
490
491
492class PropertyInvalid(ZFSError):
493    errno = errno.EINVAL
494    message = "Invalid property or property value"
495
496    def __init__(self, name):
497        self.name = name
498
499
500class DatasetTypeInvalid(ZFSError):
501    errno = errno.EINVAL
502    message = "Specified dataset type is unknown"
503
504    def __init__(self, name):
505        self.name = name
506
507
508class UnknownCryptCommand(ZFSError):
509    errno = errno.EINVAL
510    message = "Specified crypt command is invalid"
511
512    def __init__(self, name):
513        self.name = name
514
515
516class EncryptionKeyNotLoaded(ZFSError):
517    errno = errno.EACCES
518    message = "Encryption key is not currently loaded"
519
520
521class EncryptionKeyAlreadyLoaded(ZFSError):
522    errno = errno.EEXIST
523    message = "Encryption key is already loaded"
524
525
526class EncryptionKeyInvalid(ZFSError):
527    errno = errno.EACCES
528    message = "Incorrect encryption key provided"
529
530
531class ZCPError(ZFSError):
532    errno = None
533    message = None
534
535
536class ZCPSyntaxError(ZCPError):
537    errno = errno.EINVAL
538    message = "Channel program contains syntax errors"
539
540    def __init__(self, details):
541        self.details = details
542
543
544class ZCPRuntimeError(ZCPError):
545    errno = ECHRNG
546    message = "Channel programs encountered a runtime error"
547
548    def __init__(self, details):
549        self.details = details
550
551
552class ZCPLimitInvalid(ZCPError):
553    errno = errno.EINVAL
554    message = "Channel program called with invalid limits"
555
556
557class ZCPTimeout(ZCPError):
558    errno = ETIME
559    message = "Channel program timed out"
560
561
562class ZCPSpaceError(ZCPError):
563    errno = errno.ENOSPC
564    message = "Channel program exhausted the memory limit"
565
566
567class ZCPMemoryError(ZCPError):
568    errno = errno.ENOMEM
569    message = "Channel program return value too large"
570
571
572class ZCPPermissionError(ZCPError):
573    errno = errno.EPERM
574    message = "Channel programs must be run as root"
575
576
577class CheckpointExists(ZFSError):
578    errno = ZFS_ERR_CHECKPOINT_EXISTS
579    message = "Pool already has a checkpoint"
580
581
582class CheckpointNotFound(ZFSError):
583    errno = ZFS_ERR_NO_CHECKPOINT
584    message = "Pool does not have a checkpoint"
585
586
587class CheckpointDiscarding(ZFSError):
588    errno = ZFS_ERR_DISCARDING_CHECKPOINT
589    message = "Pool checkpoint is being discarded"
590
591
592class DeviceRemovalRunning(ZFSError):
593    errno = ZFS_ERR_DEVRM_IN_PROGRESS
594    message = "A vdev is currently being removed"
595
596
597class DeviceTooBig(ZFSError):
598    errno = ZFS_ERR_VDEV_TOO_BIG
599    message = "One or more top-level vdevs exceed the maximum vdev size"
600
601
602class RaidzExpansionRunning(ZFSError):
603    errno = ZFS_ERR_RAIDZ_EXPAND_IN_PROGRESS
604    message = "A raidz device is currently expanding"
605
606
607# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
608