forked from truenas/py-libzfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
libzfs.pyx
3474 lines (2686 loc) · 105 KB
/
libzfs.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# encoding: utf-8
# cython: language_level=3, c_string_type=unicode, c_string_encoding=default
import os
import stat
import enum
import errno
import itertools
import logging
import time
import threading
cimport libzfs
cimport zfs
cimport nvpair
from datetime import datetime
from libc.errno cimport errno
from libc.string cimport memset, strncpy
from libc.stdlib cimport realloc
GLOBAL_CONTEXT_LOCK = threading.Lock()
logger = logging.getLogger(__name__)
include "config.pxi"
include "nvpair.pxi"
include "converter.pxi"
class DatasetType(enum.IntEnum):
FILESYSTEM = zfs.ZFS_TYPE_FILESYSTEM
VOLUME = zfs.ZFS_TYPE_VOLUME
SNAPSHOT = zfs.ZFS_TYPE_SNAPSHOT
BOOKMARK = zfs.ZFS_TYPE_BOOKMARK
class Error(enum.IntEnum):
SUCCESS = libzfs.EZFS_SUCCESS
NOMEM = libzfs.EZFS_NOMEM
BADPROP = libzfs.EZFS_BADPROP
PROPREADONLY = libzfs.EZFS_PROPREADONLY
PROPTYPE = libzfs.EZFS_PROPTYPE
PROPNONINHERIT = libzfs.EZFS_PROPNONINHERIT
PROPSPACE = libzfs.EZFS_PROPSPACE
BADTYPE = libzfs.EZFS_BADTYPE
BUSY = libzfs.EZFS_BUSY
EXISTS = libzfs.EZFS_EXISTS
NOENT = libzfs.EZFS_NOENT
BADSTREAM = libzfs.EZFS_BADSTREAM
DSREADONLY = libzfs.EZFS_DSREADONLY
VOLTOOBIG = libzfs.EZFS_VOLTOOBIG
INVALIDNAME = libzfs.EZFS_INVALIDNAME
BADRESTORE = libzfs.EZFS_BADRESTORE
BADBACKUP = libzfs.EZFS_BADBACKUP
BADTARGET = libzfs.EZFS_BADTARGET
NODEVICE = libzfs.EZFS_NODEVICE
BADDEV = libzfs.EZFS_BADDEV
NOREPLICAS = libzfs.EZFS_NOREPLICAS
RESILVERING = libzfs.EZFS_RESILVERING
BADVERSION = libzfs.EZFS_BADVERSION
POOLUNAVAIL = libzfs.EZFS_POOLUNAVAIL
DEVOVERFLOW = libzfs.EZFS_DEVOVERFLOW
BADPATH = libzfs.EZFS_BADPATH
CROSSTARGET = libzfs.EZFS_CROSSTARGET
ZONED = libzfs.EZFS_ZONED
MOUNTFAILED = libzfs.EZFS_MOUNTFAILED
UMOUNTFAILED = libzfs.EZFS_UMOUNTFAILED
UNSHARENFSFAILED = libzfs.EZFS_UNSHARENFSFAILED
SHARENFSFAILED = libzfs.EZFS_SHARENFSFAILED
PERM = libzfs.EZFS_PERM
NOSPC = libzfs.EZFS_NOSPC
FAULT = libzfs.EZFS_FAULT
IO = libzfs.EZFS_IO
INTR = libzfs.EZFS_INTR
ISSPARE = libzfs.EZFS_ISSPARE
INVALCONFIG = libzfs.EZFS_INVALCONFIG
RECURSIVE = libzfs.EZFS_RECURSIVE
NOHISTORY = libzfs.EZFS_NOHISTORY
POOLPROPS = libzfs.EZFS_POOLPROPS
POOL_NOTSUP = libzfs.EZFS_POOL_NOTSUP
INVALARG = libzfs.EZFS_POOL_INVALARG
NAMETOOLONG = libzfs.EZFS_NAMETOOLONG
OPENFAILED = libzfs.EZFS_OPENFAILED
NOCAP = libzfs.EZFS_NOCAP
LABELFAILED = libzfs.EZFS_LABELFAILED
BADWHO = libzfs.EZFS_BADWHO
BADPERM = libzfs.EZFS_BADPERM
BADPERMSET = libzfs.EZFS_BADPERMSET
NODELEGATION = libzfs.EZFS_NODELEGATION
UNSHARESMBFAILED = libzfs.EZFS_UNSHARESMBFAILED
SHARESMBFAILED = libzfs.EZFS_SHARESMBFAILED
BADCACHE = libzfs.EZFS_BADCACHE
ISL2CACHE = libzfs.EZFS_ISL2CACHE
VDEVNOTSUP = libzfs.EZFS_VDEVNOTSUP
NOTSUP = libzfs.EZFS_NOTSUP
SPARE = libzfs.EZFS_ACTIVE_SPARE
LOGS = libzfs.EZFS_UNPLAYED_LOGS
RELE = libzfs.EZFS_REFTAG_RELE
HOLD = libzfs.EZFS_REFTAG_HOLD
TAGTOOLONG = libzfs.EZFS_TAGTOOLONG
PIPEFAILED = libzfs.EZFS_PIPEFAILED
THREADCREATEFAILED = libzfs.EZFS_THREADCREATEFAILED
ONLINE = libzfs.EZFS_POSTSPLIT_ONLINE
SCRUBBING = libzfs.EZFS_SCRUBBING
SCRUB = libzfs.EZFS_NO_SCRUB
DIFF = libzfs.EZFS_DIFF
DIFFDATA = libzfs.EZFS_DIFFDATA
POOLREADONLY = libzfs.EZFS_POOLREADONLY
UNKNOWN = libzfs.EZFS_UNKNOWN
class PropertySource(enum.IntEnum):
NONE = zfs.ZPROP_SRC_NONE
DEFAULT = zfs.ZPROP_SRC_DEFAULT
TEMPORARY = zfs.ZPROP_SRC_TEMPORARY
LOCAL = zfs.ZPROP_SRC_LOCAL
INHERITED = zfs.ZPROP_SRC_INHERITED
RECEIVED = zfs.ZPROP_SRC_RECEIVED
class VDevState(enum.IntEnum):
UNKNOWN = zfs.VDEV_STATE_UNKNOWN
CLOSED = zfs.VDEV_STATE_CLOSED
OFFLINE = zfs.VDEV_STATE_OFFLINE
REMOVED = zfs.VDEV_STATE_REMOVED
CANT_OPEN = zfs.VDEV_STATE_CANT_OPEN
FAULTED = zfs.VDEV_STATE_FAULTED
DEGRADED = zfs.VDEV_STATE_DEGRADED
HEALTHY = zfs.VDEV_STATE_HEALTHY
class VDevAuxState(enum.IntEnum):
NONE = zfs.VDEV_AUX_NONE
OPEN_FAILED = zfs.VDEV_AUX_OPEN_FAILED
CORRUPT_DATA = zfs.VDEV_AUX_CORRUPT_DATA
NO_REPLICAS = zfs.VDEV_AUX_NO_REPLICAS
BAD_GUID_SUM = zfs.VDEV_AUX_BAD_GUID_SUM
TOO_SMALL = zfs.VDEV_AUX_TOO_SMALL
BAD_LABEL = zfs.VDEV_AUX_BAD_LABEL
VERSION_NEWER = zfs.VDEV_AUX_VERSION_NEWER
VERSION_OLDER = zfs.VDEV_AUX_VERSION_OLDER
UNSUP_FEAT = zfs.VDEV_AUX_UNSUP_FEAT
SPARED = zfs.VDEV_AUX_SPARED
ERR_EXCEEDED = zfs.VDEV_AUX_ERR_EXCEEDED
IO_FAILURE = zfs.VDEV_AUX_IO_FAILURE
BAD_LOG = zfs.VDEV_AUX_BAD_LOG
EXTERNAL = zfs.VDEV_AUX_EXTERNAL
SPLIT_POOL = zfs.VDEV_AUX_SPLIT_POOL
IF HAVE_VDEV_AUX_ASHIFT_TOO_BIG:
ASHIFT_TOO_BIG = zfs.VDEV_AUX_ASHIFT_TOO_BIG
class PoolState(enum.IntEnum):
ACTIVE = zfs.POOL_STATE_ACTIVE
EXPORTED = zfs.POOL_STATE_EXPORTED
DESTROYED = zfs.POOL_STATE_DESTROYED
SPARE = zfs.POOL_STATE_SPARE
L2CACHE = zfs.POOL_STATE_L2CACHE
UNINITIALIZED = zfs.POOL_STATE_UNINITIALIZED
UNAVAIL = zfs.POOL_STATE_UNAVAIL
POTENTIALLY_ACTIVE = zfs.POOL_STATE_POTENTIALLY_ACTIVE
class ScanFunction(enum.IntEnum):
NONE = zfs.POOL_SCAN_NONE
SCRUB = zfs.POOL_SCAN_SCRUB
RESILVER = zfs.POOL_SCAN_RESILVER
class PoolStatus(enum.IntEnum):
CORRUPT_CACHE = libzfs.ZPOOL_STATUS_CORRUPT_CACHE
MISSING_DEV_R = libzfs.ZPOOL_STATUS_MISSING_DEV_R
MISSING_DEV_NR = libzfs.ZPOOL_STATUS_MISSING_DEV_NR
CORRUPT_LABEL_R = libzfs.ZPOOL_STATUS_CORRUPT_LABEL_R
CORRUPT_LABEL_NR = libzfs.ZPOOL_STATUS_CORRUPT_LABEL_NR
BAD_GUID_SUM = libzfs.ZPOOL_STATUS_BAD_GUID_SUM
CORRUPT_POOL = libzfs.ZPOOL_STATUS_CORRUPT_POOL
CORRUPT_DATA = libzfs.ZPOOL_STATUS_CORRUPT_DATA
FAILING_DEV = libzfs.ZPOOL_STATUS_FAILING_DEV
VERSION_NEWER = libzfs.ZPOOL_STATUS_VERSION_NEWER
HOSTID_MISMATCH = libzfs.ZPOOL_STATUS_HOSTID_MISMATCH
IO_FAILURE_WAIT = libzfs.ZPOOL_STATUS_IO_FAILURE_WAIT
IO_FAILURE_CONTINUE = libzfs.ZPOOL_STATUS_IO_FAILURE_CONTINUE
BAD_LOG = libzfs.ZPOOL_STATUS_BAD_LOG
UNSUP_FEAT_READ = libzfs.ZPOOL_STATUS_UNSUP_FEAT_READ
UNSUP_FEAT_WRITE = libzfs.ZPOOL_STATUS_UNSUP_FEAT_WRITE
FAULTED_DEV_R = libzfs.ZPOOL_STATUS_FAULTED_DEV_R
FAULTED_DEV_NR = libzfs.ZPOOL_STATUS_FAULTED_DEV_NR
VERSION_OLDER = libzfs.ZPOOL_STATUS_VERSION_OLDER
FEAT_DISABLED = libzfs.ZPOOL_STATUS_FEAT_DISABLED
RESILVERING = libzfs.ZPOOL_STATUS_RESILVERING
OFFLINE_DEV = libzfs.ZPOOL_STATUS_OFFLINE_DEV
REMOVED_DEV = libzfs.ZPOOL_STATUS_REMOVED_DEV
IF HAVE_ZPOOL_STATUS_NON_NATIVE_ASHIFT:
NON_NATIVE_ASHIFT = libzfs.ZPOOL_STATUS_NON_NATIVE_ASHIFT
OK = libzfs.ZPOOL_STATUS_OK
class ScanState(enum.IntEnum):
NONE = zfs.DSS_NONE
SCANNING = zfs.DSS_SCANNING
FINISHED = zfs.DSS_FINISHED
CANCELED = zfs.DSS_CANCELED
class ZIOType(enum.IntEnum):
NONE = zfs.ZIO_TYPE_NULL
READ = zfs.ZIO_TYPE_READ
WRITE = zfs.ZIO_TYPE_WRITE
FREE = zfs.ZIO_TYPE_FREE
CLAIM = zfs.ZIO_TYPE_CLAIM
IOCTL = zfs.ZIO_TYPE_IOCTL
class FeatureState(enum.Enum):
DISABLED = 0
ENABLED = 1
ACTIVE = 2
class SendFlag(enum.Enum):
IF HAVE_SENDFLAGS_T_VERBOSITY:
VERBOSITY = 0
ELSE:
VERBOSE = 0
REPLICATE = 1
DOALL = 2
FROMORIGIN = 3
DEDUP = 3
PROPS = 4
DRYRUN = 5
PARSABLE = 6
PROGRESS = 7
LARGEBLOCK = 8
EMBED_DATA = 9
IF HAVE_SENDFLAGS_T_COMPRESS:
COMPRESS = 10
class DiffRecordType(enum.Enum):
ADD = '+'
REMOVE = '-'
MODIFY = 'M'
RENAME = 'R'
class DiffFileType(enum.Enum):
BLOCK = 'B'
CHAR = 'C'
FILE = 'F'
DIRECTORY = '/'
SYMLINK = '@'
SOCKET = '='
IF HAVE_ZFS_MAX_DATASET_NAME_LEN:
cdef enum:
MAX_DATASET_NAME_LEN = zfs.ZFS_MAX_DATASET_NAME_LEN
ELSE:
cdef enum:
MAX_DATASET_NAME_LEN = libzfs.ZFS_MAXNAMELEN
cdef struct iter_state:
uintptr_t *array
size_t length
size_t alloc
cdef struct prop_iter_state:
zfs.zfs_type_t type
void *props
class DiffRecord(object):
def __init__(self, raw):
timestamp, cmd, typ, rest = raw.split(maxsplit=3)
paths = rest.split('->', maxsplit=2)
self.raw = raw
self.timestamp = datetime.utcfromtimestamp(float(timestamp))
self.cmd = DiffRecordType(cmd)
self.type = DiffFileType(typ)
self.path = paths[0].strip()
if self.cmd == DiffRecordType.RENAME:
self.oldpath = paths[1].strip()
def __str__(self):
return self.raw
def __repr__(self):
return str(self)
def __getstate__(self):
return {
'timestamp': self.timestamp,
'cmd': self.cmd.name,
'type': self.type.name,
'path': self.path,
'oldpath': getattr(self, 'oldpath', None)
}
IF HAVE_LZC_SEND_FLAG_EMBED_DATA:
class SendFlags(enum.IntEnum):
EMBED_DATA = libzfs.LZC_SEND_FLAG_EMBED_DATA
class ZFSException(RuntimeError):
def __init__(self, code, message):
super(ZFSException, self).__init__(message)
self.code = code
def __reduce__(self):
return (self.__class__, (self.code, self.args))
class ZFSVdevStatsException(ZFSException):
def __init__(self, code):
super(ZFSVdevStatsException, self).__init__(code, 'Failed to fetch ZFS Vdev Stats')
cdef class ZFS(object):
cdef libzfs.libzfs_handle_t* handle
cdef boolean_t mnttab_cache_enable
cdef int history
cdef char *history_prefix
proptypes = {}
def __cinit__(self, history=True, history_prefix='', mnttab_cache=True):
cdef zfs.zfs_type_t c_type
cdef prop_iter_state iter
self.mnttab_cache_enable=mnttab_cache
with nogil:
self.handle = libzfs.libzfs_init()
if isinstance(history, bool):
self.history = history
else:
raise ZFSException(Error.BADTYPE, 'history is a boolean parameter')
if self.history:
if isinstance(history_prefix, str):
self.history_prefix = history_prefix
else:
raise ZFSException(Error.BADTYPE, 'history_prefix is a string parameter')
for t in DatasetType.__members__.values():
proptypes = []
c_type = <zfs.zfs_type_t>t
iter.type = c_type
iter.props = <void *>proptypes
with nogil:
libzfs.zprop_iter(self.__iterate_props, <void*>&iter, True, True, c_type)
props = self.proptypes.setdefault(t, [])
if set(proptypes) != set(props):
self.proptypes[t] = proptypes
def __enter__(self):
GLOBAL_CONTEXT_LOCK.acquire()
return self
def __exit__(self, exc_type, value, traceback):
self.__libzfs_fini()
GLOBAL_CONTEXT_LOCK.release()
if exc_type is not None:
raise
def __libzfs_fini(self):
if self.handle:
with nogil:
libzfs.libzfs_fini(self.handle)
self.handle = NULL
def __dealloc__(self):
ZFS.__libzfs_fini(self)
def __getstate__(self):
return [p.__getstate__() for p in self.pools]
@staticmethod
cdef int __iterate_props(int proptype, void *arg) nogil:
cdef prop_iter_state *iter
cdef boolean_t ret = False
iter = <prop_iter_state *>arg
IF HAVE_ZFS_PROP_VALID_FOR_TYPE == 3:
ret = zfs.zfs_prop_valid_for_type(proptype, iter.type, ret)
ELSE:
ret = zfs.zfs_prop_valid_for_type(proptype, iter.type)
if not ret:
return zfs.ZPROP_CONT
with gil:
proptypes = <object>iter.props
proptypes.append(proptype)
return zfs.ZPROP_CONT
@staticmethod
cdef int __iterate_pools(libzfs.zpool_handle_t *handle, void *arg) nogil:
with gil:
pools = <object>arg
pools.append(<uintptr_t>handle)
cdef object get_error(self):
return ZFSException(
Error(libzfs.libzfs_errno(self.handle)),
libzfs.libzfs_error_description(self.handle)
)
cdef ZFSVdev make_vdev_tree(self, topology):
cdef ZFSVdev root
root = ZFSVdev(self, zfs.VDEV_TYPE_ROOT)
root.children = topology.get('data', [])
if 'cache' in topology:
root.nvlist[zfs.ZPOOL_CONFIG_L2CACHE] = [(<ZFSVdev>i).nvlist for i in topology['cache']]
if 'spare' in topology:
root.nvlist[zfs.ZPOOL_CONFIG_SPARES] = [(<ZFSVdev>i).nvlist for i in topology['spare']]
if 'log' in topology:
for i in topology['log']:
(<ZFSVdev>i).nvlist[zfs.ZPOOL_CONFIG_IS_LOG] = 1L
root.add_child_vdev(i)
return root
@staticmethod
cdef int __dataset_handles(libzfs.zfs_handle_t* handle, void *arg) nogil:
cdef int prop_id
cdef char csrcstr[MAX_DATASET_NAME_LEN + 1]
cdef char crawvalue[MAX_DATASET_NAME_LEN + 1]
cdef char cvalue[libzfs.ZFS_MAXPROPLEN + 1]
cdef zfs.zprop_source_t csource
cdef const char *name
cdef zfs.zfs_type_t typ
cdef nvpair.nvlist_t *nvlist
name = libzfs.zfs_get_name(handle)
typ = libzfs.zfs_get_type(handle)
nvlist = libzfs.zfs_get_user_props(handle)
with gil:
dataset_type = DatasetType(typ)
data_list = <object> arg
configuration_data = data_list[0]
data = data_list[1]
children = []
child_data = [configuration_data, {}]
properties = {}
for key, value in NVList(<uintptr_t>nvlist).items() if configuration_data['user_props'] else []:
src = 'NONE'
if value.get('source'):
src = value.pop('source')
if src == name:
src = PropertySource.LOCAL.name
elif src == '$recvd':
src = PropertySource.RECEIVED.name
else:
src = PropertySource.INHERITED.name
properties[key] = {
'value': value.get('value'),
'rawvalue': value.get('value'),
'source': src,
'parsed': value.get('value')
}
for prop_name, prop_id in configuration_data['props'].get(dataset_type, {}).items():
with nogil:
strncpy(cvalue, '', libzfs.ZFS_MAXPROPLEN + 1)
strncpy(crawvalue, '', MAX_DATASET_NAME_LEN + 1)
strncpy(csrcstr, '', MAX_DATASET_NAME_LEN + 1)
libzfs.zfs_prop_get(
handle, prop_id, cvalue, libzfs.ZFS_MAXPROPLEN,
&csource, csrcstr, MAX_DATASET_NAME_LEN, False
)
libzfs.zfs_prop_get(
handle, prop_id, crawvalue, libzfs.ZFS_MAXPROPLEN,
NULL, NULL, 0, True
)
properties[prop_name] = {
'parsed': parse_zfs_prop(prop_name, crawvalue),
'rawvalue': crawvalue,
'value': cvalue,
'source': PropertySource(<int>csource).name
}
libzfs.zfs_iter_filesystems(handle, ZFS.__dataset_handles, <void*>child_data)
with gil:
data[name] = {}
child_data = child_data[1]
data[name].update({
'properties': properties,
'id': name,
'type': dataset_type.name,
'children': list(child_data.values()),
'name': name,
'pool': configuration_data['pool']
})
for top_level_prop in configuration_data['top_level_props']:
data[name][top_level_prop] = properties.get(top_level_prop, {}).get('value')
if top_level_prop == 'mountpoint' and data[name][top_level_prop] == 'none':
data[name]['mountpoint'] = None
libzfs.zfs_close(handle)
def datasets_serialized(self, props=None, top_level_props=None, user_props=True):
cdef libzfs.zfs_handle_t* handle
cdef const char *c_name
cdef int prop_id
prop_mapping = {}
if top_level_props is None:
if props is None or 'mountpoint' in props:
# We want to add default mountpoint key here to keep existing behavior.
top_level_props = ['mountpoint']
else:
top_level_props = []
# If props is None, we include all properties, if it's an empty list, no property is retrieved
for dataset_type in [DatasetType.FILESYSTEM, DatasetType.VOLUME] if props is None or len(props) else []:
prop_mapping[dataset_type] = {}
for prop_id in ZFS.proptypes[dataset_type]:
with nogil:
prop_name = libzfs.zfs_prop_to_name(prop_id)
if props is None or prop_name in props:
prop_mapping[dataset_type][prop_name] = prop_id
all_props = set(itertools.chain(*[prop_mapping[t] for t in prop_mapping]))
for top_level_prop in top_level_props:
if top_level_prop not in all_props:
raise ValueError(f'{top_level_prop} should be present in props.')
for p in self.pools:
c_name = handle = NULL
name = p.name
c_name = name
dataset = [
{
'pool': name,
'props': prop_mapping,
'top_level_props': top_level_props,
'user_props': user_props,
},
{}
]
with nogil:
handle = libzfs.zfs_open(self.handle, c_name, zfs.ZFS_TYPE_FILESYSTEM)
if handle == NULL:
with gil:
e_args = self.get_error().args
logger.error(
'Failed to retrieve root dataset handle for %s: %s', c_name, e_args[0] if e_args else ''
)
continue
else:
ZFS.__dataset_handles(handle, <void*>dataset)
yield dataset[1][name]
@staticmethod
cdef int __snapshot_details(libzfs.zfs_handle_t *handle, void *arg) nogil:
cdef int prop_id, ret, simple_handle, holds, mounted
cdef char csrcstr[MAX_DATASET_NAME_LEN + 1]
cdef char crawvalue[MAX_DATASET_NAME_LEN + 1]
cdef char cvalue[libzfs.ZFS_MAXPROPLEN + 1]
cdef zfs.zprop_source_t csource
cdef const char *name, *mntpt
cdef nvpair.nvlist_t *ptr, *nvlist
with gil:
snap_list = <object> arg
configuration_data = snap_list[0]
pool = configuration_data['pool']
props = configuration_data['props']
holds = configuration_data['holds']
mounted = configuration_data['mounted']
properties = {}
simple_handle = len(props) == 1 and 'name' in props
snap_data = {}
IF HAVE_ZFS_ITER_SNAPSHOTS == 6:
libzfs.zfs_iter_snapshots(handle, simple_handle, ZFS.__snapshot_details, <void*>snap_list, 0, 0)
ELSE:
libzfs.zfs_iter_snapshots(handle, simple_handle, ZFS.__snapshot_details, <void*>snap_list)
if libzfs.zfs_get_type(handle) != zfs.ZFS_TYPE_SNAPSHOT:
return 0
nvlist = libzfs.zfs_get_user_props(handle)
name = libzfs.zfs_get_name(handle)
with gil:
# Gathering user props
nvl = NVList(<uintptr_t>nvlist)
for key, value in nvl.items():
src = 'NONE'
if value.get('source'):
src = value.pop('source')
if src == name:
src = PropertySource.LOCAL.name
elif src == '$recvd':
src = PropertySource.RECEIVED.name
else:
src = PropertySource.INHERITED.name
properties[key] = {
'value': value.get('value'),
'rawvalue': value.get('value'),
'source': src,
'parsed': value.get('value')
}
for prop_name, prop_id in (props if not simple_handle else {}).items():
with nogil:
strncpy(cvalue, '', libzfs.ZFS_MAXPROPLEN + 1)
strncpy(crawvalue, '', MAX_DATASET_NAME_LEN + 1)
strncpy(csrcstr, '', MAX_DATASET_NAME_LEN + 1)
libzfs.zfs_prop_get(
handle, prop_id, cvalue, libzfs.ZFS_MAXPROPLEN,
&csource, csrcstr, MAX_DATASET_NAME_LEN, False
)
libzfs.zfs_prop_get(
handle, prop_id, crawvalue, libzfs.ZFS_MAXPROPLEN,
NULL, NULL, 0, True
)
properties[prop_name] = {
'parsed': parse_zfs_prop(prop_name, crawvalue),
'rawvalue': crawvalue,
'value': cvalue,
'source': PropertySource(<int>csource).name
}
if holds:
ret = libzfs.zfs_get_holds(handle, &ptr)
with gil:
if ret != 0:
snap_data['holds'] = {}
else:
snap_data['holds'] = dict(NVList(<uintptr_t> ptr))
if mounted:
ret = libzfs.zfs_is_mounted(handle, &mntpt)
with gil:
snap_data['mountpoint'] = mntpt if ret !=0 else None
with gil:
if not simple_handle:
snap_data['properties'] = properties
snap_data.update({
'pool': pool,
'name': name,
'type': DatasetType.SNAPSHOT.name,
'snapshot_name': name.split('@')[-1],
'dataset': name.split('@')[0],
'id': name
})
snap_list.append(snap_data)
libzfs.zfs_close(handle)
@staticmethod
cdef int __datasets_snapshots(libzfs.zfs_handle_t *handle, void *arg) nogil:
ZFS.__snapshot_details(handle, arg)
libzfs.zfs_iter_filesystems(handle, ZFS.__datasets_snapshots, arg)
libzfs.zfs_close(handle)
def snapshots_serialized(self, props=None, holds=False, mounted=False):
cdef libzfs.zfs_handle_t* handle
cdef const char *c_name
cdef int prop_id
prop_mapping = {}
props = props or []
for prop_id in ZFS.proptypes[DatasetType.SNAPSHOT]:
with nogil:
prop_name = libzfs.zfs_prop_to_name(prop_id)
if not props or prop_name in props:
prop_mapping[prop_name] = prop_id
snap_list = [{
'props': prop_mapping,
'holds': holds,
'mounted': mounted
}]
for p in self.pools:
c_name = handle = NULL
name = p.name
c_name = name
snap_list[0]['pool'] = name
with nogil:
handle = libzfs.zfs_open(self.handle, c_name, zfs.ZFS_TYPE_FILESYSTEM)
ZFS.__datasets_snapshots(handle, <void*>snap_list)
return snap_list[1:]
property errno:
def __get__(self):
return Error(libzfs.libzfs_errno(self.handle))
property errstr:
def __get__(self):
return libzfs.libzfs_error_description(self.handle)
property pools:
def __get__(self):
if self.mnttab_cache_enable:
with nogil:
libzfs.libzfs_mnttab_cache(self.handle, self.mnttab_cache_enable)
cdef ZFSPool pool
pools = []
with nogil:
libzfs.zpool_iter(self.handle, self.__iterate_pools, <void*>pools)
for h in pools:
pool = ZFSPool.__new__(ZFSPool)
pool.root = self
pool.handle = <libzfs.zpool_handle_t*><uintptr_t>h
if pool.name == '$import':
continue
yield pool
if self.mnttab_cache_enable:
with nogil:
libzfs.libzfs_mnttab_cache(self.handle, False)
property datasets:
def __get__(self):
for p in self.pools:
try:
yield p.root_dataset
for c in p.root_dataset.children_recursive:
yield c
except ZFSException:
continue
property snapshots:
def __get__(self):
for p in self.pools:
try:
for c in p.root_dataset.snapshots_recursive:
yield c
except ZFSException:
continue
def get(self, name):
cdef const char *c_name = name
cdef libzfs.zpool_handle_t* handle = NULL
cdef ZFSPool pool
with nogil:
handle = libzfs.zpool_open_canfail(self.handle, c_name)
if handle == NULL:
raise ZFSException(Error.NOENT, 'Pool {0} not found'.format(name))
pool = ZFSPool.__new__(ZFSPool)
pool.root = self
pool.handle = handle
return pool
def find_import(self, cachefile=None, name=None, destroyed=False, search_paths=None):
cdef ZFSImportablePool pool
cdef libzfs.importargs_t iargs
cdef char* paths = "/dev"
cdef char* c_name
cdef nvpair.nvlist_t* result
iargs.path = &paths
iargs.paths = 1
iargs.poolname = NULL
iargs.guid = 0
iargs.cachefile = NULL
if name:
encoded = name.encode('utf-8')
c_name = encoded
iargs.poolname = c_name
if search_paths:
iargs.path = <char **>malloc(len(search_paths) * sizeof(char *))
iargs.paths = len(search_paths)
for i, p in enumerate(search_paths):
iargs.path[i] = <char *>p
if cachefile:
iargs.cachefile = cachefile
with nogil:
IF HAVE_ZPOOL_SEARCH_IMPORT_LIBZUTIL and HAVE_ZPOOL_SEARCH_IMPORT_PARAMS == 3:
result = libzfs.zpool_search_import(self.handle, &iargs, &libzfs.libzfs_config_ops)
ELSE:
result = libzfs.zpool_search_import(self.handle, &iargs)
if result is NULL:
return
nv = NVList(nvlist=<uintptr_t>result)
for name, config in nv.items(raw=True):
pool = ZFSImportablePool.__new__(ZFSImportablePool)
pool.name = name
pool.free = False
pool.nvlist = config
# Skip destroyed pools
if config['state'] == PoolState.DESTROYED and not destroyed:
continue
yield pool
def import_pool(self, ZFSImportablePool pool, newname, opts, missing_log=False, any_host=False):
cdef const char *c_newname = newname
cdef NVList copts = NVList(otherdict=opts)
cdef int ret
cdef int flags = 0
cdef ZFSPool newpool
if missing_log:
flags |= zfs.ZFS_IMPORT_MISSING_LOG
if any_host:
flags |= zfs.ZFS_IMPORT_ANY_HOST
with nogil:
ret = libzfs.zpool_import_props(
self.handle,
pool.nvlist.handle,
c_newname,
copts.handle,
flags
)
if ret != 0:
raise self.get_error()
newpool = self.get(newname)
with nogil:
ret = libzfs.zpool_enable_datasets(newpool.handle, NULL, 0)
if ret != 0:
raise self.get_error()
self.write_history('zpool import', str(pool.guid), newname if newname else pool.name)
return newpool
def export_pool(self, ZFSPool pool):
cdef int ret
with nogil:
ret = libzfs.zpool_disable_datasets(pool.handle, True)
if ret != 0:
raise self.get_error()
with nogil:
ret = libzfs.zpool_export(pool.handle, True, "export")
if ret != 0:
raise self.get_error()
self.write_history('zpool export', str(pool.name))
def get_dataset(self, name):
cdef const char *c_name = name
cdef libzfs.zfs_handle_t* handle = NULL
cdef ZFSPool pool
cdef ZFSDataset dataset
with nogil:
handle = libzfs.zfs_open(self.handle, c_name, zfs.ZFS_TYPE_FILESYSTEM|zfs.ZFS_TYPE_VOLUME)
if handle == NULL:
raise ZFSException(Error.NOENT, 'Dataset {0} not found'.format(name))
pool = ZFSPool.__new__(ZFSPool)
pool.root = self
pool.free = False
with nogil:
pool.handle = libzfs.zfs_get_pool_handle(handle)
dataset = ZFSDataset.__new__(ZFSDataset)
dataset.root = self
dataset.pool = pool
dataset.handle = handle
return dataset
def get_snapshot(self, name):
cdef libzfs.zfs_handle_t* handle = NULL
cdef ZFSPool pool
cdef ZFSSnapshot snap
cdef const char *c_name = name
with nogil:
handle = libzfs.zfs_open(self.handle, c_name, zfs.ZFS_TYPE_SNAPSHOT)
if handle == NULL:
raise ZFSException(Error.NOENT, 'Snapshot {0} not found'.format(name))
pool = ZFSPool.__new__(ZFSPool)
pool.root = self
pool.free = False
with nogil:
pool.handle = libzfs.zfs_get_pool_handle(handle)
snap = ZFSSnapshot.__new__(ZFSSnapshot)
snap.root = self
snap.pool = pool
snap.handle = handle
return snap
def get_object(self, name):
try:
return self.get_dataset(name)
except ZFSException as err:
if err.code == Error.NOENT:
return self.get_snapshot(name)
raise err
def get_dataset_by_path(self, path):
cdef libzfs.zfs_handle_t* handle
cdef char *c_path = path
cdef zfs.zfs_type_t dataset_type = DatasetType.FILESYSTEM.value
with nogil:
handle = libzfs.zfs_path_to_zhandle(self.handle, c_path, dataset_type)
cdef ZFSPool pool
cdef ZFSDataset dataset
if handle == NULL:
raise ZFSException(Error.NOENT, 'Dataset with path {0} not found'.format(path))
pool = ZFSPool.__new__(ZFSPool)
pool.root = self
pool.free = False
with nogil:
pool.handle = libzfs.zfs_get_pool_handle(handle)
dataset = ZFSDataset.__new__(ZFSDataset)
dataset.root = self
dataset.pool = pool
dataset.handle = handle
return dataset
def create(self, name, topology, opts, fsopts, enable_all_feat=True):
cdef NVList root = self.make_vdev_tree(topology).nvlist
cdef NVList copts
cdef NVList cfsopts = NVList(otherdict=fsopts)
cdef const char *c_name = name
cdef int ret
if enable_all_feat:
opts = opts.copy()
for i in range(0, zfs.SPA_FEATURES):
feat = &zfs.spa_feature_table[i]
opts['feature@{}'.format(feat.fi_uname)] = 'enabled'
copts = NVList(otherdict=opts)
with nogil: