Skip to content

Commit

Permalink
Allow custom names in zpool status|iostat -c
Browse files Browse the repository at this point in the history
This patch updates the "zpool status|iostat -c" command to allow custom column
names from the output.  If the user's command outputs a line like:

	name=value

then "name" is used for the column name, and "value" is its value.  Multiple
columns can be specified via multiple lines.  Column names and values can
have spaces:

$ zpool iostat -vc 'echo "vdev path=$VDEV_UPATH"; echo size=$(lsblk -n --nodeps -o size $VDEV_UPATH)'
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write  vdev path  size
----------  -----  -----  -----  -----  -----  -----  ---------  ----
mypool       269K  1008M      0      0    130  1.12K
  mirror     269K  1008M      0      0    130  1.12K
    sdb         -      -      0      0    124    574   /dev/sdb    1G
    sdc         -      -      0      0      6    574   /dev/sdc    1G
----------  -----  -----  -----  -----  -----  -----  ---------  ----

After all the "name=value" lines are read (if any), zpool will
take the next line of output (if any) and print it without a column header.

This patch also disables the -c option with the latency and request size
histograms, since it produced awkward output and made the code harder to
maintain.
  • Loading branch information
tonyhutter committed Mar 1, 2017
1 parent 100790a commit 4dfda30
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 45 deletions.
118 changes: 107 additions & 11 deletions cmd/zpool/zpool_iter.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,83 @@ for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data)
return (for_each_vdev_cb(zhp, nvroot, func, data));
}

/*
* Process a line of command output
*
* When running 'zpool iostat|status -c' the lines of output can either be
* in the form of:
*
* column_name=value
*
* Or just:
*
* value
*
* Process the column_name (if any) and value.
*
* Returns 1 if line was processed, and there are more lines can still be
* processed.
*
* Returns 0 if this was the last line to process, or error.
*/
static int
vdev_process_cmd_output(vdev_cmd_data_t *data, char *line)
{
char *col = NULL;
char *val = line;
char *equals;

if (!line)
return (0);

equals = strchr(line, '=');

if (equals) {
/*
* We have a 'column=value' type line. Split it into the
* column and value strings by turning the '=' into a '\0'.
*/
*equals = '\0';
col = line;
val = equals + 1;
} else {
val = line;
}

if (val != NULL) {
data->lines = realloc(data->lines,
(data->lines_cnt + 1) * sizeof (*data->lines));

if (data->lines == NULL)
return (0);


data->lines[data->lines_cnt] = strdup(val);
data->lines_cnt++;
}

if (col != NULL) {
data->cols = realloc(data->cols,
(data->cols_cnt + 1) * sizeof (*data->cols));

data->cols_width = realloc(data->cols_width,
(data->cols_cnt + 1) * sizeof (*data->cols_width));

if (data->cols == NULL || data->cols_width == NULL)
return (0);

data->cols[data->cols_cnt] = strdup(col);
data->cols_width[data->cols_cnt] =
MAX(strlen(col), strlen(val));
data->cols_cnt++;
}

if (val != NULL && col == NULL)
return (0);

return (1);
}

/* Thread function run for each vdev */
static void
vdev_run_cmd_thread(void *cb_cmd_data)
Expand All @@ -330,6 +407,7 @@ vdev_run_cmd_thread(void *cb_cmd_data)
FILE *fp;
size_t len = 0;
char cmd[_POSIX_ARG_MAX];
char *line;

/* Set our VDEV_PATH and VDEV_UPATH env vars and run command */
if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=\"%s\" && "
Expand All @@ -345,16 +423,24 @@ vdev_run_cmd_thread(void *cb_cmd_data)
if (fp == NULL)
return;

data->line = NULL;
data->lines = data->cols = NULL;
data->cols_width = NULL;
data->lines_cnt = data->cols_cnt = 0;

