Update to json output.
Missing proper state output for spare devices. Everything else should be ok.
This commit is contained in:
parent
802c258fc1
commit
f7574ccff3
|
@ -12,7 +12,14 @@
|
||||||
#include <sys/json_stats.h>
|
#include <sys/json_stats.h>
|
||||||
#include <sys/nvpair_impl.h>
|
#include <sys/nvpair_impl.h>
|
||||||
|
|
||||||
#define JSON_STATUS_VERSION 4
|
#define JSON_STATUS_VERSION_MAJOR 1
|
||||||
|
#define JSON_STATUS_VERSION_MINOR 4
|
||||||
|
|
||||||
|
#ifdef ZFS_DEBUG
|
||||||
|
#define ZFS_DEBUG_STR " (DEBUG mode)"
|
||||||
|
#else
|
||||||
|
#define ZFS_DEBUG_STR ""
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
json_stats_destroy(spa_t *spa)
|
json_stats_destroy(spa_t *spa)
|
||||||
|
@ -88,44 +95,37 @@ null_filter(jprint_t *jp __maybe_unused, const char *name __maybe_unused,
|
||||||
static void nvlist_to_json(nvlist_t *nvl, jprint_t *jp, nvj_filter_t f);
|
static void nvlist_to_json(nvlist_t *nvl, jprint_t *jp, nvj_filter_t f);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert source (src) to string -- up to 105 characters, so pass in 256
|
* Convert source (src) to string array.
|
||||||
* byte buffer (for future)
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
source_to_string(uint64_t src, char *buf)
|
source_to_string_array(jprint_t *jp, uint64_t src)
|
||||||
{
|
{
|
||||||
buf[0] = '\0';
|
jp_printf(jp, "source: [");
|
||||||
if (src & ZPROP_SRC_NONE) {
|
src &= ZPROP_SRC_ALL;
|
||||||
if (buf[0] != '\0')
|
/*
|
||||||
strcat(buf, "|");
|
* -- none is "-" if "sudo zpool get all", we translate to the
|
||||||
strcat(buf, "ZPROP_SRC_NONE");
|
* empty array.
|
||||||
}
|
*
|
||||||
if (src & ZPROP_SRC_DEFAULT) {
|
* if (src & ZPROP_SRC_NONE)
|
||||||
if (buf[0] != '\0')
|
* jp_printf(jp, "%s", "none");
|
||||||
strcat(buf, "|");
|
*/
|
||||||
strcat(buf, "ZPROP_SRC_DEFAULT");
|
if (src & ZPROP_SRC_DEFAULT)
|
||||||
}
|
jp_printf(jp, "%s", "default");
|
||||||
if (src & ZPROP_SRC_TEMPORARY) {
|
if (src & ZPROP_SRC_TEMPORARY)
|
||||||
if (buf[0] != '\0')
|
jp_printf(jp, "%s", "temporary");
|
||||||
strcat(buf, "|");
|
if (src & ZPROP_SRC_LOCAL)
|
||||||
strcat(buf, "ZPROP_SRC_TEMPORARY");
|
jp_printf(jp, "%s", "local");
|
||||||
}
|
if (src & ZPROP_SRC_INHERITED)
|
||||||
if (src & ZPROP_SRC_INHERITED) {
|
jp_printf(jp, "%s", "inherited");
|
||||||
if (buf[0] != '\0')
|
if (src & ZPROP_SRC_RECEIVED)
|
||||||
strcat(buf, "|");
|
jp_printf(jp, "%s", "received");
|
||||||
strcat(buf, "ZPROP_SRC_INHERITED");
|
jp_printf(jp, "]");
|
||||||
}
|
|
||||||
if (src & ZPROP_SRC_RECEIVED) {
|
|
||||||
if (buf[0] != '\0')
|
|
||||||
strcat(buf, "|");
|
|
||||||
strcat(buf, "ZPROP_SRC_RECEIVED");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spa_props_filter replace source: with string. The way source is
|
* spa_props_filter replace source: with string. The way source is
|
||||||
* defined it could be bitmap -- so generate the | sequence as
|
* defined it could be bitmap -- so generate a nested array of strings.
|
||||||
* needed.
|
* this is an approach to this kind of display.
|
||||||
*/
|
*/
|
||||||
static boolean_t
|
static boolean_t
|
||||||
spa_props_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
spa_props_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||||
|
@ -133,9 +133,7 @@ spa_props_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||||
if ((strcmp(name, "source") == 0) &&
|
if ((strcmp(name, "source") == 0) &&
|
||||||
(type == DATA_TYPE_UINT64)) {
|
(type == DATA_TYPE_UINT64)) {
|
||||||
uint64_t src = *(uint64_t *)value;
|
uint64_t src = *(uint64_t *)value;
|
||||||
char buf[256];
|
source_to_string_array(jp, src);
|
||||||
source_to_string(src, buf);
|
|
||||||
jp_printf(jp, "source: %s", buf);
|
|
||||||
return (B_TRUE);
|
return (B_TRUE);
|
||||||
}
|
}
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
|
@ -166,7 +164,7 @@ stats_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are going to suppress vdev_tree:, and generate the
|
* We are going to suppress vdev_tree:, and generate the
|
||||||
* data our rselves.
|
* data ourselves.
|
||||||
* It does seem like a bit of a waste going through this
|
* It does seem like a bit of a waste going through this
|
||||||
* twice... but for now, this seems prudent.
|
* twice... but for now, this seems prudent.
|
||||||
*/
|
*/
|
||||||
|
@ -192,7 +190,7 @@ stats_filter(jprint_t *jp, const char *name, data_type_t type, void *value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code is NOT hightly abstracted -- just some duplication, until I
|
* This code is NOT highly abstracted -- just some duplication, until I
|
||||||
* actually understand what is needed.
|
* actually understand what is needed.
|
||||||
*
|
*
|
||||||
* In avoiding early abstraction, we find the need for a "filter". Which
|
* In avoiding early abstraction, we find the need for a "filter". Which
|
||||||
|
@ -301,29 +299,56 @@ nvlist_to_json(nvlist_t *nvl, jprint_t *jp, nvj_filter_t f)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
vdev_state_string(uint64_t n)
|
vdev_state_string(vdev_t *v)
|
||||||
{
|
{
|
||||||
const char *s;
|
const char *s = "UNKNOWN";
|
||||||
switch (n) {
|
switch (v->vdev_state) {
|
||||||
case VDEV_STATE_UNKNOWN: s = "HEALTHY"; break;
|
case VDEV_STATE_CLOSED:
|
||||||
case VDEV_STATE_CLOSED: s = "CLOSED"; break;
|
case VDEV_STATE_OFFLINE:
|
||||||
case VDEV_STATE_OFFLINE: s = "OFFLINE"; break;
|
s = "OFFLINE";
|
||||||
case VDEV_STATE_REMOVED: s = "REMOVED"; break;
|
break;
|
||||||
case VDEV_STATE_CANT_OPEN: s = "CAN'T OPEN"; break;
|
case VDEV_STATE_REMOVED:
|
||||||
case VDEV_STATE_FAULTED: s = "FAULTED"; break;
|
s = "REMOVED";
|
||||||
case VDEV_STATE_DEGRADED: s = "DEGRADED"; break;
|
break;
|
||||||
case VDEV_STATE_HEALTHY: s = "HEALTHY"; break;
|
case VDEV_STATE_CANT_OPEN:
|
||||||
default: s = "?";
|
if (v->vdev_stat.vs_aux == VDEV_AUX_CORRUPT_DATA ||
|
||||||
|
v->vdev_stat.vs_aux == VDEV_AUX_BAD_LOG)
|
||||||
|
s = "FAULTED";
|
||||||
|
else if (v->vdev_stat.vs_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 = "ONLINE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = "UNKNOWN";
|
||||||
}
|
}
|
||||||
return (s);
|
return (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flags for vdev_to_json -- under no circumstance do we want to recurse
|
||||||
|
* for l2cache and spares. If spares, we want AVAIL/UNAVAIL as status
|
||||||
|
*/
|
||||||
|
#define NO_RECURSE 1
|
||||||
|
#define USE_AVAIL 2
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vdev_to_json(vdev_t *v, pool_scan_stat_t *ps, jprint_t *jp)
|
vdev_to_json(vdev_t *v, pool_scan_stat_t *ps, jprint_t *jp, uint_t flags)
|
||||||
{
|
{
|
||||||
uint64_t i, n;
|
uint64_t i, n;
|
||||||
vdev_t **a;
|
vdev_t **a;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
if (v == NULL)
|
||||||
|
return;
|
||||||
jp_printf(jp, "type: %s", v->vdev_ops->vdev_op_type);
|
jp_printf(jp, "type: %s", v->vdev_ops->vdev_op_type);
|
||||||
jp_printf(jp, "id: %U", v->vdev_id);
|
jp_printf(jp, "id: %U", v->vdev_id);
|
||||||
jp_printf(jp, "guid: %U", v->vdev_guid);
|
jp_printf(jp, "guid: %U", v->vdev_guid);
|
||||||
|
@ -353,7 +378,14 @@ vdev_to_json(vdev_t *v, pool_scan_stat_t *ps, jprint_t *jp)
|
||||||
if (v->vdev_enc_sysfs_path != NULL)
|
if (v->vdev_enc_sysfs_path != NULL)
|
||||||
jp_printf(jp, "enc_sysfs_path: %s",
|
jp_printf(jp, "enc_sysfs_path: %s",
|
||||||
v->vdev_enc_sysfs_path);
|
v->vdev_enc_sysfs_path);
|
||||||
jp_printf(jp, "state: %s", vdev_state_string(v->vdev_state));
|
s = vdev_state_string(v);
|
||||||
|
if (flags & USE_AVAIL) {
|
||||||
|
if (v->vdev_stat.vs_aux == VDEV_AUX_SPARED)
|
||||||
|
s = "INUSE";
|
||||||
|
else if (v->vdev_state == VDEV_STATE_HEALTHY)
|
||||||
|
s = "AVAIL";
|
||||||
|
}
|
||||||
|
jp_printf(jp, "state: %s", s);
|
||||||
/*
|
/*
|
||||||
* Try for some of the extended status annotations that
|
* Try for some of the extended status annotations that
|
||||||
* zpool status provides.
|
* zpool status provides.
|
||||||
|
@ -439,17 +471,19 @@ vdev_to_json(vdev_t *v, pool_scan_stat_t *ps, jprint_t *jp)
|
||||||
jp_printf(jp, "trim_errors: %U",
|
jp_printf(jp, "trim_errors: %U",
|
||||||
v->vdev_stat.vs_trim_errors);
|
v->vdev_stat.vs_trim_errors);
|
||||||
}
|
}
|
||||||
n = v->vdev_children;
|
if ((flags & NO_RECURSE) == 0) {
|
||||||
a = v->vdev_child;
|
n = v->vdev_children;
|
||||||
jp_printf(jp, "vdev_children: %U", n);
|
a = v->vdev_child;
|
||||||
if (n != 0) {
|
jp_printf(jp, "vdev_children: %U", n);
|
||||||
jp_printf(jp, "children: [");
|
if (n != 0) {
|
||||||
for (i = 0; i < n; ++i) {
|
jp_printf(jp, "children: [");
|
||||||
jp_printf(jp, "{");
|
for (i = 0; i < n; ++i) {
|
||||||
vdev_to_json(a[i], ps, jp);
|
jp_printf(jp, "{");
|
||||||
jp_printf(jp, "}");
|
vdev_to_json(a[i], ps, jp, flags);
|
||||||
|
jp_printf(jp, "}");
|
||||||
|
}
|
||||||
|
jp_printf(jp, "]");
|
||||||
}
|
}
|
||||||
jp_printf(jp, "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,12 +492,13 @@ iterate_vdevs(spa_t *spa, pool_scan_stat_t *ps, jprint_t *jp)
|
||||||
{
|
{
|
||||||
vdev_t *v = spa->spa_root_vdev;
|
vdev_t *v = spa->spa_root_vdev;
|
||||||
if (v == NULL) {
|
if (v == NULL) {
|
||||||
|
/*
|
||||||
|
* How do we NOT have a root vdev?
|
||||||
|
*/
|
||||||
jp_printf(jp, "error: %s", "NO ROOT VDEV");
|
jp_printf(jp, "error: %s", "NO ROOT VDEV");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jp_printf(jp, "vdev_tree: {");
|
vdev_to_json(v, ps, jp, 0);
|
||||||
vdev_to_json(v, ps, jp);
|
|
||||||
jp_printf(jp, "}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
|
@ -476,7 +511,7 @@ pss_func_to_string(uint64_t n)
|
||||||
case POOL_SCAN_RESILVER: s = "RESILVER"; break;
|
case POOL_SCAN_RESILVER: s = "RESILVER"; break;
|
||||||
case POOL_SCAN_FUNCS: s = "?";
|
case POOL_SCAN_FUNCS: s = "?";
|
||||||
}
|
}
|
||||||
return s;
|
return (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *pss_state_to_string(uint64_t n)
|
static const char *pss_state_to_string(uint64_t n)
|
||||||
|
@ -489,7 +524,7 @@ static const char *pss_state_to_string(uint64_t n)
|
||||||
case DSS_CANCELED: s = "CANCELED"; break;
|
case DSS_CANCELED: s = "CANCELED"; break;
|
||||||
case DSS_NUM_STATES: s = "?";
|
case DSS_NUM_STATES: s = "?";
|
||||||
}
|
}
|
||||||
return s;
|
return (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -506,7 +541,13 @@ json_data(char *buf, size_t size, void *data)
|
||||||
|
|
||||||
if (nvlist_dup(spa->spa_config, &nvl, 0) != 0) {
|
if (nvlist_dup(spa->spa_config, &nvl, 0) != 0) {
|
||||||
/*
|
/*
|
||||||
* FIXME, what to do here?!?
|
* This we DO NOT EXPECT. Should never happen, unless
|
||||||
|
* other issues. dump into error log and bail. Note that
|
||||||
|
* the json output isn't even open at this time! But...
|
||||||
|
* we don't use the f... format yet, because we don't
|
||||||
|
* want to just hang (that is used a bit below). Later,
|
||||||
|
* when we have the nvlist, we are confident enough to
|
||||||
|
* use f... functions.
|
||||||
*/
|
*/
|
||||||
zfs_dbgmsg("json_data: nvlist_dup failed");
|
zfs_dbgmsg("json_data: nvlist_dup failed");
|
||||||
return (0);
|
return (0);
|
||||||
|
@ -534,7 +575,13 @@ json_data(char *buf, size_t size, void *data)
|
||||||
jp_open(&jp, buf, size);
|
jp_open(&jp, buf, size);
|
||||||
jp_printf(&jp, "{");
|
jp_printf(&jp, "{");
|
||||||
|
|
||||||
jp_printf(&jp, "status_json_version: %d", JSON_STATUS_VERSION);
|
jp_printf(&jp, "status_json_version_major: %d",
|
||||||
|
JSON_STATUS_VERSION_MAJOR);
|
||||||
|
jp_printf(&jp, "status_json_version_minor: %d",
|
||||||
|
JSON_STATUS_VERSION_MINOR);
|
||||||
|
jp_printf(&jp, "zfs_module_version: %s",
|
||||||
|
"v" ZFS_META_VERSION "-" ZFS_META_RELEASE ZFS_DEBUG_STR);
|
||||||
|
|
||||||
jp_printf(&jp, "scl_config_lock: %b", scl_config_lock != 0);
|
jp_printf(&jp, "scl_config_lock: %b", scl_config_lock != 0);
|
||||||
jp_printf(&jp, "scan_error: %d", ps_error);
|
jp_printf(&jp, "scan_error: %d", ps_error);
|
||||||
jp_printf(&jp, "scan_stats: {");
|
jp_printf(&jp, "scan_stats: {");
|
||||||
|
@ -566,17 +613,43 @@ json_data(char *buf, size_t size, void *data)
|
||||||
|
|
||||||
jp_printf(&jp, "state: %s", spa_state_to_name(spa));
|
jp_printf(&jp, "state: %s", spa_state_to_name(spa));
|
||||||
|
|
||||||
spa_add_spares(spa, nvl);
|
if (scl_config_lock) {
|
||||||
spa_add_l2cache(spa, nvl);
|
spa_add_spares(spa, nvl);
|
||||||
spa_add_feature_stats(spa, nvl);
|
spa_add_l2cache(spa, nvl);
|
||||||
|
spa_add_feature_stats(spa, nvl);
|
||||||
|
}
|
||||||
|
|
||||||
/* iterate and transfer nvl to json */
|
/* iterate and transfer nvl to json */
|
||||||
nvlist_to_json(nvl, &jp, stats_filter);
|
nvlist_to_json(nvl, &jp, stats_filter);
|
||||||
|
|
||||||
|
jp_printf(&jp, "vdev_tree: {");
|
||||||
|
|
||||||
iterate_vdevs(spa, &ps, &jp);
|
iterate_vdevs(spa, &ps, &jp);
|
||||||
|
|
||||||
|
jp_printf(&jp, "spares_children: %d", spa->spa_spares.sav_count);
|
||||||
|
jp_printf(&jp, "spares: [");
|
||||||
|
for (int i = 0; i < spa->spa_spares.sav_count; ++i) {
|
||||||
|
jp_printf(&jp, "{");
|
||||||
|
vdev_to_json(spa->spa_spares.sav_vdevs[i], NULL, &jp,
|
||||||
|
NO_RECURSE | USE_AVAIL);
|
||||||
|
jp_printf(&jp, "}");
|
||||||
|
}
|
||||||
|
jp_printf(&jp, "]");
|
||||||
|
|
||||||
|
jp_printf(&jp, "cache_children: %d", spa->spa_l2cache.sav_count);
|
||||||
|
jp_printf(&jp, "cache: [");
|
||||||
|
for (int i = 0; i < spa->spa_l2cache.sav_count; ++i) {
|
||||||
|
jp_printf(&jp, "{");
|
||||||
|
vdev_to_json(spa->spa_l2cache.sav_vdevs[i], NULL, &jp,
|
||||||
|
NO_RECURSE);
|
||||||
|
jp_printf(&jp, "}");
|
||||||
|
}
|
||||||
|
jp_printf(&jp, "]");
|
||||||
|
|
||||||
|
jp_printf(&jp, "}"); /* Close up vdev_tree */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* close the root object
|
* Close the root object
|
||||||
*/
|
*/
|
||||||
jp_printf(&jp, "}");
|
jp_printf(&jp, "}");
|
||||||
|
|
||||||
|
@ -596,11 +669,18 @@ json_data(char *buf, size_t size, void *data)
|
||||||
* If this does happen, we simply put the string where
|
* If this does happen, we simply put the string where
|
||||||
* the json should go... this is expected to trigger
|
* the json should go... this is expected to trigger
|
||||||
* a json decode error, and report "upstream"
|
* a json decode error, and report "upstream"
|
||||||
|
*
|
||||||
|
* The only way we get this is if we produced malformed
|
||||||
|
* json via bad format string. Or, if we didn't nest
|
||||||
|
* correctly. Either way, it is a coding defect, and
|
||||||
|
* not an operational one. We force bad json, and dump
|
||||||
|
* to the error log.
|
||||||
*/
|
*/
|
||||||
snprintf(buf, size,
|
snprintf(buf, size,
|
||||||
"jprint error %s (%d) callno %d, size %ld\n",
|
"jprint error %s (%d) callno %d, size %ld\n",
|
||||||
jp_errorstring(error), error, jp_errorpos(&jp), size);
|
jp_errorstring(error), error, jp_errorpos(&jp), size);
|
||||||
error = 0;
|
error = 0;
|
||||||
|
zfs_dbgmsg("json_stats error: %s\n", buf);
|
||||||
}
|
}
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue