- Implemented PoolImportByGUID and renamed VDevSpec to VDevTree

This commit is contained in:
Faruk Kasumovic 2015-12-06 21:54:43 +01:00
parent bc19737222
commit 4f32480fa0
3 changed files with 121 additions and 49 deletions

View File

@ -12,7 +12,7 @@ func Test(t *testing.T) {
zpoolTestExport(t) zpoolTestExport(t)
zpoolTestImport(t) zpoolTestImport(t)
zpoolTestExportForce(t) zpoolTestExportForce(t)
zpoolTestImport(t) zpoolTestImportByGUID(t)
zpoolTestPoolProp(t) zpoolTestPoolProp(t)
zpoolTestPoolStatusAndState(t) zpoolTestPoolStatusAndState(t)
zpoolTestPoolOpenAll(t) zpoolTestPoolOpenAll(t)

129
zpool.go
View File

@ -20,6 +20,14 @@ const (
// PoolProperties type is map of pool properties name -> value // PoolProperties type is map of pool properties name -> value
type PoolProperties map[Prop]string type PoolProperties map[Prop]string
// VDevTree ZFS virtual device tree
type VDevTree struct {
Type VDevType
Devices []VDevTree // groups other devices (e.g. mirror)
Parity uint
Path string
}
// Pool object represents handler to single ZFS pool // Pool object represents handler to single ZFS pool
// //
/* Pool.Properties map[string]Property /* Pool.Properties map[string]Property
@ -48,12 +56,13 @@ func PoolOpen(name string) (pool Pool, err error) {
return return
} }
// PoolImport given a list of directories to search, find and import pool with matching func poolSearchImport(q string, searchpaths []string, guid bool) (name string,
// name stored on disk. err error) {
func PoolImport(name string, searchpaths []string) (pool Pool, err error) { var config *C.nvlist_t
var cname *C.char
config = nil
errPoolList := errors.New("Failed to list pools") errPoolList := errors.New("Failed to list pools")
var elem *C.nvpair_t var elem *C.nvpair_t
var config *C.nvlist_t
numofp := len(searchpaths) numofp := len(searchpaths)
cpaths := C.alloc_strings(C.int(numofp)) cpaths := C.alloc_strings(C.int(numofp))
for i, path := range searchpaths { for i, path := range searchpaths {
@ -65,39 +74,87 @@ func PoolImport(name string, searchpaths []string) (pool Pool, err error) {
elem = C.nvlist_next_nvpair(pools, elem) elem = C.nvlist_next_nvpair(pools, elem)
for ; elem != nil; elem = C.nvlist_next_nvpair(pools, elem) { for ; elem != nil; elem = C.nvlist_next_nvpair(pools, elem) {
var cname *C.char var cq *C.char
var tconfig *C.nvlist_t var tconfig *C.nvlist_t
retcode := C.nvpair_value_nvlist(elem, &tconfig) retcode := C.nvpair_value_nvlist(elem, &tconfig)
if retcode != 0 { if retcode != 0 {
err = errPoolList err = errPoolList
return return
} }
retcode = C.nvlist_lookup_string(tconfig, if guid {
C.CString(C.ZPOOL_CONFIG_POOL_NAME), &cname) var iguid C.uint64_t
if retcode != 0 { if retcode = C.nvlist_lookup_uint64(tconfig,
err = errPoolList C.CString(C.ZPOOL_CONFIG_POOL_GUID), &iguid); retcode != 0 {
return err = errPoolList
} return
oname := C.GoString(cname) }
if name == oname { sguid := fmt.Sprint(iguid)
config = tconfig if q == sguid {
break config = tconfig
break
}
} else {
if retcode = C.nvlist_lookup_string(tconfig,
C.CString(C.ZPOOL_CONFIG_POOL_NAME), &cq); retcode != 0 {
err = errPoolList
return
}
cname = cq
name = C.GoString(cq)
if q == name {
config = tconfig
break
}
} }
} }
if config == nil { if config == nil {
err = errors.New("No pools to import found with name " + name) err = fmt.Errorf("No pool found %s", q)
return return
} }
if guid {
retcode := C.zpool_import(libzfsHandle, config, C.CString(name), nil) // We need to get name so we can open pool by name
if retcode != 0 { if retcode := C.nvlist_lookup_string(config,
C.CString(C.ZPOOL_CONFIG_POOL_NAME), &cname); retcode != 0 {
err = errPoolList
return
}
name = C.GoString(cname)
}
if retcode := C.zpool_import(libzfsHandle, config, cname,
nil); retcode != 0 {
err = LastError() err = LastError()
return return
} }
return
}
// 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) {
_, err = poolSearchImport(name, searchpaths, false)
if err != nil {
return
}
pool, err = PoolOpen(name) pool, err = PoolOpen(name)
return return
} }
// PoolImportByGUID given a list of directories to search, find and import pool
// with matching GUID stored on disk.
func PoolImportByGUID(guid string, searchpaths []string) (pool Pool, err error) {
var name string
name, err = poolSearchImport(guid, searchpaths, true)
if err != nil {
return
}
pool, err = PoolOpen(name)
return
}
// func PoolList(paths []string, cache string) (pools []Pool, err error) {
//
// }
// PoolOpenAll open all active ZFS pools on current system. // PoolOpenAll open all active ZFS pools on current system.
// Returns array of Pool handlers, each have to be closed after not needed // Returns array of Pool handlers, each have to be closed after not needed
// anymore. Call Pool.Close() method. // anymore. Call Pool.Close() method.
@ -164,9 +221,17 @@ func (pool *Pool) ReloadProperties() (err error) {
// read features // read features
pool.Features = map[string]string{ pool.Features = map[string]string{
"async_destroy": "disabled", "async_destroy": "disabled",
"empty_bpobj": "disabled", "empty_bpobj": "disabled",
"lz4_compress": "disabled"} "lz4_compress": "disabled",
"spacemap_histogram": "disabled",
"enabled_txg": "disabled",
"hole_birth": "disabled",
"extensible_dataset": "disabled",
"embedded_data": "disabled",
"bookmarks": "disabled",
"filesystem_limits": "disabled",
"large_blocks": "disabled"}
for name := range pool.Features { for name := range pool.Features {
pool.GetFeature(name) pool.GetFeature(name)
} }
@ -265,15 +330,7 @@ func (pool *Pool) State() (state PoolState, err error) {
return return
} }
// VDevSpec ZFS virtual device specification func (vdev *VDevTree) isGrouping() (grouping bool, mindevs, maxdevs int) {
type VDevSpec struct {
Type VDevType
Devices []VDevSpec // groups other devices (e.g. mirror)
Parity uint
Path string
}
func (vdev *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
maxdevs = int(^uint(0) >> 1) maxdevs = int(^uint(0) >> 1)
if vdev.Type == VDevTypeRaidz { if vdev.Type == VDevTypeRaidz {
grouping = true grouping = true
@ -295,7 +352,7 @@ func (vdev *VDevSpec) isGrouping() (grouping bool, mindevs, maxdevs int) {
return return
} }
func (vdev *VDevSpec) isLog() (r C.uint64_t) { func (vdev *VDevTree) isLog() (r C.uint64_t) {
r = 0 r = 0
if vdev.Type == VDevTypeLog { if vdev.Type == VDevTypeLog {
r = 1 r = 1
@ -335,7 +392,7 @@ func toCDatasetProperties(props DatasetProperties) (cprops *C.nvlist_t) {
return return
} }
func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec, func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
props PoolProperties) (err error) { props PoolProperties) (err error) {
count := len(vdevs) count := len(vdevs)
if count == 0 { if count == 0 {
@ -396,7 +453,7 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
return return
} }
} }
if err = buildVDevSpec(child, vdev.Type, vdev.Devices, if err = buildVDevTree(child, vdev.Type, vdev.Devices,
props); err != nil { props); err != nil {
return return
} }
@ -470,7 +527,7 @@ func buildVDevSpec(root *C.nvlist_t, rtype VDevType, vdevs []VDevSpec,
} }
// PoolCreate create ZFS pool per specs, features and properties of pool and root dataset // PoolCreate create ZFS pool per specs, features and properties of pool and root dataset
func PoolCreate(name string, vdevs []VDevSpec, features map[string]string, func PoolCreate(name string, vdevs []VDevTree, features map[string]string,
props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) { props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) {
// create root vdev nvroot // create root vdev nvroot
var nvroot *C.nvlist_t var nvroot *C.nvlist_t
@ -486,7 +543,7 @@ func PoolCreate(name string, vdevs []VDevSpec, features map[string]string,
defer C.nvlist_free(nvroot) defer C.nvlist_free(nvroot)
// Now we need to build specs (vdev hierarchy) // Now we need to build specs (vdev hierarchy)
if err = buildVDevSpec(nvroot, VDevTypeRoot, vdevs, props); err != nil { if err = buildVDevTree(nvroot, VDevTypeRoot, vdevs, props); err != nil {
return return
} }

View File

@ -13,6 +13,7 @@ import (
// HELPERS: // HELPERS:
var TSTPoolName = "TESTPOOL" var TSTPoolName = "TESTPOOL"
var TSTPoolGUID string
func CreateTmpSparse(prefix string, size int64) (path string, err error) { func CreateTmpSparse(prefix string, size int64) (path string, err error) {
sf, err := ioutil.TempFile("/tmp", prefix) sf, err := ioutil.TempFile("/tmp", prefix)
@ -83,16 +84,16 @@ func zpoolTestPoolCreate(t *testing.T) {
disks := [2]string{s1path, s2path} disks := [2]string{s1path, s2path}
var vdevs, mdevs, sdevs []zfs.VDevSpec var vdevs, mdevs, sdevs []zfs.VDevTree
for _, d := range disks { for _, d := range disks {
mdevs = append(mdevs, mdevs = append(mdevs,
zfs.VDevSpec{Type: zfs.VDevTypeFile, Path: d}) zfs.VDevTree{Type: zfs.VDevTypeFile, Path: d})
} }
sdevs = []zfs.VDevSpec{ sdevs = []zfs.VDevTree{
{Type: zfs.VDevTypeFile, Path: s3path}} {Type: zfs.VDevTypeFile, Path: s3path}}
vdevs = []zfs.VDevSpec{ vdevs = []zfs.VDevTree{
zfs.VDevSpec{Type: zfs.VDevTypeMirror, Devices: mdevs}, zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs},
zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs}, zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs},
} }
props := make(map[zfs.Prop]string) props := make(map[zfs.Prop]string)
@ -114,6 +115,9 @@ func zpoolTestPoolCreate(t *testing.T) {
} }
defer pool.Close() defer pool.Close()
pguid, _ := pool.GetProperty(zfs.PoolPropGUID)
TSTPoolGUID = pguid.Value
print("PASS\n\n") print("PASS\n\n")
} }
@ -209,6 +213,17 @@ func zpoolTestImport(t *testing.T) {
print("PASS\n\n") print("PASS\n\n")
} }
func zpoolTestImportByGUID(t *testing.T) {
println("TEST POOL ImportByGUID( ", TSTPoolGUID, " ) ... ")
p, err := zfs.PoolImportByGUID(TSTPoolGUID, []string{"/tmp"})
if err != nil {
t.Error(err)
return
}
defer p.Close()
print("PASS\n\n")
}
func zpoolTestPoolProp(t *testing.T) { func zpoolTestPoolProp(t *testing.T) {
println("TEST PoolProp on ", TSTPoolName, " ... ") println("TEST PoolProp on ", TSTPoolName, " ... ")
if pool, err := zfs.PoolOpen(TSTPoolName); err == nil { if pool, err := zfs.PoolOpen(TSTPoolName); err == nil {
@ -335,22 +350,22 @@ func ExamplePoolOpenAll() {
func ExamplePoolCreate() { func ExamplePoolCreate() {
disks := [2]string{"/dev/disk/by-id/ATA-123", "/dev/disk/by-id/ATA-456"} disks := [2]string{"/dev/disk/by-id/ATA-123", "/dev/disk/by-id/ATA-456"}
var vdevs, mdevs, sdevs []zfs.VDevSpec var vdevs, mdevs, sdevs []zfs.VDevTree
// build mirror devices specs // build mirror devices specs
for _, d := range disks { for _, d := range disks {
mdevs = append(mdevs, mdevs = append(mdevs,
zfs.VDevSpec{Type: zfs.VDevTypeDisk, Path: d}) zfs.VDevTree{Type: zfs.VDevTypeDisk, Path: d})
} }
// spare device specs // spare device specs
sdevs = []zfs.VDevSpec{ sdevs = []zfs.VDevTree{
{Type: zfs.VDevTypeDisk, Path: "/dev/disk/by-id/ATA-789"}} {Type: zfs.VDevTypeDisk, Path: "/dev/disk/by-id/ATA-789"}}
// pool specs // pool specs
vdevs = []zfs.VDevSpec{ vdevs = []zfs.VDevTree{
zfs.VDevSpec{Type: zfs.VDevTypeMirror, Devices: mdevs}, zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs},
zfs.VDevSpec{Type: zfs.VDevTypeSpare, Devices: sdevs}, zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs},
} }
// pool properties // pool properties