Update to json output.

Missing proper state output for spare devices. Everything else
should be ok.
This commit is contained in:
Fred Weigel 2023-06-22 18:03:50 +00:00 committed by Geoff Amey
parent 802c258fc1
commit f7574ccff3
1 changed files with 152 additions and 72 deletions

View File

@ -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);
} }