diff --git a/README.md b/README.md index adea694..cdd0dcf 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/common.go b/common.go index 6089530..d89a3e5 100644 --- a/common.go +++ b/common.go @@ -1,4 +1,4 @@ -// Implements basic manipulation of ZFS pools and data sets. +// Package zfs implements basic manipulation of ZFS pools and data sets. // Use libzfs C library instead CLI zfs tools, with goal // to let using and manipulating OpenZFS form with in go project. // @@ -23,36 +23,41 @@ import ( "errors" ) +// VDevType type of device in the pool type VDevType string -var libzfs_handle *C.struct_libzfs_handle +var libzfsHandle *C.struct_libzfs_handle func init() { - libzfs_handle = C.libzfs_init() + libzfsHandle = C.libzfs_init() return } // Types of Virtual Devices const ( - VDevTypeRoot VDevType = "root" - VDevTypeMirror = "mirror" - VDevTypeReplacing = "replacing" - VDevTypeRaidz = "raidz" - VDevTypeDisk = "disk" - VDevTypeFile = "file" - VDevTypeMissing = "missing" - VDevTypeHole = "hole" - VDevTypeSpare = "spare" - VDevTypeLog = "log" - VDevTypeL2cache = "l2cache" + VDevTypeRoot VDevType = "root" // VDevTypeRoot root device in ZFS pool + VDevTypeMirror = "mirror" // VDevTypeMirror mirror device in ZFS pool + VDevTypeReplacing = "replacing" // VDevTypeReplacing replacing + VDevTypeRaidz = "raidz" // VDevTypeRaidz RAIDZ device + VDevTypeDisk = "disk" // VDevTypeDisk device is disk + VDevTypeFile = "file" // VDevTypeFile device is file + VDevTypeMissing = "missing" // VDevTypeMissing missing device + VDevTypeHole = "hole" // VDevTypeHole hole + VDevTypeSpare = "spare" // VDevTypeSpare spare device + VDevTypeLog = "log" // VDevTypeLog ZIL device + VDevTypeL2cache = "l2cache" // VDevTypeL2cache cache device (disk) ) -type PoolProp int -type ZFSProp int +// Prop type to enumerate all different properties suppoerted by ZFS +type Prop int + +// PoolStatus type representing status of the pool type PoolStatus int + +// PoolState type representing pool state type PoolState uint64 -// Zfs pool or dataset property +// Property ZFS pool or dataset property value type Property struct { Value string Source string @@ -64,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 +) diff --git a/zfs.c b/zfs.c index 395a8e4..46b7721 100644 --- a/zfs.c +++ b/zfs.c @@ -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 #include #include @@ -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)); diff --git a/zfs.go b/zfs.go index c4a1043..288fab7 100644 --- a/zfs.go +++ b/zfs.go @@ -14,20 +14,30 @@ const ( msgDatasetIsNil = "Dataset handle not initialized or its closed" ) +// DatasetProperties type is map of dataset or volume properties prop -> value +type DatasetProperties map[Prop]string + +// DatasetType defines enum of dataset types type DatasetType int32 const ( + // DatasetTypeFilesystem - file system dataset DatasetTypeFilesystem DatasetType = (1 << 0) - DatasetTypeSnapshot = (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) diff --git a/zfs_test.go b/zfs_test.go index 2e4d833..a0d4412 100644 --- a/zfs_test.go +++ b/zfs_test.go @@ -2,15 +2,16 @@ package zfs_test import ( "fmt" - "github.com/bicomsystems/go-libzfs" "testing" + + "github.com/bicomsystems/go-libzfs" ) /* ------------------------------------------------------------------------- */ // HELPERS: -var TST_DATASET_PATH = TST_POOL_NAME + "/DATASET" -var TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME" -var TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test" +var TSTDatasetPath = TSTPoolName + "/DATASET" +var TSTVolumePath = TSTDatasetPath + "/VOLUME" +var TSTDatasetPathSnap = TSTDatasetPath + "@test" func printDatasets(ds []zfs.Dataset) error { for _, d := range ds { @@ -19,7 +20,7 @@ func printDatasets(ds []zfs.Dataset) error { if err != nil { return err } - p, err := d.GetProperty(zfs.ZFSPropType) + p, err := d.GetProperty(zfs.DatasetPropType) if err != nil { return err } @@ -36,45 +37,45 @@ func printDatasets(ds []zfs.Dataset) error { func zfsTestDatasetCreate(t *testing.T) { // reinit names used in case TESTPOOL was in conflict - TST_DATASET_PATH = TST_POOL_NAME + "/DATASET" - TST_VOLUME_PATH = TST_DATASET_PATH + "/VOLUME" - TST_DATASET_PATH_SNAP = TST_DATASET_PATH + "@test" + TSTDatasetPath = TSTPoolName + "/DATASET" + TSTVolumePath = TSTDatasetPath + "/VOLUME" + TSTDatasetPathSnap = TSTDatasetPath + "@test" - println("TEST DatasetCreate(", TST_DATASET_PATH, ") (filesystem) ... ") - props := make(map[zfs.ZFSProp]zfs.Property) - d, err := zfs.DatasetCreate(TST_DATASET_PATH, zfs.DatasetTypeFilesystem, props) + println("TEST DatasetCreate(", TSTDatasetPath, ") (filesystem) ... ") + props := make(map[zfs.Prop]zfs.Property) + d, err := zfs.DatasetCreate(TSTDatasetPath, zfs.DatasetTypeFilesystem, props) if err != nil { t.Error(err) return } d.Close() - println("PASS\n") + print("PASS\n\n") strSize := "536870912" // 512M - println("TEST DatasetCreate(", TST_VOLUME_PATH, ") (volume) ... ") - props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize} + println("TEST DatasetCreate(", TSTVolumePath, ") (volume) ... ") + props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize} // In addition I explicitly choose some more properties to be set. - props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"} - props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize} - d, err = zfs.DatasetCreate(TST_VOLUME_PATH, zfs.DatasetTypeVolume, props) + props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"} + props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize} + d, err = zfs.DatasetCreate(TSTVolumePath, zfs.DatasetTypeVolume, props) if err != nil { t.Error(err) return } d.Close() - println("PASS\n") + print("PASS\n\n") } func zfsTestDatasetOpen(t *testing.T) { - println("TEST DatasetOpen(", TST_DATASET_PATH, ") ... ") - d, err := zfs.DatasetOpen(TST_DATASET_PATH) + println("TEST DatasetOpen(", TSTDatasetPath, ") ... ") + d, err := zfs.DatasetOpen(TSTDatasetPath) if err != nil { t.Error(err) return } d.Close() - println("PASS\n") + print("PASS\n\n") } func zfsTestDatasetOpenAll(t *testing.T) { @@ -90,24 +91,24 @@ func zfsTestDatasetOpenAll(t *testing.T) { return } zfs.DatasetCloseAll(ds) - println("PASS\n") + print("PASS\n\n") } func zfsTestDatasetSnapshot(t *testing.T) { - println("TEST DatasetSnapshot(", TST_DATASET_PATH, ", true, ...) ... ") - props := make(map[zfs.ZFSProp]zfs.Property) - d, err := zfs.DatasetSnapshot(TST_DATASET_PATH_SNAP, true, props) + println("TEST DatasetSnapshot(", TSTDatasetPath, ", true, ...) ... ") + props := make(map[zfs.Prop]zfs.Property) + d, err := zfs.DatasetSnapshot(TSTDatasetPathSnap, true, props) if err != nil { t.Error(err) return } defer d.Close() - println("PASS\n") + print("PASS\n\n") } func zfsTestDatasetDestroy(t *testing.T) { - println("TEST DATASET Destroy( ", TST_DATASET_PATH, " ) ... ") - d, err := zfs.DatasetOpen(TST_DATASET_PATH) + println("TEST DATASET Destroy( ", TSTDatasetPath, " ) ... ") + d, err := zfs.DatasetOpen(TSTDatasetPath) if err != nil { t.Error(err) return @@ -117,7 +118,7 @@ func zfsTestDatasetDestroy(t *testing.T) { t.Error(err) return } - println("PASS\n") + print("PASS\n\n") } /* ------------------------------------------------------------------------- */ @@ -128,7 +129,7 @@ func ExampleDatasetCreate() { // Create map to represent ZFS dataset properties. This is equivalent to // list of properties you can get from ZFS CLI tool, and some more // internally used by libzfs. - props := make(map[zfs.ZFSProp]zfs.Property) + props := make(map[zfs.Prop]zfs.Property) // I choose to create (block) volume 1GiB in size. Size is just ZFS dataset // property and this is done as map of strings. So, You have to either @@ -136,10 +137,10 @@ func ExampleDatasetCreate() { // similar to convert in to string (base 10) from numeric type. strSize := "1073741824" - props[zfs.ZFSPropVolsize] = zfs.Property{Value: strSize} + props[zfs.DatasetPropVolsize] = zfs.Property{Value: strSize} // In addition I explicitly choose some more properties to be set. - props[zfs.ZFSPropVolblocksize] = zfs.Property{Value: "4096"} - props[zfs.ZFSPropReservation] = zfs.Property{Value: strSize} + props[zfs.DatasetPropVolblocksize] = zfs.Property{Value: "4096"} + props[zfs.DatasetPropReservation] = zfs.Property{Value: strSize} // Lets create desired volume d, err := zfs.DatasetCreate("TESTPOOL/VOLUME1", zfs.DatasetTypeVolume, props) @@ -161,10 +162,11 @@ func ExampleDatasetOpen() { } defer d.Close() var p zfs.Property - if p, err = d.GetProperty(zfs.ZFSPropAvailable); err != nil { + if p, err = d.GetProperty(zfs.DatasetPropAvailable); err != nil { panic(err.Error()) } - println(zfs.DatasetPropertyToName(zfs.ZFSPropAvailable), " = ", p.Value) + println(zfs.DatasetPropertyToName(zfs.DatasetPropAvailable), " = ", + p.Value) } func ExampleDatasetOpenAll() { @@ -180,7 +182,7 @@ func ExampleDatasetOpenAll() { if err != nil { panic(err.Error()) } - p, err := d.GetProperty(zfs.ZFSPropType) + p, err := d.GetProperty(zfs.DatasetPropType) if err != nil { panic(err.Error()) } diff --git a/zpool.go b/zpool.go index 0fb98ba..3f3fe52 100644 --- a/zpool.go +++ b/zpool.go @@ -10,16 +10,17 @@ import ( "errors" "fmt" "strconv" + "time" ) const ( msgPoolIsNil = "Pool handle not initialized or its closed" ) -type PoolProperties map[PoolProp]string -type ZFSProperties map[ZFSProp]string +// PoolProperties type is map of pool properties name -> value +type PoolProperties map[Prop]string -// Object represents handler to single ZFS pool +// Pool object represents handler to single ZFS pool // /* Pool.Properties map[string]Property */ @@ -34,11 +35,11 @@ type Pool struct { Features map[string]string } -// Open ZFS pool handler by name. +// PoolOpen open ZFS pool handler by name. // Returns Pool object, requires Pool.Close() to be called explicitly // for memory cleanup after object is not needed anymore. func PoolOpen(name string) (pool Pool, err error) { - pool.list = C.zpool_list_open(libzfs_handle, C.CString(name)) + pool.list = C.zpool_list_open(libzfsHandle, C.CString(name)) if pool.list != nil { err = pool.ReloadProperties() return @@ -47,7 +48,7 @@ func PoolOpen(name string) (pool Pool, err error) { return } -// Given a list of directories to search, find and import pool with matching +// PoolImport given a list of directories to search, find and import pool with matching // name stored on disk. func PoolImport(name string, searchpaths []string) (pool Pool, err error) { errPoolList := errors.New("Failed to list pools") @@ -59,7 +60,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) { C.strings_setat(cpaths, C.int(i), C.CString(path)) } - pools := C.zpool_find_import(libzfs_handle, C.int(numofp), cpaths) + pools := C.zpool_find_import(libzfsHandle, C.int(numofp), cpaths) defer C.nvlist_free(pools) elem = C.nvlist_next_nvpair(pools, elem) @@ -88,7 +89,7 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) { return } - retcode := C.zpool_import(libzfs_handle, config, C.CString(name), nil) + retcode := C.zpool_import(libzfsHandle, config, C.CString(name), nil) if retcode != 0 { err = LastError() return @@ -97,12 +98,12 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) { return } -// Open all active ZFS pools on current system. +// PoolOpenAll open all active ZFS pools on current system. // Returns array of Pool handlers, each have to be closed after not needed // anymore. Call Pool.Close() method. func PoolOpenAll() (pools []Pool, err error) { var pool Pool - errcode := C.zpool_list(libzfs_handle, &pool.list) + errcode := C.zpool_list(libzfsHandle, &pool.list) for pool.list != nil { err = pool.ReloadProperties() if err != nil { @@ -117,17 +118,18 @@ func PoolOpenAll() (pools []Pool, err error) { return } +// PoolCloseAll close all pools in given slice func PoolCloseAll(pools []Pool) { for _, p := range pools { p.Close() } } -// Convert property to name +// PoolPropertyToName convert property to name // ( returns built in string representation of property name). // This is optional, you can represent each property with string // name of choice. -func PoolPropertyToName(p PoolProp) (name string) { +func PoolPropertyToName(p Prop) (name string) { if p == PoolNumProps { return "numofprops" } @@ -136,15 +138,15 @@ func PoolPropertyToName(p PoolProp) (name string) { return } -// Map POOL STATE to string. +// PoolStateToName maps POOL STATE to string. func PoolStateToName(state PoolState) (name string) { ps := C.pool_state_t(state) name = C.GoString(C.zpool_pool_state_to_name(ps)) return } -// Re-read ZFS pool properties and features, refresh Pool.Properties and -// Pool.Features map +// ReloadProperties re-read ZFS pool properties and features, refresh +// Pool.Properties and Pool.Features map func (pool *Pool) ReloadProperties() (err error) { propList := C.read_zpool_properties(pool.list.zph) if propList == nil { @@ -165,15 +167,15 @@ func (pool *Pool) ReloadProperties() (err error) { "async_destroy": "disabled", "empty_bpobj": "disabled", "lz4_compress": "disabled"} - for name, _ := range pool.Features { + for name := range pool.Features { pool.GetFeature(name) } return } -// Reload and return single specified property. This also reloads requested +// GetProperty reload and return single specified property. This also reloads requested // property in Properties map. -func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) { +func (pool *Pool) GetProperty(p Prop) (prop Property, err error) { if pool.list != nil { // First check if property exist at all if p < PoolPropName || p > PoolNumProps { @@ -194,7 +196,7 @@ func (pool *Pool) GetProperty(p PoolProp) (prop Property, err error) { return prop, errors.New(msgPoolIsNil) } -// Reload and return single specified feature. This also reloads requested +// GetFeature reload and return single specified feature. This also reloads requested // feature in Features map. func (pool *Pool) GetFeature(name string) (value string, err error) { var fvalue [512]C.char @@ -209,10 +211,10 @@ func (pool *Pool) GetFeature(name string) (value string, err error) { return } -// Set ZFS pool property to value. Not all properties can be set, +// SetProperty set ZFS pool property to value. Not all properties can be set, // some can be set only at creation time and some are read only. // Always check if returned error and its description. -func (pool *Pool) SetProperty(p PoolProp, value string) (err error) { +func (pool *Pool) SetProperty(p Prop, value string) (err error) { if pool.list != nil { // First check if property exist at all if p < PoolPropName || p > PoolNumProps { @@ -241,7 +243,7 @@ func (pool *Pool) Close() { pool.list = nil } -// Get (re-read) ZFS pool name property +// Name get (re-read) ZFS pool name property func (pool *Pool) Name() (name string, err error) { if pool.list == nil { err = errors.New(msgPoolIsNil) @@ -252,7 +254,7 @@ func (pool *Pool) Name() (name string, err error) { return } -// Get ZFS pool state +// State get ZFS pool state // Return the state of the pool (ACTIVE or UNAVAILABLE) func (pool *Pool) State() (state PoolState, err error) { if pool.list == nil { @@ -263,7 +265,7 @@ func (pool *Pool) State() (state PoolState, err error) { return } -// ZFS virtual device specification +// VDevSpec ZFS virtual device specification type VDevSpec struct { Type VDevType Devices []VDevSpec // groups other devices (e.g. mirror) @@ -271,31 +273,31 @@ type VDevSpec struct { Path string } -func (self *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) { +func (vdev *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) { maxdevs = int(^uint(0) >> 1) - if self.Type == VDevTypeRaidz { + if vdev.Type == VDevTypeRaidz { grouping = true - if self.Parity == 0 { - self.Parity = 1 + if vdev.Parity == 0 { + vdev.Parity = 1 } - if self.Parity > 254 { - self.Parity = 254 + if vdev.Parity > 254 { + vdev.Parity = 254 } - mindevs = int(self.Parity) + 1 + mindevs = int(vdev.Parity) + 1 maxdevs = 255 - } else if self.Type == VDevTypeMirror { + } else if vdev.Type == VDevTypeMirror { grouping = true mindevs = 2 - } else if self.Type == VDevTypeLog || self.Type == VDevTypeSpare || self.Type == VDevTypeL2cache { + } else if vdev.Type == VDevTypeLog || vdev.Type == VDevTypeSpare || vdev.Type == VDevTypeL2cache { grouping = true mindevs = 1 } return } -func (self *VDevSpec) isLog() (r C.uint64_t) { +func (vdev *VDevSpec) isLog() (r C.uint64_t) { r = 0 - if self.Type == VDevTypeLog { + if vdev.Type == VDevTypeLog { r = 1 } return @@ -317,7 +319,7 @@ func toCPoolProperties(props PoolProperties) (cprops *C.nvlist_t) { return } -func toCZFSProperties(props ZFSProperties) (cprops *C.nvlist_t) { +func toCDatasetProperties(props DatasetProperties) (cprops *C.nvlist_t) { cprops = nil for prop, value := range props { name := C.zfs_prop_to_name(C.zfs_prop_t(prop)) @@ -361,7 +363,7 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec, defer C.nvlist_free_array(l2cache) for i, vdev := range vdevs { grouping, mindevs, maxdevs := vdev.isGrouping() - var child *C.nvlist_t = nil + var child *C.nvlist_t // fmt.Println(vdev.Type) if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 { err = errors.New("Failed to allocate vdev") @@ -369,8 +371,9 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec, } vcount := len(vdev.Devices) if vcount < mindevs || vcount > maxdevs { - err = errors.New(fmt.Sprintf( - "Invalid vdev specification: %s supports no less than %d or more than %d devices", vdev.Type, mindevs, maxdevs)) + err = fmt.Errorf( + "Invalid vdev specification: %s supports no less than %d or more than %d devices", + vdev.Type, mindevs, maxdevs) return } if r := C.nvlist_add_string(child, C.CString(C.ZPOOL_CONFIG_TYPE), @@ -466,11 +469,11 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec, return } -// Create ZFS pool per specs, features and properties of pool and root dataset +// PoolCreate create ZFS pool per specs, features and properties of pool and root dataset func PoolCreate(name string, vdevs []VDevSpec, features map[string]string, - props PoolProperties, fsprops ZFSProperties) (pool Pool, err error) { + props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) { // create root vdev nvroot - var nvroot *C.nvlist_t = nil + var nvroot *C.nvlist_t if r := C.nvlist_alloc(&nvroot, C.NV_UNIQUE_NAME, 0); r != 0 { err = errors.New("Failed to allocate root vdev") return @@ -495,7 +498,7 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string, err = errors.New("Failed to allocate pool properties") return } - cfsprops := toCZFSProperties(fsprops) + cfsprops := toCDatasetProperties(fsprops) if cfsprops != nil { defer C.nvlist_free(cfsprops) } else if len(fsprops) > 0 { @@ -516,16 +519,34 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string, } // Create actual pool then open - if r := C.zpool_create(libzfs_handle, C.CString(name), nvroot, + if r := C.zpool_create(libzfsHandle, C.CString(name), nvroot, cprops, cfsprops); r != 0 { err = LastError() + 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() diff --git a/zpool_test.go b/zpool_test.go index 5cc3079..cc27780 100644 --- a/zpool_test.go +++ b/zpool_test.go @@ -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"