line = NULL;
do {
/* Save the first line of output from the command */
if (getline(&line, &len, fp) != -1) {
/* Success. Remove \n from the end, if necessary. */
if ((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
} else {
/* We read all the lines, or errored out */
break;
}
} while (vdev_process_cmd_output(data, line));
free(line);

/* Save the first line of output from the command */
if (getline(&data->line, &len, fp) != -1) {
/* Success. Remove newline from the end, if necessary. */
if ((pos = strchr(data->line, '\n')) != NULL)
*pos = '\0';
} else {
data->line = NULL;
}
pclose(fp);
}

Expand Down Expand Up @@ -504,12 +590,22 @@ all_pools_for_each_vdev_run(int argc, char **argv, char *cmd,
void
free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl)
{
int i;
int i, j;
for (i = 0; i < vcdl->count; i++) {
free(vcdl->data[i].path);
free(vcdl->data[i].pool);
free(vcdl->data[i].upath);
free(vcdl->data[i].line);

for (j = 0; j < vcdl->data[i].lines_cnt; j++)
free(vcdl->data[i].lines[j]);

free(vcdl->data[i].lines);

for (j = 0; j < vcdl->data[i].cols_cnt; j++)
free(vcdl->data[i].cols[j]);

free(vcdl->data[i].cols);
free(vcdl->data[i].cols_width);
free(vcdl->data[i].vdev_enc_sysfs_path);
}
free(vcdl->data);
Expand Down
122 changes: 98 additions & 24 deletions cmd/zpool/zpool_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,9 @@ get_usage(zpool_help_t idx)
"[-R root] [-F [-n]]\n"
"\t <pool | id> [newpool]\n"));
case HELP_IOSTAT:
return (gettext("\tiostat [-c CMD] [-T d | u] [-ghHLpPvy] "
"[[-lq]|[-r|-w]]\n"
"\t [[pool ...]|[pool vdev ...]|[vdev ...]] "
"[interval [count]]\n"));
return (gettext("\tiostat [[[-c CMD] [-lq]]|[-rw]] [-T d | u]"
"[-ghHLpPvy]\n\t [[pool ...]|[pool vdev ...]|[vdev ...]]"
" [interval [count]]\n"));
case HELP_LABELCLEAR:
return (gettext("\tlabelclear [-f] <vdev>\n"));
case HELP_LIST:
Expand Down Expand Up @@ -1515,17 +1514,31 @@ typedef struct status_cbdata {
vdev_cmd_data_list_t *vcdl;
} status_cbdata_t;

/* Print output line for specific vdev in a specific pool */
/* Print command output lines for specific vdev in a specific pool */
static void
zpool_print_cmd(vdev_cmd_data_list_t *vcdl, const char *pool, char *path)
{
int i;
vdev_cmd_data_t *data;
int i, j;

for (i = 0; i < vcdl->count; i++) {
if ((strcmp(vcdl->data[i].path, path) == 0) &&
(strcmp(vcdl->data[i].pool, pool) == 0)) {
printf("%s", vcdl->data[i].line);
break;
if ((strcmp(vcdl->data[i].path, path) != 0) ||
(strcmp(vcdl->data[i].pool, pool) != 0)) {
continue;
}

data = &vcdl->data[i];
for (j = 0; j < data->lines_cnt; j++) {
if (j < data->cols_cnt) {
printf("%*s", data->cols_width[j],
data->lines[j]);
} else {
printf("%s", data->lines[j]);
}
if (j != data->lines_cnt - 1)
printf(" ");
}
break;
}
}

Expand Down Expand Up @@ -2772,9 +2785,52 @@ print_iostat_labels(iostat_cbdata_t *cb, unsigned int force_column_width,

}
}
printf("\n");
}


/*
* print_cmd_columns - Print custom column titles from -c
*
* If the user specified the "zpool status|iostat -c" then print their custom
* column titles in the header. For example, print_cmd_columns() would print
* the " col1 col2" part of this:
*
* $ zpool iostat -vc 'echo col1=val1; echo col2=val2'
* ...
* capacity operations bandwidth
* pool alloc free read write read write col1 col2
* ---------- ----- ----- ----- ----- ----- ----- ---- ----
* mypool 269K 1008M 0 0 107 946
* mirror 269K 1008M 0 0 107 946
* sdb - - 0 0 102 473 val1 val2
* sdc - - 0 0 5 473 val1 val2
* ---------- ----- ----- ----- ----- ----- ----- ---- ----
*/
void
print_cmd_columns(vdev_cmd_data_list_t *vcdl, int use_dashes)
{
int i, j;
vdev_cmd_data_t *data = &vcdl->data[0];

if (vcdl->count == 0 || data == NULL)
return;
/*
* Each vdev cmd should have the same column names unless the user did
* something weird with their cmd. Just take the column names from the
* first vdev and assume it works for all of them.
*/
for (i = 0; i < data->cols_cnt; i++) {
printf(" ");
if (use_dashes) {
for (j = 0; j < data->cols_width[i]; j++)
printf("-");
} else {
printf("%*s", data->cols_width[i], data->cols[i]);
}
}
}


