From fe98c2902fd13dac93e5add60d1f10b2cef8ae37 Mon Sep 17 00:00:00 2001 From: Venky Shankar Date: Mon, 12 Aug 2013 22:09:45 +0530 Subject: glusterd/cli: Geo-Replication "status detail" cmd Provides detailed status info in the following format MASTER SLAVE NODE HEALTH UPTIME FILES SYNCD FILES PENDING BYTES PENDING DELETES PENDING ----------------------------------------------------------------------------------- This patch introdues "status detail" command to show crawl related information in CLI. These values are "pulled" from gsyncd when "status detail" is executed. Change-Id: I1fdaf7180eacce054a864d34971dc160bd7301e1 BUG: 990420 Signed-off-by: Venky Shankar Reviewed-on: http://review.gluster.org/5590 Tested-by: Gluster Build System Reviewed-by: Avra Sengupta Tested-by: Avra Sengupta Reviewed-by: Anand Avati --- cli/src/cli-cmd-parser.c | 19 +- cli/src/cli-cmd-volume.c | 2 +- cli/src/cli-rpc-ops.c | 471 +++++++++++++++++++++++++++++++++-------------- cli/src/cli.h | 14 ++ 4 files changed, 365 insertions(+), 141 deletions(-) (limited to 'cli') diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index 26ac6f38..547f1d90 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1763,7 +1763,7 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) unsigned cmdi = 0; char *opwords[] = { "create", "status", "start", "stop", "config", "force", "delete", - "push-pem", NULL }; + "push-pem", "detail", NULL }; char *w = NULL; GF_ASSERT (words); @@ -1776,7 +1776,7 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) /* new syntax: * * volume geo-replication $m $s create [push-pem] [force] - * volume geo-replication [$m [$s]] status + * volume geo-replication [$m [$s]] status [detail] * volume geo-replication [$m] $s config [[!]$opt [$val]] * volume geo-replication $m $s start|stop [force] * volume geo-replication $m $s delete @@ -1873,6 +1873,21 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) if (ret) goto out; + if (!strcmp ((char *)words[wordcount-1], "detail")) { + if (strcmp ((char *)words[wordcount-2], "status")) { + ret = -1; + goto out; + } + if (!slavei || !masteri) { + ret = -1; + goto out; + } + ret = dict_set_uint32 (dict, "status-detail", _gf_true); + if (ret) + goto out; + cmdi++; + } + if (type != GF_GSYNC_OPTION_TYPE_CONFIG && (cmdi < wordcount - 1 || glob)) goto out; diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index 8d508158..040f610a 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -1894,7 +1894,7 @@ struct cli_cmd volume_cmds[] = { #if (SYNCDAEMON_COMPILE) {"volume "GEOREP" [] [] {create [push-pem] [force]" - "|start [force]|stop [force]|config|status|delete} [options...]", + "|start [force]|stop [force]|config|status [detail]|delete} [options...]", cli_cmd_volume_gsync_set_cbk, "Geo-sync operations", cli_cmd_check_gsync_exists_cbk}, diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 25e2796a..74b7ac0c 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -3808,156 +3808,121 @@ gf_cli_gsync_config_command (dict_t *dict) return runner_run (&runner); } -static int -gf_cli_fetch_gsyncd_health_uptime (char *status, char **health, char **uptime) +int +gf_cli_fetch_gsyncd_status_values (char *status, + gf_cli_gsync_status_t *sts_val) { + int32_t ret = -1; char *tmp = NULL; char *save_ptr = NULL; - int32_t ret = -1; char *key = NULL; char *value = NULL; - if (!health || !uptime || !status) { - gf_log ("", GF_LOG_ERROR, "health or uptime or status is null"); + if (!status || !sts_val) { + gf_log ("", GF_LOG_ERROR, "status or sts_val is null"); goto out; } tmp = strtok_r (status, "\n", &save_ptr); if (tmp) - *health = gf_strdup (tmp); + sts_val->health = gf_strdup (tmp); while (tmp) { key = strtok_r (tmp, "=", &value); - if ((key) && (!strcmp(key, "Uptime"))) { - *uptime = gf_strdup (value); - break; - } - tmp = strtok_r (NULL, ";", &save_ptr); - } - if (*health) - ret = 0; + if ((key) && (!strcmp(key, "Uptime"))) + sts_val->uptime = gf_strdup (value); - if (!*uptime) - *uptime = gf_strdup ("N/A"); -out: - gf_log ("", GF_LOG_DEBUG, "Returning %d.", ret); - return ret; -} + if ((key) && (!strcmp(key, "FilesSyncd"))) + sts_val->files_syncd = gf_strdup (value); -int -gf_cli_gsync_out_status (dict_t *dict) -{ - char mst[PATH_MAX] = {0, }; - char slv[PATH_MAX] = {0, }; - char sts[PATH_MAX] = {0, }; - char nds[PATH_MAX] = {0, }; - char errmsg[1024] = ""; - char *sts_val = NULL; - char *master = NULL; - char *slave = NULL; - char **output_values = NULL; - char **dict_values = NULL; - char *hyphens = NULL; - char *title_values[] = {"NODE", "MASTER", "SLAVE", - "HEALTH", "UPTIME"}; - int gsync_count = 0; - int i = 0; - int j = 0; - int dict_val_count = 0; - int ret = 0; - int spacing[5] = {0, 0, 0, 0, 10}; - int total_spacing = 0; + if ((key) && (!strcmp(key, "FilesPending"))) + sts_val->files_pending = gf_strdup (value); - /* Checks if any session is active or not */ - ret = dict_get_int32 (dict, "gsync-count", &gsync_count); - if (ret) { - ret = dict_get_str (dict, "master", &master); + if ((key) && (!strcmp(key, "BytesPending"))) { + value = gf_uint64_2human_readable(atol(value)); + sts_val->bytes_pending = gf_strdup (value); + } - ret = dict_get_str (dict, "slave", &slave); + if ((key) && (!strcmp(key, "DeletesPending"))) + sts_val->deletes_pending = gf_strdup (value); - if (master) { - if (slave) - snprintf (errmsg, sizeof(errmsg), "No active " - "geo-replication sessions between %s" - " and %s", master, slave); - else - snprintf (errmsg, sizeof(errmsg), "No active " - "geo-replication sessions for %s", - master); - } else - snprintf (errmsg, sizeof(errmsg), "No active " - "geo-replication sessions"); + tmp = strtok_r (NULL, ";", &save_ptr); + } - gf_log ("cli", GF_LOG_INFO, "%s", errmsg); - cli_out ("%s", errmsg); + if (sts_val->health) ret = 0; - goto out; - } - /* (gsync_count = number of nodes reporting output * - 5 = number of fields) = total number of values to - be fetched from dict */ - dict_values = GF_CALLOC (gsync_count * 5, sizeof (char *), - gf_common_mt_char); - if (!dict_values) { - gf_log ("cli", GF_LOG_ERROR, "Out Of Memory"); - ret = -1; - goto out; - } + if (!sts_val->uptime) + sts_val->uptime = gf_strdup ("N/A"); - for (i = 1, j = 0; i <= gsync_count; i++) { - snprintf (nds, sizeof(nds), "node%d", i); - snprintf (mst, sizeof(mst), "master%d", i); - snprintf (slv, sizeof(slv), "slave%d", i); - snprintf (sts, sizeof(sts), "status%d", i); + if (!sts_val->files_syncd) + sts_val->files_syncd = gf_strdup ("N/A"); - /* Fetching the values from dict, and calculating - the max length for each field */ - ret = dict_get_str (dict, nds, &dict_values[j]); - if (ret) - goto out; - if (strlen (dict_values[j]) > spacing [0]) - spacing[0] = strlen (dict_values[j]); - j++; + if (!sts_val->files_pending) + sts_val->files_pending = gf_strdup ("N/A"); - ret = dict_get_str (dict, mst, &dict_values[j]); - if (ret) - goto out; - if (strlen (dict_values[j]) > spacing [1]) - spacing[1] = strlen (dict_values[j]); - j++; + if (!sts_val->bytes_pending) + sts_val->bytes_pending = gf_strdup ("N/A"); - ret = dict_get_str (dict, slv, &dict_values[j]); - if (ret) - goto out; - if (strlen (dict_values[j]) > spacing [2]) - spacing[2] = strlen (dict_values[j]); - j++; + if (!sts_val->deletes_pending) + sts_val->deletes_pending = gf_strdup ("N/A"); - ret = dict_get_str (dict, sts, &sts_val); - if (ret) - goto out; +out: + gf_log ("", GF_LOG_DEBUG, "Returning %d.", ret); + return ret; +} - /* Fetching health and uptime from sts_val */ - ret = gf_cli_fetch_gsyncd_health_uptime (sts_val, - &dict_values[j], - &dict_values[j+1]); - if (ret) - goto out; +char* +get_struct_variable (int mem_num, gf_cli_gsync_status_t *sts_val) +{ + switch (mem_num) { + case 0: return (sts_val->node); + case 1: return (sts_val->master); + case 2: return (sts_val->slave); + case 3: return (sts_val->health); + case 4: return (sts_val->uptime); + case 5: return (sts_val->files_syncd); + case 6: return (sts_val->files_pending); + case 7: return (sts_val->bytes_pending); + case 8: return (sts_val->deletes_pending); + default: + goto out; + } - if (strlen (dict_values[j]) > spacing [3]) - spacing[3] = strlen (dict_values[j]); - j++; +out: + return NULL; +} - if (strlen (dict_values[j]) > spacing [4]) - spacing[4] = strlen (dict_values[j]); - j++; - } +int +gf_cli_print_status (char **title_values, + gf_cli_gsync_status_t **sts_vals, + int *spacing, int gsync_count, + int number_of_fields, int is_detail) +{ + int indents = 0; + int i = 0; + int j = 0; + int ret = 0; + int total_spacing = 0; + char **output_values = NULL; + char *tmp = NULL; + char *hyphens = NULL; + char heading[PATH_MAX] = {0, }; + char indent_spaces[PATH_MAX] = {0, }; /* calculating spacing for hyphens */ - for (i = 0; i < 5; i++) { + for (i = 0; i < number_of_fields; i++) { + /* Suppressing master and slave output for status detail */ + if ((is_detail) && ((i == 1) || (i == 2))) { + total_spacing++; + continue; + } else if ((!is_detail) && (i > 4)) { + /* Suppressing detailed output for + * status */ + continue; + } spacing[i] += 3; /* Adding extra space to distinguish between fields */ total_spacing += spacing[i]; @@ -3965,73 +3930,298 @@ gf_cli_gsync_out_status (dict_t *dict) total_spacing += 4; /* For the spacing between the fields */ /* char pointers for each field */ - output_values = GF_CALLOC (5, sizeof (char *), gf_common_mt_char); - if (!output_values) + output_values = GF_CALLOC (number_of_fields, sizeof (char *), + gf_common_mt_char); + if (!output_values) { ret = -1; - for (i = 0; i < 5; i++) { + goto out; + } + for (i = 0; i < number_of_fields; i++) { output_values[i] = GF_CALLOC (spacing[i] + 1, sizeof (char), gf_common_mt_char); - if (!output_values[i]) + if (!output_values[i]) { ret = -1; + goto out; + } } hyphens = GF_CALLOC (total_spacing + 1, sizeof (char), gf_common_mt_char); - if (!hyphens) + if (!hyphens) { ret = -1; + goto out; + } + ret = snprintf(heading, sizeof(heading), "MASTER: %s SLAVE: %s", + sts_vals[0]->master, sts_vals[0]->slave); if (ret) { - gf_log ("cli", GF_LOG_ERROR, "Out Of Memory"); + if (ret < sizeof(heading)) + heading[ret] = '\0'; + else + heading[sizeof(heading) - 1] = '\0'; + ret = 0; + } else { ret = -1; goto out; } + if (is_detail) { + cli_out (" "); + if (strlen(heading) > total_spacing) + cli_out ("%s", heading); + else { + /* Printing the heading with centre justification */ + indents = (total_spacing - strlen(heading)) / 2; + memset (indent_spaces, ' ', indents); + indent_spaces[indents] = '\0'; + ret = snprintf (hyphens, total_spacing, "%s%s", + indent_spaces, heading); + if (ret) { + hyphens[ret] = '\0'; + cli_out ("%s", hyphens); + ret = 0; + } else { + ret = -1; + goto out; + } + } + cli_out (" "); + } + /* setting the title "NODE", "MASTER", etc. from title_values[] and printing the same */ - for (j = 0; j < 5; j++) { + for (j = 0; j < number_of_fields; j++) { + /* Suppressing master and slave output for status detail */ + if ((is_detail) && ((j == 1) || (j == 2))) { + output_values[j][0] = '\0'; + continue; + } else if ((!is_detail) && (j > 4)) { + /* Suppressing detailed output for + * status */ + output_values[j][0] = '\0'; + continue; + } memset (output_values[j], ' ', spacing[j]); memcpy (output_values[j], title_values[j], strlen(title_values[j])); output_values[j][spacing[j]] = '\0'; } - cli_out ("%s %s %s %s %s", output_values[0], output_values[1], - output_values[2], output_values[3], output_values[4]); + cli_out ("%s %s %s %s %s %s %s %s %s", output_values[0], + output_values[1], output_values[2], output_values[3], + output_values[4], output_values[5], output_values[6], + output_values[7], output_values[8]); /* setting and printing the hyphens */ memset (hyphens, '-', total_spacing); hyphens[total_spacing] = '\0'; cli_out ("%s", hyphens); - for (i = 1, dict_val_count = 0; i <= gsync_count; i++) { - /* Setting the field values for each row */ - for (j = 0; j < 5; j++) { + for (i = 0; i < gsync_count; i++) { + for (j = 0; j < number_of_fields; j++) { + /* Suppressing master and slave output for + * status detail */ + if ((is_detail) && ((j == 1) || (j == 2))) { + output_values[j][0] = '\0'; + continue; + } else if ((!is_detail) && (j > 4)) { + /* Suppressing detailed output for + * status */ + output_values[j][0] = '\0'; + continue; + } + tmp = get_struct_variable(j, sts_vals[i]); + if (!tmp) { + gf_log ("", GF_LOG_ERROR, + "struct member empty."); + ret = -1; + goto out; + } memset (output_values[j], ' ', spacing[j]); - memcpy (output_values[j], dict_values[dict_val_count], - strlen(dict_values[dict_val_count])); + memcpy (output_values[j], tmp, strlen (tmp)); output_values[j][spacing[j]] = '\0'; - dict_val_count++; } - cli_out ("%s %s %s %s %s", output_values[0], output_values[1], - output_values[2], output_values[3], output_values[4]); + + cli_out ("%s %s %s %s %s %s %s %s %s", output_values[0], + output_values[1], output_values[2], output_values[3], + output_values[4], output_values[5], output_values[6], + output_values[7], output_values[8]); } + out: if (output_values) { - for (i = 0; i < 5; i++) { + for (i = 0; i < number_of_fields; i++) { if (output_values[i]) GF_FREE (output_values[i]); } GF_FREE (output_values); } - if (dict_values) - GF_FREE (dict_values); - if (hyphens) GF_FREE (hyphens); return ret; } +int +gf_cli_read_status_data (dict_t *dict, + gf_cli_gsync_status_t **sts_vals, + int *spacing, int gsync_count, + int number_of_fields) +{ + int ret = 0; + int i = 0; + int j = 0; + char mst[PATH_MAX] = {0, }; + char slv[PATH_MAX] = {0, }; + char sts[PATH_MAX] = {0, }; + char nds[PATH_MAX] = {0, }; + char *status = NULL; + char *tmp = NULL; + + /* Storing per node status info in each object */ + for (i = 0; i < gsync_count; i++) { + snprintf (nds, sizeof(nds), "node%d", i + 1); + snprintf (mst, sizeof(mst), "master%d", i + 1); + snprintf (slv, sizeof(slv), "slave%d", i + 1); + snprintf (sts, sizeof(sts), "status%d", i + 1); + + /* Fetching the values from dict, and calculating + the max length for each field */ + ret = dict_get_str (dict, nds, &(sts_vals[i]->node)); + if (ret) + goto out; + + ret = dict_get_str (dict, mst, &(sts_vals[i]->master)); + if (ret) + goto out; + + ret = dict_get_str (dict, slv, &(sts_vals[i]->slave)); + if (ret) + goto out; + + ret = dict_get_str (dict, sts, &status); + if (ret) + goto out; + + /* Fetching health and uptime from sts_val */ + ret = gf_cli_fetch_gsyncd_status_values (status, sts_vals[i]); + if (ret) + goto out; + + for (j = 0; j < number_of_fields; j++) { + tmp = get_struct_variable(j, sts_vals[i]); + if (!tmp) { + gf_log ("", GF_LOG_ERROR, + "struct member empty."); + ret = -1; + goto out; + } + if (strlen (tmp) > spacing[j]) + spacing[j] = strlen (tmp); + } + } + +out: + return ret; +} + +int +gf_cli_gsync_status_output (dict_t *dict, int status_detail) +{ + int gsync_count = 0; + int i = 0; + int j = 0; + int ret = 0; + int spacing[10] = {0}; + int num_of_fields = 9; + char errmsg[1024] = ""; + char *master = NULL; + char *slave = NULL; + char *tmp = NULL; + char *title_values[] = {"NODE", "MASTER", "SLAVE", + "HEALTH", "UPTIME", + "FILES SYNCD", + "FILES PENDING", + "BYTES PENDING", + "DELETES PENDING"}; + gf_cli_gsync_status_t **sts_vals = NULL; + + /* Checks if any session is active or not */ + ret = dict_get_int32 (dict, "gsync-count", &gsync_count); + if (ret) { + ret = dict_get_str (dict, "master", &master); + + ret = dict_get_str (dict, "slave", &slave); + + if (master) { + if (slave) + snprintf (errmsg, sizeof(errmsg), "No active " + "geo-replication sessions between %s" + " and %s", master, slave); + else + snprintf (errmsg, sizeof(errmsg), "No active " + "geo-replication sessions for %s", + master); + } else + snprintf (errmsg, sizeof(errmsg), "No active " + "geo-replication sessions"); + + gf_log ("cli", GF_LOG_INFO, "%s", errmsg); + cli_out ("%s", errmsg); + ret = 0; + goto out; + } + + for (i = 0; i < num_of_fields; i++) + spacing[i] = strlen(title_values[i]); + + /* gsync_count = number of nodes reporting output. + each sts_val object will store output of each + node */ + sts_vals = GF_CALLOC (gsync_count, sizeof (gf_cli_gsync_status_t *), + gf_common_mt_char); + if (!sts_vals) { + ret = -1; + goto out; + } + for (i = 0; i < gsync_count; i++) { + sts_vals[i] = GF_CALLOC (1, sizeof (gf_cli_gsync_status_t), + gf_common_mt_char); + if (!sts_vals[i]) { + ret = -1; + goto out; + } + } + + ret = gf_cli_read_status_data (dict, sts_vals, spacing, + gsync_count, num_of_fields); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to read status data"); + goto out; + } + + ret = gf_cli_print_status (title_values, sts_vals, spacing, gsync_count, + num_of_fields, status_detail); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to print status output"); + goto out; + } + +out: + if (sts_vals) { + for (i = 0; i < gsync_count; i++) { + for (j = 3; j < num_of_fields; j++) { + tmp = get_struct_variable(j, sts_vals[i]); + if (tmp) + GF_FREE (tmp); + } + } + GF_FREE (sts_vals); + } + + return ret; +} + static int32_t write_contents_to_common_pem_file (dict_t *dict, int output_count) { @@ -4266,6 +4456,8 @@ gf_cli_gsync_set_cbk (struct rpc_req *req, struct iovec *iov, char *slave = NULL; int32_t type = 0; call_frame_t *frame = NULL; + gf_boolean_t status_detail = _gf_false; + if (req->rpc_status == -1) { ret = -1; @@ -4341,7 +4533,10 @@ gf_cli_gsync_set_cbk (struct rpc_req *req, struct iovec *iov, break; case GF_GSYNC_OPTION_TYPE_STATUS: - ret = gf_cli_gsync_out_status (dict); + status_detail = dict_get_str_boolean (dict, + "status-detail", + _gf_false); + ret = gf_cli_gsync_status_output (dict, status_detail); break; case GF_GSYNC_OPTION_TYPE_DELETE: diff --git a/cli/src/cli.h b/cli/src/cli.h index 259f3bd3..9b3afff3 100644 --- a/cli/src/cli.h +++ b/cli/src/cli.h @@ -147,6 +147,18 @@ struct cli_local { #endif }; +struct gf_cli_gsync_detailed_status_ { + char *node; + char *master; + char *slave; + char *health; + char *uptime; + char *files_syncd; + char *files_pending; + char *bytes_pending; + char *deletes_pending; +}; + struct cli_volume_status { int port; int online; @@ -165,6 +177,8 @@ struct cli_volume_status { #endif }; +typedef struct gf_cli_gsync_detailed_status_ gf_cli_gsync_status_t; + typedef struct cli_volume_status cli_volume_status_t; typedef struct cli_local cli_local_t; -- cgit