- Changes to make library interface more clear and to better suit go package standards

This commit is contained in:
Faruk Kasumovic 2015-12-04 23:05:19 +01:00
parent db4703b708
commit bc19737222
7 changed files with 420 additions and 299 deletions

View File

@ -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)

329
common.go
View File

@ -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,21 +69,21 @@ 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) */
PoolStatusErrata /* informational errata available */
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 */
/*
* If the pool has unsupported features but can still be opened in
@ -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 */
PoolStatusResilvering /* device being resilvered */
PoolStatusOffline_dev /* device online */
PoolStatusRemoved_dev /* removed device */
PoolStatusVersionOlder /* older legacy on-disk version */
PoolStatusFeatDisabled /* supported features are disabled */
PoolStatusResilvering /* device being resilvered */
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
)

4
zfs.c
View File

@ -1,7 +1,7 @@
/* C wrappers around some zfs calls and C in general that should simplify
* using libzfs from go language, make go code shorter and more readable.
*/
#include <libzfs.h>
#include <memory.h>
#include <string.h>
@ -72,7 +72,7 @@ int read_dataset_property(zfs_handle_t *zh, property_list_t *list, int prop) {
zprop_source_t source;
char statbuf[INT_MAX_VALUE];
r = zfs_prop_get(zh, prop,
r = zfs_prop_get(zh, prop,
list->value, INT_MAX_VALUE, &source, statbuf, INT_MAX_VALUE, 1);
if (r == 0) {
// strcpy(list->name, zpool_prop_to_name(prop));

112
zfs.go
View File

@ -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 = (1 << 1)
DatasetTypeVolume = (1 << 2)
DatasetTypePool = (1 << 3)
DatasetTypeBookmark = (1 << 4)
// 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)

View File

@ -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())
}

123
zpool.go
View File

@ -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()
err = errors.New(err.Error() + " (zpool_create)")
return
}
pool, err = PoolOpen(name)
// 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
}
// Get pool status. Let you check if pool healthy.
// 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()

View File

@ -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"