/*
* Utility function to print out a line of dashes like:
*
Expand Down Expand Up @@ -2843,7 +2899,6 @@ print_iostat_dashes(iostat_cbdata_t *cb, unsigned int force_column_width,
"--------------------");
}
}
printf("\n");
}


Expand Down Expand Up @@ -2885,12 +2940,22 @@ print_iostat_header_impl(iostat_cbdata_t *cb, unsigned int force_column_width,


print_iostat_labels(cb, force_column_width, iostat_top_labels);
printf("\n");

printf("%-*s", namewidth, title);

print_iostat_labels(cb, force_column_width, iostat_bottom_labels);
if (cb->vcdl != NULL)
print_cmd_columns(cb->vcdl, 0);

printf("\n");

print_iostat_separator_impl(cb, force_column_width);

if (cb->vcdl != NULL)
print_cmd_columns(cb->vcdl, 1);

printf("\n");
}

static void
Expand Down Expand Up @@ -3424,11 +3489,8 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
char *path;
if (nvlist_lookup_string(newnv, ZPOOL_CONFIG_PATH,
&path) == 0) {
if (!(cb->cb_flags & IOS_ANYHISTO_M))
printf(" ");
printf(" ");
zpool_print_cmd(cb->vcdl, zpool_get_name(zhp), path);
if (cb->cb_flags & IOS_ANYHISTO_M)
printf("\n");
}
}

Expand Down Expand Up @@ -3575,6 +3637,10 @@ print_iostat(zpool_handle_t *zhp, void *data)
if ((ret != 0) && !(cb->cb_flags & IOS_ANYHISTO_M) &&
!cb->cb_scripted && cb->cb_verbose && !cb->cb_vdev_names_count) {
print_iostat_separator(cb);
if (cb->vcdl != NULL) {
print_cmd_columns(cb->vcdl, 1);
}
printf("\n");
}

return (ret);
Expand Down Expand Up @@ -3965,7 +4031,7 @@ fsleep(float sec)


/*
* zpool iostat [-c CMD] [-ghHLpPvy] [[-lq]|[-r|-w]] [-n name] [-T d|u]
* zpool iostat [[-c CMD] [-lq]|[-rw]] [-ghHLpPvy] [-n name] [-T d|u]
* [[ pool ...]|[pool vdev ...]|[vdev ...]]
* [interval [count]]
*
Expand Down Expand Up @@ -4158,10 +4224,10 @@ zpool_do_iostat(int argc, char **argv)
return (1);
}

if ((l_histo || rq_histo) && (queues || latency)) {
if ((l_histo || rq_histo) && (cmd != NULL || latency || queues)) {
pool_list_free(list);
(void) fprintf(stderr,
gettext("[-r|-w] isn't allowed with [-q|-l]\n"));
gettext("[-r|-w] isn't allowed with [-c|-l|-q]\n"));
usage(B_FALSE);
return (1);
}
Expand Down Expand Up @@ -4249,6 +4315,13 @@ zpool_do_iostat(int argc, char **argv)
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);

if (cmd != NULL && cb.cb_verbose &&
!(cb.cb_flags & IOS_ANYHISTO_M)) {
cb.vcdl = all_pools_for_each_vdev_run(argc,
argv, cmd, g_zfs, cb.cb_vdev_names,
cb.cb_vdev_names_count, cb.cb_name_flags);
}

/*
* If it's the first time and we're not skipping it,
* or either skip or verbose mode, print the header.
Expand All @@ -4267,10 +4340,6 @@ zpool_do_iostat(int argc, char **argv)
continue;
}

if (cmd != NULL && cb.cb_verbose)
cb.vcdl = all_pools_for_each_vdev_run(argc,
argv, cmd, g_zfs, cb.cb_vdev_names,
cb.cb_vdev_names_count, cb.cb_name_flags);

pool_list_iter(list, B_FALSE, print_iostat, &cb);

Expand Down Expand Up @@ -6012,9 +6081,14 @@ status_callback(zpool_handle_t *zhp, void *data)
cbp->cb_namewidth = 10;

(void) printf(gettext("config:\n\n"));
(void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"),
(void) printf(gettext("\t%-*s %-8s %5s %5s %5s"),
cbp->cb_namewidth, "NAME", "STATE", "READ", "WRITE",
"CKSUM");

if (cbp->vcdl != NULL)
print_cmd_columns(cbp->vcdl, 0);

printf("\n");
print_status_config(zhp, cbp, zpool_get_name(zhp), nvroot, 0,
B_FALSE);

Expand Down
9 changes: 8 additions & 1 deletion cmd/zpool/zpool_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ libzfs_handle_t *g_zfs;

typedef struct vdev_cmd_data
{
char *line; /* cmd output */
char **lines; /* Array of lines of output, minus the column name */
int lines_cnt; /* Number of lines in the array */

char **cols; /* Array of column names */
int cols_cnt; /* Number of column names */

int *cols_width; /* Array of column widths */

char *path; /* vdev path */
char *upath; /* vdev underlying path */
char *pool; /* Pool name */
Expand Down
Loading

0 comments on commit 4dfda30

Please sign in to comment.