- Implemented: Search pools available to import but not imported AND Fetch imported pool's current vdev tree
This commit is contained in:
		
							parent
							
								
									4f32480fa0
								
							
						
					
					
						commit
						b49a2715c2
					
				| 
						 | 
					@ -9,7 +9,9 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Test(t *testing.T) {
 | 
					func Test(t *testing.T) {
 | 
				
			||||||
	zpoolTestPoolCreate(t)
 | 
						zpoolTestPoolCreate(t)
 | 
				
			||||||
 | 
						zpoolTestPoolVDevTree(t)
 | 
				
			||||||
	zpoolTestExport(t)
 | 
						zpoolTestExport(t)
 | 
				
			||||||
 | 
						zpoolTestPoolImportSearch(t)
 | 
				
			||||||
	zpoolTestImport(t)
 | 
						zpoolTestImport(t)
 | 
				
			||||||
	zpoolTestExportForce(t)
 | 
						zpoolTestExportForce(t)
 | 
				
			||||||
	zpoolTestImportByGUID(t)
 | 
						zpoolTestImportByGUID(t)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								common.go
								
								
								
								
							
							
						
						
									
										40
									
								
								common.go
								
								
								
								
							| 
						 | 
					@ -57,6 +57,12 @@ type PoolStatus int
 | 
				
			||||||
// PoolState type representing pool state
 | 
					// PoolState type representing pool state
 | 
				
			||||||
type PoolState uint64
 | 
					type PoolState uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VDevState - vdev states tye
 | 
				
			||||||
 | 
					type VDevState uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VDevAux - vdev aux states
 | 
				
			||||||
 | 
					type VDevAux uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Property ZFS pool or dataset property value
 | 
					// Property ZFS pool or dataset property value
 | 
				
			||||||
type Property struct {
 | 
					type Property struct {
 | 
				
			||||||
	Value  string
 | 
						Value  string
 | 
				
			||||||
| 
						 | 
					@ -346,3 +352,37 @@ const (
 | 
				
			||||||
	EPoolreadonly                      /* pool is in read-only mode */
 | 
						EPoolreadonly                      /* pool is in read-only mode */
 | 
				
			||||||
	EUnknown
 | 
						EUnknown
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vdev states are ordered from least to most healthy.
 | 
				
			||||||
 | 
					// A vdev that's VDevStateCantOpen or below is considered unusable.
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						VDevStateUnknown  VDevState = iota // Uninitialized vdev
 | 
				
			||||||
 | 
						VDevStateClosed                    // Not currently open
 | 
				
			||||||
 | 
						VDevStateOffline                   // Not allowed to open
 | 
				
			||||||
 | 
						VDevStateRemoved                   // Explicitly removed from system
 | 
				
			||||||
 | 
						VDevStateCantOpen                  // Tried to open, but failed
 | 
				
			||||||
 | 
						VDevStateFaulted                   // External request to fault device
 | 
				
			||||||
 | 
						VDevStateDegraded                  // Replicated vdev with unhealthy kids
 | 
				
			||||||
 | 
						VDevStateHealthy                   // Presumed good
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vdev aux states.  When a vdev is in the VDevStateCantOpen state, the aux field
 | 
				
			||||||
 | 
					// of the vdev stats structure uses these constants to distinguish why.
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						VDevAuxNone         VDevAux = iota // no error
 | 
				
			||||||
 | 
						VDevAuxOpenFailed                  // ldi_open_*() or vn_open() failed
 | 
				
			||||||
 | 
						VDevAuxCorruptData                 // bad label or disk contents
 | 
				
			||||||
 | 
						VDevAuxNoReplicas                  // insufficient number of replicas
 | 
				
			||||||
 | 
						VDevAuxBadGUIDSum                  // vdev guid sum doesn't match
 | 
				
			||||||
 | 
						VDevAuxTooSmall                    // vdev size is too small
 | 
				
			||||||
 | 
						VDevAuxBadLabel                    // the label is OK but invalid
 | 
				
			||||||
 | 
						VDevAuxVersionNewer                // on-disk version is too new
 | 
				
			||||||
 | 
						VDevAuxVersionOlder                // on-disk version is too old
 | 
				
			||||||
 | 
						VDevAuxUnsupFeat                   // unsupported features
 | 
				
			||||||
 | 
						VDevAuxSpared                      // hot spare used in another pool
 | 
				
			||||||
 | 
						VDevAuxErrExceeded                 // too many errors
 | 
				
			||||||
 | 
						VDevAuxIOFailure                   // experienced I/O failure
 | 
				
			||||||
 | 
						VDevAuxBadLog                      // cannot read log chain(s)
 | 
				
			||||||
 | 
						VDevAuxExternal                    // external diagnosis
 | 
				
			||||||
 | 
						VDevAuxSplitPool                   // vdev was split off into another pool
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								zpool.c
								
								
								
								
							
							
						
						
									
										22
									
								
								zpool.c
								
								
								
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
/* C wrappers around some zfs calls and C in general that should simplify
 | 
					/* C wrappers around some zfs calls and C in general that should simplify
 | 
				
			||||||
 * using libzfs from go language, and make go code shorter and more readable.
 | 
					 * using libzfs from go language, and make go code shorter and more readable.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
					
 | 
				
			||||||
#include <libzfs.h>
 | 
					#include <libzfs.h>
 | 
				
			||||||
#include <memory.h>
 | 
					#include <memory.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
| 
						 | 
					@ -112,17 +112,17 @@ void zprop_source_tostr(char *dst, zprop_source_t source) {
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		strcpy(dst, "default");
 | 
							strcpy(dst, "default");
 | 
				
			||||||
		break; 
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int read_zpool_property(zpool_handle_t *zh, property_list_t *list, int prop) {
 | 
					int read_zpool_property(zpool_handle_t *zh, property_list_t *list, int prop) {
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
	int r = 0;
 | 
						int r = 0;
 | 
				
			||||||
	zprop_source_t source;
 | 
						zprop_source_t source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r = zpool_get_prop(zh, prop, 
 | 
						r = zpool_get_prop(zh, prop,
 | 
				
			||||||
		list->value, INT_MAX_VALUE, &source);
 | 
							list->value, INT_MAX_VALUE, &source);
 | 
				
			||||||
	if (r == 0) {
 | 
						if (r == 0) {
 | 
				
			||||||
		// strcpy(list->name, zpool_prop_to_name(prop));
 | 
							// strcpy(list->name, zpool_prop_to_name(prop));
 | 
				
			||||||
| 
						 | 
					@ -366,6 +366,16 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
 | 
				
			||||||
	return (0);
 | 
						return (0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nvlist_lookup_uint64_array_vds(nvlist_t *nv, const char *p,
 | 
				
			||||||
 | 
						vdev_stat_t **vds, uint_t *c) {
 | 
				
			||||||
 | 
							return nvlist_lookup_uint64_array(nv, p, (uint64_t**)vds, c);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nvlist_lookup_uint64_array_ps(nvlist_t *nv, const char *p,
 | 
				
			||||||
 | 
						pool_scan_stat_t **vds, uint_t *c) {
 | 
				
			||||||
 | 
							return nvlist_lookup_uint64_array(nv, p, (uint64_t**)vds, c);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nvlist_t** nvlist_alloc_array(int count) {
 | 
					nvlist_t** nvlist_alloc_array(int count) {
 | 
				
			||||||
	return malloc(count*sizeof(nvlist_t*));
 | 
						return malloc(count*sizeof(nvlist_t*));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -381,3 +391,7 @@ void nvlist_free_array(nvlist_t **a) {
 | 
				
			||||||
void free_cstring(char *str) {
 | 
					void free_cstring(char *str) {
 | 
				
			||||||
	free(str);
 | 
						free(str);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvlist_t *nvlist_array_at(nvlist_t **a, uint_t i) {
 | 
				
			||||||
 | 
						return a[i];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										333
									
								
								zpool.go
								
								
								
								
							
							
						
						
									
										333
									
								
								zpool.go
								
								
								
								
							| 
						 | 
					@ -20,12 +20,94 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * ZIO types.  Needed to interpret vdev statistics below.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ZIOTypeNull = iota
 | 
				
			||||||
 | 
						ZIOTypeRead
 | 
				
			||||||
 | 
						ZIOTypeWrite
 | 
				
			||||||
 | 
						ZIOTypeFree
 | 
				
			||||||
 | 
						ZIOTypeClaim
 | 
				
			||||||
 | 
						ZIOTypeIOCtl
 | 
				
			||||||
 | 
						ZIOTypes
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Scan states
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						DSSNone      = iota // No scan
 | 
				
			||||||
 | 
						DSSScanning         // Scanning
 | 
				
			||||||
 | 
						DSSFinished         // Scan finished
 | 
				
			||||||
 | 
						DSSCanceled         // Scan canceled
 | 
				
			||||||
 | 
						DSSNumStates        // Total number of scan states
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Scan functions
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						PoolScanNone     = iota // No scan function
 | 
				
			||||||
 | 
						PoolScanScrub           // Pools is checked against errors
 | 
				
			||||||
 | 
						PoolScanResilver        // Pool is resilvering
 | 
				
			||||||
 | 
						PoolScanFuncs           // Number of scan functions
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VDevStat - Vdev statistics.  Note: all fields should be 64-bit because this
 | 
				
			||||||
 | 
					// is passed between kernel and userland as an nvlist uint64 array.
 | 
				
			||||||
 | 
					type VDevStat struct {
 | 
				
			||||||
 | 
						Timestamp      time.Duration    /* time since vdev load	(nanoseconds)*/
 | 
				
			||||||
 | 
						State          VDevState        /* vdev state		*/
 | 
				
			||||||
 | 
						Aux            VDevAux          /* see vdev_aux_t	*/
 | 
				
			||||||
 | 
						Alloc          uint64           /* space allocated	*/
 | 
				
			||||||
 | 
						Space          uint64           /* total capacity	*/
 | 
				
			||||||
 | 
						DSpace         uint64           /* deflated capacity	*/
 | 
				
			||||||
 | 
						RSize          uint64           /* replaceable dev size */
 | 
				
			||||||
 | 
						ESize          uint64           /* expandable dev size */
 | 
				
			||||||
 | 
						Ops            [ZIOTypes]uint64 /* operation count	*/
 | 
				
			||||||
 | 
						Bytes          [ZIOTypes]uint64 /* bytes read/written	*/
 | 
				
			||||||
 | 
						ReadErrors     uint64           /* read errors		*/
 | 
				
			||||||
 | 
						WriteErrors    uint64           /* write errors		*/
 | 
				
			||||||
 | 
						ChecksumErrors uint64           /* checksum errors	*/
 | 
				
			||||||
 | 
						SelfHealed     uint64           /* self-healed bytes	*/
 | 
				
			||||||
 | 
						ScanRemoving   uint64           /* removing?	*/
 | 
				
			||||||
 | 
						ScanProcessed  uint64           /* scan processed bytes	*/
 | 
				
			||||||
 | 
						Fragmentation  uint64           /* device fragmentation */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PoolScanStat - Pool scan statistics
 | 
				
			||||||
 | 
					type PoolScanStat struct {
 | 
				
			||||||
 | 
						// Values stored on disk
 | 
				
			||||||
 | 
						Func      uint64 // Current scan function e.g. none, scrub ...
 | 
				
			||||||
 | 
						State     uint64 // Current scan state e.g. scanning, finished ...
 | 
				
			||||||
 | 
						StartTime uint64 // Scan start time
 | 
				
			||||||
 | 
						EndTime   uint64 // Scan end time
 | 
				
			||||||
 | 
						ToExamine uint64 // Total bytes to scan
 | 
				
			||||||
 | 
						Examined  uint64 // Total bytes scaned
 | 
				
			||||||
 | 
						ToProcess uint64 // Total bytes to processed
 | 
				
			||||||
 | 
						Processed uint64 // Total bytes processed
 | 
				
			||||||
 | 
						Errors    uint64 // Scan errors
 | 
				
			||||||
 | 
						// Values not stored on disk
 | 
				
			||||||
 | 
						PassExam  uint64 // Examined bytes per scan pass
 | 
				
			||||||
 | 
						PassStart uint64 // Start time of scan pass
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VDevTree ZFS virtual device tree
 | 
					// VDevTree ZFS virtual device tree
 | 
				
			||||||
type VDevTree struct {
 | 
					type VDevTree struct {
 | 
				
			||||||
	Type    VDevType
 | 
						Type     VDevType
 | 
				
			||||||
	Devices []VDevTree // groups other devices (e.g. mirror)
 | 
						Devices  []VDevTree // groups other devices (e.g. mirror)
 | 
				
			||||||
	Parity  uint
 | 
						Parity   uint
 | 
				
			||||||
	Path    string
 | 
						Path     string
 | 
				
			||||||
 | 
						Name     string
 | 
				
			||||||
 | 
						Stat     VDevStat
 | 
				
			||||||
 | 
						ScanStat PoolScanStat
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ExportedPool is type representing ZFS pool available for import
 | 
				
			||||||
 | 
					type ExportedPool struct {
 | 
				
			||||||
 | 
						VDevs   VDevTree
 | 
				
			||||||
 | 
						Name    string
 | 
				
			||||||
 | 
						Comment string
 | 
				
			||||||
 | 
						GUID    uint64
 | 
				
			||||||
 | 
						State   PoolState
 | 
				
			||||||
 | 
						Status  PoolStatus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Pool object represents handler to single ZFS pool
 | 
					// Pool object represents handler to single ZFS pool
 | 
				
			||||||
| 
						 | 
					@ -56,6 +138,164 @@ func PoolOpen(name string) (pool Pool, err error) {
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func poolGetConfig(name string, nv *C.nvlist_t) (vdevs VDevTree, err error) {
 | 
				
			||||||
 | 
						var dtype *C.char
 | 
				
			||||||
 | 
						var c, children C.uint_t
 | 
				
			||||||
 | 
						var notpresent C.uint64_t
 | 
				
			||||||
 | 
						var vs *C.vdev_stat_t
 | 
				
			||||||
 | 
						var ps *C.pool_scan_stat_t
 | 
				
			||||||
 | 
						var child **C.nvlist_t
 | 
				
			||||||
 | 
						var vdev VDevTree
 | 
				
			||||||
 | 
						if 0 != C.nvlist_lookup_string(nv, C.CString(C.ZPOOL_CONFIG_TYPE), &dtype) {
 | 
				
			||||||
 | 
							err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_TYPE)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vdevs.Name = name
 | 
				
			||||||
 | 
						vdevs.Type = VDevType(C.GoString(dtype))
 | 
				
			||||||
 | 
						if vdevs.Type == VDevTypeMissing || vdevs.Type == VDevTypeHole {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fetch vdev state
 | 
				
			||||||
 | 
						if 0 != C.nvlist_lookup_uint64_array_vds(nv, C.CString(C.ZPOOL_CONFIG_VDEV_STATS),
 | 
				
			||||||
 | 
							&vs, &c) {
 | 
				
			||||||
 | 
							err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_VDEV_STATS)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vdevs.Stat.Timestamp = time.Duration(vs.vs_timestamp)
 | 
				
			||||||
 | 
						vdevs.Stat.State = VDevState(vs.vs_state)
 | 
				
			||||||
 | 
						vdevs.Stat.Aux = VDevAux(vs.vs_aux)
 | 
				
			||||||
 | 
						vdevs.Stat.Alloc = uint64(vs.vs_alloc)
 | 
				
			||||||
 | 
						vdevs.Stat.Space = uint64(vs.vs_space)
 | 
				
			||||||
 | 
						vdevs.Stat.DSpace = uint64(vs.vs_dspace)
 | 
				
			||||||
 | 
						vdevs.Stat.RSize = uint64(vs.vs_rsize)
 | 
				
			||||||
 | 
						vdevs.Stat.ESize = uint64(vs.vs_esize)
 | 
				
			||||||
 | 
						for z := 0; z < ZIOTypes; z++ {
 | 
				
			||||||
 | 
							vdev.Stat.Ops[z] = uint64(vs.vs_ops[z])
 | 
				
			||||||
 | 
							vdev.Stat.Bytes[z] = uint64(vs.vs_bytes[z])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vdevs.Stat.ReadErrors = uint64(vs.vs_read_errors)
 | 
				
			||||||
 | 
						vdevs.Stat.WriteErrors = uint64(vs.vs_write_errors)
 | 
				
			||||||
 | 
						vdevs.Stat.ChecksumErrors = uint64(vs.vs_checksum_errors)
 | 
				
			||||||
 | 
						vdevs.Stat.SelfHealed = uint64(vs.vs_self_healed)
 | 
				
			||||||
 | 
						vdevs.Stat.ScanRemoving = uint64(vs.vs_scan_removing)
 | 
				
			||||||
 | 
						vdevs.Stat.ScanProcessed = uint64(vs.vs_scan_processed)
 | 
				
			||||||
 | 
						vdevs.Stat.Fragmentation = uint64(vs.vs_fragmentation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fetch vdev scan stats
 | 
				
			||||||
 | 
						if 0 == C.nvlist_lookup_uint64_array_ps(nv, C.CString(C.ZPOOL_CONFIG_SCAN_STATS),
 | 
				
			||||||
 | 
							&ps, &c) {
 | 
				
			||||||
 | 
							vdevs.ScanStat.Func = uint64(ps.pss_func)
 | 
				
			||||||
 | 
							vdevs.ScanStat.State = uint64(ps.pss_state)
 | 
				
			||||||
 | 
							vdevs.ScanStat.StartTime = uint64(ps.pss_start_time)
 | 
				
			||||||
 | 
							vdevs.ScanStat.EndTime = uint64(ps.pss_end_time)
 | 
				
			||||||
 | 
							vdevs.ScanStat.ToExamine = uint64(ps.pss_to_examine)
 | 
				
			||||||
 | 
							vdevs.ScanStat.Examined = uint64(ps.pss_examined)
 | 
				
			||||||
 | 
							vdevs.ScanStat.ToProcess = uint64(ps.pss_to_process)
 | 
				
			||||||
 | 
							vdevs.ScanStat.Processed = uint64(ps.pss_processed)
 | 
				
			||||||
 | 
							vdevs.ScanStat.Errors = uint64(ps.pss_errors)
 | 
				
			||||||
 | 
							vdevs.ScanStat.PassExam = uint64(ps.pss_pass_exam)
 | 
				
			||||||
 | 
							vdevs.ScanStat.PassStart = uint64(ps.pss_pass_start)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fetch the children
 | 
				
			||||||
 | 
						if C.nvlist_lookup_nvlist_array(nv, C.CString(C.ZPOOL_CONFIG_CHILDREN),
 | 
				
			||||||
 | 
							&child, &children) != 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if children > 0 {
 | 
				
			||||||
 | 
							vdevs.Devices = make([]VDevTree, 0, children)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if C.nvlist_lookup_uint64(nv, C.CString(C.ZPOOL_CONFIG_NOT_PRESENT),
 | 
				
			||||||
 | 
							¬present) == 0 {
 | 
				
			||||||
 | 
							var path *C.char
 | 
				
			||||||
 | 
							if 0 != C.nvlist_lookup_string(nv, C.CString(C.ZPOOL_CONFIG_PATH), &path) {
 | 
				
			||||||
 | 
								err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_PATH)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							vdevs.Path = C.GoString(path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for c = 0; c < children; c++ {
 | 
				
			||||||
 | 
							var islog = C.uint64_t(C.B_FALSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							C.nvlist_lookup_uint64(C.nvlist_array_at(child, c),
 | 
				
			||||||
 | 
								C.CString(C.ZPOOL_CONFIG_IS_LOG), &islog)
 | 
				
			||||||
 | 
							if islog != C.B_FALSE {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							vname := C.zpool_vdev_name(libzfsHandle, nil, C.nvlist_array_at(child, c),
 | 
				
			||||||
 | 
								C.B_TRUE)
 | 
				
			||||||
 | 
							vdev, err = poolGetConfig(C.GoString(vname),
 | 
				
			||||||
 | 
								C.nvlist_array_at(child, c))
 | 
				
			||||||
 | 
							C.free_cstring(vname)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							vdevs.Devices = append(vdevs.Devices, vdev)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PoolImportSearch - Search pools available to import but not imported.
 | 
				
			||||||
 | 
					// Returns array of found pools.
 | 
				
			||||||
 | 
					func PoolImportSearch(searchpaths []string) (epools []ExportedPool, err error) {
 | 
				
			||||||
 | 
						var config, nvroot *C.nvlist_t
 | 
				
			||||||
 | 
						var cname, msgid, comment *C.char
 | 
				
			||||||
 | 
						var poolState, guid C.uint64_t
 | 
				
			||||||
 | 
						var reason C.zpool_status_t
 | 
				
			||||||
 | 
						var errata C.zpool_errata_t
 | 
				
			||||||
 | 
						config = nil
 | 
				
			||||||
 | 
						var elem *C.nvpair_t
 | 
				
			||||||
 | 
						numofp := len(searchpaths)
 | 
				
			||||||
 | 
						cpaths := C.alloc_strings(C.int(numofp))
 | 
				
			||||||
 | 
						for i, path := range searchpaths {
 | 
				
			||||||
 | 
							C.strings_setat(cpaths, C.int(i), C.CString(path))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pools := C.zpool_find_import(libzfsHandle, C.int(numofp), cpaths)
 | 
				
			||||||
 | 
						defer C.nvlist_free(pools)
 | 
				
			||||||
 | 
						elem = C.nvlist_next_nvpair(pools, elem)
 | 
				
			||||||
 | 
						epools = make([]ExportedPool, 0, 1)
 | 
				
			||||||
 | 
						for ; elem != nil; elem = C.nvlist_next_nvpair(pools, elem) {
 | 
				
			||||||
 | 
							ep := ExportedPool{}
 | 
				
			||||||
 | 
							if C.nvpair_value_nvlist(elem, &config) != 0 {
 | 
				
			||||||
 | 
								err = LastError()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if C.nvlist_lookup_uint64(config, C.CString(C.ZPOOL_CONFIG_POOL_STATE),
 | 
				
			||||||
 | 
								&poolState) != 0 {
 | 
				
			||||||
 | 
								err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_POOL_STATE)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ep.State = PoolState(poolState)
 | 
				
			||||||
 | 
							if C.nvlist_lookup_string(config, C.CString(C.ZPOOL_CONFIG_POOL_NAME), &cname) != 0 {
 | 
				
			||||||
 | 
								err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_POOL_NAME)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ep.Name = C.GoString(cname)
 | 
				
			||||||
 | 
							if C.nvlist_lookup_uint64(config, C.CString(C.ZPOOL_CONFIG_POOL_GUID), &guid) != 0 {
 | 
				
			||||||
 | 
								err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_POOL_GUID)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ep.GUID = uint64(guid)
 | 
				
			||||||
 | 
							reason = C.zpool_import_status(config, &msgid, &errata)
 | 
				
			||||||
 | 
							ep.Status = PoolStatus(reason)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if C.nvlist_lookup_string(config, C.CString(C.ZPOOL_CONFIG_COMMENT), &comment) == 0 {
 | 
				
			||||||
 | 
								ep.Comment = C.GoString(comment)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if C.nvlist_lookup_nvlist(config, C.CString(C.ZPOOL_CONFIG_VDEV_TREE),
 | 
				
			||||||
 | 
								&nvroot) != 0 {
 | 
				
			||||||
 | 
								err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_VDEV_TREE)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ep.VDevs, err = poolGetConfig(ep.Name, nvroot)
 | 
				
			||||||
 | 
							epools = append(epools, ep)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func poolSearchImport(q string, searchpaths []string, guid bool) (name string,
 | 
					func poolSearchImport(q string, searchpaths []string, guid bool) (name string,
 | 
				
			||||||
	err error) {
 | 
						err error) {
 | 
				
			||||||
	var config *C.nvlist_t
 | 
						var config *C.nvlist_t
 | 
				
			||||||
| 
						 | 
					@ -583,23 +823,8 @@ func PoolCreate(name string, vdevs []VDevTree, features map[string]string,
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// It can happen that pool is not immediately available,
 | 
						// Open created pool and return handle
 | 
				
			||||||
	// we know we just created it with success so lets wait and retry
 | 
						pool, err = PoolOpen(name)
 | 
				
			||||||
	// 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
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -654,3 +879,69 @@ func (pool *Pool) ExportForce(log string) (err error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VDevTree - Fetch pool's current vdev tree configuration, state and stats
 | 
				
			||||||
 | 
					func (pool *Pool) VDevTree() (vdevs VDevTree, err error) {
 | 
				
			||||||
 | 
						var nvroot *C.nvlist_t
 | 
				
			||||||
 | 
						var poolName string
 | 
				
			||||||
 | 
						config := C.zpool_get_config(pool.list.zph, nil)
 | 
				
			||||||
 | 
						if config == nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("Failed zpool_get_config")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if C.nvlist_lookup_nvlist(config, C.CString(C.ZPOOL_CONFIG_VDEV_TREE),
 | 
				
			||||||
 | 
							&nvroot) != 0 {
 | 
				
			||||||
 | 
							err = fmt.Errorf("Failed to fetch %s", C.ZPOOL_CONFIG_VDEV_TREE)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if poolName, err = pool.Name(); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return poolGetConfig(poolName, nvroot)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s PoolState) String() string {
 | 
				
			||||||
 | 
						switch s {
 | 
				
			||||||
 | 
						case PoolStateActive:
 | 
				
			||||||
 | 
							return "ACTIVE"
 | 
				
			||||||
 | 
						case PoolStateExported:
 | 
				
			||||||
 | 
							return "EXPORTED"
 | 
				
			||||||
 | 
						case PoolStateDestroyed:
 | 
				
			||||||
 | 
							return "DESTROYED"
 | 
				
			||||||
 | 
						case PoolStateSpare:
 | 
				
			||||||
 | 
							return "SPARE"
 | 
				
			||||||
 | 
						case PoolStateL2cache:
 | 
				
			||||||
 | 
							return "L2CACHE"
 | 
				
			||||||
 | 
						case PoolStateUninitialized:
 | 
				
			||||||
 | 
							return "UNINITIALIZED"
 | 
				
			||||||
 | 
						case PoolStateUnavail:
 | 
				
			||||||
 | 
							return "UNAVAILABLE"
 | 
				
			||||||
 | 
						case PoolStatePotentiallyActive:
 | 
				
			||||||
 | 
							return "POTENTIALLYACTIVE"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return "UNKNOWN"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s VDevState) String() string {
 | 
				
			||||||
 | 
						switch s {
 | 
				
			||||||
 | 
						case VDevStateUnknown:
 | 
				
			||||||
 | 
							return "UNINITIALIZED"
 | 
				
			||||||
 | 
						case VDevStateClosed:
 | 
				
			||||||
 | 
							return "CLOSED"
 | 
				
			||||||
 | 
						case VDevStateOffline:
 | 
				
			||||||
 | 
							return "OFFLINE"
 | 
				
			||||||
 | 
						case VDevStateRemoved:
 | 
				
			||||||
 | 
							return "REMOVED"
 | 
				
			||||||
 | 
						case VDevStateCantOpen:
 | 
				
			||||||
 | 
							return "CANT_OPEN"
 | 
				
			||||||
 | 
						case VDevStateFaulted:
 | 
				
			||||||
 | 
							return "FAULTED"
 | 
				
			||||||
 | 
						case VDevStateDegraded:
 | 
				
			||||||
 | 
							return "DEGRADED"
 | 
				
			||||||
 | 
						case VDevStateHealthy:
 | 
				
			||||||
 | 
							return "ONLINE"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return "UNKNOWN"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								zpool.h
								
								
								
								
							
							
						
						
									
										7
									
								
								zpool.h
								
								
								
								
							| 
						 | 
					@ -50,9 +50,16 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
 | 
				
			||||||
nvlist_t** nvlist_alloc_array(int count);
 | 
					nvlist_t** nvlist_alloc_array(int count);
 | 
				
			||||||
void nvlist_array_set(nvlist_t** a, int i, nvlist_t *item);
 | 
					void nvlist_array_set(nvlist_t** a, int i, nvlist_t *item);
 | 
				
			||||||
void nvlist_free_array(nvlist_t **a);
 | 
					void nvlist_free_array(nvlist_t **a);
 | 
				
			||||||
 | 
					nvlist_t *nvlist_array_at(nvlist_t **a, uint_t i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void free_cstring(char *str);
 | 
					void free_cstring(char *str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nvlist_lookup_uint64_array_vds(nvlist_t *nv, const char *p,
 | 
				
			||||||
 | 
						vdev_stat_t **vds, uint_t *c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nvlist_lookup_uint64_array_ps(nvlist_t *nv, const char *p,
 | 
				
			||||||
 | 
						pool_scan_stat_t **vds, uint_t *c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
/* SERVERWARE_ZPOOL_H */
 | 
					/* SERVERWARE_ZPOOL_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,6 +224,36 @@ func zpoolTestImportByGUID(t *testing.T) {
 | 
				
			||||||
	print("PASS\n\n")
 | 
						print("PASS\n\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func printVDevTree(vt zfs.VDevTree, pref string) {
 | 
				
			||||||
 | 
						first := pref + vt.Name
 | 
				
			||||||
 | 
						fmt.Printf("%-30s | %-10s | %-10s | %s\n", first, vt.Type,
 | 
				
			||||||
 | 
							vt.Stat.State.String(), vt.Path)
 | 
				
			||||||
 | 
						for _, v := range vt.Devices {
 | 
				
			||||||
 | 
							printVDevTree(v, "  "+pref)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func zpoolTestPoolImportSearch(t *testing.T) {
 | 
				
			||||||
 | 
						println("TEST PoolImportSearch")
 | 
				
			||||||
 | 
						pools, err := zfs.PoolImportSearch([]string{"/tmp"})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, p := range pools {
 | 
				
			||||||
 | 
							println()
 | 
				
			||||||
 | 
							println("---------------------------------------------------------------")
 | 
				
			||||||
 | 
							println("pool: ", p.Name)
 | 
				
			||||||
 | 
							println("guid: ", p.GUID)
 | 
				
			||||||
 | 
							println("state: ", p.State.String())
 | 
				
			||||||
 | 
							fmt.Printf("%-30s | %-10s | %-10s | %s\n", "NAME", "TYPE", "STATE", "PATH")
 | 
				
			||||||
 | 
							println("---------------------------------------------------------------")
 | 
				
			||||||
 | 
							printVDevTree(p.VDevs, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
| 
						 | 
					@ -237,11 +267,19 @@ func zpoolTestPoolProp(t *testing.T) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Test fetching property
 | 
							// Test fetching property
 | 
				
			||||||
		_, err := pool.GetProperty(zfs.PoolPropHealth)
 | 
							propHealth, err := pool.GetProperty(zfs.PoolPropHealth)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Error(err)
 | 
								t.Error(err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							println("Pool property health: ", propHealth.Value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							propGUID, err := pool.GetProperty(zfs.PoolPropGUID)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							println("Pool property GUID: ", propGUID.Value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// this test pool should not be bootable
 | 
							// this test pool should not be bootable
 | 
				
			||||||
		prop, err := pool.GetProperty(zfs.PoolPropBootfs)
 | 
							prop, err := pool.GetProperty(zfs.PoolPropBootfs)
 | 
				
			||||||
| 
						 | 
					@ -290,6 +328,26 @@ func zpoolTestPoolStatusAndState(t *testing.T) {
 | 
				
			||||||
	print("PASS\n\n")
 | 
						print("PASS\n\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func zpoolTestPoolVDevTree(t *testing.T) {
 | 
				
			||||||
 | 
						var vdevs zfs.VDevTree
 | 
				
			||||||
 | 
						println("TEST pool VDevTree ( ", TSTPoolName, " ) ... ")
 | 
				
			||||||
 | 
						pool, err := zfs.PoolOpen(TSTPoolName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer pool.Close()
 | 
				
			||||||
 | 
						vdevs, err = pool.VDevTree()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Printf("%-30s | %-10s | %-10s | %s\n", "NAME", "TYPE", "STATE", "PATH")
 | 
				
			||||||
 | 
						println("---------------------------------------------------------------")
 | 
				
			||||||
 | 
						printVDevTree(vdevs, "")
 | 
				
			||||||
 | 
						print("PASS\n\n")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* ------------------------------------------------------------------------- */
 | 
					/* ------------------------------------------------------------------------- */
 | 
				
			||||||
// EXAMPLES:
 | 
					// EXAMPLES:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue