- 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.
|
// similar to convert in to string (base 10) from numeric type.
|
||||||
strSize := "1073741824"
|
strSize := "1073741824"
|
||||||
|
|
||||||
props[ZFSPropVolsize] = Property{Value: strSize}
|
props[DatasetPropVolsize] = Property{Value: strSize}
|
||||||
// In addition I explicitly choose some more properties to be set.
|
// In addition I explicitly choose some more properties to be set.
|
||||||
props[ZFSPropVolblocksize] = Property{Value: "4096"}
|
props[DatasetPropVolblocksize] = Property{Value: "4096"}
|
||||||
props[ZFSPropReservation] = Property{Value: strSize}
|
props[DatasetPropReservation] = Property{Value: strSize}
|
||||||
|
|
||||||
// Lets create desired volume
|
// Lets create desired volume
|
||||||
d, err := DatasetCreate("TESTPOOL/VOLUME1", DatasetTypeVolume, props)
|
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
|
// Use libzfs C library instead CLI zfs tools, with goal
|
||||||
// to let using and manipulating OpenZFS form with in go project.
|
// to let using and manipulating OpenZFS form with in go project.
|
||||||
//
|
//
|
||||||
|
@ -23,36 +23,41 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VDevType type of device in the pool
|
||||||
type VDevType string
|
type VDevType string
|
||||||
|
|
||||||
var libzfs_handle *C.struct_libzfs_handle
|
var libzfsHandle *C.struct_libzfs_handle
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
libzfs_handle = C.libzfs_init()
|
libzfsHandle = C.libzfs_init()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Types of Virtual Devices
|
// Types of Virtual Devices
|
||||||
const (
|
const (
|
||||||
VDevTypeRoot VDevType = "root"
|
VDevTypeRoot VDevType = "root" // VDevTypeRoot root device in ZFS pool
|
||||||
VDevTypeMirror = "mirror"
|
VDevTypeMirror = "mirror" // VDevTypeMirror mirror device in ZFS pool
|
||||||
VDevTypeReplacing = "replacing"
|
VDevTypeReplacing = "replacing" // VDevTypeReplacing replacing
|
||||||
VDevTypeRaidz = "raidz"
|
VDevTypeRaidz = "raidz" // VDevTypeRaidz RAIDZ device
|
||||||
VDevTypeDisk = "disk"
|
VDevTypeDisk = "disk" // VDevTypeDisk device is disk
|
||||||
VDevTypeFile = "file"
|
VDevTypeFile = "file" // VDevTypeFile device is file
|
||||||
VDevTypeMissing = "missing"
|
VDevTypeMissing = "missing" // VDevTypeMissing missing device
|
||||||
VDevTypeHole = "hole"
|
VDevTypeHole = "hole" // VDevTypeHole hole
|
||||||
VDevTypeSpare = "spare"
|
VDevTypeSpare = "spare" // VDevTypeSpare spare device
|
||||||
VDevTypeLog = "log"
|
VDevTypeLog = "log" // VDevTypeLog ZIL device
|
||||||
VDevTypeL2cache = "l2cache"
|
VDevTypeL2cache = "l2cache" // VDevTypeL2cache cache device (disk)
|
||||||
)
|
)
|
||||||
|
|
||||||
type PoolProp int
|
// Prop type to enumerate all different properties suppoerted by ZFS
|
||||||
type ZFSProp int
|
type Prop int
|
||||||
|
|
||||||
|
// PoolStatus type representing status of the pool
|
||||||
type PoolStatus int
|
type PoolStatus int
|
||||||
|
|
||||||
|
// PoolState type representing pool state
|
||||||
type PoolState uint64
|
type PoolState uint64
|
||||||
|
|
||||||
// Zfs pool or dataset property
|
// Property ZFS pool or dataset property value
|
||||||
type Property struct {
|
type Property struct {
|
||||||
Value string
|
Value string
|
||||||
Source string
|
Source string
|
||||||
|
@ -64,20 +69,20 @@ const (
|
||||||
* The following correspond to faults as defined in the (fault.fs.zfs.*)
|
* The following correspond to faults as defined in the (fault.fs.zfs.*)
|
||||||
* event namespace. Each is associated with a corresponding message ID.
|
* event namespace. Each is associated with a corresponding message ID.
|
||||||
*/
|
*/
|
||||||
PoolStatusCorrupt_cache PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
|
PoolStatusCorruptCache PoolStatus = iota /* corrupt /kernel/drv/zpool.cache */
|
||||||
PoolStatusMissing_dev_r /* missing device with replicas */
|
PoolStatusMissingDevR /* missing device with replicas */
|
||||||
PoolStatusMissing_dev_nr /* missing device with no replicas */
|
PoolStatusMissingDevNr /* missing device with no replicas */
|
||||||
PoolStatusCorrupt_label_r /* bad device label with replicas */
|
PoolStatusCorruptLabelR /* bad device label with replicas */
|
||||||
PoolStatusCorrupt_label_nr /* bad device label with no replicas */
|
PoolStatusCorruptLabelNr /* bad device label with no replicas */
|
||||||
PoolStatusBad_guid_sum /* sum of device guids didn't match */
|
PoolStatusBadGUIDSum /* sum of device guids didn't match */
|
||||||
PoolStatusCorrupt_pool /* pool metadata is corrupted */
|
PoolStatusCorruptPool /* pool metadata is corrupted */
|
||||||
PoolStatusCorrupt_data /* data errors in user (meta)data */
|
PoolStatusCorruptData /* data errors in user (meta)data */
|
||||||
PoolStatusFailing_dev /* device experiencing errors */
|
PoolStatusFailingDev /* device experiencing errors */
|
||||||
PoolStatusVersion_newer /* newer on-disk version */
|
PoolStatusVersionNewer /* newer on-disk version */
|
||||||
PoolStatusHostid_mismatch /* last accessed by another system */
|
PoolStatusHostidMismatch /* last accessed by another system */
|
||||||
PoolStatusIo_failure_wait /* failed I/O, failmode 'wait' */
|
PoolStatusIoFailureWait /* failed I/O, failmode 'wait' */
|
||||||
PoolStatusIo_failure_continue /* failed I/O, failmode 'continue' */
|
PoolStatusIoFailureContinue /* failed I/O, failmode 'continue' */
|
||||||
PoolStatusBad_log /* cannot read log chain(s) */
|
PoolStatusBadLog /* cannot read log chain(s) */
|
||||||
PoolStatusErrata /* informational errata available */
|
PoolStatusErrata /* informational errata available */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -86,27 +91,27 @@ const (
|
||||||
* pool has unsupported features but cannot be opened at all, its
|
* pool has unsupported features but cannot be opened at all, its
|
||||||
* status is ZPOOL_STATUS_UNSUP_FEAT_READ.
|
* status is ZPOOL_STATUS_UNSUP_FEAT_READ.
|
||||||
*/
|
*/
|
||||||
PoolStatusUnsup_feat_read /* unsupported features for read */
|
PoolStatusUnsupFeatRead /* unsupported features for read */
|
||||||
PoolStatusUnsup_feat_write /* unsupported features for write */
|
PoolStatusUnsupFeatWrite /* unsupported features for write */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These faults have no corresponding message ID. At the time we are
|
* 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
|
* checking the status, the original reason for the FMA fault (I/O or
|
||||||
* checksum errors) has been lost.
|
* checksum errors) has been lost.
|
||||||
*/
|
*/
|
||||||
PoolStatusFaulted_dev_r /* faulted device with replicas */
|
PoolStatusFaultedDevR /* faulted device with replicas */
|
||||||
PoolStatusFaulted_dev_nr /* faulted device with no replicas */
|
PoolStatusFaultedDevNr /* faulted device with no replicas */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following are not faults per se, but still an error possibly
|
* The following are not faults per se, but still an error possibly
|
||||||
* requiring administrative attention. There is no corresponding
|
* requiring administrative attention. There is no corresponding
|
||||||
* message ID.
|
* message ID.
|
||||||
*/
|
*/
|
||||||
PoolStatusVersion_older /* older legacy on-disk version */
|
PoolStatusVersionOlder /* older legacy on-disk version */
|
||||||
PoolStatusFeat_disabled /* supported features are disabled */
|
PoolStatusFeatDisabled /* supported features are disabled */
|
||||||
PoolStatusResilvering /* device being resilvered */
|
PoolStatusResilvering /* device being resilvered */
|
||||||
PoolStatusOffline_dev /* device online */
|
PoolStatusOfflineDev /* device online */
|
||||||
PoolStatusRemoved_dev /* removed device */
|
PoolStatusRemovedDev /* removed device */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finally, the following indicates a healthy pool.
|
* 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. Enumerates available ZFS pool properties. Use it to access
|
||||||
// pool properties either to read or set soecific property.
|
// pool properties either to read or set soecific property.
|
||||||
const (
|
const (
|
||||||
PoolPropName PoolProp = iota
|
PoolPropName Prop = iota
|
||||||
PoolPropSize
|
PoolPropSize
|
||||||
PoolPropCapacity
|
PoolPropCapacity
|
||||||
PoolPropAltroot
|
PoolPropAltroot
|
||||||
PoolPropHealth
|
PoolPropHealth
|
||||||
PoolPropGuid
|
PoolPropGUID
|
||||||
PoolPropVersion
|
PoolPropVersion
|
||||||
PoolPropBootfs
|
PoolPropBootfs
|
||||||
PoolPropDelegation
|
PoolPropDelegation
|
||||||
|
@ -166,102 +171,178 @@ const (
|
||||||
* the property table in module/zcommon/zfs_prop.c.
|
* the property table in module/zcommon/zfs_prop.c.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
ZFSPropType ZFSProp = iota
|
DatasetPropType Prop = iota
|
||||||
ZFSPropCreation
|
DatasetPropCreation
|
||||||
ZFSPropUsed
|
DatasetPropUsed
|
||||||
ZFSPropAvailable
|
DatasetPropAvailable
|
||||||
ZFSPropReferenced
|
DatasetPropReferenced
|
||||||
ZFSPropCompressratio
|
DatasetPropCompressratio
|
||||||
ZFSPropMounted
|
DatasetPropMounted
|
||||||
ZFSPropOrigin
|
DatasetPropOrigin
|
||||||
ZFSPropQuota
|
DatasetPropQuota
|
||||||
ZFSPropReservation
|
DatasetPropReservation
|
||||||
ZFSPropVolsize
|
DatasetPropVolsize
|
||||||
ZFSPropVolblocksize
|
DatasetPropVolblocksize
|
||||||
ZFSPropRecordsize
|
DatasetPropRecordsize
|
||||||
ZFSPropMountpoint
|
DatasetPropMountpoint
|
||||||
ZFSPropSharenfs
|
DatasetPropSharenfs
|
||||||
ZFSPropChecksum
|
DatasetPropChecksum
|
||||||
ZFSPropCompression
|
DatasetPropCompression
|
||||||
ZFSPropAtime
|
DatasetPropAtime
|
||||||
ZFSPropDevices
|
DatasetPropDevices
|
||||||
ZFSPropExec
|
DatasetPropExec
|
||||||
ZFSPropSetuid
|
DatasetPropSetuid
|
||||||
ZFSPropReadonly
|
DatasetPropReadonly
|
||||||
ZFSPropZoned
|
DatasetPropZoned
|
||||||
ZFSPropSnapdir
|
DatasetPropSnapdir
|
||||||
ZFSPropPrivate /* not exposed to user, temporary */
|
DatasetPropPrivate /* not exposed to user, temporary */
|
||||||
ZFSPropAclinherit
|
DatasetPropAclinherit
|
||||||
ZFSPropCreatetxg /* not exposed to the user */
|
DatasetPropCreatetxg /* not exposed to the user */
|
||||||
ZFSPropName /* not exposed to the user */
|
DatasetPropName /* not exposed to the user */
|
||||||
ZFSPropCanmount
|
DatasetPropCanmount
|
||||||
ZFSPropIscsioptions /* not exposed to the user */
|
DatasetPropIscsioptions /* not exposed to the user */
|
||||||
ZFSPropXattr
|
DatasetPropXattr
|
||||||
ZFSPropNumclones /* not exposed to the user */
|
DatasetPropNumclones /* not exposed to the user */
|
||||||
ZFSPropCopies
|
DatasetPropCopies
|
||||||
ZFSPropVersion
|
DatasetPropVersion
|
||||||
ZFSPropUtf8only
|
DatasetPropUtf8only
|
||||||
ZFSPropNormalize
|
DatasetPropNormalize
|
||||||
ZFSPropCase
|
DatasetPropCase
|
||||||
ZFSPropVscan
|
DatasetPropVscan
|
||||||
ZFSPropNbmand
|
DatasetPropNbmand
|
||||||
ZFSPropSharesmb
|
DatasetPropSharesmb
|
||||||
ZFSPropRefquota
|
DatasetPropRefquota
|
||||||
ZFSPropRefreservation
|
DatasetPropRefreservation
|
||||||
ZFSPropGuid
|
DatasetPropGUID
|
||||||
ZFSPropPrimarycache
|
DatasetPropPrimarycache
|
||||||
ZFSPropSecondarycache
|
DatasetPropSecondarycache
|
||||||
ZFSPropUsedsnap
|
DatasetPropUsedsnap
|
||||||
ZFSPropUsedds
|
DatasetPropUsedds
|
||||||
ZFSPropUsedchild
|
DatasetPropUsedchild
|
||||||
ZFSPropUsedrefreserv
|
DatasetPropUsedrefreserv
|
||||||
ZFSPropUseraccounting /* not exposed to the user */
|
DatasetPropUseraccounting /* not exposed to the user */
|
||||||
ZFSPropStmf_shareinfo /* not exposed to the user */
|
DatasetPropStmfShareinfo /* not exposed to the user */
|
||||||
ZFSPropDefer_destroy
|
DatasetPropDeferDestroy
|
||||||
ZFSPropUserrefs
|
DatasetPropUserrefs
|
||||||
ZFSPropLogbias
|
DatasetPropLogbias
|
||||||
ZFSPropUnique /* not exposed to the user */
|
DatasetPropUnique /* not exposed to the user */
|
||||||
ZFSPropObjsetid /* not exposed to the user */
|
DatasetPropObjsetid /* not exposed to the user */
|
||||||
ZFSPropDedup
|
DatasetPropDedup
|
||||||
ZFSPropMlslabel
|
DatasetPropMlslabel
|
||||||
ZFSPropSync
|
DatasetPropSync
|
||||||
ZFSPropRefratio
|
DatasetPropRefratio
|
||||||
ZFSPropWritten
|
DatasetPropWritten
|
||||||
ZFSPropClones
|
DatasetPropClones
|
||||||
ZFSPropLogicalused
|
DatasetPropLogicalused
|
||||||
ZFSPropLogicalreferenced
|
DatasetPropLogicalreferenced
|
||||||
ZFSPropInconsistent /* not exposed to the user */
|
DatasetPropInconsistent /* not exposed to the user */
|
||||||
ZFSPropSnapdev
|
DatasetPropSnapdev
|
||||||
ZFSPropAcltype
|
DatasetPropAcltype
|
||||||
ZFSPropSelinux_context
|
DatasetPropSelinuxContext
|
||||||
ZFSPropSelinux_fscontext
|
DatasetPropSelinuxFsContext
|
||||||
ZFSPropSelinux_defcontext
|
DatasetPropSelinuxDefContext
|
||||||
ZFSPropSelinux_rootcontext
|
DatasetPropSelinuxRootContext
|
||||||
ZFSPropRelatime
|
DatasetPropRelatime
|
||||||
ZFSPropRedundant_metadata
|
DatasetPropRedundantMetadata
|
||||||
ZFSPropOverlay
|
DatasetPropOverlay
|
||||||
ZFSNumProps
|
DatasetNumProps
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get last underlying libzfs error description if any
|
// LastError get last underlying libzfs error description if any
|
||||||
func LastError() (err error) {
|
func LastError() (err error) {
|
||||||
errno := C.libzfs_errno(libzfs_handle)
|
errno := C.libzfs_errno(libzfsHandle)
|
||||||
if errno == 0 {
|
if errno == 0 {
|
||||||
return nil
|
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) {
|
func ClearLastError() (err error) {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
C.clear_last_error(libzfs_handle)
|
C.clear_last_error(libzfsHandle)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolean_t(b bool) (r C.boolean_t) {
|
func booleanT(b bool) (r C.boolean_t) {
|
||||||
if b {
|
if b {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
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"
|
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
|
type DatasetType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// DatasetTypeFilesystem - file system dataset
|
||||||
DatasetTypeFilesystem DatasetType = (1 << 0)
|
DatasetTypeFilesystem DatasetType = (1 << 0)
|
||||||
|
// DatasetTypeSnapshot - snapshot of dataset
|
||||||
DatasetTypeSnapshot = (1 << 1)
|
DatasetTypeSnapshot = (1 << 1)
|
||||||
|
// DatasetTypeVolume - volume (virtual block device) dataset
|
||||||
DatasetTypeVolume = (1 << 2)
|
DatasetTypeVolume = (1 << 2)
|
||||||
|
// DatasetTypePool - pool dataset
|
||||||
DatasetTypePool = (1 << 3)
|
DatasetTypePool = (1 << 3)
|
||||||
|
// DatasetTypeBookmark - bookmark dataset
|
||||||
DatasetTypeBookmark = (1 << 4)
|
DatasetTypeBookmark = (1 << 4)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Dataset - ZFS dataset object
|
||||||
type Dataset struct {
|
type Dataset struct {
|
||||||
list *C.dataset_list_t
|
list *C.dataset_list_t
|
||||||
Type DatasetType
|
Type DatasetType
|
||||||
Properties map[ZFSProp]Property
|
Properties map[Prop]Property
|
||||||
Children []Dataset
|
Children []Dataset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +47,7 @@ func (d *Dataset) openChildren() (err error) {
|
||||||
errcode := C.dataset_list_children(d.list.zh, &(dataset.list))
|
errcode := C.dataset_list_children(d.list.zh, &(dataset.list))
|
||||||
for dataset.list != nil {
|
for dataset.list != nil {
|
||||||
dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
|
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()
|
err = dataset.ReloadProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -49,7 +59,7 @@ func (d *Dataset) openChildren() (err error) {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for ci, _ := range d.Children {
|
for ci := range d.Children {
|
||||||
if err = d.Children[ci].openChildren(); err != nil {
|
if err = d.Children[ci].openChildren(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -57,11 +67,11 @@ func (d *Dataset) openChildren() (err error) {
|
||||||
return
|
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).
|
// (file-systems, volumes or snapshots).
|
||||||
func DatasetOpenAll() (datasets []Dataset, err error) {
|
func DatasetOpenAll() (datasets []Dataset, err error) {
|
||||||
var dataset Dataset
|
var dataset Dataset
|
||||||
errcode := C.dataset_list_root(libzfs_handle, &dataset.list)
|
errcode := C.dataset_list_root(libzfsHandle, &dataset.list)
|
||||||
for dataset.list != nil {
|
for dataset.list != nil {
|
||||||
dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
|
dataset.Type = DatasetType(C.zfs_get_type(dataset.list.zh))
|
||||||
err = dataset.ReloadProperties()
|
err = dataset.ReloadProperties()
|
||||||
|
@ -75,7 +85,7 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for ci, _ := range datasets {
|
for ci := range datasets {
|
||||||
if err = datasets[ci].openChildren(); err != nil {
|
if err = datasets[ci].openChildren(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,24 +93,25 @@ func DatasetOpenAll() (datasets []Dataset, err error) {
|
||||||
return
|
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) {
|
func DatasetCloseAll(datasets []Dataset) {
|
||||||
for _, d := range datasets {
|
for _, d := range datasets {
|
||||||
d.Close()
|
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) {
|
func DatasetOpen(path string) (d Dataset, err error) {
|
||||||
d.list = C.create_dataset_list_item()
|
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 {
|
if d.list.zh == nil {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Type = DatasetType(C.zfs_get_type(d.list.zh))
|
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()
|
err = d.ReloadProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -109,7 +120,7 @@ func DatasetOpen(path string) (d Dataset, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
|
func datasetPropertiesTonvlist(props map[Prop]Property) (
|
||||||
cprops *C.nvlist_t, err error) {
|
cprops *C.nvlist_t, err error) {
|
||||||
// convert properties to nvlist C type
|
// convert properties to nvlist C type
|
||||||
r := C.nvlist_alloc(&cprops, C.NV_UNIQUE_NAME, 0)
|
r := C.nvlist_alloc(&cprops, C.NV_UNIQUE_NAME, 0)
|
||||||
|
@ -129,16 +140,17 @@ func datasetPropertiesTo_nvlist(props map[ZFSProp]Property) (
|
||||||
return
|
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,
|
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
|
var cprops *C.nvlist_t
|
||||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer C.nvlist_free(cprops)
|
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)
|
C.zfs_type_t(dtype), cprops)
|
||||||
if errcode != 0 {
|
if errcode != 0 {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
|
@ -146,7 +158,8 @@ func DatasetCreate(path string, dtype DatasetType,
|
||||||
return
|
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() {
|
func (d *Dataset) Close() {
|
||||||
if d.list != nil && d.list.zh != nil {
|
if d.list != nil && d.list.zh != nil {
|
||||||
C.dataset_list_close(d.list)
|
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
|
// 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.
|
// to true to defer destruction for when dataset is not in use.
|
||||||
func (d *Dataset) Destroy(Defer bool) (err error) {
|
func (d *Dataset) Destroy(Defer bool) (err error) {
|
||||||
|
@ -165,13 +178,13 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dsType, e := d.GetProperty(ZFSPropType)
|
dsType, e := d.GetProperty(DatasetPropType)
|
||||||
err = errors.New("Cannot destroy dataset " + path +
|
err = errors.New("Cannot destroy dataset " + path +
|
||||||
": " + dsType.Value + " has children")
|
": " + dsType.Value + " has children")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if d.list != nil {
|
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()
|
err = LastError()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -180,7 +193,7 @@ func (d *Dataset) Destroy(Defer bool) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively destroy children of dataset and dataset.
|
// DestroyRecursive recursively destroy children of dataset and dataset.
|
||||||
func (d *Dataset) DestroyRecursive() (err error) {
|
func (d *Dataset) DestroyRecursive() (err error) {
|
||||||
if len(d.Children) > 0 {
|
if len(d.Children) > 0 {
|
||||||
for _, c := range d.Children {
|
for _, c := range d.Children {
|
||||||
|
@ -197,6 +210,7 @@ func (d *Dataset) DestroyRecursive() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pool returns pool dataset belongs to
|
||||||
func (d *Dataset) Pool() (p Pool, err error) {
|
func (d *Dataset) Pool() (p Pool, err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
|
@ -212,6 +226,7 @@ func (d *Dataset) Pool() (p Pool, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReloadProperties re-read dataset's properties
|
||||||
func (d *Dataset) ReloadProperties() (err error) {
|
func (d *Dataset) ReloadProperties() (err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
|
@ -220,8 +235,8 @@ func (d *Dataset) ReloadProperties() (err error) {
|
||||||
var plist *C.property_list_t
|
var plist *C.property_list_t
|
||||||
plist = C.new_property_list()
|
plist = C.new_property_list()
|
||||||
defer C.free_properties(plist)
|
defer C.free_properties(plist)
|
||||||
d.Properties = make(map[ZFSProp]Property)
|
d.Properties = make(map[Prop]Property)
|
||||||
for prop := ZFSPropType; prop < ZFSNumProps; prop++ {
|
for prop := DatasetPropType; prop < DatasetNumProps; prop++ {
|
||||||
errcode := C.read_dataset_property(d.list.zh, plist, C.int(prop))
|
errcode := C.read_dataset_property(d.list.zh, plist, C.int(prop))
|
||||||
if errcode != 0 {
|
if errcode != 0 {
|
||||||
continue
|
continue
|
||||||
|
@ -232,9 +247,9 @@ func (d *Dataset) ReloadProperties() (err error) {
|
||||||
return
|
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.
|
// 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 {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
return
|
return
|
||||||
|
@ -253,10 +268,10 @@ func (d *Dataset) GetProperty(p ZFSProp) (prop Property, err error) {
|
||||||
return
|
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.
|
// some can be set only at creation time and some are read only.
|
||||||
// Always check if returned error and its description.
|
// 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 {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
return
|
return
|
||||||
|
@ -273,15 +288,15 @@ func (d *Dataset) SetProperty(p ZFSProp, value string) (err error) {
|
||||||
return
|
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.
|
// 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
|
var cprops *C.nvlist_t
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer C.nvlist_free(cprops)
|
defer C.nvlist_free(cprops)
|
||||||
|
@ -293,14 +308,14 @@ func (d *Dataset) Clone(target string, props map[ZFSProp]Property) (rd Dataset,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create dataset snapshot. Set recur to true to snapshot child datasets.
|
// DatasetSnapshot 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) {
|
func DatasetSnapshot(path string, recur bool, props map[Prop]Property) (rd Dataset, err error) {
|
||||||
var cprops *C.nvlist_t
|
var cprops *C.nvlist_t
|
||||||
if cprops, err = datasetPropertiesTo_nvlist(props); err != nil {
|
if cprops, err = datasetPropertiesTonvlist(props); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer C.nvlist_free(cprops)
|
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()
|
err = LastError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -308,7 +323,7 @@ func DatasetSnapshot(path string, recur bool, props map[ZFSProp]Property) (rd Da
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return zfs dataset path/name
|
// Path return zfs dataset path/name
|
||||||
func (d *Dataset) Path() (path string, err error) {
|
func (d *Dataset) Path() (path string, err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
|
@ -319,14 +334,14 @@ func (d *Dataset) Path() (path string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollabck dataset snapshot
|
// Rollback rollabck's dataset snapshot
|
||||||
func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
|
func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errc := C.zfs_rollback(d.list.zh,
|
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()
|
err = LastError()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -334,20 +349,20 @@ func (d *Dataset) Rollback(snap *Dataset, force bool) (err error) {
|
||||||
|
|
||||||
// Rename dataset
|
// Rename dataset
|
||||||
func (d *Dataset) Rename(newname string, recur,
|
func (d *Dataset) Rename(newname string, recur,
|
||||||
force_umount bool) (err error) {
|
forceUnmount bool) (err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errc := C.zfs_rename(d.list.zh, C.CString(newname),
|
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()
|
err = LastError()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks to see if the mount is active. If the filesystem is mounted, fills
|
// IsMounted checks to see if the mount is active. If the filesystem is mounted,
|
||||||
// in 'where' with the current mountpoint, and returns true. Otherwise,
|
// sets in 'where' argument the current mountpoint, and returns true. Otherwise,
|
||||||
// returns false.
|
// returns false.
|
||||||
func (d *Dataset) IsMounted() (mounted bool, where string) {
|
func (d *Dataset) IsMounted() (mounted bool, where string) {
|
||||||
var cw *C.char
|
var cw *C.char
|
||||||
|
@ -386,7 +401,8 @@ func (d *Dataset) Unmount(flags int) (err error) {
|
||||||
return
|
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) {
|
func (d *Dataset) UnmountAll(flags int) (err error) {
|
||||||
if d.list == nil {
|
if d.list == nil {
|
||||||
err = errors.New(msgDatasetIsNil)
|
err = errors.New(msgDatasetIsNil)
|
||||||
|
@ -398,12 +414,12 @@ func (d *Dataset) UnmountAll(flags int) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert property to name
|
// DatasetPropertyToName convert property to name
|
||||||
// ( returns built in string representation of property name).
|
// ( returns built in string representation of property name).
|
||||||
// This is optional, you can represent each property with string
|
// This is optional, you can represent each property with string
|
||||||
// name of choice.
|
// name of choice.
|
||||||
func DatasetPropertyToName(p ZFSProp) (name string) {
|
func DatasetPropertyToName(p Prop) (name string) {
|
||||||
if p == ZFSNumProps {
|
if p == DatasetNumProps {
|
||||||
return "numofprops"
|
return "numofprops"
|
||||||
}
|
}
|
||||||
prop := C.zfs_prop_t(p)
|
prop := C.zfs_prop_t(p)
|
||||||
|
|
74
zfs_test.go
74
zfs_test.go
|
@ -2,15 +2,16 @@ package zfs_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bicomsystems/go-libzfs"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bicomsystems/go-libzfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
// HELPERS:
|
// HELPERS:
|
||||||
var TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
|
var TSTDatasetPath = TSTPoolName + "/DATASET"
|
||||||
var TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
|
var TSTVolumePath = TSTDatasetPath + "/VOLUME"
|
||||||
var TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
|
var TSTDatasetPathSnap = TSTDatasetPath + "@test"
|
||||||
|
|
||||||
func printDatasets(ds []zfs.Dataset) error {
|
func printDatasets(ds []zfs.Dataset) error {
|
||||||
for _, d := range ds {
|
for _, d := range ds {
|
||||||
|
@ -19,7 +20,7 @@ func printDatasets(ds []zfs.Dataset) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p, err := d.GetProperty(zfs.ZFSPropType)
|
p, err := d.GetProperty(zfs.DatasetPropType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -36,45 +37,45 @@ func printDatasets(ds []zfs.Dataset) error {
|
||||||
|
|
||||||
func zfsTestDatasetCreate(t *testing.T) {
|
func zfsTestDatasetCreate(t *testing.T) {
|
||||||
// reinit names used in case TESTPOOL was in conflict
|
// reinit names used in case TESTPOOL was in conflict
|
||||||
TST_DATASET_PATH = TST_POOL_NAME + "/DATASET"
|
TSTDatasetPath = TSTPoolName + "/DATASET"
|
||||||
TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME"
|
TSTVolumePath = TSTDatasetPath + "/VOLUME"
|
||||||
TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test"
|
TSTDatasetPathSnap = TSTDatasetPath + "@test"
|
||||||
|
|
||||||
println("TEST DatasetCreate(", TST_DATASET_PATH, ") (filesystem) ... ")
|
println("TEST DatasetCreate(", TSTDatasetPath, ") (filesystem) ... ")
|
||||||
props := make(map[zfs.ZFSProp]zfs.Property)
|
props := make(map[zfs.Prop]zfs.Property)
|
||||||
d, err := zfs.DatasetCreate(TST_DATASET_PATH, zfs.DatasetTypeFilesystem, props)
|
d, err := zfs.DatasetCreate(TSTDatasetPath, zfs.DatasetTypeFilesystem, props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Close()
|
d.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
|
|
||||||
strSize := "536870912" // 512M
|
strSize := "536870912" // 512M
|
||||||
|
|
||||||
println("TEST DatasetCreate(", TST_VOLUME_PATH, ") (volume) ... ")
|
println("TEST DatasetCreate(", TSTVolumePath, ") (volume) ... ")
|
||||||
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.
|
// In addition I explicitly choose some more properties to be set.
|
||||||
props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
|
props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||||
props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
|
props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
|
||||||
d, err = zfs.DatasetCreate(TST_VOLUME_PATH, zfs.DatasetTypeVolume, props)
|
d, err = zfs.DatasetCreate(TSTVolumePath, zfs.DatasetTypeVolume, props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Close()
|
d.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zfsTestDatasetOpen(t *testing.T) {
|
func zfsTestDatasetOpen(t *testing.T) {
|
||||||
println("TEST DatasetOpen(", TST_DATASET_PATH, ") ... ")
|
println("TEST DatasetOpen(", TSTDatasetPath, ") ... ")
|
||||||
d, err := zfs.DatasetOpen(TST_DATASET_PATH)
|
d, err := zfs.DatasetOpen(TSTDatasetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Close()
|
d.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zfsTestDatasetOpenAll(t *testing.T) {
|
func zfsTestDatasetOpenAll(t *testing.T) {
|
||||||
|
@ -90,24 +91,24 @@ func zfsTestDatasetOpenAll(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
zfs.DatasetCloseAll(ds)
|
zfs.DatasetCloseAll(ds)
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zfsTestDatasetSnapshot(t *testing.T) {
|
func zfsTestDatasetSnapshot(t *testing.T) {
|
||||||
println("TEST DatasetSnapshot(", TST_DATASET_PATH, ", true, ...) ... ")
|
println("TEST DatasetSnapshot(", TSTDatasetPath, ", true, ...) ... ")
|
||||||
props := make(map[zfs.ZFSProp]zfs.Property)
|
props := make(map[zfs.Prop]zfs.Property)
|
||||||
d, err := zfs.DatasetSnapshot(TST_DATASET_PATH_SNAP, true, props)
|
d, err := zfs.DatasetSnapshot(TSTDatasetPathSnap, true, props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zfsTestDatasetDestroy(t *testing.T) {
|
func zfsTestDatasetDestroy(t *testing.T) {
|
||||||
println("TEST DATASET Destroy( ", TST_DATASET_PATH, " ) ... ")
|
println("TEST DATASET Destroy( ", TSTDatasetPath, " ) ... ")
|
||||||
d, err := zfs.DatasetOpen(TST_DATASET_PATH)
|
d, err := zfs.DatasetOpen(TSTDatasetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -117,7 +118,7 @@ func zfsTestDatasetDestroy(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
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
|
// Create map to represent ZFS dataset properties. This is equivalent to
|
||||||
// list of properties you can get from ZFS CLI tool, and some more
|
// list of properties you can get from ZFS CLI tool, and some more
|
||||||
// internally used by libzfs.
|
// 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
|
// 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
|
// 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.
|
// similar to convert in to string (base 10) from numeric type.
|
||||||
strSize := "1073741824"
|
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.
|
// In addition I explicitly choose some more properties to be set.
|
||||||
props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"}
|
props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"}
|
||||||
props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize}
|
props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize}
|
||||||
|
|
||||||
// Lets create desired volume
|
// Lets create desired volume
|
||||||
d, err := zfs.DatasetCreate("TESTPOOL/VOLUME1", zfs.DatasetTypeVolume, props)
|
d, err := zfs.DatasetCreate("TESTPOOL/VOLUME1", zfs.DatasetTypeVolume, props)
|
||||||
|
@ -161,10 +162,11 @@ func ExampleDatasetOpen() {
|
||||||
}
|
}
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
var p zfs.Property
|
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())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
println(zfs.DatasetPropertyToName(zfs.ZFSPropAvailable), " = ", p.Value)
|
println(zfs.DatasetPropertyToName(zfs.DatasetPropAvailable), " = ",
|
||||||
|
p.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDatasetOpenAll() {
|
func ExampleDatasetOpenAll() {
|
||||||
|
@ -180,7 +182,7 @@ func ExampleDatasetOpenAll() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
p, err := d.GetProperty(zfs.ZFSPropType)
|
p, err := d.GetProperty(zfs.DatasetPropType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
|
|
127
zpool.go
127
zpool.go
|
@ -10,16 +10,17 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
msgPoolIsNil = "Pool handle not initialized or its closed"
|
msgPoolIsNil = "Pool handle not initialized or its closed"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PoolProperties map[PoolProp]string
|
// PoolProperties type is map of pool properties name -> value
|
||||||
type ZFSProperties map[ZFSProp]string
|
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
|
/* Pool.Properties map[string]Property
|
||||||
*/
|
*/
|
||||||
|
@ -34,11 +35,11 @@ type Pool struct {
|
||||||
Features map[string]string
|
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
|
// Returns Pool object, requires Pool.Close() to be called explicitly
|
||||||
// for memory cleanup after object is not needed anymore.
|
// for memory cleanup after object is not needed anymore.
|
||||||
func PoolOpen(name string) (pool Pool, err error) {
|
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 {
|
if pool.list != nil {
|
||||||
err = pool.ReloadProperties()
|
err = pool.ReloadProperties()
|
||||||
return
|
return
|
||||||
|
@ -47,7 +48,7 @@ func PoolOpen(name string) (pool Pool, err error) {
|
||||||
return
|
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.
|
// name stored on disk.
|
||||||
func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
||||||
errPoolList := errors.New("Failed to list pools")
|
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))
|
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)
|
defer C.nvlist_free(pools)
|
||||||
|
|
||||||
elem = C.nvlist_next_nvpair(pools, elem)
|
elem = C.nvlist_next_nvpair(pools, elem)
|
||||||
|
@ -88,7 +89,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
||||||
return
|
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 {
|
if retcode != 0 {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
return
|
return
|
||||||
|
@ -97,12 +98,12 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
|
||||||
return
|
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
|
// Returns array of Pool handlers, each have to be closed after not needed
|
||||||
// anymore. Call Pool.Close() method.
|
// anymore. Call Pool.Close() method.
|
||||||
func PoolOpenAll() (pools []Pool, err error) {
|
func PoolOpenAll() (pools []Pool, err error) {
|
||||||
var pool Pool
|
var pool Pool
|
||||||
errcode := C.zpool_list(libzfs_handle, &pool.list)
|
errcode := C.zpool_list(libzfsHandle, &pool.list)
|
||||||
for pool.list != nil {
|
for pool.list != nil {
|
||||||
err = pool.ReloadProperties()
|
err = pool.ReloadProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,17 +118,18 @@ func PoolOpenAll() (pools []Pool, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PoolCloseAll close all pools in given slice
|
||||||
func PoolCloseAll(pools []Pool) {
|
func PoolCloseAll(pools []Pool) {
|
||||||
for _, p := range pools {
|
for _, p := range pools {
|
||||||
p.Close()
|
p.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert property to name
|
// PoolPropertyToName convert property to name
|
||||||
// ( returns built in string representation of property name).
|
// ( returns built in string representation of property name).
|
||||||
// This is optional, you can represent each property with string
|
// This is optional, you can represent each property with string
|
||||||
// name of choice.
|
// name of choice.
|
||||||
func PoolPropertyToName(p PoolProp) (name string) {
|
func PoolPropertyToName(p Prop) (name string) {
|
||||||
if p == PoolNumProps {
|
if p == PoolNumProps {
|
||||||
return "numofprops"
|
return "numofprops"
|
||||||
}
|
}
|
||||||
|
@ -136,15 +138,15 @@ func PoolPropertyToName(p PoolProp) (name string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map POOL STATE to string.
|
// PoolStateToName maps POOL STATE to string.
|
||||||
func PoolStateToName(state PoolState) (name string) {
|
func PoolStateToName(state PoolState) (name string) {
|
||||||
ps := C.pool_state_t(state)
|
ps := C.pool_state_t(state)
|
||||||
name = C.GoString(C.zpool_pool_state_to_name(ps))
|
name = C.GoString(C.zpool_pool_state_to_name(ps))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-read ZFS pool properties and features, refresh Pool.Properties and
|
// ReloadProperties re-read ZFS pool properties and features, refresh
|
||||||
// Pool.Features map
|
// Pool.Properties and Pool.Features map
|
||||||
func (pool *Pool) ReloadProperties() (err error) {
|
func (pool *Pool) ReloadProperties() (err error) {
|
||||||
propList := C.read_zpool_properties(pool.list.zph)
|
propList := C.read_zpool_properties(pool.list.zph)
|
||||||
if propList == nil {
|
if propList == nil {
|
||||||
|
@ -165,15 +167,15 @@ func (pool *Pool) ReloadProperties() (err error) {
|
||||||
"async_destroy": "disabled",
|
"async_destroy": "disabled",
|
||||||
"empty_bpobj": "disabled",
|
"empty_bpobj": "disabled",
|
||||||
"lz4_compress": "disabled"}
|
"lz4_compress": "disabled"}
|
||||||
for name, _ := range pool.Features {
|
for name := range pool.Features {
|
||||||
pool.GetFeature(name)
|
pool.GetFeature(name)
|
||||||
}
|
}
|
||||||
return
|
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.
|
// 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 {
|
if pool.list != nil {
|
||||||
// First check if property exist at all
|
// First check if property exist at all
|
||||||
if p < PoolPropName || p > PoolNumProps {
|
if p < PoolPropName || p > PoolNumProps {
|
||||||
|
@ -194,7 +196,7 @@ func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) {
|
||||||
return prop, errors.New(msgPoolIsNil)
|
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.
|
// feature in Features map.
|
||||||
func (pool *Pool) GetFeature(name string) (value string, err error) {
|
func (pool *Pool) GetFeature(name string) (value string, err error) {
|
||||||
var fvalue [512]C.char
|
var fvalue [512]C.char
|
||||||
|
@ -209,10 +211,10 @@ func (pool *Pool) GetFeature(name string) (value string, err error) {
|
||||||
return
|
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.
|
// some can be set only at creation time and some are read only.
|
||||||
// Always check if returned error and its description.
|
// 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 {
|
if pool.list != nil {
|
||||||
// First check if property exist at all
|
// First check if property exist at all
|
||||||
if p < PoolPropName || p > PoolNumProps {
|
if p < PoolPropName || p > PoolNumProps {
|
||||||
|
@ -241,7 +243,7 @@ func (pool *Pool) Close() {
|
||||||
pool.list = nil
|
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) {
|
func (pool *Pool) Name() (name string, err error) {
|
||||||
if pool.list == nil {
|
if pool.list == nil {
|
||||||
err = errors.New(msgPoolIsNil)
|
err = errors.New(msgPoolIsNil)
|
||||||
|
@ -252,7 +254,7 @@ func (pool *Pool) Name() (name string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ZFS pool state
|
// State get ZFS pool state
|
||||||
// Return the state of the pool (ACTIVE or UNAVAILABLE)
|
// Return the state of the pool (ACTIVE or UNAVAILABLE)
|
||||||
func (pool *Pool) State() (state PoolState, err error) {
|
func (pool *Pool) State() (state PoolState, err error) {
|
||||||
if pool.list == nil {
|
if pool.list == nil {
|
||||||
|
@ -263,7 +265,7 @@ func (pool *Pool) State() (state PoolState, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZFS virtual device specification
|
// VDevSpec ZFS virtual device specification
|
||||||
type VDevSpec struct {
|
type VDevSpec struct {
|
||||||
Type VDevType
|
Type VDevType
|
||||||
Devices []VDevSpec // groups other devices (e.g. mirror)
|
Devices []VDevSpec // groups other devices (e.g. mirror)
|
||||||
|
@ -271,31 +273,31 @@ type VDevSpec struct {
|
||||||
Path string
|
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)
|
maxdevs = int(^uint(0) >> 1)
|
||||||
if self.Type == VDevTypeRaidz {
|
if vdev.Type == VDevTypeRaidz {
|
||||||
grouping = true
|
grouping = true
|
||||||
if self.Parity == 0 {
|
if vdev.Parity == 0 {
|
||||||
self.Parity = 1
|
vdev.Parity = 1
|
||||||
}
|
}
|
||||||
if self.Parity > 254 {
|
if vdev.Parity > 254 {
|
||||||
self.Parity = 254
|
vdev.Parity = 254
|
||||||
}
|
}
|
||||||
mindevs = int(self.Parity) + 1
|
mindevs = int(vdev.Parity) + 1
|
||||||
maxdevs = 255
|
maxdevs = 255
|
||||||
} else if self.Type == VDevTypeMirror {
|
} else if vdev.Type == VDevTypeMirror {
|
||||||
grouping = true
|
grouping = true
|
||||||
mindevs = 2
|
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
|
grouping = true
|
||||||
mindevs = 1
|
mindevs = 1
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VDevSpec) isLog() (r C.uint64_t) {
|
func (vdev *VDevSpec) isLog() (r C.uint64_t) {
|
||||||
r = 0
|
r = 0
|
||||||
if self.Type == VDevTypeLog {
|
if vdev.Type == VDevTypeLog {
|
||||||
r = 1
|
r = 1
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -317,7 +319,7 @@ func toCPoolProperties(props PoolProperties) (cprops *C.nvlist_t) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func toCZFSProperties(props ZFSProperties) (cprops *C.nvlist_t) {
|
func toCDatasetProperties(props DatasetProperties) (cprops *C.nvlist_t) {
|
||||||
cprops = nil
|
cprops = nil
|
||||||
for prop, value := range props {
|
for prop, value := range props {
|
||||||
name := C.zfs_prop_to_name(C.zfs_prop_t(prop))
|
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)
|
defer C.nvlist_free_array(l2cache)
|
||||||
for i, vdev := range vdevs {
|
for i, vdev := range vdevs {
|
||||||
grouping, mindevs, maxdevs := vdev.isGrouping()
|
grouping, mindevs, maxdevs := vdev.isGrouping()
|
||||||
var child *C.nvlist_t = nil
|
var child *C.nvlist_t
|
||||||
// fmt.Println(vdev.Type)
|
// fmt.Println(vdev.Type)
|
||||||
if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
|
if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
|
||||||
err = errors.New("Failed to allocate vdev")
|
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)
|
vcount := len(vdev.Devices)
|
||||||
if vcount < mindevs || vcount > maxdevs {
|
if vcount < mindevs || vcount > maxdevs {
|
||||||
err = errors.New(fmt.Sprintf(
|
err = fmt.Errorf(
|
||||||
"Invalid vdev specification: %s supports no less than %d or more than %d devices", vdev.Type, mindevs, maxdevs))
|
"Invalid vdev specification: %s supports no less than %d or more than %d devices",
|
||||||
|
vdev.Type, mindevs, maxdevs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r := C.nvlist_add_string(child, C.CString(C.ZPOOL_CONFIG_TYPE),
|
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
|
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,
|
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
|
// 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 {
|
if r := C.nvlist_alloc(&nvroot, C.NV_UNIQUE_NAME, 0); r != 0 {
|
||||||
err = errors.New("Failed to allocate root vdev")
|
err = errors.New("Failed to allocate root vdev")
|
||||||
return
|
return
|
||||||
|
@ -495,7 +498,7 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
|
||||||
err = errors.New("Failed to allocate pool properties")
|
err = errors.New("Failed to allocate pool properties")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfsprops := toCZFSProperties(fsprops)
|
cfsprops := toCDatasetProperties(fsprops)
|
||||||
if cfsprops != nil {
|
if cfsprops != nil {
|
||||||
defer C.nvlist_free(cfsprops)
|
defer C.nvlist_free(cfsprops)
|
||||||
} else if len(fsprops) > 0 {
|
} else if len(fsprops) > 0 {
|
||||||
|
@ -516,16 +519,34 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create actual pool then open
|
// 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 {
|
cprops, cfsprops); r != 0 {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
return
|
err = errors.New(err.Error() + " (zpool_create)")
|
||||||
}
|
|
||||||
pool, err = PoolOpen(name)
|
|
||||||
return
|
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) {
|
func (pool *Pool) Status() (status PoolStatus, err error) {
|
||||||
var msgid *C.char
|
var msgid *C.char
|
||||||
var reason C.zpool_status_t
|
var reason C.zpool_status_t
|
||||||
|
@ -554,22 +575,22 @@ func (pool *Pool) Destroy(logStr string) (err error) {
|
||||||
return
|
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.
|
// 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
|
// A pool can not be exported if it has a shared spare that is currently
|
||||||
// being used.
|
// being used.
|
||||||
func (pool *Pool) Export(force bool, log string) (err error) {
|
func (pool *Pool) Export(force bool, log string) (err error) {
|
||||||
var force_t C.boolean_t = 0
|
var forcet C.boolean_t
|
||||||
if force {
|
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()
|
err = LastError()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hard force
|
// ExportForce hard force export of the pool from the system.
|
||||||
func (pool *Pool) ExportForce(log string) (err error) {
|
func (pool *Pool) ExportForce(log string) (err error) {
|
||||||
if rc := C.zpool_export_force(pool.list.zph, C.CString(log)); rc != 0 {
|
if rc := C.zpool_export_force(pool.list.zph, C.CString(log)); rc != 0 {
|
||||||
err = LastError()
|
err = LastError()
|
||||||
|
|
|
@ -2,16 +2,17 @@ package zfs_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bicomsystems/go-libzfs"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bicomsystems/go-libzfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
// HELPERS:
|
// HELPERS:
|
||||||
|
|
||||||
var TST_POOL_NAME = "TESTPOOL"
|
var TSTPoolName = "TESTPOOL"
|
||||||
|
|
||||||
func CreateTmpSparse(prefix string, size int64) (path string, err error) {
|
func CreateTmpSparse(prefix string, size int64) (path string, err error) {
|
||||||
sf, err := ioutil.TempFile("/tmp", prefix)
|
sf, err := ioutil.TempFile("/tmp", prefix)
|
||||||
|
@ -66,12 +67,12 @@ func zpoolTestPoolCreate(t *testing.T) {
|
||||||
// first check if pool with same name already exist
|
// first check if pool with same name already exist
|
||||||
// we don't want conflict
|
// we don't want conflict
|
||||||
for {
|
for {
|
||||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
p, err := zfs.PoolOpen(TSTPoolName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.Close()
|
p.Close()
|
||||||
TST_POOL_NAME += "0"
|
TSTPoolName += "0"
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -94,15 +95,15 @@ func zpoolTestPoolCreate(t *testing.T) {
|
||||||
zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs},
|
zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs},
|
||||||
}
|
}
|
||||||
|
|
||||||
props := make(map[zfs.PoolProp]string)
|
props := make(map[zfs.Prop]string)
|
||||||
fsprops := make(map[zfs.ZFSProp]string)
|
fsprops := make(map[zfs.Prop]string)
|
||||||
features := make(map[string]string)
|
features := make(map[string]string)
|
||||||
fsprops[zfs.ZFSPropMountpoint] = "none"
|
fsprops[zfs.DatasetPropMountpoint] = "none"
|
||||||
features["async_destroy"] = "enabled"
|
features["async_destroy"] = "enabled"
|
||||||
features["empty_bpobj"] = "enabled"
|
features["empty_bpobj"] = "enabled"
|
||||||
features["lz4_compress"] = "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 {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
// try cleanup
|
// try cleanup
|
||||||
|
@ -113,7 +114,7 @@ func zpoolTestPoolCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer pool.Close()
|
defer pool.Close()
|
||||||
|
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open and list all pools and them state on the system
|
// 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)
|
println("\tPool: ", pname, " state: ", pstate)
|
||||||
p.Close()
|
p.Close()
|
||||||
}
|
}
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestPoolDestroy(t *testing.T) {
|
func zpoolTestPoolDestroy(t *testing.T) {
|
||||||
println("TEST POOL Destroy( ", TST_POOL_NAME, " ) ... ")
|
println("TEST POOL Destroy( ", TSTPoolName, " ) ... ")
|
||||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
p, err := zfs.PoolOpen(TSTPoolName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer p.Close()
|
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())
|
t.Error(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestFailPoolOpen(t *testing.T) {
|
func zpoolTestFailPoolOpen(t *testing.T) {
|
||||||
|
@ -166,7 +167,7 @@ func zpoolTestFailPoolOpen(t *testing.T) {
|
||||||
pname := "fail to open this pool"
|
pname := "fail to open this pool"
|
||||||
p, err := zfs.PoolOpen(pname)
|
p, err := zfs.PoolOpen(pname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Error("PoolOpen pass when it should fail")
|
t.Error("PoolOpen pass when it should fail")
|
||||||
|
@ -174,43 +175,43 @@ func zpoolTestFailPoolOpen(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestExport(t *testing.T) {
|
func zpoolTestExport(t *testing.T) {
|
||||||
println("TEST POOL Export( ", TST_POOL_NAME, " ) ... ")
|
println("TEST POOL Export( ", TSTPoolName, " ) ... ")
|
||||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
p, err := zfs.PoolOpen(TSTPoolName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Export(false, "Test exporting pool")
|
p.Export(false, "Test exporting pool")
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestExportForce(t *testing.T) {
|
func zpoolTestExportForce(t *testing.T) {
|
||||||
println("TEST POOL ExportForce( ", TST_POOL_NAME, " ) ... ")
|
println("TEST POOL ExportForce( ", TSTPoolName, " ) ... ")
|
||||||
p, err := zfs.PoolOpen(TST_POOL_NAME)
|
p, err := zfs.PoolOpen(TSTPoolName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.ExportForce("Test force exporting pool")
|
p.ExportForce("Test force exporting pool")
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestImport(t *testing.T) {
|
func zpoolTestImport(t *testing.T) {
|
||||||
println("TEST POOL Import( ", TST_POOL_NAME, " ) ... ")
|
println("TEST POOL Import( ", TSTPoolName, " ) ... ")
|
||||||
p, err := zfs.PoolImport(TST_POOL_NAME, []string{"/tmp"})
|
p, err := zfs.PoolImport(TSTPoolName, []string{"/tmp"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestPoolProp(t *testing.T) {
|
func zpoolTestPoolProp(t *testing.T) {
|
||||||
println("TEST PoolProp on ", TST_POOL_NAME, " ... ")
|
println("TEST PoolProp on ", TSTPoolName, " ... ")
|
||||||
if pool, err := zfs.PoolOpen(TST_POOL_NAME); err == nil {
|
if pool, err := zfs.PoolOpen(TSTPoolName); err == nil {
|
||||||
defer pool.Close()
|
defer pool.Close()
|
||||||
// Turn on snapshot listing for pool
|
// Turn on snapshot listing for pool
|
||||||
pool.SetProperty(zfs.PoolPropListsnaps, "on")
|
pool.SetProperty(zfs.PoolPropListsnaps, "on")
|
||||||
|
@ -247,12 +248,12 @@ func zpoolTestPoolProp(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
println("PASS\n")
|
print("PASS\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func zpoolTestPoolStatusAndState(t *testing.T) {
|
func zpoolTestPoolStatusAndState(t *testing.T) {
|
||||||
println("TEST pool Status/State ( ", TST_POOL_NAME, " ) ... ")
|
println("TEST pool Status/State ( ", TSTPoolName, " ) ... ")
|
||||||
pool, err := zfs.PoolOpen(TST_POOL_NAME)
|
pool, err := zfs.PoolOpen(TSTPoolName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -269,9 +270,9 @@ func zpoolTestPoolStatusAndState(t *testing.T) {
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
return
|
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
|
// Iterate pool properties and print name, value and source
|
||||||
for key, prop := range p.Properties {
|
for key, prop := range p.Properties {
|
||||||
pkey := zfs.PoolProp(key)
|
pkey := zfs.Prop(key)
|
||||||
if pkey == zfs.PoolPropName {
|
if pkey == zfs.PoolPropName {
|
||||||
continue // Skip name its already printed above
|
continue // Skip name its already printed above
|
||||||
}
|
}
|
||||||
|
@ -353,14 +354,14 @@ func ExamplePoolCreate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pool properties
|
// pool properties
|
||||||
props := make(map[zfs.PoolProp]string)
|
props := make(map[zfs.Prop]string)
|
||||||
// root dataset filesystem properties
|
// root dataset filesystem properties
|
||||||
fsprops := make(map[zfs.ZFSProp]string)
|
fsprops := make(map[zfs.Prop]string)
|
||||||
// pool features
|
// pool features
|
||||||
features := make(map[string]string)
|
features := make(map[string]string)
|
||||||
|
|
||||||
// Turn off auto mounting by ZFS
|
// Turn off auto mounting by ZFS
|
||||||
fsprops[zfs.ZFSPropMountpoint] = "none"
|
fsprops[zfs.DatasetPropMountpoint] = "none"
|
||||||
|
|
||||||
// Enable some features
|
// Enable some features
|
||||||
features["async_destroy"] = "enabled"
|
features["async_destroy"] = "enabled"
|
||||||
|
|
Loading…
Reference in New Issue