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