From 441e099de9619d1e99746b4c99c5ed67b3c0fcad Mon Sep 17 00:00:00 2001 From: Faruk Kasumovic Date: Tue, 26 Jun 2018 14:01:39 +0200 Subject: [PATCH] zpool online, offline and clear devices --- zpool.c | 16 ++++++ zpool.h | 13 +++++ zpool_vdev.c | 37 +++++++++++++ zpool_vdev.go | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 zpool_vdev.c create mode 100644 zpool_vdev.go diff --git a/zpool.c b/zpool.c index a6c982b..d592be4 100644 --- a/zpool.c +++ b/zpool.c @@ -496,4 +496,20 @@ nvlist_ptr go_zpool_search_import(libzfs_handle_ptr zfsh, int paths, char **path // idata.scan = 0; return zpool_search_import(zfsh, &idata); +} + + +int do_zpool_clear(zpool_list_t *pool, const char *device, u_int32_t rewind_policy) { + nvlist_t *policy = NULL; + int ret = 0; + if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0) + return (1); + + if (zpool_clear(pool->zph, device, policy) != 0) + ret = 1; + + nvlist_free(policy); + + return (ret); } \ No newline at end of file diff --git a/zpool.h b/zpool.h index cdb5892..33d416f 100644 --- a/zpool.h +++ b/zpool.h @@ -5,6 +5,15 @@ #ifndef SERVERWARE_ZPOOL_H #define SERVERWARE_ZPOOL_H +/* Rewind request information */ +#define ZPOOL_NO_REWIND 1 /* No policy - default behavior */ +#define ZPOOL_NEVER_REWIND 2 /* Do not search for best txg or rewind */ +#define ZPOOL_TRY_REWIND 4 /* Search for best txg, but do not rewind */ +#define ZPOOL_DO_REWIND 8 /* Rewind to best txg w/in deferred frees */ +#define ZPOOL_EXTREME_REWIND 16 /* Allow extreme measures to find best txg */ +#define ZPOOL_REWIND_MASK 28 /* All the possible rewind bits */ +#define ZPOOL_REWIND_POLICIES 31 /* All the possible policy bits */ + struct zpool_list { zpool_handle_t *zph; void *pnext; @@ -70,6 +79,10 @@ nvlist_ptr get_zpool_vdev_tree(nvlist_ptr nv); nvlist_ptr go_zpool_search_import(libzfs_handle_ptr zfsh, int paths, char **path, boolean_t do_scan); +__uint64_t set_zpool_vdev_online(zpool_list_t *pool, const char *path, int flags); +int set_zpool_vdev_offline(zpool_list_t *pool, const char *path, boolean_t istmp, boolean_t force); +int do_zpool_clear(zpool_list_t *pool, const char *device, u_int32_t rewind_policy); + extern char *sZPOOL_CONFIG_VERSION; extern char *sZPOOL_CONFIG_POOL_NAME; diff --git a/zpool_vdev.c b/zpool_vdev.c new file mode 100644 index 0000000..67bdbde --- /dev/null +++ b/zpool_vdev.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +#include "common.h" +#include "zpool.h" + + +__uint64_t set_zpool_vdev_online(zpool_list_t *pool, const char *path, int flags) { + vdev_state_t newstate = VDEV_STATE_UNKNOWN; + zpool_vdev_online(pool->zph, path, flags, &newstate); + return newstate; +} + +int set_zpool_vdev_offline(zpool_list_t *pool, const char *path, boolean_t istmp, boolean_t force) { + int ret = 0; + // if (force) { + // uint64_t guid = zpool_vdev_path_to_guid(pool->zph, path); + // vdev_aux_t aux; + // if (istmp == B_FALSE) { + // /* Force the fault to persist across imports */ + // aux = VDEV_AUX_EXTERNAL_PERSIST; + // } else { + // aux = VDEV_AUX_EXTERNAL; + // } + + // if (guid == 0 || zpool_vdev_fault(pool->zph, guid, aux) != 0) + // ret = 1; + // } else { + if (zpool_vdev_offline(pool->zph, path, istmp) != 0) + ret = 1; + // } + return ret; +} + diff --git a/zpool_vdev.go b/zpool_vdev.go new file mode 100644 index 0000000..379ea9e --- /dev/null +++ b/zpool_vdev.go @@ -0,0 +1,143 @@ +package zfs + +// #include +// #include +// #include "common.h" +// #include "zpool.h" +// #include "zfs.h" +import "C" +import ( + "fmt" + "unsafe" +) + +// Online try to set dev online +// expand - expand storage +func (pool *Pool) Online(expand bool, devs ...string) (err error) { + cflags := C.int(0) + if expand { + cflags = C.ZFS_ONLINE_EXPAND + } + for _, dev := range devs { + csdev := C.CString(dev) + var newstate VDevState + if newstate = VDevState(C.set_zpool_vdev_online(pool.list, csdev, cflags)); newstate != VDevStateUnknown { + if newstate != VDevStateHealthy { + err = fmt.Errorf( + "Device '%s' onlined, but remains in faulted state", + dev) + } + } else { + err = LastError() + } + C.free(unsafe.Pointer(csdev)) + } + return +} + +// Offline Take the device/s in offline state +func (pool *Pool) Offline(force bool, devs ...string) (err error) { + return pool.offline(false, force, devs...) +} + +// OfflineTemp Take the device/s in offline state temporary, +// upon reboot, the specified physical device reverts to its previous state. +// force - Force the device into a faulted state. +func (pool *Pool) OfflineTemp(force bool, devs ...string) (err error) { + return pool.offline(true, force, devs...) +} + +// temp - Upon reboot, the specified physical device reverts to its previous state. +// force - Force the device into a faulted state. +func (pool *Pool) offline(temp, force bool, devs ...string) (err error) { + for _, dev := range devs { + csdev := C.CString(dev) + var newstate VDevState + if newstate = VDevState(C.set_zpool_vdev_offline(pool.list, csdev, booleanT(temp), booleanT(force))); newstate != VDevStateUnknown { + if newstate != VDevStateHealthy { + err = fmt.Errorf( + "Device '%s' offlined, but remains in faulted state", + dev) + } + } else { + err = LastError() + } + C.free(unsafe.Pointer(csdev)) + } + return +} + +// Clear - Clear all errors associated with a pool or a particular device. +func (pool *Pool) Clear(device string) (err error) { + csdev := C.CString(device) + if len(device) == 0 { + csdev = nil + } + if sc := C.do_zpool_clear(pool.list, csdev, C.ZPOOL_NO_REWIND); sc != 0 { + err = fmt.Errorf("Pool clear failed") + } + return +} + +// Attach test +// func (pool *Pool) attach(props PoolProperties, devs ...string) (err error) { +// cprops := toCPoolProperties(props) +// if cprops != nil { +// defer C.nvlist_free(cprops) +// } else { +// return fmt.Errorf("Out of memory [Pool Attach properties]") +// } +// cdevs := C.alloc_cstrings(C.int(len(devs))) +// if cdevs != nil { +// defer C.free(unsafe.Pointer(cdevs)) +// } else { +// return fmt.Errorf("Out of memory [Pool Attach args]") +// } +// for i, dp := range devs { +// tmp := C.CString(dp) +// if tmp != nil { +// defer C.free(unsafe.Pointer(tmp)) +// } else { +// return fmt.Errorf("Out of memory [Pool Attach dev]") +// } +// C.strings_setat(cdevs, C.int(i), tmp) +// } +// // vroot := C.make_root_vdev(pool.list.zph, cprops, 0, 0, 0, 0, len(devs), cdevs) +// var nvroot *C.struct_nvlist +// if r := C.nvlist_alloc(&nvroot, C.NV_UNIQUE_NAME, 0); r != 0 { +// err = errors.New("Failed to allocate root vdev") +// return +// } +// csTypeRoot := C.CString(string(VDevTypeRoot)) +// r := C.nvlist_add_string(nvroot, C.sZPOOL_CONFIG_TYPE, +// csTypeRoot) +// C.free(unsafe.Pointer(csTypeRoot)) +// if r != 0 { +// err = errors.New("Failed to allocate root vdev") +// return +// } +// defer C.nvlist_free(nvroot) + +// // Now we need to build specs (vdev hierarchy) +// if err = buildVDevTree(nvroot, VDevTypeRoot, vdev.Devices, vdev.Spares, vdev.L2Cache, props); err != nil { +// return +// } + +// return +// } + +// func (pool *Pool) AttachForce(devs ...string) (err error) { +// return +// } + +// func (pool *Pool) Detach(devs ...string) (err error) { +// return +// } + +// func (pool *Pool) DetachForce(devs ...string) (err error) { +// return +// } + +// func (pool *Pool) Replace(devs ...string) (err error) { +// return +// }