- Changes to make library interface more clear and to better suit go package standards
This commit is contained in:
parent
db4703b708
commit
bc19737222
|
@ -46,10 +46,10 @@ props := make(map[ZFSProp]Property)
|
|||
// similar to convert in to string (base 10) from numeric type.
|
||||
strSize := "1073741824"
|
||||
|
||||
props[ZFSPropVolsize] = Property{Value: strSize}
|
||||
props[DatasetPropVolsize] = Property{Value: strSize}
|
||||
// In addition I explicitly choose some more properties to be set.
|
||||
props[ZFSPropVolblocksize] = Property{Value: "4096"}
|
||||
props[ZFSPropReservation] = Property{Value: strSize}
|
||||
props[DatasetPropVolblocksize] = Property{Value: "4096"}
|
||||
props[DatasetPropReservation] = Property{Value: strSize}
|
||||
|
||||
// Lets create desired volume
|
||||
d, err := DatasetCreate("TESTPOOL/VOLUME1", DatasetTypeVolume, props)
|
||||
|
|
325
common.go
325
common.go
|
@ -1,4 +1,4 @@
|
|||
// Implements basic manipulation of ZFS pools and data sets.
|
||||
// Package zfs implements basic manipulation of ZFS pools and data sets.
|
||||
// Use libzfs C library instead CLI zfs tools, with goal
|
||||
// to let using and manipulating OpenZFS form with in go project.
|
||||
//
|
||||
|
@ -23,36 +23,41 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// VDevType type of device in the pool
|
||||
type VDevType string
|
||||
|
||||
var libzfs_handle *C.struct_libzfs_handle
|
||||
var libzfsHandle *C.struct_libzfs_handle
|
||||
|
||||
func init() {
|
||||
libzfs_handle = C.libzfs_init()
|
||||
libzfsHandle = C.libzfs_init()
|
||||
return
|
||||
}
|
||||
|
||||
// Types of Virtual Devices
|
||||
const (
|
||||
VDevTypeRoot VDevType = "root"
|
||||
VDevTypeMirror = "mirror"
|
||||
VDevTypeReplacing = "replacing"
|
||||
VDevTypeRaidz = "raidz"
|
||||
VDevTypeDisk = "disk"
|
||||
VDevTypeFile = "file"
|
||||
VDevTypeMissing = "missing"
|
||||
VDevTypeHole = "hole"
|
||||
VDevTypeSpare = "spare"
|
||||
VDevTypeLog = "log"
|
||||
VDevTypeL2cache = "l2cache"
|
||||
VDevTypeRoot VDevType = "root" // VDevTypeRoot root device in ZFS pool
|
||||
VDevTypeMirror = "mirror" // VDevTypeMirror mirror device in ZFS pool
|
||||
VDevTypeReplacing = "replacing" // VDevTypeReplacing replacing
|
||||
VDevTypeRaidz = "raidz" // VDevTypeRaidz RAIDZ device
|
||||
VDevTypeDisk = "disk" // VDevTypeDisk device is disk
|
||||
VDevTypeFile = "file" // VDevTypeFile device is file
|
||||
VDevTypeMissing = "missing" // VDevTypeMissing missing device
|
||||
VDevTypeHole = "hole" // VDevTypeHole hole
|
||||
VDevTypeSpare = "spare" // VDevTypeSpare spare device
|
||||
VDevTypeLog = "log" // VDevTypeLog ZIL device
|
||||
VDevTypeL2cache = "l2cache" // VDevTypeL2cache cache device (disk)
|
||||
)
|
||||
|
||||
type PoolProp int
|
||||
type ZFSProp int
|
||||
// Prop type to enumerate all different properties suppoerted by ZFS
|
||||
type Prop int
|
||||
|
||||
// PoolStatus type representing status of the pool
|
||||
type PoolStatus int
|
||||
|
||||
// PoolState type representing pool state
|
||||
type PoolState uint64
|
||||
|
||||
// Zfs pool or dataset property
|
||||
// Property ZFS pool or dataset property value
|
||||
type Property struct {
|
||||
Value string
|
||||
Source string
|
||||
|
@ -64,20 +69,20 @@ const (
|
|||
* The following correspond to faults as defined in the (fault.fs.zfs.*)
|
||||
* event namespace. Each is associated with a corresponding message ID.
|
||||
*/
|
||||
PoolStatusCorrupt_cache PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
|
||||
PoolStatusMissing_dev_r /* missing device with replicas */
|
||||
PoolStatusMissing_dev_nr /* missing device with no replicas */
|
||||
PoolStatusCorrupt_label_r /* bad device label with replicas */
|
||||
PoolStatusCorrupt_label_nr /* bad device label with no replicas */
|
||||
PoolStatusBad_guid_sum /* sum of device guids didn't match */
|
||||
PoolStatusCorrupt_pool /* pool metadata is corrupted */
|
||||
PoolStatusCorrupt_data /* data errors in user (meta)data */
|
||||
PoolStatusFailing_dev /* device experiencing errors */
|
||||
PoolStatusVersion_newer /* newer on-disk version */
|
||||
PoolStatusHostid_mismatch /* last accessed by another system */
|
||||
PoolStatusIo_failure_wait /* failed I/O, failmode 'wait' */
|
||||
PoolStatusIo_failure_continue /* failed I/O, failmode 'continue' */
|
||||
PoolStatusBad_log /* cannot read log chain(s) */
|
||||
PoolStatusCorruptCache PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
|
||||
PoolStatusMissingDevR /* missing device with replicas */
|
||||
PoolStatusMissingDevNr /* missing device with no replicas */
|
||||
PoolStatusCorruptLabelR /* bad device label with replicas */
|
||||
PoolStatusCorruptLabelNr /* bad device label with no replicas */
|
||||
PoolStatusBadGUIDSum /* sum of device guids didn't match */
|
||||
PoolStatusCorruptPool /* pool metadata is corrupted */
|
||||
PoolStatusCorruptData /* data errors in user (meta)data */
|
||||
PoolStatusFailingDev /* device experiencing errors */
|
||||
PoolStatusVersionNewer /* newer on-disk version */
|
||||
PoolStatusHostidMismatch /* last accessed by another system */
|
||||
PoolStatusIoFailureWait /* failed I/O, failmode 'wait' */
|
||||
PoolStatusIoFailureContinue /* failed I/O, failmode 'continue' */
|
||||
PoolStatusBadLog /* cannot read log chain(s) */
|
||||
PoolStatusErrata /* informational errata available */
|
||||
|
||||
/*
|
||||
|
@ -86,27 +91,27 @@ const (
|
|||
* pool has unsupported features but cannot be opened at all, its
|
||||
* status is ZPOOL_STATUS_UNSUP_FEAT_READ.
|
||||
*/
|
||||
PoolStatusUnsup_feat_read /* unsupported features for read */
|
||||
PoolStatusUnsup_feat_write /* unsupported features for write */
|
||||
PoolStatusUnsupFeatRead /* unsupported features for read */
|
||||
PoolStatusUnsupFeatWrite /* unsupported features for write */
|
||||
|
||||
/*
|
||||
* These faults have no corresponding message ID. At the time we are
|
||||
* checking the status, the original reason for the FMA fault (I/O or
|
||||
* checksum errors) has been lost.
|
||||
*/
|
||||
PoolStatusFaulted_dev_r /* faulted device with replicas */
|
||||
PoolStatusFaulted_dev_nr /* faulted device with no replicas */
|
||||
PoolStatusFaultedDevR /* faulted device with replicas */
|
||||
PoolStatusFaultedDevNr /* faulted device with no replicas */
|
||||
|
||||
/*
|
||||
* The following are not faults per se, but still an error possibly
|
||||
* requiring administrative attention. There is no corresponding
|
||||
* message ID.
|
||||
*/
|
||||
PoolStatusVersion_older /* older legacy on-disk version */
|
||||
PoolStatusFeat_disabled /* supported features are disabled */
|
||||
PoolStatusVersionOlder /* older legacy on-disk version */
|
||||
PoolStatusFeatDisabled /* supported features are disabled */
|
||||
PoolStatusResilvering /* device being resilvered */
|
||||
PoolStatusOffline_dev /* device online */
|
||||
PoolStatusRemoved_dev /* removed device */
|
||||
PoolStatusOfflineDev /* device online */
|
||||
PoolStatusRemovedDev /* removed device */
|
||||
|
||||
/*
|
||||
* Finally, the following indicates a healthy pool.
|
||||
|
@ -129,12 +134,12 @@ const (
|
|||
// Pool properties. Enumerates available ZFS pool properties. Use it to access
|
||||
// pool properties either to read or set soecific property.
|
||||
const (
|
||||
PoolPropName PoolProp = iota
|
||||
PoolPropName Prop = iota
|
||||
PoolPropSize
|
||||
PoolPropCapacity
|
||||
PoolPropAltroot
|
||||
PoolPropHealth
|
||||
PoolPropGuid
|
||||
PoolPropGUID
|
||||
PoolPropVersion
|
||||
PoolPropBootfs
|
||||
PoolPropDelegation
|
||||
|
@ -166,102 +171,178 @@ const (
|
|||
* the property table in module/zcommon/zfs_prop.c.
|
||||
*/
|
||||
const (
|
||||
ZFSPropType ZFSProp = iota
|
||||
ZFSPropCreation
|
||||
ZFSPropUsed
|
||||
ZFSPropAvailable
|
||||
ZFSPropReferenced
|
||||
ZFSPropCompressratio
|
||||
ZFSPropMounted
|
||||
ZFSPropOrigin
|
||||
ZFSPropQuota
|
||||
ZFSPropReservation
|
||||
ZFSPropVolsize
|
||||
ZFSPropVolblocksize
|
||||
ZFSPropRecordsize
|
||||
ZFSPropMountpoint
|
||||
ZFSPropSharenfs
|
||||
ZFSPropChecksum
|
||||
ZFSPropCompression
|
||||
ZFSPropAtime
|
||||
ZFSPropDevices
|
||||
ZFSPropExec
|
||||
ZFSPropSetuid
|
||||
ZFSPropReadonly
|
||||
ZFSPropZoned
|
||||
ZFSPropSnapdir
|
||||
ZFSPropPrivate /* not exposed to user, temporary */
|
||||
ZFSPropAclinherit
|
||||
ZFSPropCreatetxg /* not exposed to the user */
|
||||
ZFSPropName /* not exposed to the user */
|
||||
ZFSPropCanmount
|
||||
ZFSPropIscsioptions /* not exposed to the user */
|
||||
ZFSPropXattr
|
||||
ZFSPropNumclones /* not exposed to the user */
|
||||
ZFSPropCopies
|
||||
ZFSPropVersion
|
||||
ZFSPropUtf8only
|
||||
ZFSPropNormalize
|
||||
ZFSPropCase
|
||||
ZFSPropVscan
|
||||
ZFSPropNbmand
|
||||
ZFSPropSharesmb
|
||||
ZFSPropRefquota
|
||||
ZFSPropRefreservation
|
||||
ZFSPropGuid
|
||||
ZFSPropPrimarycache
|
||||
ZFSPropSecondarycache
|
||||
ZFSPropUsedsnap
|
||||
ZFSPropUsedds
|
||||
ZFSPropUsedchild
|
||||
ZFSPropUsedrefreserv
|
||||
ZFSPropUseraccounting /* not exposed to the user */
|
||||
ZFSPropStmf_shareinfo /* not exposed to the user */
|
||||
ZFSPropDefer_destroy
|
||||
ZFSPropUserrefs
|
||||
ZFSPropLogbias
|
||||
ZFSPropUnique /* not exposed to the user */
|
||||
ZFSPropObjsetid /* not exposed to the user */
|
||||
ZFSPropDedup
|
||||
ZFSPropMlslabel
|
||||
ZFSPropSync
|
||||
ZFSPropRefratio
|
||||
ZFSPropWritten
|
||||
ZFSPropClones
|
||||
ZFSPropLogicalused
|
||||
ZFSPropLogicalreferenced
|
||||
ZFSPropInconsistent /* not exposed to the user */
|
||||
ZFSPropSnapdev
|
||||
ZFSPropAcltype
|
||||
ZFSPropSelinux_context
|
||||
ZFSPropSelinux_fscontext
|
||||
ZFSPropSelinux_defcontext
|
||||
ZFSPropSelinux_rootcontext
|
||||
ZFSPropRelatime
|
||||
ZFSPropRedundant_metadata
|
||||
ZFSPropOverlay
|
||||
ZFSNumProps
|
||||
DatasetPropType Prop = iota
|
||||
DatasetPropCreation
|
||||
DatasetPropUsed
|
||||
DatasetPropAvailable
|
||||
DatasetPropReferenced
|
||||
DatasetPropCompressratio
|
||||
DatasetPropMounted
|
||||
DatasetPropOrigin
|
||||
DatasetPropQuota
|
||||
DatasetPropReservation
|
||||
DatasetPropVolsize
|
||||
DatasetPropVolblocksize
|
||||
DatasetPropRecordsize
|
||||
DatasetPropMountpoint
|
||||
DatasetPropSharenfs
|
||||
DatasetPropChecksum
|
||||
DatasetPropCompression
|
||||
DatasetPropAtime
|
||||
DatasetPropDevices
|
||||
DatasetPropExec
|
||||
DatasetPropSetuid
|
||||
DatasetPropReadonly
|
||||
DatasetPropZoned
|
||||
DatasetPropSnapdir
|
||||
DatasetPropPrivate /* not exposed to user, temporary */
|
||||
DatasetPropAclinherit
|
||||
DatasetPropCreatetxg /* not exposed to the user */
|
||||
DatasetPropName /* not exposed to the user */
|
||||
DatasetPropCanmount
|
||||
DatasetPropIscsioptions /* not exposed to the user */
|
||||
DatasetPropXattr
|
||||
DatasetPropNumclones /* not exposed to the user */
|
||||
DatasetPropCopies
|
||||
DatasetPropVersion
|
||||
DatasetPropUtf8only
|
||||
DatasetPropNormalize
|
||||
DatasetPropCase
|
||||
DatasetPropVscan
|
||||
DatasetPropNbmand
|
||||
DatasetPropSharesmb
|
||||
DatasetPropRefquota
|
||||
DatasetPropRefreservation
|
||||
DatasetPropGUID
|
||||
DatasetPropPrimarycache
|
||||
DatasetPropSecondarycache
|
||||
DatasetPropUsedsnap
|
||||
DatasetPropUsedds
|
||||
DatasetPropUsedchild
|
||||
DatasetPropUsedrefreserv
|
||||
DatasetPropUseraccounting /* not exposed to the user */
|
||||
DatasetPropStmfShareinfo /* not exposed to the user */
|
||||
DatasetPropDeferDestroy
|
||||
DatasetPropUserrefs
|
||||
DatasetPropLogbias
|
||||
DatasetPropUnique /* not exposed to the user */
|
||||
DatasetPropObjsetid /* not exposed to the user */
|
||||
DatasetPropDedup
|
||||
DatasetPropMlslabel
|
||||
DatasetPropSync
|
||||
DatasetPropRefratio
|
||||
DatasetPropWritten
|
||||
DatasetPropClones
|
||||
DatasetPropLogicalused
|
||||
DatasetPropLogicalreferenced
|
||||
DatasetPropInconsistent /* not exposed to the user */
|
||||
DatasetPropSnapdev
|
||||
DatasetPropAcltype
|
||||
DatasetPropSelinuxContext
|
||||
DatasetPropSelinuxFsContext
|
||||
DatasetPropSelinuxDefContext
|
||||
DatasetPropSelinuxRootContext
|
||||
DatasetPropRelatime
|
||||
DatasetPropRedundantMetadata
|
||||
DatasetPropOverlay
|
||||
DatasetNumProps
|
||||
)
|
||||
|
||||
// Get last underlying libzfs error description if any
|
||||
// LastError get last underlying libzfs error description if any
|
||||
func LastError() (err error) {
|
||||
errno := C.libzfs_errno(libzfs_handle)
|
||||
errno := C.libzfs_errno(libzfsHandle)
|
||||
if errno == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New(C.GoString(C.libzfs_error_description(libzfs_handle)))
|
||||
return errors.New(C.GoString(C.libzfs_error_description(libzfsHandle)))
|
||||
}
|
||||
|
||||
// Force clear of any last error set by undeliying libzfs
|
||||
// ClearLastError force clear of any last error set by undeliying libzfs
|
||||
func ClearLastError() (err error) {
|
||||
err = LastError()
|
||||
C.clear_last_error(libzfs_handle)
|
||||
C.clear_last_error(libzfsHandle)
|
||||
return
|
||||
}
|
||||
|
||||
func boolean_t(b bool) (r C.boolean_t) {
|
||||
func booleanT(b bool) (r C.boolean_t) {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ZFS errors
|
||||
const (
|
||||
ESuccess = 0 /* no error -- success */
|
||||
ENomem = 2000 << iota /* out of memory */
|
||||
EBadprop /* invalid property value */
|
||||
EPropreadonly /* cannot set readonly property */
|
||||
EProptype /* property does not apply to dataset type */
|
||||
EPropnoninherit /* property is not inheritable */
|
||||
EPropspace /* bad quota or reservation */
|
||||
EBadtype /* dataset is not of appropriate type */
|
||||
EBusy /* pool or dataset is busy */
|
||||
EExists /* pool or dataset already exists */
|
||||
ENoent /* no such pool or dataset */
|
||||
EBadstream /* bad backup stream */
|
||||
EDsreadonly /* dataset is readonly */
|
||||
EVoltoobig /* volume is too large for 32-bit system */
|
||||
EInvalidname /* invalid dataset name */
|
||||
EBadrestore /* unable to restore to destination */
|
||||
EBadbackup /* backup failed */
|
||||
EBadtarget /* bad attach/detach/replace target */
|
||||
ENodevice /* no such device in pool */
|
||||
EBaddev /* invalid device to add */
|
||||
ENoreplicas /* no valid replicas */
|
||||
EResilvering /* currently resilvering */
|
||||
EBadversion /* unsupported version */
|
||||
EPoolunavail /* pool is currently unavailable */
|
||||
EDevoverflow /* too many devices in one vdev */
|
||||
EBadpath /* must be an absolute path */
|
||||
ECrosstarget /* rename or clone across pool or dataset */
|
||||
EZoned /* used improperly in local zone */
|
||||
EMountfailed /* failed to mount dataset */
|
||||
EUmountfailed /* failed to unmount dataset */
|
||||
EUnsharenfsfailed /* unshare(1M) failed */
|
||||
ESharenfsfailed /* share(1M) failed */
|
||||
EPerm /* permission denied */
|
||||
ENospc /* out of space */
|
||||
EFault /* bad address */
|
||||
EIo /* I/O error */
|
||||
EIntr /* signal received */
|
||||
EIsspare /* device is a hot spare */
|
||||
EInvalconfig /* invalid vdev configuration */
|
||||
ERecursive /* recursive dependency */
|
||||
ENohistory /* no history object */
|
||||
EPoolprops /* couldn't retrieve pool props */
|
||||
EPoolNotsup /* ops not supported for this type of pool */
|
||||
EPoolInvalarg /* invalid argument for this pool operation */
|
||||
ENametoolong /* dataset name is too long */
|
||||
EOpenfailed /* open of device failed */
|
||||
ENocap /* couldn't get capacity */
|
||||
ELabelfailed /* write of label failed */
|
||||
EBadwho /* invalid permission who */
|
||||
EBadperm /* invalid permission */
|
||||
EBadpermset /* invalid permission set name */
|
||||
ENodelegation /* delegated administration is disabled */
|
||||
EUnsharesmbfailed /* failed to unshare over smb */
|
||||
ESharesmbfailed /* failed to share over smb */
|
||||
EBadcache /* bad cache file */
|
||||
EIsl2CACHE /* device is for the level 2 ARC */
|
||||
EVdevnotsup /* unsupported vdev type */
|
||||
ENotsup /* ops not supported on this dataset */
|
||||
EActiveSpare /* pool has active shared spare devices */
|
||||
EUnplayedLogs /* log device has unplayed logs */
|
||||
EReftagRele /* snapshot release: tag not found */
|
||||
EReftagHold /* snapshot hold: tag already exists */
|
||||
ETagtoolong /* snapshot hold/rele: tag too long */
|
||||
EPipefailed /* pipe create failed */
|
||||
EThreadcreatefailed /* thread create failed */
|
||||
EPostsplitOnline /* onlining a disk after splitting it */
|
||||
EScrubbing /* currently scrubbing */
|
||||
ENoScrub /* no active scrub */
|
||||
EDiff /* general failure of zfs diff */
|
||||
EDiffdata /* bad zfs diff data */
|
||||
EPoolreadonly /* pool is in read-only mode */
|
||||
EUnknown
|
||||
)
|
||||
|
|
104
zfs.go
104
zfs.go
|
@ -14,20 +14,30 @@ const (
|
|||
msgDatasetIsNil = "Dataset handle not initialized or its closed"
|
||||
)
|
||||
|
||||
// DatasetProperties type is map of dataset or volume properties prop -> value
|
||||
type DatasetProperties map[Prop]string
|
||||
|
||||
// DatasetType defines enum of dataset types
|
||||
type DatasetType int32
|
||||
|
||||
const (
|
||||
// DatasetTypeFilesystem - file system dataset
|
||||
DatasetTypeFilesystem DatasetType = (1 << 0)
|
||||
// DatasetTypeSnapshot - snapshot of dataset
|
||||
DatasetTypeSnapshot = (1 << 1)
|
||||
// DatasetTypeVolume - volume (virtual block device) dataset
|
||||
DatasetTypeVolume = (1 << 2)
|
||||
// DatasetTypePool - pool dataset
|
||||
DatasetTypePool = (1 << 3)
|
||||
// DatasetTypeBookmark - bookmark dataset
|
||||
DatasetTypeBookmark = (1 << 4)
|
||||
)
|
||||
|
||||
// Dataset - ZFS dataset object
|
||||
type Dataset struct {
|
||||
list *C.dataset_list_t
|
||||
Type DatasetType
|
||||
Properties map[ZFSProp]Property
|
||||
Properties map[Prop]Property
|
||||
Children []Dataset
|
||||
}
|
||||
|
||||
|
@ -37,7 +47,7 @@ func (d *Dataset) openChildren() (err error) {
|
|||
errcode := C.dataset_list_children(d.list.zh, &(dataset.list))
|
||||
for dataset.list != nil {
|
||||
dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
|
||||
dataset.Properties = make(map[ZFSProp]Property)
|
||||
dataset.Properties = make(map[Prop]Property)
|
||||
err = dataset.ReloadProperties()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -49,7 +59,7 @@ func (d *Dataset) openChildren() (err error) {
|
|||
err = LastError()
|
||||
return
|
||||
}
|
||||
for ci, _ := range d.Children {
|
||||
for ci := range d.Children {
|
||||
if err = d.Children[ci].openChildren(); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -57,11 +67,11 @@ func (d *Dataset) openChildren() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Recursive get handles to all available datasets on system
|
||||
// DatasetOpenAll recursive get handles to all available datasets on system
|
||||
// (file-systems, volumes or snapshots).
|
||||
func DatasetOpenAll() (datasets []Dataset, err error) {
|
||||
var dataset Dataset
|
||||
errcode := C.dataset_list_root(libzfs_handle, &dataset.list)
|
||||
errcode := C.dataset_list_root(libzfsHandle, &dataset.list)
|
||||
for dataset.list != nil {
|
||||
dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
|
||||
err = dataset.ReloadProperties()
|
||||
|
@ -75,7 +85,7 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
|
|||
err = LastError()
|
||||
return
|
||||
}
|
||||
for ci, _ := range datasets {
|
||||
for ci := range datasets {
|
||||
if err = datasets[ci].openChildren(); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -83,24 +93,25 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Close all datasets in slice and all of its recursive children datasets
|
||||
// DatasetCloseAll close all datasets in slice and all of its recursive
|
||||
// children datasets
|
||||
func DatasetCloseAll(datasets []Dataset) {
|
||||
for _, d := range datasets {
|
||||
d.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Open dataset and all of its recursive children datasets
|
||||
// DatasetOpen open dataset and all of its recursive children datasets
|
||||
func DatasetOpen(path string) (d Dataset, err error) {
|
||||
d.list = C.create_dataset_list_item()
|
||||
d.list.zh = C.zfs_open(libzfs_handle, C.CString(path), 0xF)
|
||||
d.list.zh = C.zfs_open(libzfsHandle, C.CString(path), 0xF)
|
||||
|
||||
if d.list.zh == nil {
|
||||
err = LastError()
|
||||
return
|
||||
}
|
||||
d.Type = DatasetType(C.zfs_get_type(d.list.zh))
|
||||
d.Properties = make(map[ZFSProp]Property)
|
||||
d.Properties = make(map[Prop]Property)
|
||||
err = d.ReloadProperties()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -109,7 +120,7 @@ func DatasetOpen(path string) (d Dataset, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
|
||||
func datasetPropertiesTonvlist(props map[Prop]Property) (
|
||||
cprops *C.nvlist_t, err error) {
|
||||
// convert properties to nvlist C type
|
||||
r := C.nvlist_alloc(&cprops, C.NV_UNIQUE_NAME, 0)
|
||||
|
@ -129,16 +140,17 @@ func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
|
|||
return
|
||||
}
|
||||
|
||||
// Create a new filesystem or volume on path representing pool/dataset or pool/parent/dataset
|
||||
// DatasetCreate create a new filesystem or volume on path representing
|
||||
// pool/dataset or pool/parent/dataset
|
||||
func DatasetCreate(path string, dtype DatasetType,
|
||||
props map[ZFSProp]Property) (d Dataset, err error) {
|
||||
props map[Prop]Property) (d Dataset, err error) {
|
||||
var cprops *C.nvlist_t
|
||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
||||
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||
return
|
||||
}
|
||||
defer C.nvlist_free(cprops)
|
||||
|
||||
errcode := C.zfs_create(libzfs_handle, C.CString(path),
|
||||
errcode := C.zfs_create(libzfsHandle, C.CString(path),
|
||||
C.zfs_type_t(dtype), cprops)
|
||||
if errcode != 0 {
|
||||
err = LastError()
|
||||
|
@ -146,7 +158,8 @@ func DatasetCreate(path string, dtype DatasetType,
|
|||
return
|
||||
}
|
||||
|
||||
// Close dataset and all its recursive children datasets (close handle and cleanup dataset object/s from memory)
|
||||
// Close close dataset and all its recursive children datasets (close handle
|
||||
// and cleanup dataset object/s from memory)
|
||||
func (d *Dataset) Close() {
|
||||
if d.list != nil && d.list.zh != nil {
|
||||
C.dataset_list_close(d.list)
|
||||
|
@ -156,7 +169,7 @@ func (d *Dataset) Close() {
|
|||
}
|
||||
}
|
||||
|
||||
// Destroys the dataset. The caller must make sure that the filesystem
|
||||
// Destroy destroys the dataset. The caller must make sure that the filesystem
|
||||
// isn't mounted, and that there are no active dependents. Set Defer argument
|
||||
// to true to defer destruction for when dataset is not in use.
|
||||
func (d *Dataset) Destroy(Defer bool) (err error) {
|
||||
|
@ -165,13 +178,13 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
|
|||
if e != nil {
|
||||
return
|
||||
}
|
||||
dsType, e := d.GetProperty(ZFSPropType)
|
||||
dsType, e := d.GetProperty(DatasetPropType)
|
||||
err = errors.New("Cannot destroy dataset " + path +
|
||||
": " + dsType.Value + " has children")
|
||||
return
|
||||
}
|
||||
if d.list != nil {
|
||||
if ec := C.zfs_destroy(d.list.zh, boolean_t(Defer)); ec != 0 {
|
||||
if ec := C.zfs_destroy(d.list.zh, booleanT(Defer)); ec != 0 {
|
||||
err = LastError()
|
||||
}
|
||||
} else {
|
||||
|
@ -180,7 +193,7 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Recursively destroy children of dataset and dataset.
|
||||
// DestroyRecursive recursively destroy children of dataset and dataset.
|
||||
func (d *Dataset) DestroyRecursive() (err error) {
|
||||
if len(d.Children) > 0 {
|
||||
for _, c := range d.Children {
|
||||
|
@ -197,6 +210,7 @@ func (d *Dataset) DestroyRecursive() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Pool returns pool dataset belongs to
|
||||
func (d *Dataset) Pool() (p Pool, err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
|
@ -212,6 +226,7 @@ func (d *Dataset) Pool() (p Pool, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// ReloadProperties re-read dataset's properties
|
||||
func (d *Dataset) ReloadProperties() (err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
|
@ -220,8 +235,8 @@ func (d *Dataset) ReloadProperties() (err error) {
|
|||
var plist *C.property_list_t
|
||||
plist = C.new_property_list()
|
||||
defer C.free_properties(plist)
|
||||
d.Properties = make(map[ZFSProp]Property)
|
||||
for prop := ZFSPropType; prop < ZFSNumProps; prop++ {
|
||||
d.Properties = make(map[Prop]Property)
|
||||
for prop := DatasetPropType; prop < DatasetNumProps; prop++ {
|
||||
errcode := C.read_dataset_property(d.list.zh, plist, C.int(prop))
|
||||
if errcode != 0 {
|
||||
continue
|
||||
|
@ -232,9 +247,9 @@ func (d *Dataset) ReloadProperties() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Reload and return single specified property. This also reloads requested
|
||||
// GetProperty reload and return single specified property. This also reloads requested
|
||||
// property in Properties map.
|
||||
func (d *Dataset) GetProperty(p ZFSProp) (prop Property, err error) {
|
||||
func (d *Dataset) GetProperty(p Prop) (prop Property, err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
return
|
||||
|
@ -253,10 +268,10 @@ func (d *Dataset) GetProperty(p ZFSProp) (prop Property, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Set ZFS dataset property to value. Not all properties can be set,
|
||||
// SetProperty set ZFS dataset property to value. Not all properties can be set,
|
||||
// some can be set only at creation time and some are read only.
|
||||
// Always check if returned error and its description.
|
||||
func (d *Dataset) SetProperty(p ZFSProp, value string) (err error) {
|
||||
func (d *Dataset) SetProperty(p Prop, value string) (err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
return
|
||||
|
@ -273,15 +288,15 @@ func (d *Dataset) SetProperty(p ZFSProp, value string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Clones the dataset. The target must be of the same type as
|
||||
// Clone - clones the dataset. The target must be of the same type as
|
||||
// the source.
|
||||
func (d *Dataset) Clone(target string, props map[ZFSProp]Property) (rd Dataset, err error) {
|
||||
func (d *Dataset) Clone(target string, props map[Prop]Property) (rd Dataset, err error) {
|
||||
var cprops *C.nvlist_t
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
return
|
||||
}
|
||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
||||
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||
return
|
||||
}
|
||||
defer C.nvlist_free(cprops)
|
||||
|
@ -293,14 +308,14 @@ func (d *Dataset) Clone(target string, props map[ZFSProp]Property) (rd Dataset,
|
|||
return
|
||||
}
|
||||
|
||||
// Create dataset snapshot. Set recur to true to snapshot child datasets.
|
||||
func DatasetSnapshot(path string, recur bool, props map[ZFSProp]Property) (rd Dataset, err error) {
|
||||
// DatasetSnapshot create dataset snapshot. Set recur to true to snapshot child datasets.
|
||||
func DatasetSnapshot(path string, recur bool, props map[Prop]Property) (rd Dataset, err error) {
|
||||
var cprops *C.nvlist_t
|
||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
||||
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||
return
|
||||
}
|
||||
defer C.nvlist_free(cprops)
|
||||
if errc := C.zfs_snapshot(libzfs_handle, C.CString(path), boolean_t(recur), cprops); errc != 0 {
|
||||
if errc := C.zfs_snapshot(libzfsHandle, C.CString(path), booleanT(recur), cprops); errc != 0 {
|
||||
err = LastError()
|
||||
return
|
||||
}
|
||||
|
@ -308,7 +323,7 @@ func DatasetSnapshot(path string, recur bool, props map[ZFSProp]Property) (rd Da
|
|||
return
|
||||
}
|
||||
|
||||
// Return zfs dataset path/name
|
||||
// Path return zfs dataset path/name
|
||||
func (d *Dataset) Path() (path string, err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
|
@ -319,14 +334,14 @@ func (d *Dataset) Path() (path string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Rollabck dataset snapshot
|
||||
// Rollback rollabck's dataset snapshot
|
||||
func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
return
|
||||
}
|
||||
if errc := C.zfs_rollback(d.list.zh,
|
||||
snap.list.zh, boolean_t(force)); errc != 0 {
|
||||
snap.list.zh, booleanT(force)); errc != 0 {
|
||||
err = LastError()
|
||||
}
|
||||
return
|
||||
|
@ -334,20 +349,20 @@ func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
|
|||
|
||||
// Rename dataset
|
||||
func (d *Dataset) Rename(newname string, recur,
|
||||
force_umount bool) (err error) {
|
||||
forceUnmount bool) (err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
return
|
||||
}
|
||||
if errc := C.zfs_rename(d.list.zh, C.CString(newname),
|
||||
boolean_t(recur), boolean_t(force_umount)); errc != 0 {
|
||||
booleanT(recur), booleanT(forceUnmount)); errc != 0 {
|
||||
err = LastError()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Checks to see if the mount is active. If the filesystem is mounted, fills
|
||||
// in 'where' with the current mountpoint, and returns true. Otherwise,
|
||||
// IsMounted checks to see if the mount is active. If the filesystem is mounted,
|
||||
// sets in 'where' argument the current mountpoint, and returns true. Otherwise,
|
||||
// returns false.
|
||||
func (d *Dataset) IsMounted() (mounted bool, where string) {
|
||||
var cw *C.char
|
||||
|
@ -386,7 +401,8 @@ func (d *Dataset) Unmount(flags int) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Unmount this filesystem and any children inheriting the mountpoint property.
|
||||
// UnmountAll unmount this filesystem and any children inheriting the
|
||||
// mountpoint property.
|
||||
func (d *Dataset) UnmountAll(flags int) (err error) {
|
||||
if d.list == nil {
|
||||
err = errors.New(msgDatasetIsNil)
|
||||
|
@ -398,12 +414,12 @@ func (d *Dataset) UnmountAll(flags int) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Convert property to name
|
||||
// DatasetPropertyToName convert property to name
|
||||
// ( returns built in string representation of property name).
|
||||
// This is optional, you can represent each property with string
|
||||
// name of choice.
|
||||
func DatasetPropertyToName(p ZFSProp) (name string) {
|
||||
if p == ZFSNumProps {
|
||||
func DatasetPropertyToName(p Prop) (name string) {
|
||||
if p == DatasetNumProps {
|
||||
return "numofprops"
|
||||
}
|
||||
prop := C.zfs_prop_t(p)
|
||||
|
|
74
zfs_test.go
74
zfs_test.go
|
@ -2,15 +2,16 @@ package zfs_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bicomsystems/go-libzfs"
|
||||
"testing"
|
||||
|
||||
"github.com/bicomsystems/go-libzfs"
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
// HELPERS:
|
||||
var TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
|
||||
var TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
|
||||
var TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
|
||||
var TSTDatasetPath = TSTPoolName + "/DATASET"
|
||||
var TSTVolumePath = TSTDatasetPath + "/VOLUME"
|
||||
var TSTDatasetPathSnap = TSTDatasetPath + "@test"
|
||||
|
||||
func printDatasets(ds []zfs.Dataset) error {
|
||||
for _, d := range ds {
|
||||
|
@ -19,7 +20,7 @@ func printDatasets(ds []zfs.Dataset) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, err := d.GetProperty(zfs.ZFSPropType)
|
||||
p, err := d.GetProperty(zfs.DatasetPropType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -36,45 +37,45 @@ func printDatasets(ds []zfs.Dataset) error {
|
|||
|
||||
func zfsTestDatasetCreate(t *testing.T) {
|
||||
// reinit names used in case TESTPOOL was in conflict
|
||||
TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
|
||||
TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
|
||||
TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
|
||||
TSTDatasetPath = TSTPoolName + "/DATASET"
|
||||
TSTVolumePath = TSTDatasetPath + "/VOLUME"
|
||||
TSTDatasetPathSnap = TSTDatasetPath + "@test"
|
||||
|
||||
println("TEST DatasetCreate(", TST_DATASET_PATH, ") (filesystem) ... ")
|
||||
props := make(map[zfs.ZFSProp]zfs.Property)
|
||||
d, err := zfs.DatasetCreate(TST_DATASET_PATH, zfs.DatasetTypeFilesystem, props)
|
||||
println("TEST DatasetCreate(", TSTDatasetPath, ") (filesystem) ... ")
|
||||
props := make(map[zfs.Prop]zfs.Property)
|
||||
d, err := zfs.DatasetCreate(TSTDatasetPath, zfs.DatasetTypeFilesystem, props)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
d.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
|
||||
strSize := "536870912" // 512M
|
||||
|
||||
println("TEST DatasetCreate(", TST_VOLUME_PATH, ") (volume) ... ")
|
||||
props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize}
|
||||
println("TEST DatasetCreate(", TSTVolumePath, ") (volume) ... ")
|
||||
props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize}
|
||||
// In addition I explicitly choose some more properties to be set.
|
||||
props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||
props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
|
||||
d, err = zfs.DatasetCreate(TST_VOLUME_PATH, zfs.DatasetTypeVolume, props)
|
||||
props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||
props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
|
||||
d, err = zfs.DatasetCreate(TSTVolumePath, zfs.DatasetTypeVolume, props)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
d.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zfsTestDatasetOpen(t *testing.T) {
|
||||
println("TEST DatasetOpen(", TST_DATASET_PATH, ") ... ")
|
||||
d, err := zfs.DatasetOpen(TST_DATASET_PATH)
|
||||
println("TEST DatasetOpen(", TSTDatasetPath, ") ... ")
|
||||
d, err := zfs.DatasetOpen(TSTDatasetPath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
d.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zfsTestDatasetOpenAll(t *testing.T) {
|
||||
|
@ -90,24 +91,24 @@ func zfsTestDatasetOpenAll(t *testing.T) {
|
|||
return
|
||||
}
|
||||
zfs.DatasetCloseAll(ds)
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zfsTestDatasetSnapshot(t *testing.T) {
|
||||
println("TEST DatasetSnapshot(", TST_DATASET_PATH, ", true, ...) ... ")
|
||||
props := make(map[zfs.ZFSProp]zfs.Property)
|
||||
d, err := zfs.DatasetSnapshot(TST_DATASET_PATH_SNAP, true, props)
|
||||
println("TEST DatasetSnapshot(", TSTDatasetPath, ", true, ...) ... ")
|
||||
props := make(map[zfs.Prop]zfs.Property)
|
||||
d, err := zfs.DatasetSnapshot(TSTDatasetPathSnap, true, props)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer d.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zfsTestDatasetDestroy(t *testing.T) {
|
||||
println("TEST DATASET Destroy( ", TST_DATASET_PATH, " ) ... ")
|
||||
d, err := zfs.DatasetOpen(TST_DATASET_PATH)
|
||||
println("TEST DATASET Destroy( ", TSTDatasetPath, " ) ... ")
|
||||
d, err := zfs.DatasetOpen(TSTDatasetPath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -117,7 +118,7 @@ func zfsTestDatasetDestroy(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -128,7 +129,7 @@ func ExampleDatasetCreate() {
|
|||
// Create map to represent ZFS dataset properties. This is equivalent to
|
||||
// list of properties you can get from ZFS CLI tool, and some more
|
||||
// internally used by libzfs.
|
||||
props := make(map[zfs.ZFSProp]zfs.Property)
|
||||
props := make(map[zfs.Prop]zfs.Property)
|
||||
|
||||
// I choose to create (block) volume 1GiB in size. Size is just ZFS dataset
|
||||
// property and this is done as map of strings. So, You have to either
|
||||
|
@ -136,10 +137,10 @@ func ExampleDatasetCreate() {
|
|||
// similar to convert in to string (base 10) from numeric type.
|
||||
strSize := "1073741824"
|
||||
|
||||
props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize}
|
||||
props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize}
|
||||
// In addition I explicitly choose some more properties to be set.
|
||||
props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||
props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
|
||||
props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||
props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
|
||||
|
||||
// Lets create desired volume
|
||||
d, err := zfs.DatasetCreate("TESTPOOL/VOLUME1", zfs.DatasetTypeVolume, props)
|
||||
|
@ -161,10 +162,11 @@ func ExampleDatasetOpen() {
|
|||
}
|
||||
defer d.Close()
|
||||
var p zfs.Property
|
||||
if p, err = d.GetProperty(zfs.ZFSPropAvailable); err != nil {
|
||||
if p, err = d.GetProperty(zfs.DatasetPropAvailable); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
println(zfs.DatasetPropertyToName(zfs.ZFSPropAvailable), " = ", p.Value)
|
||||
println(zfs.DatasetPropertyToName(zfs.DatasetPropAvailable), " = ",
|
||||
p.Value)
|
||||
}
|
||||
|
||||
func ExampleDatasetOpenAll() {
|
||||
|
@ -180,7 +182,7 @@ func ExampleDatasetOpenAll() {
|
|||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
p, err := d.GetProperty(zfs.ZFSPropType)
|
||||
p, err := d.GetProperty(zfs.DatasetPropType)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
|
127
zpool.go
127
zpool.go
|
@ -10,16 +10,17 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
msgPoolIsNil = "Pool handle not initialized or its closed"
|
||||
)
|
||||
|
||||
type PoolProperties map[PoolProp]string
|
||||
type ZFSProperties map[ZFSProp]string
|
||||
// PoolProperties type is map of pool properties name -> value
|
||||
type PoolProperties map[Prop]string
|
||||
|
||||
// Object represents handler to single ZFS pool
|
||||
// Pool object represents handler to single ZFS pool
|
||||
//
|
||||
/* Pool.Properties map[string]Property
|
||||
*/
|
||||
|
@ -34,11 +35,11 @@ type Pool struct {
|
|||
Features map[string]string
|
||||
}
|
||||
|
||||
// Open ZFS pool handler by name.
|
||||
// PoolOpen open ZFS pool handler by name.
|
||||
// Returns Pool object, requires Pool.Close() to be called explicitly
|
||||
// for memory cleanup after object is not needed anymore.
|
||||
func PoolOpen(name string) (pool Pool, err error) {
|
||||
pool.list = C.zpool_list_open(libzfs_handle, C.CString(name))
|
||||
pool.list = C.zpool_list_open(libzfsHandle, C.CString(name))
|
||||
if pool.list != nil {
|
||||
err = pool.ReloadProperties()
|
||||
return
|
||||
|
@ -47,7 +48,7 @@ func PoolOpen(name string) (pool Pool, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Given a list of directories to search, find and import pool with matching
|
||||
// PoolImport given a list of directories to search, find and import pool with matching
|
||||
// name stored on disk.
|
||||
func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
||||
errPoolList := errors.New("Failed to list pools")
|
||||
|
@ -59,7 +60,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
|||
C.strings_setat(cpaths, C.int(i), C.CString(path))
|
||||
}
|
||||
|
||||
pools := C.zpool_find_import(libzfs_handle, C.int(numofp), cpaths)
|
||||
pools := C.zpool_find_import(libzfsHandle, C.int(numofp), cpaths)
|
||||
defer C.nvlist_free(pools)
|
||||
|
||||
elem = C.nvlist_next_nvpair(pools, elem)
|
||||
|
@ -88,7 +89,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
retcode := C.zpool_import(libzfs_handle, config, C.CString(name), nil)
|
||||
retcode := C.zpool_import(libzfsHandle, config, C.CString(name), nil)
|
||||
if retcode != 0 {
|
||||
err = LastError()
|
||||
return
|
||||
|
@ -97,12 +98,12 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Open all active ZFS pools on current system.
|
||||
// PoolOpenAll open all active ZFS pools on current system.
|
||||
// Returns array of Pool handlers, each have to be closed after not needed
|
||||
// anymore. Call Pool.Close() method.
|
||||
func PoolOpenAll() (pools []Pool, err error) {
|
||||
var pool Pool
|
||||
errcode := C.zpool_list(libzfs_handle, &pool.list)
|
||||
errcode := C.zpool_list(libzfsHandle, &pool.list)
|
||||
for pool.list != nil {
|
||||
err = pool.ReloadProperties()
|
||||
if err != nil {
|
||||
|
@ -117,17 +118,18 @@ func PoolOpenAll() (pools []Pool, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// PoolCloseAll close all pools in given slice
|
||||
func PoolCloseAll(pools []Pool) {
|
||||
for _, p := range pools {
|
||||
p.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Convert property to name
|
||||
// PoolPropertyToName convert property to name
|
||||
// ( returns built in string representation of property name).
|
||||
// This is optional, you can represent each property with string
|
||||
// name of choice.
|
||||
func PoolPropertyToName(p PoolProp) (name string) {
|
||||
func PoolPropertyToName(p Prop) (name string) {
|
||||
if p == PoolNumProps {
|
||||
return "numofprops"
|
||||
}
|
||||
|
@ -136,15 +138,15 @@ func PoolPropertyToName(p PoolProp) (name string) {
|
|||
return
|
||||
}
|
||||
|
||||
// Map POOL STATE to string.
|
||||
// PoolStateToName maps POOL STATE to string.
|
||||
func PoolStateToName(state PoolState) (name string) {
|
||||
ps := C.pool_state_t(state)
|
||||
name = C.GoString(C.zpool_pool_state_to_name(ps))
|
||||
return
|
||||
}
|
||||
|
||||
// Re-read ZFS pool properties and features, refresh Pool.Properties and
|
||||
// Pool.Features map
|
||||
// ReloadProperties re-read ZFS pool properties and features, refresh
|
||||
// Pool.Properties and Pool.Features map
|
||||
func (pool *Pool) ReloadProperties() (err error) {
|
||||
propList := C.read_zpool_properties(pool.list.zph)
|
||||
if propList == nil {
|
||||
|
@ -165,15 +167,15 @@ func (pool *Pool) ReloadProperties() (err error) {
|
|||
"async_destroy": "disabled",
|
||||
"empty_bpobj": "disabled",
|
||||
"lz4_compress": "disabled"}
|
||||
for name, _ := range pool.Features {
|
||||
for name := range pool.Features {
|
||||
pool.GetFeature(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Reload and return single specified property. This also reloads requested
|
||||
// GetProperty reload and return single specified property. This also reloads requested
|
||||
// property in Properties map.
|
||||
func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) {
|
||||
func (pool *Pool) GetProperty(p Prop) (prop Property, err error) {
|
||||
if pool.list != nil {
|
||||
// First check if property exist at all
|
||||
if p < PoolPropName || p > PoolNumProps {
|
||||
|
@ -194,7 +196,7 @@ func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) {
|
|||
return prop, errors.New(msgPoolIsNil)
|
||||
}
|
||||
|
||||
// Reload and return single specified feature. This also reloads requested
|
||||
// GetFeature reload and return single specified feature. This also reloads requested
|
||||
// feature in Features map.
|
||||
func (pool *Pool) GetFeature(name string) (value string, err error) {
|
||||
var fvalue [512]C.char
|
||||
|
@ -209,10 +211,10 @@ func (pool *Pool) GetFeature(name string) (value string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Set ZFS pool property to value. Not all properties can be set,
|
||||
// SetProperty set ZFS pool property to value. Not all properties can be set,
|
||||
// some can be set only at creation time and some are read only.
|
||||
// Always check if returned error and its description.
|
||||
func (pool *Pool) SetProperty(p PoolProp, value string) (err error) {
|
||||
func (pool *Pool) SetProperty(p Prop, value string) (err error) {
|
||||
if pool.list != nil {
|
||||
// First check if property exist at all
|
||||
if p < PoolPropName || p > PoolNumProps {
|
||||
|
@ -241,7 +243,7 @@ func (pool *Pool) Close() {
|
|||
pool.list = nil
|
||||
}
|
||||
|
||||
// Get (re-read) ZFS pool name property
|
||||
// Name get (re-read) ZFS pool name property
|
||||
func (pool *Pool) Name() (name string, err error) {
|
||||
if pool.list == nil {
|
||||
err = errors.New(msgPoolIsNil)
|
||||
|
@ -252,7 +254,7 @@ func (pool *Pool) Name() (name string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Get ZFS pool state
|
||||
// State get ZFS pool state
|
||||
// Return the state of the pool (ACTIVE or UNAVAILABLE)
|
||||
func (pool *Pool) State() (state PoolState, err error) {
|
||||
if pool.list == nil {
|
||||
|
@ -263,7 +265,7 @@ func (pool *Pool) State() (state PoolState, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// ZFS virtual device specification
|
||||
// VDevSpec ZFS virtual device specification
|
||||
type VDevSpec struct {
|
||||
Type VDevType
|
||||
Devices []VDevSpec // groups other devices (e.g. mirror)
|
||||
|
@ -271,31 +273,31 @@ type VDevSpec struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
func (self *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
|
||||
func (vdev *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
|
||||
maxdevs = int(^uint(0) >> 1)
|
||||
if self.Type == VDevTypeRaidz {
|
||||
if vdev.Type == VDevTypeRaidz {
|
||||
grouping = true
|
||||
if self.Parity == 0 {
|
||||
self.Parity = 1
|
||||
if vdev.Parity == 0 {
|
||||
vdev.Parity = 1
|
||||
}
|
||||
if self.Parity > 254 {
|
||||
self.Parity = 254
|
||||
if vdev.Parity > 254 {
|
||||
vdev.Parity = 254
|
||||
}
|
||||
mindevs = int(self.Parity) + 1
|
||||
mindevs = int(vdev.Parity) + 1
|
||||
maxdevs = 255
|
||||
} else if self.Type == VDevTypeMirror {
|
||||
} else if vdev.Type == VDevTypeMirror {
|
||||
grouping = true
|
||||
mindevs = 2
|
||||
} else if self.Type == VDevTypeLog || self.Type == VDevTypeSpare || self.Type == VDevTypeL2cache {
|
||||
} else if vdev.Type == VDevTypeLog || vdev.Type == VDevTypeSpare || vdev.Type == VDevTypeL2cache {
|
||||
grouping = true
|
||||
mindevs = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VDevSpec) isLog() (r C.uint64_t) {
|
||||
func (vdev *VDevSpec) isLog() (r C.uint64_t) {
|
||||
r = 0
|
||||
if self.Type == VDevTypeLog {
|
||||
if vdev.Type == VDevTypeLog {
|
||||
r = 1
|
||||
}
|
||||
return
|
||||
|
@ -317,7 +319,7 @@ func toCPoolProperties(props PoolProperties) (cprops *C.nvlist_t) {
|
|||
return
|
||||
}
|
||||
|
||||
func toCZFSProperties(props ZFSProperties) (cprops *C.nvlist_t) {
|
||||
func toCDatasetProperties(props DatasetProperties) (cprops *C.nvlist_t) {
|
||||
cprops = nil
|
||||
for prop, value := range props {
|
||||
name := C.zfs_prop_to_name(C.zfs_prop_t(prop))
|
||||
|
@ -361,7 +363,7 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
|
|||
defer C.nvlist_free_array(l2cache)
|
||||
for i, vdev := range vdevs {
|
||||
grouping, mindevs, maxdevs := vdev.isGrouping()
|
||||
var child *C.nvlist_t = nil
|
||||
var child *C.nvlist_t
|
||||
// fmt.Println(vdev.Type)
|
||||
if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
|
||||
err = errors.New("Failed to allocate vdev")
|
||||
|
@ -369,8 +371,9 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
|
|||
}
|
||||
vcount := len(vdev.Devices)
|
||||
if vcount < mindevs || vcount > maxdevs {
|
||||
err = errors.New(fmt.Sprintf(
|
||||
"Invalid vdev specification: %s supports no less than %d or more than %d devices", vdev.Type, mindevs, maxdevs))
|
||||
err = fmt.Errorf(
|
||||
"Invalid vdev specification: %s supports no less than %d or more than %d devices",
|
||||
vdev.Type, mindevs, maxdevs)
|
||||
return
|
||||
}
|
||||
if r := C.nvlist_add_string(child, C.CString(C.ZPOOL_CONFIG_TYPE),
|
||||
|
@ -466,11 +469,11 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
|
|||
return
|
||||
}
|
||||
|
||||
// Create ZFS pool per specs, features and properties of pool and root dataset
|
||||
// PoolCreate create ZFS pool per specs, features and properties of pool and root dataset
|
||||
func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
|
||||
props PoolProperties, fsprops ZFSProperties) (pool Pool, err error) {
|
||||
props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) {
|
||||
// create root vdev nvroot
|
||||
var nvroot *C.nvlist_t = nil
|
||||
var nvroot *C.nvlist_t
|
||||
if r := C.nvlist_alloc(&nvroot, C.NV_UNIQUE_NAME, 0); r != 0 {
|
||||
err = errors.New("Failed to allocate root vdev")
|
||||
return
|
||||
|
@ -495,7 +498,7 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
|
|||
err = errors.New("Failed to allocate pool properties")
|
||||
return
|
||||
}
|
||||
cfsprops := toCZFSProperties(fsprops)
|
||||
cfsprops := toCDatasetProperties(fsprops)
|
||||
if cfsprops != nil {
|
||||
defer C.nvlist_free(cfsprops)
|
||||
} else if len(fsprops) > 0 {
|
||||
|
@ -516,16 +519,34 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
|
|||
}
|
||||
|
||||
// Create actual pool then open
|
||||
if r := C.zpool_create(libzfs_handle, C.CString(name), nvroot,
|
||||
if r := C.zpool_create(libzfsHandle, C.CString(name), nvroot,
|
||||
cprops, cfsprops); r != 0 {
|
||||
err = LastError()
|
||||
return
|
||||
}
|
||||
pool, err = PoolOpen(name)
|
||||
err = errors.New(err.Error() + " (zpool_create)")
|
||||
return
|
||||
}
|
||||
|
||||
// Get pool status. Let you check if pool healthy.
|
||||
// It can happen that pool is not immediately available,
|
||||
// we know we just created it with success so lets wait and retry
|
||||
// but only in case EZFS_NOENT error
|
||||
retr := 0
|
||||
for pool, err = PoolOpen(name); err != nil && retr < 3; retr++ {
|
||||
errno := C.libzfs_errno(libzfsHandle)
|
||||
if errno == ENoent {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
} else {
|
||||
err = errors.New(err.Error() + " (PoolOpen)")
|
||||
return
|
||||
}
|
||||
pool, err = PoolOpen(name)
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.New(err.Error() + " (PoolOpen)")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status get pool status. Let you check if pool healthy.
|
||||
func (pool *Pool) Status() (status PoolStatus, err error) {
|
||||
var msgid *C.char
|
||||
var reason C.zpool_status_t
|
||||
|
@ -554,22 +575,22 @@ func (pool *Pool) Destroy(logStr string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Exports the pool from the system.
|
||||
// Export exports the pool from the system.
|
||||
// Before exporting the pool, all datasets within the pool are unmounted.
|
||||
// A pool can not be exported if it has a shared spare that is currently
|
||||
// being used.
|
||||
func (pool *Pool) Export(force bool, log string) (err error) {
|
||||
var force_t C.boolean_t = 0
|
||||
var forcet C.boolean_t
|
||||
if force {
|
||||
force_t = 1
|
||||
forcet = 1
|
||||
}
|
||||
if rc := C.zpool_export(pool.list.zph, force_t, C.CString(log)); rc != 0 {
|
||||
if rc := C.zpool_export(pool.list.zph, forcet, C.CString(log)); rc != 0 {
|
||||
err = LastError()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Hard force
|
||||
// ExportForce hard force export of the pool from the system.
|
||||
func (pool *Pool) ExportForce(log string) (err error) {
|
||||
if rc := C.zpool_export_force(pool.list.zph, C.CString(log)); rc != 0 {
|
||||
err = LastError()
|
||||
|
|
|
@ -2,16 +2,17 @@ package zfs_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bicomsystems/go-libzfs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/bicomsystems/go-libzfs"
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
// HELPERS:
|
||||
|
||||
var TST_POOL_NAME = "TESTPOOL"
|
||||
var TSTPoolName = "TESTPOOL"
|
||||
|
||||
func CreateTmpSparse(prefix string, size int64) (path string, err error) {
|
||||
sf, err := ioutil.TempFile("/tmp", prefix)
|
||||
|
@ -66,12 +67,12 @@ func zpoolTestPoolCreate(t *testing.T) {
|
|||
// first check if pool with same name already exist
|
||||
// we don't want conflict
|
||||
for {
|
||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
||||
p, err := zfs.PoolOpen(TSTPoolName)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
p.Close()
|
||||
TST_POOL_NAME += "0"
|
||||
TSTPoolName += "0"
|
||||
}
|
||||
var err error
|
||||
|
||||
|
@ -94,15 +95,15 @@ func zpoolTestPoolCreate(t *testing.T) {
|
|||
zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs},
|
||||
}
|
||||
|
||||
props := make(map[zfs.PoolProp]string)
|
||||
fsprops := make(map[zfs.ZFSProp]string)
|
||||
props := make(map[zfs.Prop]string)
|
||||
fsprops := make(map[zfs.Prop]string)
|
||||
features := make(map[string]string)
|
||||
fsprops[zfs.ZFSPropMountpoint] = "none"
|
||||
fsprops[zfs.DatasetPropMountpoint] = "none"
|
||||
features["async_destroy"] = "enabled"
|
||||
features["empty_bpobj"] = "enabled"
|
||||
features["lz4_compress"] = "enabled"
|
||||
|
||||
pool, err := zfs.PoolCreate(TST_POOL_NAME, vdevs, features, props, fsprops)
|
||||
pool, err := zfs.PoolCreate(TSTPoolName, vdevs, features, props, fsprops)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
// try cleanup
|
||||
|
@ -113,7 +114,7 @@ func zpoolTestPoolCreate(t *testing.T) {
|
|||
}
|
||||
defer pool.Close()
|
||||
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
// Open and list all pools and them state on the system
|
||||
|
@ -143,22 +144,22 @@ func zpoolTestPoolOpenAll(t *testing.T) {
|
|||
println("\tPool: ", pname, " state: ", pstate)
|
||||
p.Close()
|
||||
}
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestPoolDestroy(t *testing.T) {
|
||||
println("TEST POOL Destroy( ", TST_POOL_NAME, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
||||
println("TEST POOL Destroy( ", TSTPoolName, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TSTPoolName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer p.Close()
|
||||
if err = p.Destroy("Test of pool destroy (" + TST_POOL_NAME + ")"); err != nil {
|
||||
if err = p.Destroy("Test of pool destroy (" + TSTPoolName + ")"); err != nil {
|
||||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestFailPoolOpen(t *testing.T) {
|
||||
|
@ -166,7 +167,7 @@ func zpoolTestFailPoolOpen(t *testing.T) {
|
|||
pname := "fail to open this pool"
|
||||
p, err := zfs.PoolOpen(pname)
|
||||
if err != nil {
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
return
|
||||
}
|
||||
t.Error("PoolOpen pass when it should fail")
|
||||
|
@ -174,43 +175,43 @@ func zpoolTestFailPoolOpen(t *testing.T) {
|
|||
}
|
||||
|
||||
func zpoolTestExport(t *testing.T) {
|
||||
println("TEST POOL Export( ", TST_POOL_NAME, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
||||
println("TEST POOL Export( ", TSTPoolName, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TSTPoolName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
p.Export(false, "Test exporting pool")
|
||||
defer p.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestExportForce(t *testing.T) {
|
||||
println("TEST POOL ExportForce( ", TST_POOL_NAME, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
||||
println("TEST POOL ExportForce( ", TSTPoolName, " ) ... ")
|
||||
p, err := zfs.PoolOpen(TSTPoolName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
p.ExportForce("Test force exporting pool")
|
||||
defer p.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestImport(t *testing.T) {
|
||||
println("TEST POOL Import( ", TST_POOL_NAME, " ) ... ")
|
||||
p, err := zfs.PoolImport(TST_POOL_NAME, []string{"/tmp"})
|
||||
println("TEST POOL Import( ", TSTPoolName, " ) ... ")
|
||||
p, err := zfs.PoolImport(TSTPoolName, []string{"/tmp"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer p.Close()
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestPoolProp(t *testing.T) {
|
||||
println("TEST PoolProp on ", TST_POOL_NAME, " ... ")
|
||||
if pool, err := zfs.PoolOpen(TST_POOL_NAME); err == nil {
|
||||
println("TEST PoolProp on ", TSTPoolName, " ... ")
|
||||
if pool, err := zfs.PoolOpen(TSTPoolName); err == nil {
|
||||
defer pool.Close()
|
||||
// Turn on snapshot listing for pool
|
||||
pool.SetProperty(zfs.PoolPropListsnaps, "on")
|
||||
|
@ -247,12 +248,12 @@ func zpoolTestPoolProp(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
func zpoolTestPoolStatusAndState(t *testing.T) {
|
||||
println("TEST pool Status/State ( ", TST_POOL_NAME, " ) ... ")
|
||||
pool, err := zfs.PoolOpen(TST_POOL_NAME)
|
||||
println("TEST pool Status/State ( ", TSTPoolName, " ) ... ")
|
||||
pool, err := zfs.PoolOpen(TSTPoolName)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
return
|
||||
|
@ -269,9 +270,9 @@ func zpoolTestPoolStatusAndState(t *testing.T) {
|
|||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
println("POOL", TST_POOL_NAME, "state:", zfs.PoolStateToName(pstate))
|
||||
println("POOL", TSTPoolName, "state:", zfs.PoolStateToName(pstate))
|
||||
|
||||
println("PASS\n")
|
||||
print("PASS\n\n")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -315,7 +316,7 @@ func ExamplePoolOpenAll() {
|
|||
|
||||
// Iterate pool properties and print name, value and source
|
||||
for key, prop := range p.Properties {
|
||||
pkey := zfs.PoolProp(key)
|
||||
pkey := zfs.Prop(key)
|
||||
if pkey == zfs.PoolPropName {
|
||||
continue // Skip name its already printed above
|
||||
}
|
||||
|
@ -353,14 +354,14 @@ func ExamplePoolCreate() {
|
|||
}
|
||||
|
||||
// pool properties
|
||||
props := make(map[zfs.PoolProp]string)
|
||||
props := make(map[zfs.Prop]string)
|
||||
// root dataset filesystem properties
|
||||
fsprops := make(map[zfs.ZFSProp]string)
|
||||
fsprops := make(map[zfs.Prop]string)
|
||||
// pool features
|
||||
features := make(map[string]string)
|
||||
|
||||
// Turn off auto mounting by ZFS
|
||||
fsprops[zfs.ZFSPropMountpoint] = "none"
|
||||
fsprops[zfs.DatasetPropMountpoint] = "none"
|
||||
|
||||
// Enable some features
|
||||
features["async_destroy"] = "enabled"
|
||||
|
|
Loading…
Reference in New Issue