Merge 138612c989
into 1713aa7b4d
This commit is contained in:
commit
1109f2066b
|
@ -176,6 +176,7 @@ COMMON_H = \
|
||||||
|
|
||||||
|
|
||||||
KERNEL_H = \
|
KERNEL_H = \
|
||||||
|
sys/spa_json_stats.h \
|
||||||
sys/zfs_ioctl.h \
|
sys/zfs_ioctl.h \
|
||||||
sys/zfs_ioctl_impl.h \
|
sys/zfs_ioctl_impl.h \
|
||||||
sys/zfs_onexit.h \
|
sys/zfs_onexit.h \
|
||||||
|
|
|
@ -67,8 +67,8 @@ struct list_head {};
|
||||||
#define KSTAT_FLAG_VIRTUAL 0x01
|
#define KSTAT_FLAG_VIRTUAL 0x01
|
||||||
#define KSTAT_FLAG_VAR_SIZE 0x02
|
#define KSTAT_FLAG_VAR_SIZE 0x02
|
||||||
#define KSTAT_FLAG_WRITABLE 0x04
|
#define KSTAT_FLAG_WRITABLE 0x04
|
||||||
#define KSTAT_FLAG_PERSISTENT 0x08
|
#define KSTAT_FLAG_RESTRICTED 0x08
|
||||||
#define KSTAT_FLAG_DORMANT 0x10
|
#define KSTAT_FLAG_UNUSED 0x10
|
||||||
#define KSTAT_FLAG_INVALID 0x20
|
#define KSTAT_FLAG_INVALID 0x20
|
||||||
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
||||||
#define KSTAT_FLAG_NO_HEADERS 0x80
|
#define KSTAT_FLAG_NO_HEADERS 0x80
|
||||||
|
|
|
@ -66,8 +66,8 @@
|
||||||
#define KSTAT_FLAG_VIRTUAL 0x01
|
#define KSTAT_FLAG_VIRTUAL 0x01
|
||||||
#define KSTAT_FLAG_VAR_SIZE 0x02
|
#define KSTAT_FLAG_VAR_SIZE 0x02
|
||||||
#define KSTAT_FLAG_WRITABLE 0x04
|
#define KSTAT_FLAG_WRITABLE 0x04
|
||||||
#define KSTAT_FLAG_PERSISTENT 0x08
|
#define KSTAT_FLAG_RESTRICTED 0x08
|
||||||
#define KSTAT_FLAG_DORMANT 0x10
|
#define KSTAT_FLAG_UNUSED 0x10
|
||||||
#define KSTAT_FLAG_INVALID 0x20
|
#define KSTAT_FLAG_INVALID 0x20
|
||||||
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
||||||
#define KSTAT_FLAG_NO_HEADERS 0x80
|
#define KSTAT_FLAG_NO_HEADERS 0x80
|
||||||
|
|
|
@ -213,6 +213,8 @@ _SYS_NVPAIR_H int nvlist_remove(nvlist_t *, const char *, data_type_t);
|
||||||
_SYS_NVPAIR_H int nvlist_remove_all(nvlist_t *, const char *);
|
_SYS_NVPAIR_H int nvlist_remove_all(nvlist_t *, const char *);
|
||||||
_SYS_NVPAIR_H int nvlist_remove_nvpair(nvlist_t *, nvpair_t *);
|
_SYS_NVPAIR_H int nvlist_remove_nvpair(nvlist_t *, nvpair_t *);
|
||||||
|
|
||||||
|
_SYS_NVPAIR_H int nvlist_to_json(nvlist_t *, char **, size_t);
|
||||||
|
|
||||||
_SYS_NVPAIR_H int nvlist_lookup_boolean(const nvlist_t *, const char *);
|
_SYS_NVPAIR_H int nvlist_lookup_boolean(const nvlist_t *, const char *);
|
||||||
_SYS_NVPAIR_H int nvlist_lookup_boolean_value(const nvlist_t *, const char *,
|
_SYS_NVPAIR_H int nvlist_lookup_boolean_value(const nvlist_t *, const char *,
|
||||||
boolean_t *);
|
boolean_t *);
|
||||||
|
|
|
@ -771,6 +771,9 @@ extern void spa_scan_stat_init(spa_t *spa);
|
||||||
extern int spa_scan_get_stats(spa_t *spa, pool_scan_stat_t *ps);
|
extern int spa_scan_get_stats(spa_t *spa, pool_scan_stat_t *ps);
|
||||||
extern int bpobj_enqueue_alloc_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
extern int bpobj_enqueue_alloc_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
||||||
extern int bpobj_enqueue_free_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
extern int bpobj_enqueue_free_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
||||||
|
extern void spa_add_spares(spa_t *spa, nvlist_t *config);
|
||||||
|
extern void spa_add_l2cache(spa_t *spa, nvlist_t *config);
|
||||||
|
extern void spa_add_feature_stats(spa_t *spa, nvlist_t *config);
|
||||||
|
|
||||||
#define SPA_ASYNC_CONFIG_UPDATE 0x01
|
#define SPA_ASYNC_CONFIG_UPDATE 0x01
|
||||||
#define SPA_ASYNC_REMOVE 0x02
|
#define SPA_ASYNC_REMOVE 0x02
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <sys/spa.h>
|
#include <sys/spa.h>
|
||||||
#include <sys/spa_checkpoint.h>
|
#include <sys/spa_checkpoint.h>
|
||||||
#include <sys/spa_log_spacemap.h>
|
#include <sys/spa_log_spacemap.h>
|
||||||
|
#include <sys/spa_json_stats.h>
|
||||||
#include <sys/vdev.h>
|
#include <sys/vdev.h>
|
||||||
#include <sys/vdev_rebuild.h>
|
#include <sys/vdev_rebuild.h>
|
||||||
#include <sys/vdev_removal.h>
|
#include <sys/vdev_removal.h>
|
||||||
|
@ -442,6 +443,7 @@ struct spa {
|
||||||
uint64_t spa_autotrim; /* automatic background trim? */
|
uint64_t spa_autotrim; /* automatic background trim? */
|
||||||
uint64_t spa_errata; /* errata issues detected */
|
uint64_t spa_errata; /* errata issues detected */
|
||||||
spa_stats_t spa_stats; /* assorted spa statistics */
|
spa_stats_t spa_stats; /* assorted spa statistics */
|
||||||
|
spa_json_stats_t spa_json_stats; /* diagnostic status in JSON */
|
||||||
spa_keystore_t spa_keystore; /* loaded crypto keys */
|
spa_keystore_t spa_keystore; /* loaded crypto keys */
|
||||||
|
|
||||||
/* arc_memory_throttle() parameters during low memory condition */
|
/* arc_memory_throttle() parameters during low memory condition */
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* CDDL HEADER START
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the terms of the
|
||||||
|
* Common Development and Distribution License (the "License").
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||||
|
* or http://opensource.org/licenses/CDDL-1.0.
|
||||||
|
* See the License for the specific language governing permissions
|
||||||
|
* and limitations under the License.
|
||||||
|
*
|
||||||
|
* When distributing Covered Code, include this CDDL HEADER in each
|
||||||
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||||
|
* If applicable, add the following below this CDDL HEADER, with the
|
||||||
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||||
|
*
|
||||||
|
* CDDL HEADER END
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Klara Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SYS_SPA_JSON_STATS_H
|
||||||
|
#define _SYS_SPA_JSON_STATS_H
|
||||||
|
|
||||||
|
#include <sys/zfs_context.h>
|
||||||
|
#include <sys/spa_impl.h>
|
||||||
|
#include <sys/kstat.h>
|
||||||
|
|
||||||
|
typedef struct spa_json_stats {
|
||||||
|
kmutex_t lock;
|
||||||
|
kstat_t *kstat;
|
||||||
|
} spa_json_stats_t;
|
||||||
|
|
||||||
|
extern int spa_generate_json_stats(spa_t *spa, char *buf, size_t size);
|
||||||
|
|
||||||
|
#endif /* _SYS_SPA_JSON_STATS_H */
|
|
@ -117,6 +117,7 @@
|
||||||
<elf-symbol name='nvlist_empty' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_empty' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='nvlist_exists' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_exists' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='nvlist_free' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_free' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
<elf-symbol name='nvlist_to_json' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='nvlist_lookup_boolean' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_lookup_boolean' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='nvlist_lookup_boolean_array' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_lookup_boolean_array' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='nvlist_lookup_boolean_value' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='nvlist_lookup_boolean_value' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
@ -2780,6 +2781,12 @@
|
||||||
<parameter type-id='3fa542f0' name='nvp'/>
|
<parameter type-id='3fa542f0' name='nvp'/>
|
||||||
<return type-id='95e97e5e'/>
|
<return type-id='95e97e5e'/>
|
||||||
</function-decl>
|
</function-decl>
|
||||||
|
<function-decl name='nvlist_to_json' mangled-name='nvlist_to_json' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='nvlist_to_json'>
|
||||||
|
<parameter type-id='5ce45b60' name='nvl'/>
|
||||||
|
<parameter type-id='9b23c9ad' name='buf'/>
|
||||||
|
<parameter type-id='b59d7dce' name='size'/>
|
||||||
|
<return type-id='95e97e5e'/>
|
||||||
|
</function-decl>
|
||||||
<function-decl name='nvlist_add_boolean' mangled-name='nvlist_add_boolean' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='nvlist_add_boolean'>
|
<function-decl name='nvlist_add_boolean' mangled-name='nvlist_add_boolean' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='nvlist_add_boolean'>
|
||||||
<parameter type-id='5ce45b60' name='nvl'/>
|
<parameter type-id='5ce45b60' name='nvl'/>
|
||||||
<parameter type-id='80f4b756' name='name'/>
|
<parameter type-id='80f4b756' name='name'/>
|
||||||
|
|
|
@ -239,24 +239,13 @@ typedef struct kstat {
|
||||||
* The ks_snapshot routine (see below) does not need to check for
|
* The ks_snapshot routine (see below) does not need to check for
|
||||||
* this; permission checking is handled in the kstat driver.
|
* this; permission checking is handled in the kstat driver.
|
||||||
*
|
*
|
||||||
* KSTAT_FLAG_PERSISTENT:
|
* KSTAT_FLAG_RESTRICTED:
|
||||||
*
|
*
|
||||||
* Indicates that this kstat is to be persistent over time.
|
* Indicates that this kstat has restricted access and is
|
||||||
* For persistent kstats, kstat_delete() simply marks the
|
* not world readable.
|
||||||
* kstat as dormant; a subsequent kstat_create() reactivates
|
|
||||||
* the kstat. This feature is provided so that statistics
|
|
||||||
* are not lost across driver close/open (e.g., raw disk I/O
|
|
||||||
* on a disk with no mounted partitions.)
|
|
||||||
* NOTE: Persistent kstats cannot be virtual, since ks_data
|
|
||||||
* points to garbage as soon as the driver goes away.
|
|
||||||
*
|
*
|
||||||
* The following flags are maintained by the kstat framework:
|
* The following flags are maintained by the kstat framework:
|
||||||
*
|
*
|
||||||
* KSTAT_FLAG_DORMANT:
|
|
||||||
*
|
|
||||||
* For persistent kstats, indicates that the kstat is in the
|
|
||||||
* dormant state (e.g., the corresponding device is closed).
|
|
||||||
*
|
|
||||||
* KSTAT_FLAG_INVALID:
|
* KSTAT_FLAG_INVALID:
|
||||||
*
|
*
|
||||||
* This flag is set when a kstat is in a transitional state,
|
* This flag is set when a kstat is in a transitional state,
|
||||||
|
@ -268,8 +257,8 @@ typedef struct kstat {
|
||||||
#define KSTAT_FLAG_VIRTUAL 0x01
|
#define KSTAT_FLAG_VIRTUAL 0x01
|
||||||
#define KSTAT_FLAG_VAR_SIZE 0x02
|
#define KSTAT_FLAG_VAR_SIZE 0x02
|
||||||
#define KSTAT_FLAG_WRITABLE 0x04
|
#define KSTAT_FLAG_WRITABLE 0x04
|
||||||
#define KSTAT_FLAG_PERSISTENT 0x08
|
#define KSTAT_FLAG_RESTRICTED 0x08
|
||||||
#define KSTAT_FLAG_DORMANT 0x10
|
#define KSTAT_FLAG_UNUSED 0x10
|
||||||
#define KSTAT_FLAG_INVALID 0x20
|
#define KSTAT_FLAG_INVALID 0x20
|
||||||
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
#define KSTAT_FLAG_LONGSTRINGS 0x40
|
||||||
#define KSTAT_FLAG_NO_HEADERS 0x80
|
#define KSTAT_FLAG_NO_HEADERS 0x80
|
||||||
|
@ -722,33 +711,8 @@ extern void kstat_init(void); /* initialize kstat framework */
|
||||||
* you must NOT be holding that kstat's ks_lock. Otherwise, you may
|
* you must NOT be holding that kstat's ks_lock. Otherwise, you may
|
||||||
* deadlock with a kstat reader.
|
* deadlock with a kstat reader.
|
||||||
*
|
*
|
||||||
* Persistent kstats
|
* Persistent kstats are not implemented since there is no persistent
|
||||||
*
|
* namespace for them to reside.
|
||||||
* From the provider's point of view, persistence is transparent. The only
|
|
||||||
* difference between ephemeral (normal) kstats and persistent kstats
|
|
||||||
* is that you pass KSTAT_FLAG_PERSISTENT to kstat_create(). Magically,
|
|
||||||
* this has the effect of making your data visible even when you're
|
|
||||||
* not home. Persistence is important to tools like iostat, which want
|
|
||||||
* to get a meaningful picture of disk activity. Without persistence,
|
|
||||||
* raw disk i/o statistics could never accumulate: they would come and
|
|
||||||
* go with each open/close of the raw device.
|
|
||||||
*
|
|
||||||
* The magic of persistence works by slightly altering the behavior of
|
|
||||||
* kstat_create() and kstat_delete(). The first call to kstat_create()
|
|
||||||
* creates a new kstat, as usual. However, kstat_delete() does not
|
|
||||||
* actually delete the kstat: it performs one final update of the data
|
|
||||||
* (i.e., calls the ks_update routine), marks the kstat as dormant, and
|
|
||||||
* sets the ks_lock, ks_update, ks_private, and ks_snapshot fields back
|
|
||||||
* to their default values (since they might otherwise point to garbage,
|
|
||||||
* e.g. if the provider is going away). kstat clients can still access
|
|
||||||
* the dormant kstat just like a live kstat; they just continue to see
|
|
||||||
* the final data values as long as the kstat remains dormant.
|
|
||||||
* All subsequent kstat_create() calls simply find the already-existing,
|
|
||||||
* dormant kstat and return a pointer to it, without altering any fields.
|
|
||||||
* The provider then performs its usual initialization sequence, and
|
|
||||||
* calls kstat_install(). kstat_install() uses the old data values to
|
|
||||||
* initialize the native data (i.e., ks_update is called with KSTAT_WRITE),
|
|
||||||
* thus making it seem like you were never gone.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern kstat_t *kstat_create(const char *, int, const char *, const char *,
|
extern kstat_t *kstat_create(const char *, int, const char *, const char *,
|
||||||
|
|
|
@ -132,6 +132,7 @@ nodist_libzpool_la_SOURCES = \
|
||||||
module/zfs/spa_config.c \
|
module/zfs/spa_config.c \
|
||||||
module/zfs/spa_errlog.c \
|
module/zfs/spa_errlog.c \
|
||||||
module/zfs/spa_history.c \
|
module/zfs/spa_history.c \
|
||||||
|
module/zfs/spa_json_stats.c \
|
||||||
module/zfs/spa_log_spacemap.c \
|
module/zfs/spa_log_spacemap.c \
|
||||||
module/zfs/spa_misc.c \
|
module/zfs/spa_misc.c \
|
||||||
module/zfs/spa_stats.c \
|
module/zfs/spa_stats.c \
|
||||||
|
|
|
@ -373,6 +373,7 @@ ZFS_OBJS := \
|
||||||
spa_config.o \
|
spa_config.o \
|
||||||
spa_errlog.o \
|
spa_errlog.o \
|
||||||
spa_history.o \
|
spa_history.o \
|
||||||
|
spa_json_stats.o \
|
||||||
spa_log_spacemap.o \
|
spa_log_spacemap.o \
|
||||||
spa_misc.o \
|
spa_misc.o \
|
||||||
spa_stats.o \
|
spa_stats.o \
|
||||||
|
|
|
@ -304,6 +304,7 @@ SRCS+= abd.c \
|
||||||
spa_config.c \
|
spa_config.c \
|
||||||
spa_errlog.c \
|
spa_errlog.c \
|
||||||
spa_history.c \
|
spa_history.c \
|
||||||
|
spa_json_stats.c \
|
||||||
spa_log_spacemap.c \
|
spa_log_spacemap.c \
|
||||||
spa_misc.c \
|
spa_misc.c \
|
||||||
spa_stats.c \
|
spa_stats.c \
|
||||||
|
|
|
@ -994,6 +994,369 @@ nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp)
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define JPRINTF(start, end, ...) \
|
||||||
|
do { \
|
||||||
|
if (start < end) { \
|
||||||
|
int ret = snprintf(start, end - start, __VA_ARGS__); \
|
||||||
|
if (ret < 0) \
|
||||||
|
return (ENOMEM); \
|
||||||
|
start += ret; \
|
||||||
|
} else \
|
||||||
|
return (ENOMEM); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static int
|
||||||
|
nvlist_json_string(const char *s, char **buf, size_t size)
|
||||||
|
{
|
||||||
|
char *p = *buf;
|
||||||
|
char *end = *buf + size;
|
||||||
|
static const char *hex = "0123456789ABCDEF";
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (s == NULL) {
|
||||||
|
JPRINTF(p, end, "null");
|
||||||
|
*buf = p;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JPRINTF(p, end, "\"");
|
||||||
|
while (*s) {
|
||||||
|
c = (int)*s++;
|
||||||
|
/* formfeed, newline, return, tab, backspace */
|
||||||
|
if (c == 12)
|
||||||
|
JPRINTF(p, end, "\\f");
|
||||||
|
else if (c == 10)
|
||||||
|
JPRINTF(p, end, "\\n");
|
||||||
|
else if (c == 13)
|
||||||
|
JPRINTF(p, end, "\\r");
|
||||||
|
else if (c == 9)
|
||||||
|
JPRINTF(p, end, "\\t");
|
||||||
|
else if (c == 8)
|
||||||
|
JPRINTF(p, end, "\\b");
|
||||||
|
/*
|
||||||
|
* all characters from 0x00 to 0x1f, and 0x7f are
|
||||||
|
* escaped as: \u00xx
|
||||||
|
*/
|
||||||
|
else if (((0 <= c) && (c <= 0x1f)) || (c == 0x7f)) {
|
||||||
|
JPRINTF(p, end, "\\u00%c%c",
|
||||||
|
hex[(c >> 4) & 0x0f], hex[c & 0x0f]);
|
||||||
|
} else if (c == '"')
|
||||||
|
JPRINTF(p, end, "\\\"");
|
||||||
|
else if (c == '\\')
|
||||||
|
JPRINTF(p, end, "\\\\");
|
||||||
|
else if (c == '/')
|
||||||
|
JPRINTF(p, end, "\\/");
|
||||||
|
/*
|
||||||
|
* all other printable characters ' ' to '~', and
|
||||||
|
* any utf-8 sequences (high bit set):
|
||||||
|
* 1xxxxxxx 10xxxxxx ...
|
||||||
|
* is a utf-8 sequence (10xxxxxx may occur 1 to 3 times).
|
||||||
|
* Note that this is simply distinguished here as high
|
||||||
|
* bit set.
|
||||||
|
*/
|
||||||
|
else
|
||||||
|
JPRINTF(p, end, "%c", c);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "\"");
|
||||||
|
*buf = p;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nvlist_to_json(nvlist_t *nvl, char **buf, size_t size)
|
||||||
|
{
|
||||||
|
boolean_t first = B_TRUE;
|
||||||
|
char *p = *buf;
|
||||||
|
char *end = *buf + size;
|
||||||
|
nvpair_t *curr = nvlist_next_nvpair(nvl, NULL);
|
||||||
|
|
||||||
|
JPRINTF(p, end, "{");
|
||||||
|
|
||||||
|
while (curr) {
|
||||||
|
if (!first)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
else
|
||||||
|
first = B_FALSE;
|
||||||
|
|
||||||
|
if (nvlist_json_string(nvpair_name(curr), &p, end - p) != 0)
|
||||||
|
return (ENOMEM);
|
||||||
|
JPRINTF(p, end, ":");
|
||||||
|
|
||||||
|
switch (nvpair_type(curr)) {
|
||||||
|
case DATA_TYPE_STRING: {
|
||||||
|
if (nvlist_json_string(fnvpair_value_string(curr), &p,
|
||||||
|
end - p) != 0)
|
||||||
|
return (ENOMEM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_BOOLEAN: {
|
||||||
|
JPRINTF(p, end, "true");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_BOOLEAN_VALUE: {
|
||||||
|
JPRINTF(p, end, "%s",
|
||||||
|
fnvpair_value_boolean_value(curr) == B_TRUE ?
|
||||||
|
"true" : "false");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_BYTE: {
|
||||||
|
JPRINTF(p, end, "%hhu", fnvpair_value_byte(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT8: {
|
||||||
|
JPRINTF(p, end, "%hhd", fnvpair_value_int8(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT8: {
|
||||||
|
JPRINTF(p, end, "%hhu", fnvpair_value_uint8(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT16: {
|
||||||
|
JPRINTF(p, end, "%hd", fnvpair_value_int16(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT16: {
|
||||||
|
JPRINTF(p, end, "%hu", fnvpair_value_uint16(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT32: {
|
||||||
|
JPRINTF(p, end, "%d", fnvpair_value_int32(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT32: {
|
||||||
|
JPRINTF(p, end, "%u", fnvpair_value_uint32(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT64: {
|
||||||
|
JPRINTF(p, end, "%lld",
|
||||||
|
(long long)fnvpair_value_int64(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT64: {
|
||||||
|
JPRINTF(p, end, "%llu",
|
||||||
|
(unsigned long long)fnvpair_value_uint64(curr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_HRTIME: {
|
||||||
|
hrtime_t val;
|
||||||
|
VERIFY0(nvpair_value_hrtime(curr, &val));
|
||||||
|
JPRINTF(p, end, "%llu", (unsigned long long)val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(_KERNEL)
|
||||||
|
case DATA_TYPE_DOUBLE: {
|
||||||
|
double val;
|
||||||
|
VERIFY0(nvpair_value_double(curr, &val));
|
||||||
|
JPRINTF(p, end, "%f", val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case DATA_TYPE_NVLIST: {
|
||||||
|
if (nvlist_to_json(fnvpair_value_nvlist(curr), &p,
|
||||||
|
end - p) != 0)
|
||||||
|
return (ENOMEM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_STRING_ARRAY: {
|
||||||
|
const char **val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
if (nvlist_json_string(val[i], &p,
|
||||||
|
end - p) != 0)
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_NVLIST_ARRAY: {
|
||||||
|
nvlist_t **val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
if (nvlist_to_json(val[i], &p, end - p) != 0)
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_BOOLEAN_ARRAY: {
|
||||||
|
boolean_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, val[i] == B_TRUE ?
|
||||||
|
"true" : "false");
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_BYTE_ARRAY: {
|
||||||
|
uchar_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%hhu", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT8_ARRAY: {
|
||||||
|
uint8_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%hhu", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT8_ARRAY: {
|
||||||
|
int8_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%hhd", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT16_ARRAY: {
|
||||||
|
uint16_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%hu", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT16_ARRAY: {
|
||||||
|
int16_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "%hd", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT32_ARRAY: {
|
||||||
|
uint32_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%u", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT32_ARRAY: {
|
||||||
|
int32_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%d", val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT64_ARRAY: {
|
||||||
|
uint64_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%llu",
|
||||||
|
(unsigned long long)val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_INT64_ARRAY: {
|
||||||
|
int64_t *val;
|
||||||
|
uint_t valsz, i;
|
||||||
|
VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
|
||||||
|
JPRINTF(p, end, "[");
|
||||||
|
for (i = 0; i < valsz; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
JPRINTF(p, end, ",");
|
||||||
|
JPRINTF(p, end, "%lld", (long long)val[i]);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DATA_TYPE_UNKNOWN:
|
||||||
|
case DATA_TYPE_DONTCARE:
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
curr = nvlist_next_nvpair(nvl, curr);
|
||||||
|
}
|
||||||
|
JPRINTF(p, end, "}");
|
||||||
|
*buf = p;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function calculates the size of an nvpair value.
|
* This function calculates the size of an nvpair value.
|
||||||
*
|
*
|
||||||
|
|
|
@ -654,7 +654,7 @@ __kstat_install(kstat_t *ksp)
|
||||||
ASSERT(ksp);
|
ASSERT(ksp);
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
/* Specify permission modes for different kstats */
|
/* Specify permission modes for different kstats */
|
||||||
if (strncmp(ksp->ks_proc.kpe_name, "dbufs", KSTAT_STRLEN) == 0) {
|
if (ksp->ks_flags & KSTAT_FLAG_RESTRICTED) {
|
||||||
mode = 0600;
|
mode = 0600;
|
||||||
} else {
|
} else {
|
||||||
mode = 0644;
|
mode = 0644;
|
||||||
|
|
|
@ -189,7 +189,7 @@ dbuf_stats_hash_table_init(dbuf_hash_table_t *hash)
|
||||||
dsh->hash = hash;
|
dsh->hash = hash;
|
||||||
|
|
||||||
ksp = kstat_create("zfs", 0, "dbufs", "misc",
|
ksp = kstat_create("zfs", 0, "dbufs", "misc",
|
||||||
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
|
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_RESTRICTED);
|
||||||
dsh->kstat = ksp;
|
dsh->kstat = ksp;
|
||||||
|
|
||||||
if (ksp) {
|
if (ksp) {
|
||||||
|
|
|
@ -5942,7 +5942,7 @@ spa_inject_delref(spa_t *spa)
|
||||||
/*
|
/*
|
||||||
* Add spares device information to the nvlist.
|
* Add spares device information to the nvlist.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
spa_add_spares(spa_t *spa, nvlist_t *config)
|
spa_add_spares(spa_t *spa, nvlist_t *config)
|
||||||
{
|
{
|
||||||
nvlist_t **spares;
|
nvlist_t **spares;
|
||||||
|
@ -5992,7 +5992,7 @@ spa_add_spares(spa_t *spa, nvlist_t *config)
|
||||||
/*
|
/*
|
||||||
* Add l2cache device information to the nvlist, including vdev stats.
|
* Add l2cache device information to the nvlist, including vdev stats.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
spa_add_l2cache(spa_t *spa, nvlist_t *config)
|
spa_add_l2cache(spa_t *spa, nvlist_t *config)
|
||||||
{
|
{
|
||||||
nvlist_t **l2cache;
|
nvlist_t **l2cache;
|
||||||
|
@ -6104,7 +6104,7 @@ spa_feature_stats_from_cache(spa_t *spa, nvlist_t *features)
|
||||||
* ensures we don't block here on I/O on a suspended pool so 'zpool
|
* ensures we don't block here on I/O on a suspended pool so 'zpool
|
||||||
* clear' can resume the pool.
|
* clear' can resume the pool.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
spa_add_feature_stats(spa_t *spa, nvlist_t *config)
|
spa_add_feature_stats(spa_t *spa, nvlist_t *config)
|
||||||
{
|
{
|
||||||
nvlist_t *features;
|
nvlist_t *features;
|
||||||
|
|
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* CDDL HEADER START
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the terms of the
|
||||||
|
* Common Development and Distribution License (the "License").
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||||
|
* or http://opensource.org/licenses/CDDL-1.0.
|
||||||
|
* See the License for the specific language governing permissions
|
||||||
|
* and limitations under the License.
|
||||||
|
*
|
||||||
|
* When distributing Covered Code, include this CDDL HEADER in each
|
||||||
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||||
|
* If applicable, add the following below this CDDL HEADER, with the
|
||||||
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||||
|
*
|
||||||
|
* CDDL HEADER END
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Klara Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/zfs_context.h>
|
||||||
|
#include <sys/spa_impl.h>
|
||||||
|
#include <sys/vdev_impl.h>
|
||||||
|
#include <sys/spa.h>
|
||||||
|
#include <zfs_comutil.h>
|
||||||
|
#include <sys/spa_json_stats.h>
|
||||||
|
#include <sys/nvpair_impl.h>
|
||||||
|
|
||||||
|
#define JSON_STATUS_VERSION 4
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
vdev_state_string(vdev_state_t state, vdev_aux_t aux)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
switch (state) {
|
||||||
|
case VDEV_STATE_UNKNOWN: s = "HEALTHY"; break;
|
||||||
|
case VDEV_STATE_CLOSED: s = "CLOSED"; break;
|
||||||
|
case VDEV_STATE_OFFLINE: s = "OFFLINE"; break;
|
||||||
|
case VDEV_STATE_REMOVED: s = "REMOVED"; break;
|
||||||
|
case VDEV_STATE_CANT_OPEN:
|
||||||
|
if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG)
|
||||||
|
s = "FAULTED";
|
||||||
|
else if (aux == VDEV_AUX_SPLIT_POOL)
|
||||||
|
s = "SPLIT";
|
||||||
|
else
|
||||||
|
s = "UNAVAIL";
|
||||||
|
break;
|
||||||
|
case VDEV_STATE_FAULTED: s = "FAULTED"; break;
|
||||||
|
case VDEV_STATE_DEGRADED: s = "DEGRADED"; break;
|
||||||
|
case VDEV_STATE_HEALTHY: s = "HEALTHY"; break;
|
||||||
|
default: s = "?";
|
||||||
|
}
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vdev_to_nvlist(vdev_t *vd, pool_scan_stat_t *ps, nvlist_t *tree)
|
||||||
|
{
|
||||||
|
uint64_t n;
|
||||||
|
int nparity = vdev_get_nparity(vd);
|
||||||
|
vdev_t **a;
|
||||||
|
const char *s;
|
||||||
|
nvlist_t *init_state, *trim_state;
|
||||||
|
|
||||||
|
nvlist_add_string(tree, "type", vd->vdev_ops->vdev_op_type);
|
||||||
|
|
||||||
|
/* dRAID vdevs have additional config keys */
|
||||||
|
if (vd->vdev_ops == &vdev_draid_ops &&
|
||||||
|
vd->vdev_ops->vdev_op_config_generate != NULL) {
|
||||||
|
nvlist_t *nvl = fnvlist_alloc();
|
||||||
|
vd->vdev_ops->vdev_op_config_generate(vd, nvl);
|
||||||
|
fnvlist_merge(tree, nvl);
|
||||||
|
nvlist_free(nvl);
|
||||||
|
} else if (nparity > 0) {
|
||||||
|
/* RAIDZ parity */
|
||||||
|
fnvlist_add_uint64(tree, "nparity", nparity);
|
||||||
|
}
|
||||||
|
|
||||||
|
fnvlist_add_uint64(tree, "id", vd->vdev_id);
|
||||||
|
fnvlist_add_uint64(tree, "guid", vd->vdev_guid);
|
||||||
|
if (strcmp(vd->vdev_ops->vdev_op_type, "root") != 0) {
|
||||||
|
fnvlist_add_uint64(tree, "asize", vd->vdev_asize);
|
||||||
|
fnvlist_add_uint64(tree, "ashift", vd->vdev_ashift);
|
||||||
|
if (vd->vdev_ops->vdev_op_leaf) {
|
||||||
|
fnvlist_add_boolean_value(tree, "whole_disk",
|
||||||
|
(vd->vdev_wholedisk == 0) ? B_FALSE : B_TRUE);
|
||||||
|
}
|
||||||
|
fnvlist_add_boolean_value(tree, "offline",
|
||||||
|
(vd->vdev_offline == 0) ? B_FALSE : B_TRUE);
|
||||||
|
fnvlist_add_boolean_value(tree, "faulted",
|
||||||
|
(vd->vdev_faulted == 0) ? B_FALSE : B_TRUE);
|
||||||
|
fnvlist_add_boolean_value(tree, "degraded",
|
||||||
|
(vd->vdev_degraded == 0) ? B_FALSE : B_TRUE);
|
||||||
|
fnvlist_add_boolean_value(tree, "removed",
|
||||||
|
(vd->vdev_removed == 0) ? B_FALSE : B_TRUE);
|
||||||
|
fnvlist_add_boolean_value(tree, "not_present",
|
||||||
|
(vd->vdev_not_present == 0) ? B_FALSE : B_TRUE);
|
||||||
|
fnvlist_add_boolean_value(tree, "is_log",
|
||||||
|
(vd->vdev_islog == 0) ? B_FALSE : B_TRUE);
|
||||||
|
|
||||||
|
if (vd->vdev_path != NULL)
|
||||||
|
fnvlist_add_string(tree, "path", vd->vdev_path);
|
||||||
|
if (vd->vdev_devid != NULL)
|
||||||
|
fnvlist_add_string(tree, "devid", vd->vdev_devid);
|
||||||
|
if (vd->vdev_physpath != NULL)
|
||||||
|
fnvlist_add_string(tree, "physpath", vd->vdev_physpath);
|
||||||
|
if (vd->vdev_enc_sysfs_path != NULL) {
|
||||||
|
fnvlist_add_string(tree, "enc_sysfs_path",
|
||||||
|
vd->vdev_enc_sysfs_path);
|
||||||
|
}
|
||||||
|
fnvlist_add_string(tree, "state",
|
||||||
|
vdev_state_string(vd->vdev_state, vd->vdev_stat.vs_aux));
|
||||||
|
/*
|
||||||
|
* Try for some of the extended status annotations that
|
||||||
|
* zpool status provides.
|
||||||
|
*/
|
||||||
|
fnvlist_add_boolean_value(tree, "vs_scan_removing",
|
||||||
|
vd->vdev_stat.vs_scan_removing != 0);
|
||||||
|
fnvlist_add_boolean_value(tree, "vs_noalloc",
|
||||||
|
vd->vdev_stat.vs_noalloc != 0);
|
||||||
|
fnvlist_add_boolean_value(tree, "vs_resilver_deferred",
|
||||||
|
vd->vdev_stat.vs_resilver_deferred);
|
||||||
|
s = "none";
|
||||||
|
if ((vd->vdev_state == VDEV_STATE_UNKNOWN) ||
|
||||||
|
(vd->vdev_state == VDEV_STATE_HEALTHY)) {
|
||||||
|
if (vd->vdev_stat.vs_scan_processed != 0) {
|
||||||
|
if (ps &&
|
||||||
|
(ps->pss_state == DSS_SCANNING)) {
|
||||||
|
s = (ps->pss_func ==
|
||||||
|
POOL_SCAN_RESILVER) ?
|
||||||
|
"resilvering" : "repairing";
|
||||||
|
} else if (ps &&
|
||||||
|
vd->vdev_stat.vs_resilver_deferred) {
|
||||||
|
s = "awaiting resilver";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fnvlist_add_string(tree, "resilver_repair", s);
|
||||||
|
|
||||||
|
init_state = fnvlist_alloc();
|
||||||
|
s = "VDEV_INITIALIZE_NONE";
|
||||||
|
if (vd->vdev_stat.vs_initialize_state == VDEV_INITIALIZE_ACTIVE)
|
||||||
|
s = "VDEV_INITIALIZE_ACTIVE";
|
||||||
|
if (vd->vdev_stat.vs_initialize_state ==
|
||||||
|
VDEV_INITIALIZE_SUSPENDED)
|
||||||
|
s = "VDEV_INITIALIZE_SUSPENDED";
|
||||||
|
if (vd->vdev_stat.vs_initialize_state ==
|
||||||
|
VDEV_INITIALIZE_COMPLETE)
|
||||||
|
s = "VDEV_INITIALIZE_COMPLETE";
|
||||||
|
fnvlist_add_string(init_state, "vs_initialize_state", s);
|
||||||
|
fnvlist_add_uint64(init_state, "vs_initialize_bytes_done:",
|
||||||
|
vd->vdev_stat.vs_initialize_bytes_done);
|
||||||
|
fnvlist_add_uint64(init_state, "vs_initialize_bytes_est",
|
||||||
|
vd->vdev_stat.vs_initialize_bytes_est);
|
||||||
|
fnvlist_add_uint64(init_state, "vs_initialize_action_time",
|
||||||
|
vd->vdev_stat.vs_initialize_action_time);
|
||||||
|
fnvlist_add_nvlist(tree, "initialize_state", init_state);
|
||||||
|
fnvlist_free(init_state);
|
||||||
|
|
||||||
|
trim_state = fnvlist_alloc();
|
||||||
|
s = "VDEV_UNTRIMMED";
|
||||||
|
if (vd->vdev_stat.vs_trim_state == VDEV_TRIM_ACTIVE)
|
||||||
|
s = "VDEV_TRIM_ACTIVE";
|
||||||
|
if (vd->vdev_stat.vs_trim_state == VDEV_TRIM_SUSPENDED)
|
||||||
|
s = "VDEV_TRIM_SUSPENDED";
|
||||||
|
if (vd->vdev_stat.vs_trim_state == VDEV_TRIM_COMPLETE)
|
||||||
|
s = "VDEV_TRIM_COMPLETE";
|
||||||
|
if (vd->vdev_stat.vs_trim_notsup)
|
||||||
|
s = "VDEV_TRIM_UNSUPPORTED";
|
||||||
|
fnvlist_add_string(trim_state, "vs_trim_state", s);
|
||||||
|
if (!vd->vdev_stat.vs_trim_notsup) {
|
||||||
|
fnvlist_add_uint64(trim_state, "vs_trim_action_time",
|
||||||
|
vd->vdev_stat.vs_trim_action_time);
|
||||||
|
fnvlist_add_uint64(trim_state, "vs_trim_bytes_done",
|
||||||
|
vd->vdev_stat.vs_trim_bytes_done);
|
||||||
|
fnvlist_add_uint64(trim_state, "vs_trim_bytes_est",
|
||||||
|
vd->vdev_stat.vs_trim_bytes_est);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist(tree, "trim_state", trim_state);
|
||||||
|
fnvlist_free(trim_state);
|
||||||
|
|
||||||
|
fnvlist_add_uint64(tree, "read_errors",
|
||||||
|
vd->vdev_stat.vs_read_errors);
|
||||||
|
fnvlist_add_uint64(tree, "write_errors",
|
||||||
|
vd->vdev_stat.vs_write_errors);
|
||||||
|
fnvlist_add_uint64(tree, "checksum_errors",
|
||||||
|
vd->vdev_stat.vs_checksum_errors);
|
||||||
|
fnvlist_add_uint64(tree, "slow_ios",
|
||||||
|
vd->vdev_stat.vs_slow_ios);
|
||||||
|
fnvlist_add_uint64(tree, "trim_errors",
|
||||||
|
vd->vdev_stat.vs_trim_errors);
|
||||||
|
}
|
||||||
|
n = vd->vdev_children;
|
||||||
|
a = vd->vdev_child;
|
||||||
|
if (n != 0) {
|
||||||
|
fnvlist_add_uint64(tree, "vdev_children", n);
|
||||||
|
nvlist_t **ch = kmem_alloc(sizeof (nvlist_t *) * n, KM_NOSLEEP);
|
||||||
|
for (uint64_t i = 0; i < n; ++i) {
|
||||||
|
ch[i] = fnvlist_alloc();
|
||||||
|
vdev_to_nvlist(a[i], ps, ch[i]);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist_array(tree, "children",
|
||||||
|
(const nvlist_t * const *) ch, n);
|
||||||
|
for (uint64_t i = 0; i < n; ++i)
|
||||||
|
fnvlist_free(ch[i]);
|
||||||
|
kmem_free(ch, sizeof (nvlist_t *) * n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
iterate_vdevs(spa_t *spa, pool_scan_stat_t *ps, nvlist_t *nvl)
|
||||||
|
{
|
||||||
|
nvlist_t *vt = fnvlist_alloc();
|
||||||
|
vdev_t *v = spa->spa_root_vdev;
|
||||||
|
if (v == NULL) {
|
||||||
|
zfs_dbgmsg("error: NO ROOT VDEV");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vdev_to_nvlist(v, ps, vt);
|
||||||
|
int nspares = spa->spa_spares.sav_count;
|
||||||
|
if (nspares != 0) {
|
||||||
|
nvlist_t **sp = kmem_alloc(sizeof (nvlist_t *) * nspares,
|
||||||
|
KM_NOSLEEP);
|
||||||
|
for (int i = 0; i < nspares; i++) {
|
||||||
|
v = spa->spa_spares.sav_vdevs[i];
|
||||||
|
sp[i] = fnvlist_alloc();
|
||||||
|
vdev_to_nvlist(v, ps, sp[i]);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist_array(vt, ZPOOL_CONFIG_SPARES,
|
||||||
|
(const nvlist_t * const *) sp, nspares);
|
||||||
|
for (int i = 0; i < nspares; i++)
|
||||||
|
fnvlist_free(sp[i]);
|
||||||
|
kmem_free(sp, sizeof (nvlist_t *) * nspares);
|
||||||
|
}
|
||||||
|
int nl2cache = spa->spa_l2cache.sav_count;
|
||||||
|
if (nl2cache != 0) {
|
||||||
|
nvlist_t **l2 = kmem_alloc(sizeof (nvlist_t *) * nl2cache,
|
||||||
|
KM_NOSLEEP);
|
||||||
|
for (int i = 0; i < nl2cache; i++) {
|
||||||
|
v = spa->spa_l2cache.sav_vdevs[i];
|
||||||
|
l2[i] = fnvlist_alloc();
|
||||||
|
vdev_to_nvlist(v, ps, l2[i]);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist_array(vt, ZPOOL_CONFIG_L2CACHE,
|
||||||
|
(const nvlist_t * const *) l2, nl2cache);
|
||||||
|
for (int i = 0; i < nspares; i++)
|
||||||
|
fnvlist_free(l2[i]);
|
||||||
|
kmem_free(l2, sizeof (nvlist_t *) * nl2cache);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist(nvl, "vdev_tree", vt);
|
||||||
|
fnvlist_free(vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
pss_func_to_string(uint64_t n)
|
||||||
|
{
|
||||||
|
const char *s = "?";
|
||||||
|
switch (n) {
|
||||||
|
case POOL_SCAN_NONE: s = "NONE"; break;
|
||||||
|
case POOL_SCAN_SCRUB: s = "SCRUB"; break;
|
||||||
|
case POOL_SCAN_RESILVER: s = "RESILVER"; break;
|
||||||
|
case POOL_SCAN_FUNCS: s = "?";
|
||||||
|
}
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *pss_state_to_string(uint64_t n)
|
||||||
|
{
|
||||||
|
const char *s = "?";
|
||||||
|
switch (n) {
|
||||||
|
case DSS_NONE: s = "NONE"; break;
|
||||||
|
case DSS_SCANNING: s = "SCANNING"; break;
|
||||||
|
case DSS_FINISHED: s = "FINISHED"; break;
|
||||||
|
case DSS_CANCELED: s = "CANCELED"; break;
|
||||||
|
case DSS_NUM_STATES: s = "?";
|
||||||
|
}
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_props_json(spa_t *spa, nvlist_t **nvl)
|
||||||
|
{
|
||||||
|
nvpair_t *curr = NULL, *item = NULL;
|
||||||
|
nvlist_t *prop;
|
||||||
|
data_type_t type;
|
||||||
|
char buf[256];
|
||||||
|
const char *name;
|
||||||
|
uint64_t src;
|
||||||
|
|
||||||
|
if (spa_prop_get(spa, nvl) != 0)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
for (curr = nvlist_next_nvpair(*nvl, NULL); curr;
|
||||||
|
curr = nvlist_next_nvpair(*nvl, curr)) {
|
||||||
|
if (nvpair_type(curr) == DATA_TYPE_NVLIST) {
|
||||||
|
prop = fnvpair_value_nvlist(curr);
|
||||||
|
for (item = nvlist_next_nvpair(prop, NULL); item;
|
||||||
|
item = nvlist_next_nvpair(prop, item)) {
|
||||||
|
name = nvpair_name(item);
|
||||||
|
type = nvpair_type(item);
|
||||||
|
if ((strcmp(name, "source") == 0) &&
|
||||||
|
(type == DATA_TYPE_UINT64)) {
|
||||||
|
src = fnvpair_value_uint64(item);
|
||||||
|
memset(buf, 0, 256);
|
||||||
|
if (src & ZPROP_SRC_NONE) {
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
strcat(buf, "|");
|
||||||
|
strcat(buf, "ZPROP_SRC_NONE");
|
||||||
|
}
|
||||||
|
if (src & ZPROP_SRC_DEFAULT) {
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
strcat(buf, "|");
|
||||||
|
strcat(buf,
|
||||||
|
"ZPROP_SRC_DEFAULT");
|
||||||
|
}
|
||||||
|
if (src & ZPROP_SRC_TEMPORARY) {
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
strcat(buf, "|");
|
||||||
|
strcat(buf,
|
||||||
|
"ZPROP_SRC_TEMPORARY");
|
||||||
|
}
|
||||||
|
if (src & ZPROP_SRC_INHERITED) {
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
strcat(buf, "|");
|
||||||
|
strcat(buf,
|
||||||
|
"ZPROP_SRC_INHERITED");
|
||||||
|
}
|
||||||
|
if (src & ZPROP_SRC_RECEIVED) {
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
strcat(buf, "|");
|
||||||
|
strcat(buf,
|
||||||
|
"ZPROP_SRC_RECEIVED");
|
||||||
|
}
|
||||||
|
fnvlist_add_string(prop, "source", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Collect the spa status without any locking and return as a JSON string.
|
||||||
|
*
|
||||||
|
* Currently used by the 'zfs/<pool>/stats.json' kstat.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
spa_generate_json_stats(spa_t *spa, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
int ps_error = 0;
|
||||||
|
char *curr = buf;
|
||||||
|
nvlist_t *spa_config, *spa_props = NULL, *scan_stats, *nvl;
|
||||||
|
uint64_t loadtimes[2];
|
||||||
|
pool_scan_stat_t ps;
|
||||||
|
int scl_config_lock;
|
||||||
|
|
||||||
|
nvl = fnvlist_alloc();
|
||||||
|
if (nvlist_dup(spa->spa_config, &spa_config, 0) != 0) {
|
||||||
|
zfs_dbgmsg("json_data: nvlist_dup failed");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist(spa_config, ZPOOL_CONFIG_LOAD_INFO,
|
||||||
|
spa->spa_load_info);
|
||||||
|
|
||||||
|
scl_config_lock =
|
||||||
|
spa_config_tryenter(spa, SCL_CONFIG, FTAG, RW_READER);
|
||||||
|
|
||||||
|
ps_error = spa_scan_get_stats(spa, &ps);
|
||||||
|
(void) ps_error;
|
||||||
|
|
||||||
|
if (spa_props_json(spa, &spa_props) == 0)
|
||||||
|
fnvlist_add_nvlist(spa_config, "spa_props", spa_props);
|
||||||
|
|
||||||
|
loadtimes[0] = spa->spa_loaded_ts.tv_sec;
|
||||||
|
loadtimes[1] = spa->spa_loaded_ts.tv_nsec;
|
||||||
|
fnvlist_add_uint64_array(spa_config, ZPOOL_CONFIG_LOADED_TIME,
|
||||||
|
loadtimes, 2);
|
||||||
|
fnvlist_add_uint64(spa_config, ZPOOL_CONFIG_ERRCOUNT,
|
||||||
|
spa_approx_errlog_size(spa));
|
||||||
|
fnvlist_add_boolean_value(spa_config, ZPOOL_CONFIG_SUSPENDED,
|
||||||
|
spa_suspended(spa));
|
||||||
|
if (spa_suspended(spa)) {
|
||||||
|
const char *failmode;
|
||||||
|
switch (spa->spa_failmode) {
|
||||||
|
case ZIO_FAILURE_MODE_WAIT:
|
||||||
|
failmode = "wait";
|
||||||
|
break;
|
||||||
|
case ZIO_FAILURE_MODE_CONTINUE:
|
||||||
|
failmode = "continue";
|
||||||
|
break;
|
||||||
|
case ZIO_FAILURE_MODE_PANIC:
|
||||||
|
failmode = "panic";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
failmode = "???";
|
||||||
|
}
|
||||||
|
fnvlist_add_string(spa_config, "failmode", failmode);
|
||||||
|
if (spa->spa_suspended != ZIO_SUSPEND_NONE) {
|
||||||
|
fnvlist_add_string(spa_config,
|
||||||
|
ZPOOL_CONFIG_SUSPENDED_REASON,
|
||||||
|
(spa->spa_suspended == ZIO_SUSPEND_MMP) ?
|
||||||
|
"MMP" : "IO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fnvlist_add_uint32(nvl, "status_json_version", JSON_STATUS_VERSION);
|
||||||
|
fnvlist_add_boolean_value(nvl, "scl_config_lock", scl_config_lock != 0);
|
||||||
|
fnvlist_add_uint32(nvl, "scan_error", ps_error);
|
||||||
|
|
||||||
|
scan_stats = fnvlist_alloc();
|
||||||
|
if (ps_error == 0) {
|
||||||
|
fnvlist_add_string(scan_stats, "func",
|
||||||
|
pss_func_to_string(ps.pss_func));
|
||||||
|
fnvlist_add_string(scan_stats, "state",
|
||||||
|
pss_state_to_string(ps.pss_state));
|
||||||
|
fnvlist_add_uint64(scan_stats, "start_time", ps.pss_start_time);
|
||||||
|
fnvlist_add_uint64(scan_stats, "end_time", ps.pss_end_time);
|
||||||
|
fnvlist_add_uint64(scan_stats, "to_examine", ps.pss_to_examine);
|
||||||
|
fnvlist_add_uint64(scan_stats, "examined", ps.pss_examined);
|
||||||
|
fnvlist_add_uint64(scan_stats, "processed", ps.pss_processed);
|
||||||
|
fnvlist_add_uint64(scan_stats, "errors", ps.pss_errors);
|
||||||
|
fnvlist_add_uint64(scan_stats, "pass_exam", ps.pss_pass_exam);
|
||||||
|
fnvlist_add_uint64(scan_stats, "pass_start", ps.pss_pass_start);
|
||||||
|
fnvlist_add_uint64(scan_stats, "pass_scrub_pause",
|
||||||
|
ps.pss_pass_scrub_pause);
|
||||||
|
fnvlist_add_uint64(scan_stats, "pass_scrub_spent_paused",
|
||||||
|
ps.pss_pass_scrub_spent_paused);
|
||||||
|
fnvlist_add_uint64(scan_stats, "pass_issued",
|
||||||
|
ps.pss_pass_issued);
|
||||||
|
fnvlist_add_uint64(scan_stats, "issued", ps.pss_issued);
|
||||||
|
} else if (ps_error == ENOENT) {
|
||||||
|
fnvlist_add_string(scan_stats, "func", "NONE");
|
||||||
|
fnvlist_add_string(scan_stats, "state", "NONE");
|
||||||
|
} else {
|
||||||
|
fnvlist_add_string(scan_stats, "func", "NONE");
|
||||||
|
fnvlist_add_string(scan_stats, "state", "NONE");
|
||||||
|
}
|
||||||
|
fnvlist_add_nvlist(nvl, "scan_stats", scan_stats);
|
||||||
|
fnvlist_add_string(nvl, "state", spa_state_to_name(spa));
|
||||||
|
|
||||||
|
fnvlist_remove(spa_config, "state");
|
||||||
|
spa_add_spares(spa, spa_config);
|
||||||
|
spa_add_l2cache(spa, spa_config);
|
||||||
|
spa_add_feature_stats(spa, spa_config);
|
||||||
|
|
||||||
|
/* add spa_config to output nvlist */
|
||||||
|
fnvlist_merge(nvl, spa_config);
|
||||||
|
iterate_vdevs(spa, &ps, nvl);
|
||||||
|
|
||||||
|
if (scl_config_lock)
|
||||||
|
spa_config_exit(spa, SCL_CONFIG, FTAG);
|
||||||
|
|
||||||
|
error = nvlist_to_json(nvl, &curr, size);
|
||||||
|
nvlist_free(nvl);
|
||||||
|
nvlist_free(spa_config);
|
||||||
|
nvlist_free(spa_props);
|
||||||
|
nvlist_free(scan_stats);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
#include <sys/vdev_impl.h>
|
#include <sys/vdev_impl.h>
|
||||||
#include <sys/spa.h>
|
#include <sys/spa.h>
|
||||||
#include <zfs_comutil.h>
|
#include <zfs_comutil.h>
|
||||||
|
#include <sys/spa_json_stats.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keeps stats on last N reads per spa_t, disabled by default.
|
* Keeps stats on last N reads per spa_t, disabled by default.
|
||||||
|
@ -988,6 +989,52 @@ spa_iostats_destroy(spa_t *spa)
|
||||||
mutex_destroy(&shk->lock);
|
mutex_destroy(&shk->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
spa_json_addr(kstat_t *ksp, loff_t n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return (ksp->ks_private);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_json_data(char *buf, size_t size, void *data)
|
||||||
|
{
|
||||||
|
spa_t *spa = (spa_t *)data;
|
||||||
|
|
||||||
|
return (spa_generate_json_stats(spa, buf, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_json_stats_init(spa_t *spa)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
mutex_init(&spa->spa_json_stats.lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
name = kmem_asprintf("zfs/%s", spa_name(spa));
|
||||||
|
ksp = kstat_create(name, 0, "status.json", "misc", KSTAT_TYPE_RAW, 0,
|
||||||
|
KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_RESTRICTED | KSTAT_FLAG_NO_HEADERS);
|
||||||
|
spa->spa_json_stats.kstat = ksp;
|
||||||
|
if (ksp) {
|
||||||
|
ksp->ks_lock = &spa->spa_json_stats.lock;
|
||||||
|
ksp->ks_data = NULL;
|
||||||
|
ksp->ks_private = spa;
|
||||||
|
kstat_set_raw_ops(ksp, NULL, spa_json_data, spa_json_addr);
|
||||||
|
kstat_install(ksp);
|
||||||
|
}
|
||||||
|
|
||||||
|
kmem_strfree(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_json_stats_destroy(spa_t *spa)
|
||||||
|
{
|
||||||
|
if (spa->spa_json_stats.kstat)
|
||||||
|
kstat_delete(spa->spa_json_stats.kstat);
|
||||||
|
mutex_destroy(&spa->spa_json_stats.lock);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
spa_stats_init(spa_t *spa)
|
spa_stats_init(spa_t *spa)
|
||||||
{
|
{
|
||||||
|
@ -998,11 +1045,13 @@ spa_stats_init(spa_t *spa)
|
||||||
spa_state_init(spa);
|
spa_state_init(spa);
|
||||||
spa_guid_init(spa);
|
spa_guid_init(spa);
|
||||||
spa_iostats_init(spa);
|
spa_iostats_init(spa);
|
||||||
|
spa_json_stats_init(spa);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
spa_stats_destroy(spa_t *spa)
|
spa_stats_destroy(spa_t *spa)
|
||||||
{
|
{
|
||||||
|
spa_json_stats_destroy(spa);
|
||||||
spa_iostats_destroy(spa);
|
spa_iostats_destroy(spa);
|
||||||
spa_health_destroy(spa);
|
spa_health_destroy(spa);
|
||||||
spa_tx_assign_destroy(spa);
|
spa_tx_assign_destroy(spa);
|
||||||
|
|
|
@ -559,7 +559,8 @@ tests = ['zpool_status_001_pos', 'zpool_status_002_pos',
|
||||||
'zpool_status_003_pos', 'zpool_status_004_pos',
|
'zpool_status_003_pos', 'zpool_status_004_pos',
|
||||||
'zpool_status_005_pos', 'zpool_status_006_pos',
|
'zpool_status_005_pos', 'zpool_status_006_pos',
|
||||||
'zpool_status_007_pos', 'zpool_status_008_pos',
|
'zpool_status_007_pos', 'zpool_status_008_pos',
|
||||||
'zpool_status_features_001_pos']
|
'zpool_status_features_001_pos',
|
||||||
|
'zpool_status_kstat_pos']
|
||||||
tags = ['functional', 'cli_root', 'zpool_status']
|
tags = ['functional', 'cli_root', 'zpool_status']
|
||||||
|
|
||||||
[tests/functional/cli_root/zpool_sync]
|
[tests/functional/cli_root/zpool_sync]
|
||||||
|
|
|
@ -1256,6 +1256,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||||
functional/cli_root/zpool_status/zpool_status_007_pos.ksh \
|
functional/cli_root/zpool_status/zpool_status_007_pos.ksh \
|
||||||
functional/cli_root/zpool_status/zpool_status_008_pos.ksh \
|
functional/cli_root/zpool_status/zpool_status_008_pos.ksh \
|
||||||
functional/cli_root/zpool_status/zpool_status_features_001_pos.ksh \
|
functional/cli_root/zpool_status/zpool_status_features_001_pos.ksh \
|
||||||
|
functional/cli_root/zpool_status/zpool_status_kstat_pos.ksh \
|
||||||
functional/cli_root/zpool_sync/cleanup.ksh \
|
functional/cli_root/zpool_sync/cleanup.ksh \
|
||||||
functional/cli_root/zpool_sync/setup.ksh \
|
functional/cli_root/zpool_sync/setup.ksh \
|
||||||
functional/cli_root/zpool_sync/zpool_sync_001_pos.ksh \
|
functional/cli_root/zpool_sync/zpool_sync_001_pos.ksh \
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#!/bin/ksh -p
|
||||||
|
|
||||||
|
#
|
||||||
|
# CDDL HEADER START
|
||||||
|
#
|
||||||
|
# This file and its contents are supplied under the terms of the
|
||||||
|
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
# You may only use this file in accordance with the terms of version
|
||||||
|
# 1.0 of the CDDL.
|
||||||
|
#
|
||||||
|
# A full copy of the text of the CDDL should have accompanied this
|
||||||
|
# source. A copy of the CDDL is also available via the Internet at
|
||||||
|
# http://www.illumos.org/license/CDDL.
|
||||||
|
#
|
||||||
|
# CDDL HEADER END
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Klara
|
||||||
|
#
|
||||||
|
|
||||||
|
. $STF_SUITE/include/libtest.shlib
|
||||||
|
|
||||||
|
#
|
||||||
|
# DESCRIPTION:
|
||||||
|
# Verify the status.json kstat
|
||||||
|
#
|
||||||
|
# STRATEGY:
|
||||||
|
# 1. Create zpool
|
||||||
|
# 2. Confirm the output of the kstat is valid json
|
||||||
|
# 3. Confirm that some expected keys are present
|
||||||
|
#
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
{
|
||||||
|
poolexists $TESTPOOL2 && destroy_pool $TESTPOOL2
|
||||||
|
log_must rm -f $all_vdevs
|
||||||
|
[[ -f $tmpfile ]] && rm -f $tmpfile
|
||||||
|
}
|
||||||
|
|
||||||
|
log_assert "Verify status.json kstat"
|
||||||
|
|
||||||
|
log_onexit cleanup
|
||||||
|
|
||||||
|
all_vdevs=$(echo $TESTDIR/vdev{1..6})
|
||||||
|
log_must mkdir -p $TESTDIR
|
||||||
|
log_must truncate -s $MINVDEVSIZE $all_vdevs
|
||||||
|
tmpfile=$TEST_BASE_DIR/tmpfile.$$
|
||||||
|
|
||||||
|
for raid_type in "draid2:3d:6c:1s" "raidz2"; do
|
||||||
|
|
||||||
|
log_must zpool create -f $TESTPOOL2 $raid_type $all_vdevs
|
||||||
|
|
||||||
|
# Verify that the JSON output is valid
|
||||||
|
log_must eval "kstat ${TESTPOOL2}/status.json | python3 -m json.tool > $tmpfile"
|
||||||
|
|
||||||
|
# Verify that some of the expected keys are present
|
||||||
|
log_must eval "grep '\"vdev_children\": 6' $tmpfile"
|
||||||
|
log_must eval "grep '\"nparity\": 2' $tmpfile"
|
||||||
|
log_must eval "grep '\"state\": \"ONLINE\"' $tmpfile"
|
||||||
|
log_must eval "grep '\"name\": \"$TESTPOOL2\"' $tmpfile"
|
||||||
|
|
||||||
|
zpool destroy $TESTPOOL2
|
||||||
|
done
|
||||||
|
|
||||||
|
log_pass "Verify status.json kstat"
|
Loading…
Reference in New Issue