- Ability to create pool with spares, and l2cache

This breaks previous API PoolCreate function call, changes to fix this
break will be necessary.
This commit is contained in:
Faruk Kasumovic 2018-01-08 15:29:42 +01:00
parent 006e8a798a
commit 08a4903509
4 changed files with 230 additions and 91 deletions

24
zpool.c
View File

@ -408,6 +408,30 @@ vdev_children_ptr get_vdev_children(nvlist_t *nv) {
return children;
}
vdev_children_ptr get_vdev_spares(nvlist_t *nv) {
int r;
vdev_children_ptr children = malloc(sizeof(vdev_children_t));
memset(children, 0, sizeof(vdev_children_t));
r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &(children->first), &(children->count));
if (r != 0) {
free(children);
return NULL;
}
return children;
}
vdev_children_ptr get_vdev_l2cache(nvlist_t *nv) {
int r;
vdev_children_ptr children = malloc(sizeof(vdev_children_t));
memset(children, 0, sizeof(vdev_children_t));
r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &(children->first), &(children->count));
if (r != 0) {
free(children);
return NULL;
}
return children;
}
const char *get_vdev_path(nvlist_ptr nv) {
char *path = NULL;
uint64_t notpresent = 0;

268
zpool.go
View File

@ -101,6 +101,9 @@ type PoolScanStat struct {
type VDevTree struct {
Type VDevType
Devices []VDevTree // groups other devices (e.g. mirror)
Spares []VDevTree
L2Cache []VDevTree
Logs []VDevTree
Parity uint
Path string
Name string
@ -233,6 +236,60 @@ func poolGetConfig(name string, nv C.nvlist_ptr) (vdevs VDevTree, err error) {
}
vdevs.Devices = append(vdevs.Devices, vdev)
}
if vdevs.Spares, err = poolGetSpares(name, nv); err != nil {
return
}
if vdevs.L2Cache, err = poolGetL2Cache(name, nv); err != nil {
return
}
return
}
func poolGetSpares(name string, nv C.nvlist_ptr) (vdevs []VDevTree, err error) {
// Fetch the spares
var spares C.vdev_children_ptr
spares = C.get_vdev_spares(nv)
if spares != nil {
// this object that reference spares and count should be deallocated from memory
defer C.free(unsafe.Pointer(spares))
vdevs = make([]VDevTree, 0, spares.count)
}
for c := C.uint_t(0); spares != nil && c < spares.count; c++ {
vname := C.zpool_vdev_name(C.libzfsHandle, nil, C.nvlist_array_at(spares.first, c),
C.B_TRUE)
var vdev VDevTree
vdev, err = poolGetConfig(C.GoString(vname),
C.nvlist_array_at(spares.first, c))
C.free(unsafe.Pointer(vname))
if err != nil {
return
}
vdevs = append(vdevs, vdev)
}
return
}
func poolGetL2Cache(name string, nv C.nvlist_ptr) (vdevs []VDevTree, err error) {
// Fetch the spares
var l2cache C.vdev_children_ptr
l2cache = C.get_vdev_l2cache(nv)
if l2cache != nil {
// this object that reference l2cache and count should be deallocated from memory
defer C.free(unsafe.Pointer(l2cache))
vdevs = make([]VDevTree, 0, l2cache.count)
}
for c := C.uint_t(0); l2cache != nil && c < l2cache.count; c++ {
vname := C.zpool_vdev_name(C.libzfsHandle, nil, C.nvlist_array_at(l2cache.first, c),
C.B_TRUE)
var vdev VDevTree
vdev, err = poolGetConfig(C.GoString(vname),
C.nvlist_array_at(l2cache.first, c))
C.free(unsafe.Pointer(vname))
if err != nil {
return
}
vdevs = append(vdevs, vdev)
}
return
}
@ -651,7 +708,52 @@ func toCDatasetProperties(props DatasetProperties) (cprops C.nvlist_ptr) {
return
}
func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
func buildVdev(vdev VDevTree, ashift int) (nvvdev *C.struct_nvlist, err error) {
if r := C.nvlist_alloc(&nvvdev, C.NV_UNIQUE_NAME, 0); r != 0 {
err = errors.New("Failed to allocate vdev")
return
}
csType := C.CString(string(vdev.Type))
r := C.nvlist_add_string(nvvdev, C.sZPOOL_CONFIG_TYPE,
csType)
C.free(unsafe.Pointer(csType))
if r != 0 {
err = errors.New("Failed to set vdev type")
return
}
if r := C.nvlist_add_uint64(nvvdev, C.sZPOOL_CONFIG_IS_LOG,
vdev.isLog()); r != 0 {
err = errors.New("Failed to allocate vdev (is_log)")
return
}
if r := C.nvlist_add_uint64(nvvdev,
C.sZPOOL_CONFIG_WHOLE_DISK, 1); r != 0 {
err = errors.New("Failed to allocate vdev nvvdev (whdisk)")
return
}
if len(vdev.Path) > 0 {
csPath := C.CString(vdev.Path)
r := C.nvlist_add_string(
nvvdev, C.sZPOOL_CONFIG_PATH,
csPath)
C.free(unsafe.Pointer(csPath))
if r != 0 {
err = errors.New("Failed to allocate vdev nvvdev (type)")
return
}
if ashift > 0 {
if r := C.nvlist_add_uint64(nvvdev,
C.sZPOOL_CONFIG_ASHIFT,
C.uint64_t(ashift)); r != 0 {
err = errors.New("Failed to allocate vdev nvvdev (ashift)")
return
}
}
}
return
}
func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs, spares, l2cache []VDevTree,
props PoolProperties) (err error) {
count := len(vdevs)
if count == 0 {
@ -663,28 +765,9 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
return
}
defer C.nvlist_free_array(childrens)
spares := C.nvlist_alloc_array(C.int(count))
if childrens == nil {
err = errors.New("No enough memory")
return
}
nspares := 0
defer C.nvlist_free_array(spares)
l2cache := C.nvlist_alloc_array(C.int(count))
if childrens == nil {
err = errors.New("No enough memory")
return
}
nl2cache := 0
defer C.nvlist_free_array(l2cache)
for i, vdev := range vdevs {
grouping, mindevs, maxdevs := vdev.isGrouping()
var child *C.struct_nvlist
// fmt.Println(vdev.Type)
if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
err = errors.New("Failed to allocate vdev")
return
}
vcount := len(vdev.Devices)
if vcount < mindevs || vcount > maxdevs {
err = fmt.Errorf(
@ -692,20 +775,19 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
vdev.Type, mindevs, maxdevs)
return
}
csType := C.CString(string(vdev.Type))
r := C.nvlist_add_string(child, C.sZPOOL_CONFIG_TYPE,
csType)
C.free(unsafe.Pointer(csType))
if r != 0 {
err = errors.New("Failed to set vdev type")
return
}
if r := C.nvlist_add_uint64(child, C.sZPOOL_CONFIG_IS_LOG,
vdev.isLog()); r != 0 {
err = errors.New("Failed to allocate vdev (is_log)")
return
}
if grouping {
if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 {
err = errors.New("Failed to allocate vdev")
return
}
csType := C.CString(string(vdev.Type))
r := C.nvlist_add_string(child, C.sZPOOL_CONFIG_TYPE,
csType)
C.free(unsafe.Pointer(csType))
if r != 0 {
err = errors.New("Failed to set vdev type")
return
}
if vdev.Type == VDevTypeRaidz {
r := C.nvlist_add_uint64(child,
C.sZPOOL_CONFIG_NPARITY,
@ -715,49 +797,15 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
return
}
}
if err = buildVDevTree(child, vdev.Type, vdev.Devices,
if err = buildVDevTree(child, vdev.Type, vdev.Devices, nil, nil,
props); err != nil {
return
}
} else {
// if vdev.Type == VDevTypeDisk {
if r := C.nvlist_add_uint64(child,
C.sZPOOL_CONFIG_WHOLE_DISK, 1); r != 0 {
err = errors.New("Failed to allocate vdev child (whdisk)")
ashift, _ := strconv.Atoi(props[PoolPropAshift])
if child, err = buildVdev(vdev, ashift); err != nil {
return
}
// }
if len(vdev.Path) > 0 {
csPath := C.CString(vdev.Path)
r := C.nvlist_add_string(
child, C.sZPOOL_CONFIG_PATH,
csPath)
C.free(unsafe.Pointer(csPath))
if r != 0 {
err = errors.New("Failed to allocate vdev child (type)")
return
}
ashift, _ := strconv.Atoi(props[PoolPropAshift])
if ashift > 0 {
if r := C.nvlist_add_uint64(child,
C.sZPOOL_CONFIG_ASHIFT,
C.uint64_t(ashift)); r != 0 {
err = errors.New("Failed to allocate vdev child (ashift)")
return
}
}
}
if vdev.Type == VDevTypeSpare {
C.nvlist_array_set(spares, C.int(nspares), child)
nspares++
count--
continue
} else if vdev.Type == VDevTypeL2cache {
C.nvlist_array_set(l2cache, C.int(nl2cache), child)
nl2cache++
count--
continue
}
}
C.nvlist_array_set(childrens, C.int(i), child)
}
@ -768,31 +816,74 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree,
err = errors.New("Failed to allocate vdev children")
return
}
// fmt.Println("childs", root, count, rtype)
// debug.PrintStack()
}
if nl2cache > 0 {
if r := C.nvlist_add_nvlist_array(root,
C.sZPOOL_CONFIG_L2CACHE, l2cache,
C.uint_t(nl2cache)); r != 0 {
err = errors.New("Failed to allocate vdev cache")
if len(spares) > 0 {
ashift, _ := strconv.Atoi(props[PoolPropAshift])
if err = buildVdevSpares(root, VDevTypeRoot, spares, ashift); err != nil {
return
}
}
if nspares > 0 {
if r := C.nvlist_add_nvlist_array(root,
C.sZPOOL_CONFIG_SPARES, spares,
C.uint_t(nspares)); r != 0 {
err = errors.New("Failed to allocate vdev spare")
if len(l2cache) > 0 {
ashift, _ := strconv.Atoi(props[PoolPropAshift])
if err = buildVdevL2Cache(root, VDevTypeRoot, l2cache, ashift); err != nil {
return
}
// fmt.Println("spares", root, count)
}
return
}
func buildVdevSpares(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, ashift int) (err error) {
count := len(vdevs)
if count == 0 {
return
}
spares := C.nvlist_alloc_array(C.int(count))
if spares == nil {
err = errors.New("No enough memory buildVdevSpares")
return
}
defer C.nvlist_free_array(spares)
for i, vdev := range vdevs {
var child *C.struct_nvlist
if child, err = buildVdev(vdev, ashift); err != nil {
return
}
C.nvlist_array_set(spares, C.int(i), child)
}
if r := C.nvlist_add_nvlist_array(root,
C.sZPOOL_CONFIG_SPARES, spares, C.uint_t(len(vdevs))); r != 0 {
err = errors.New("Failed to allocate vdev spare")
}
return
}
func buildVdevL2Cache(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, ashift int) (err error) {
count := len(vdevs)
if count == 0 {
return
}
l2cache := C.nvlist_alloc_array(C.int(count))
if l2cache == nil {
err = errors.New("No enough memory buildVdevL2Cache")
return
}
defer C.nvlist_free_array(l2cache)
for i, vdev := range vdevs {
var child *C.struct_nvlist
if child, err = buildVdev(vdev, ashift); err != nil {
return
}
C.nvlist_array_set(l2cache, C.int(i), child)
}
if r := C.nvlist_add_nvlist_array(root,
C.sZPOOL_CONFIG_SPARES, l2cache, C.uint_t(len(vdevs))); r != 0 {
err = errors.New("Failed to allocate vdev l2cache")
}
return
}
// PoolCreate create ZFS pool per specs, features and properties of pool and root dataset
func PoolCreate(name string, vdevs []VDevTree, features map[string]string,
func PoolCreate(name string, vdev VDevTree, features map[string]string,
props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) {
// create root vdev nvroot
var nvroot *C.struct_nvlist
@ -811,7 +902,7 @@ func PoolCreate(name string, vdevs []VDevTree, features map[string]string,
defer C.nvlist_free(nvroot)
// Now we need to build specs (vdev hierarchy)
if err = buildVDevTree(nvroot, VDevTypeRoot, vdevs, props); err != nil {
if err = buildVDevTree(nvroot, VDevTypeRoot, vdev.Devices, vdev.Spares, vdev.L2Cache, props); err != nil {
return
}
@ -948,7 +1039,12 @@ func (pool *Pool) VDevTree() (vdevs VDevTree, err error) {
if poolName, err = pool.Name(); err != nil {
return
}
return poolGetConfig(poolName, nvroot)
if vdevs, err = poolGetConfig(poolName, nvroot); err != nil {
return
}
vdevs.Spares, err = poolGetSpares(poolName, nvroot)
vdevs.L2Cache, err = poolGetL2Cache(poolName, nvroot)
return
}
func (s PoolState) String() string {

View File

@ -56,6 +56,8 @@ const char *get_vdev_type(nvlist_ptr nv);
const vdev_stat_ptr get_vdev_stats(nvlist_ptr nv);
pool_scan_stat_ptr get_vdev_scan_stats(nvlist_t *nv);
vdev_children_ptr get_vdev_children(nvlist_t *nv);
vdev_children_ptr get_vdev_spares(nvlist_t *nv);
vdev_children_ptr get_vdev_l2cache(nvlist_t *nv);
const char *get_vdev_path(nvlist_ptr nv);
uint64_t get_vdev_is_log(nvlist_ptr nv);

View File

@ -84,6 +84,7 @@ func zpoolTestPoolCreate(t *testing.T) {
disks := [2]string{s1path, s2path}
var vdev zfs.VDevTree
var vdevs, mdevs, sdevs []zfs.VDevTree
for _, d := range disks {
mdevs = append(mdevs,
@ -93,8 +94,9 @@ func zpoolTestPoolCreate(t *testing.T) {
{Type: zfs.VDevTypeFile, Path: s3path}}
vdevs = []zfs.VDevTree{
zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs},
zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs},
}
vdev.Devices = vdevs
vdev.Spares = sdevs
props := make(map[zfs.Prop]string)
fsprops := make(map[zfs.Prop]string)
@ -104,7 +106,7 @@ func zpoolTestPoolCreate(t *testing.T) {
features["empty_bpobj"] = zfs.FENABLED
features["lz4_compress"] = zfs.FENABLED
pool, err := zfs.PoolCreate(TSTPoolName, vdevs, features, props, fsprops)
pool, err := zfs.PoolCreate(TSTPoolName, vdev, features, props, fsprops)
if err != nil {
t.Error(err)
// try cleanup
@ -231,6 +233,19 @@ func printVDevTree(vt zfs.VDevTree, pref string) {
for _, v := range vt.Devices {
printVDevTree(v, " "+pref)
}
if len(vt.Spares) > 0 {
fmt.Println("spares:")
for _, v := range vt.Spares {
printVDevTree(v, " "+pref)
}
}
if len(vt.L2Cache) > 0 {
fmt.Println("l2cache:")
for _, v := range vt.L2Cache {
printVDevTree(v, " "+pref)
}
}
}
func zpoolTestPoolImportSearch(t *testing.T) {
@ -249,7 +264,6 @@ func zpoolTestPoolImportSearch(t *testing.T) {
fmt.Printf("%-30s | %-10s | %-10s | %s\n", "NAME", "TYPE", "STATE", "PATH")
println("---------------------------------------------------------------")
printVDevTree(p.VDevs, "")
}
print("PASS\n\n")
}
@ -408,6 +422,7 @@ func ExamplePoolOpenAll() {
func ExamplePoolCreate() {
disks := [2]string{"/dev/disk/by-id/ATA-123", "/dev/disk/by-id/ATA-456"}
var vdev zfs.VDevTree
var vdevs, mdevs, sdevs []zfs.VDevTree
// build mirror devices specs
@ -423,9 +438,11 @@ func ExamplePoolCreate() {
// pool specs
vdevs = []zfs.VDevTree{
zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs},
zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs},
}
vdev.Devices = vdevs
vdev.Spares = sdevs
// pool properties
props := make(map[zfs.Prop]string)
// root dataset filesystem properties
@ -443,7 +460,7 @@ func ExamplePoolCreate() {
// Based on specs formed above create test pool as 2 disk mirror and
// one spare disk
pool, err := zfs.PoolCreate("TESTPOOL", vdevs, features, props, fsprops)
pool, err := zfs.PoolCreate("TESTPOOL", vdev, features, props, fsprops)
if err != nil {
println("Error: ", err.Error())
return