diff options
Diffstat (limited to 'cli')
| -rw-r--r-- | cli/src/Makefile.am | 6 | ||||
| -rw-r--r-- | cli/src/cli-cmd-misc.c | 5 | ||||
| -rw-r--r-- | cli/src/cli-cmd-parser.c | 1487 | ||||
| -rw-r--r-- | cli/src/cli-cmd-peer.c | 52 | ||||
| -rw-r--r-- | cli/src/cli-cmd-snapshot.c | 146 | ||||
| -rw-r--r-- | cli/src/cli-cmd-system.c | 263 | ||||
| -rw-r--r-- | cli/src/cli-cmd-volume.c | 91 | ||||
| -rw-r--r-- | cli/src/cli-cmd.c | 10 | ||||
| -rw-r--r-- | cli/src/cli-cmd.h | 16 | ||||
| -rw-r--r-- | cli/src/cli-rl.c | 3 | ||||
| -rw-r--r-- | cli/src/cli-rpc-ops.c | 3532 | ||||
| -rw-r--r-- | cli/src/cli-xml-output.c | 842 | ||||
| -rw-r--r-- | cli/src/cli.c | 75 | ||||
| -rw-r--r-- | cli/src/cli.h | 44 |
14 files changed, 5781 insertions, 791 deletions
diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index 393077688..216d1bb55 100644 --- a/cli/src/Makefile.am +++ b/cli/src/Makefile.am @@ -2,12 +2,12 @@ sbin_PROGRAMS = gluster gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c \ cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\ - cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c + cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-cmd-snapshot.c gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\ $(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ - $(GF_GLUSTERFS_LIBS) $(LIBXML2_LIBS) + $(GF_GLUSTERFS_LIBS) $(XML_LIBS) gluster_LDFLAGS = $(GF_LDFLAGS) noinst_HEADERS = cli.h cli-mem-types.h cli-cmd.h @@ -19,7 +19,7 @@ AM_CPPFLAGS = $(GF_CPPFLAGS) \ -DCONFDIR=\"$(sysconfdir)/glusterfs\" \ -DGSYNCD_PREFIX=\"$(libexecdir)/glusterfs\"\ -DSYNCDAEMON_COMPILE=$(SYNCDAEMON_COMPILE) -DSBIN_DIR=\"$(sbindir)\"\ - $(LIBXML2_CFLAGS) + $(XML_CPPFLAGS) AM_CFLAGS = -Wall $(GF_GLUSTERFS_CFLAGS) diff --git a/cli/src/cli-cmd-misc.c b/cli/src/cli-cmd-misc.c index f3ef12147..566d7c978 100644 --- a/cli/src/cli-cmd-misc.c +++ b/cli/src/cli-cmd-misc.c @@ -31,6 +31,8 @@ extern struct cli_cmd volume_cmds[]; extern struct cli_cmd cli_probe_cmds[]; extern struct cli_cmd cli_log_cmds[]; extern struct cli_cmd cli_system_cmds[]; +extern struct cli_cmd cli_bd_cmds[]; +extern struct cli_cmd snapshot_cmds[]; struct cli_cmd cli_misc_cmds[]; int @@ -45,7 +47,8 @@ cli_cmd_display_help (struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { struct cli_cmd *cmd[] = {volume_cmds, cli_probe_cmds, - cli_misc_cmds, NULL}; + cli_misc_cmds, snapshot_cmds, + NULL}; struct cli_cmd *cmd_ind = NULL; int i = 0; diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index 4181e6c81..5ab208b8f 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2010-2013 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -26,6 +26,38 @@ #include "protocol-common.h" #include "cli1-xdr.h" +#define MAX_SNAP_DESCRIPTION_LEN 1024 + +struct snap_config_opt_vals_ snap_confopt_vals[] = { + {.op_name = "snap-max-hard-limit", + .question = "Changing snapshot-max-hard-limit " + "will lead to deletion of snapshots " + "if they exceed the new limit.\n" + "Do you want to continue?" + }, + {.op_name = "snap-max-soft-limit", + .question = "Changing snapshot-max-soft-limit " + "will lead to deletion of snapshots " + "if they exceed the new limit.\n" + "Do you want to continue?" + }, + {.op_name = "both", + .question = "Changing snapshot-max-hard-limit & " + "snapshot-max-soft-limit will lead to " + "deletion of snapshots if they exceed " + "the new limit.\nDo you want to continue?" + }, + {.op_name = NULL, + } +}; + +enum cli_snap_config_set_types { + GF_SNAP_CONFIG_SET_HARD = 0, + GF_SNAP_CONFIG_SET_SOFT = 1, + GF_SNAP_CONFIG_SET_BOTH = 2, +}; +typedef enum cli_snap_config_set_types cli_snap_config_set_types; + static const char * id_sel (void *wcon) { @@ -160,12 +192,18 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options char *bricks = NULL; int32_t brick_count = 0; char *opwords[] = { "replica", "stripe", "transport", NULL }; + char *invalid_volnames[] = {"volume", "type", "subvolumes", "option", - "end-volume", "all", NULL}; + "end-volume", "all", "volume_not_in_ring", + "description", "force", + "snap-max-hard-limit", + "snap-max-soft-limit", NULL}; char *w = NULL; int op_count = 0; int32_t replica_count = 1; int32_t stripe_count = 1; + gf_boolean_t is_force = _gf_false; + int wc = wordcount; GF_ASSERT (words); GF_ASSERT (options); @@ -300,7 +338,7 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options goto out; } index += 2; - } else { + } else { GF_ASSERT (!"opword mismatch"); ret = -1; goto out; @@ -323,7 +361,12 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options brick_index = index; - ret = cli_cmd_bricks_parse (words, wordcount, brick_index, &bricks, + if (strcmp (words[wordcount - 1], "force") == 0) { + is_force = _gf_true; + wc = wordcount - 1; + } + + ret = cli_cmd_bricks_parse (words, wc, brick_index, &bricks, &brick_count); if (ret) goto out; @@ -373,6 +416,10 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options if (ret) goto out; + ret = dict_set_int32 (dict, "force", is_force); + if (ret) + goto out; + *options = dict; out: @@ -652,7 +699,7 @@ cli_is_key_spl (char *key) #define GLUSTERD_DEFAULT_WORKDIR "/var/lib/glusterd" static int -cli_add_key_group (dict_t *dict, char *key, char *value) +cli_add_key_group (dict_t *dict, char *key, char *value, char **op_errstr) { int ret = -1; int opt_count = 0; @@ -666,6 +713,7 @@ cli_add_key_group (dict_t *dict, char *key, char *value) char *tagpath = NULL; char *buf = NULL; char line[PATH_MAX + 256] = {0,}; + char errstr[2048] = ""; FILE *fp = NULL; ret = gf_asprintf (&tagpath, "%s/groups/%s", @@ -678,6 +726,10 @@ cli_add_key_group (dict_t *dict, char *key, char *value) fp = fopen (tagpath, "r"); if (!fp) { ret = -1; + snprintf(errstr, sizeof(errstr), "Unable to open file '%s'." + " Error: %s", tagpath, strerror (errno)); + if (op_errstr) + *op_errstr = gf_strdup(errstr); goto out; } @@ -690,6 +742,10 @@ cli_add_key_group (dict_t *dict, char *key, char *value) tok_val = strtok_r (NULL, "=", &saveptr); if (!tok_key || !tok_val) { ret = -1; + snprintf(errstr, sizeof(errstr), "'%s' file format " + "not valid.", tagpath); + if (op_errstr) + *op_errstr = gf_strdup(errstr); goto out; } @@ -711,6 +767,10 @@ cli_add_key_group (dict_t *dict, char *key, char *value) if (!opt_count) { ret = -1; + snprintf(errstr, sizeof(errstr), "'%s' file format " + "not valid.", tagpath); + if (op_errstr) + *op_errstr = gf_strdup(errstr); goto out; } ret = dict_set_int32 (dict, "count", opt_count); @@ -731,7 +791,8 @@ out: #undef GLUSTERD_DEFAULT_WORKDIR int32_t -cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options) +cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options, + char **op_errstr) { dict_t *dict = NULL; char *volname = NULL; @@ -784,7 +845,12 @@ cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options) if (ret == -1) goto out; - ret = cli_add_key_group (dict, key, value); + if (strlen (value) == 0) { + ret = -1; + goto out; + } + + ret = cli_add_key_group (dict, key, value, op_errstr); if (ret == 0) *options = dict; goto out; @@ -806,6 +872,11 @@ cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options) if (ret == -1) goto out; + if (strlen (value) == 0) { + ret = -1; + goto out; + } + if (cli_is_key_spl (key)) { ret = -1; goto out; @@ -851,6 +922,8 @@ cli_cmd_volume_add_brick_parse (const char **words, int wordcount, int count = 1; char *w = NULL; int index; + gf_boolean_t is_force = _gf_false; + int wc = wordcount; GF_ASSERT (words); GF_ASSERT (options); @@ -928,7 +1001,13 @@ cli_cmd_volume_add_brick_parse (const char **words, int wordcount, brick_index = index; parse_bricks: - ret = cli_cmd_bricks_parse (words, wordcount, brick_index, &bricks, + + if (strcmp (words[wordcount - 1], "force") == 0) { + is_force = _gf_true; + wc = wordcount - 1; + } + + ret = cli_cmd_bricks_parse (words, wc, brick_index, &bricks, &brick_count); if (ret) goto out; @@ -942,6 +1021,10 @@ parse_bricks: if (ret) goto out; + ret = dict_set_int32 (dict, "force", is_force); + if (ret) + goto out; + *options = dict; out: @@ -1144,6 +1227,7 @@ cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, char *opwords[] = { "start", "commit", "pause", "abort", "status", NULL }; char *w = NULL; + gf_boolean_t is_force = _gf_false; GF_ASSERT (words); GF_ASSERT (options); @@ -1241,12 +1325,17 @@ cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, } if (wordcount == (op_index + 1)) { - if (replace_op != GF_REPLACE_OP_COMMIT) { + if ((replace_op != GF_REPLACE_OP_COMMIT) && + (replace_op != GF_REPLACE_OP_START)) { ret = -1; goto out; } if (!strcmp ("force", words[op_index])) { - replace_op = GF_REPLACE_OP_COMMIT_FORCE; + if (replace_op == GF_REPLACE_OP_COMMIT) + replace_op = GF_REPLACE_OP_COMMIT_FORCE; + + else if (replace_op == GF_REPLACE_OP_START) + is_force = _gf_true; } } @@ -1260,6 +1349,9 @@ cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, if (ret) goto out; + ret = dict_set_int32 (dict, "force", is_force); + if (ret) + goto out; *options = dict; @@ -1504,22 +1596,161 @@ gsyncd_glob_check (const char *w) return !!strpbrk (w, "*?["); } +static int +config_parse (const char **words, int wordcount, dict_t *dict, + unsigned cmdi, unsigned glob) +{ + int32_t ret = -1; + int32_t i = -1; + char *append_str = NULL; + size_t append_len = 0; + char *subop = NULL; + + switch ((wordcount - 1) - cmdi) { + case 0: + subop = gf_strdup ("get-all"); + break; + case 1: + if (words[cmdi + 1][0] == '!') { + (words[cmdi + 1])++; + if (gf_asprintf (&subop, "del%s", + glob ? "-glob" : "") == -1) + subop = NULL; + } else + subop = gf_strdup ("get"); + + ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); + if (ret < 0) + goto out; + break; + default: + if (gf_asprintf (&subop, "set%s", glob ? "-glob" : "") == -1) + subop = NULL; + + ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); + if (ret < 0) + goto out; + + /* join the varargs by spaces to get the op_value */ + + for (i = cmdi + 2; i < wordcount; i++) + append_len += (strlen (words[i]) + 1); + /* trailing strcat will add two bytes, make space for that */ + append_len++; + + append_str = GF_CALLOC (1, append_len, cli_mt_append_str); + if (!append_str) { + ret = -1; + goto out; + } + + for (i = cmdi + 2; i < wordcount; i++) { + strcat (append_str, words[i]); + strcat (append_str, " "); + } + append_str[append_len - 2] = '\0'; + /* "checkpoint now" is special: we resolve that "now" */ + if (strcmp (words[cmdi + 1], "checkpoint") == 0 && + strcmp (append_str, "now") == 0) { + struct timeval tv = {0,}; + + ret = gettimeofday (&tv, NULL); + if (ret == -1) + goto out; /* FIXME: free append_str? */ + + GF_FREE (append_str); + append_str = GF_CALLOC (1, 300, cli_mt_append_str); + if (!append_str) { + ret = -1; + goto out; + } + strcpy (append_str, "as of "); + gf_time_fmt (append_str + strlen ("as of "), + 300 - strlen ("as of "), + tv.tv_sec, gf_timefmt_FT); + } + + ret = dict_set_dynstr (dict, "op_value", append_str); + } + + ret = -1; + if (subop) { + ret = dict_set_dynstr (dict, "subop", subop); + if (!ret) + subop = NULL; + } + +out: + if (ret && append_str) + GF_FREE (append_str); + + GF_FREE (subop); + + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +static int32_t +force_push_pem_parse (const char **words, int wordcount, + dict_t *dict, unsigned *cmdi) +{ + int32_t ret = 0; + + if (!strcmp ((char *)words[wordcount-1], "force")) { + if ((strcmp ((char *)words[wordcount-2], "start")) && + (strcmp ((char *)words[wordcount-2], "stop")) && + (strcmp ((char *)words[wordcount-2], "create")) && + (strcmp ((char *)words[wordcount-2], "push-pem"))) { + ret = -1; + goto out; + } + ret = dict_set_uint32 (dict, "force", + _gf_true); + if (ret) + goto out; + (*cmdi)++; + + if (!strcmp ((char *)words[wordcount-2], "push-pem")) { + if (strcmp ((char *)words[wordcount-3], "create")) { + ret = -1; + goto out; + } + ret = dict_set_int32 (dict, "push_pem", 1); + if (ret) + goto out; + (*cmdi)++; + } + } else if (!strcmp ((char *)words[wordcount-1], "push-pem")) { + if (strcmp ((char *)words[wordcount-2], "create")) { + ret = -1; + goto out; + } + ret = dict_set_int32 (dict, "push_pem", 1); + if (ret) + goto out; + (*cmdi)++; + } + +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + + int32_t cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) { int32_t ret = -1; dict_t *dict = NULL; gf1_cli_gsync_set type = GF_GSYNC_OPTION_TYPE_NONE; - char *append_str = NULL; - size_t append_len = 0; - char *subop = NULL; int i = 0; unsigned masteri = 0; unsigned slavei = 0; unsigned glob = 0; unsigned cmdi = 0; - char *opwords[] = { "status", "start", "stop", "config", - "log-rotate", NULL }; + char *opwords[] = { "create", "status", "start", "stop", + "config", "force", "delete", + "push-pem", "detail", NULL }; char *w = NULL; GF_ASSERT (words); @@ -1531,10 +1762,11 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) /* new syntax: * - * volume geo-replication [$m [$s]] status + * volume geo-replication $m $s create [push-pem] [force] + * volume geo-replication [$m [$s]] status [detail] * volume geo-replication [$m] $s config [[!]$opt [$val]] - * volume geo-replication $m $s start|stop - * volume geo-replication $m [$s] log-rotate + * volume geo-replication $m $s start|stop [force] + * volume geo-replication $m $s delete */ if (wordcount < 3) @@ -1565,6 +1797,13 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) if (slavei == 3) masteri = 2; } else if (i <= 3) { + if (!strcmp ((char *)words[wordcount-1], "detail")) { + /* For status detail it is mandatory to provide + * both master and slave */ + ret = -1; + goto out; + } + /* no $s, can only be status cmd * (with either a single $m before it or nothing) * -- these conditions imply that i <= 3 after @@ -1591,7 +1830,12 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) if (!w) goto out; - if (strcmp (w, "status") == 0) { + if (strcmp (w, "create") == 0) { + type = GF_GSYNC_OPTION_TYPE_CREATE; + + if (!masteri || !slavei) + goto out; + } else if (strcmp (w, "status") == 0) { type = GF_GSYNC_OPTION_TYPE_STATUS; if (slavei && !masteri) @@ -1611,14 +1855,33 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) if (!masteri || !slavei) goto out; - } else if (strcmp(w, "log-rotate") == 0) { - type = GF_GSYNC_OPTION_TYPE_ROTATE; + } else if (strcmp (w, "delete") == 0) { + type = GF_GSYNC_OPTION_TYPE_DELETE; - if (slavei && !masteri) + if (!masteri || !slavei) goto out; } else GF_ASSERT (!"opword mismatch"); + ret = force_push_pem_parse (words, wordcount, dict, &cmdi); + 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; @@ -1627,97 +1890,26 @@ cli_cmd_gsync_set_parse (const char **words, int wordcount, dict_t **options) ret = 0; - if (masteri) + if (masteri) { ret = dict_set_str (dict, "master", (char *)words[masteri]); + if (!ret) + ret = dict_set_str (dict, "volname", + (char *)words[masteri]); + } if (!ret && slavei) ret = dict_set_str (dict, "slave", (char *)words[slavei]); if (!ret) ret = dict_set_int32 (dict, "type", type); - if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) { - switch ((wordcount - 1) - cmdi) { - case 0: - subop = gf_strdup ("get-all"); - break; - case 1: - if (words[cmdi + 1][0] == '!') { - (words[cmdi + 1])++; - if (gf_asprintf (&subop, "del%s", glob ? "-glob" : "") == -1) - subop = NULL; - } else - subop = gf_strdup ("get"); - - ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); - if (ret < 0) - goto out; - break; - default: - if (gf_asprintf (&subop, "set%s", glob ? "-glob" : "") == -1) - subop = NULL; - - ret = dict_set_str (dict, "op_name", ((char *)words[cmdi + 1])); - if (ret < 0) - goto out; - - /* join the varargs by spaces to get the op_value */ - - for (i = cmdi + 2; i < wordcount; i++) - append_len += (strlen (words[i]) + 1); - /* trailing strcat will add two bytes, make space for that */ - append_len++; - - append_str = GF_CALLOC (1, append_len, cli_mt_append_str); - if (!append_str) { - ret = -1; - goto out; - } - - for (i = cmdi + 2; i < wordcount; i++) { - strcat (append_str, words[i]); - strcat (append_str, " "); - } - append_str[append_len - 2] = '\0'; - - /* "checkpoint now" is special: we resolve that "now" */ - if (strcmp (words[cmdi + 1], "checkpoint") == 0 && - strcmp (append_str, "now") == 0) { - struct timeval tv = {0,}; - - ret = gettimeofday (&tv, NULL); - if (ret == -1) - goto out; /* FIXME: free append_str? */ - - GF_FREE (append_str); - append_str = GF_CALLOC (1, 300, cli_mt_append_str); - if (!append_str) { - ret = -1; - goto out; - } - strcpy (append_str, "as of "); - gf_time_fmt (append_str + strlen ("as of "), - 300 - strlen ("as of "), - tv.tv_sec, gf_timefmt_FT); - } - - ret = dict_set_dynstr (dict, "op_value", append_str); - } - - ret = -1; - if (subop) { - ret = dict_set_dynstr (dict, "subop", subop); - if (!ret) - subop = NULL; - } - } + if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) + ret = config_parse (words, wordcount, dict, cmdi, glob); out: if (ret) { if (dict) dict_destroy (dict); - GF_FREE (append_str); } else *options = dict; - GF_FREE (subop); return ret; } @@ -1977,7 +2169,7 @@ cli_cmd_get_statusop (const char *arg) uint32_t ret = GF_CLI_STATUS_NONE; char *w = NULL; char *opwords[] = {"detail", "mem", "clients", "fd", - "inode", "callpool", NULL}; + "inode", "callpool", "tasks", NULL}; struct { char *opname; uint32_t opcode; @@ -1988,6 +2180,7 @@ cli_cmd_get_statusop (const char *arg) { "fd", GF_CLI_STATUS_FD }, { "inode", GF_CLI_STATUS_INODE }, { "callpool", GF_CLI_STATUS_CALLPOOL }, + { "tasks", GF_CLI_STATUS_TASKS }, { NULL } }; @@ -2102,8 +2295,9 @@ cli_cmd_volume_status_parse (const char **words, int wordcount, if (!strcmp (words[3], "nfs")) { if (cmd == GF_CLI_STATUS_FD || - cmd == GF_CLI_STATUS_DETAIL) { - cli_err ("Detail/FD status not available" + cmd == GF_CLI_STATUS_DETAIL || + cmd == GF_CLI_STATUS_TASKS) { + cli_err ("Detail/FD/Tasks status not available" " for NFS Servers"); ret = -1; goto out; @@ -2112,14 +2306,21 @@ cli_cmd_volume_status_parse (const char **words, int wordcount, } else if (!strcmp (words[3], "shd")){ if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || - cmd == GF_CLI_STATUS_DETAIL) { - cli_err ("Detail/FD/Clients status not " + cmd == GF_CLI_STATUS_DETAIL || + cmd == GF_CLI_STATUS_TASKS) { + cli_err ("Detail/FD/Clients/Tasks status not " "available for Self-heal Daemons"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_SHD; } else { + if (cmd == GF_CLI_STATUS_TASKS) { + cli_err ("Tasks status not available for " + "bricks"); + ret = -1; + goto out; + } cmd |= GF_CLI_STATUS_BRICK; ret = dict_set_str (dict, "brick", (char *)words[3]); } @@ -2268,12 +2469,103 @@ out: return ret; } +static int +extract_hostname_path_from_token (const char *tmp_words, char **hostname, + char **path) +{ + int ret = 0; + char *delimiter = NULL; + char *tmp_host = NULL; + char *host_name = NULL; + char *words = NULL; + + *hostname = NULL; + *path = NULL; + + words = GF_CALLOC (1, strlen (tmp_words) + 1, gf_common_mt_char); + if (!words){ + ret = -1; + goto out; + } + + strncpy (words, tmp_words, strlen (tmp_words) + 1); + + if (validate_brick_name (words)) { + cli_err ("Wrong brick type: %s, use <HOSTNAME>:" + "<export-dir-abs-path>", words); + ret = -1; + goto out; + } else { + delimiter = strrchr (words, ':'); + ret = gf_canonicalize_path (delimiter + 1); + if (ret) { + goto out; + } else { + *path = GF_CALLOC (1, strlen (delimiter+1) +1, + gf_common_mt_char); + if (!*path) { + ret = -1; + goto out; + + } + strncpy (*path, delimiter +1, + strlen(delimiter + 1) + 1); + } + } + + tmp_host = gf_strdup (words); + if (!tmp_host) { + gf_log ("cli", GF_LOG_ERROR, "Out of memory"); + ret = -1; + goto out; + } + get_host_name (tmp_host, &host_name); + if (!host_name) { + ret = -1; + gf_log("cli",GF_LOG_ERROR, "Unable to allocate " + "memory"); + goto out; + } + if (!(strcmp (host_name, "localhost") && + strcmp (host_name, "127.0.0.1") && + strncmp (host_name, "0.", 2))) { + cli_err ("Please provide a valid hostname/ip other " + "than localhost, 127.0.0.1 or loopback " + "address (0.0.0.0 to 0.255.255.255)."); + ret = -1; + goto out; + } + if (!valid_internet_address (host_name, _gf_false)) { + cli_err ("internet address '%s' does not conform to " + "standards", host_name); + ret = -1; + goto out; + } + + *hostname = GF_CALLOC (1, strlen (host_name) + 1, + gf_common_mt_char); + if (!*hostname) { + ret = -1; + goto out; + } + strncpy (*hostname, host_name, strlen (host_name) + 1); + ret = 0; + +out: + GF_FREE (words); + GF_FREE (tmp_host); + return ret; +} + + int cli_cmd_volume_heal_options_parse (const char **words, int wordcount, dict_t **options) { int ret = 0; dict_t *dict = NULL; + char *hostname = NULL; + char *path = NULL; dict = dict_new (); if (!dict) @@ -2295,6 +2587,11 @@ cli_cmd_volume_heal_options_parse (const char **words, int wordcount, ret = dict_set_int32 (dict, "heal-op", GF_AFR_OP_HEAL_FULL); goto done; + } else if (!strcmp (words[3], "statistics")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_STATISTICS); + goto done; + } else if (!strcmp (words[3], "info")) { ret = dict_set_int32 (dict, "heal-op", GF_AFR_OP_INDEX_SUMMARY); @@ -2305,28 +2602,66 @@ cli_cmd_volume_heal_options_parse (const char **words, int wordcount, } } if (wordcount == 5) { - if (strcmp (words[3], "info")) { + if (strcmp (words[3], "info") && + strcmp (words[3], "statistics")) { ret = -1; goto out; } - if (!strcmp (words[4], "healed")) { - ret = dict_set_int32 (dict, "heal-op", - GF_AFR_OP_HEALED_FILES); - goto done; - } - if (!strcmp (words[4], "heal-failed")) { - ret = dict_set_int32 (dict, "heal-op", - GF_AFR_OP_HEAL_FAILED_FILES); - goto done; + + if (!strcmp (words[3], "info")) { + if (!strcmp (words[4], "healed")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_HEALED_FILES); + goto done; + } + if (!strcmp (words[4], "heal-failed")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_HEAL_FAILED_FILES); + goto done; + } + if (!strcmp (words[4], "split-brain")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_SPLIT_BRAIN_FILES); + goto done; + } } - if (!strcmp (words[4], "split-brain")) { - ret = dict_set_int32 (dict, "heal-op", - GF_AFR_OP_SPLIT_BRAIN_FILES); - goto done; + + if (!strcmp (words[3], "statistics")) { + if (!strcmp (words[4], "heal-count")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_STATISTICS_HEAL_COUNT); + goto done; + } } ret = -1; goto out; } + if (wordcount == 7) { + if (!strcmp (words[3], "statistics") + && !strcmp (words[4], "heal-count") + && !strcmp (words[5], "replica")) { + + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); + if (ret) + goto out; + ret = extract_hostname_path_from_token (words[6], + &hostname, &path); + if (ret) + goto out; + ret = dict_set_dynstr (dict, "per-replica-cmd-hostname", + hostname); + if (ret) + goto out; + ret = dict_set_dynstr (dict, "per-replica-cmd-path", + path); + if (ret) + goto out; + else + goto done; + + } + } ret = -1; goto out; done: @@ -2431,3 +2766,917 @@ out: return ret; } + +int32_t +cli_snap_create_desc_parse (dict_t *dict, const char **words, + size_t wordcount, int32_t desc_opt_loc) +{ + int32_t ret = -1; + char *desc = NULL; + int32_t desc_len = 0; + + desc = GF_CALLOC (MAX_SNAP_DESCRIPTION_LEN + 1, sizeof(char), + gf_common_mt_char); + if (!desc) { + ret = -1; + goto out; + } + + + if (strlen (words[desc_opt_loc]) >= MAX_SNAP_DESCRIPTION_LEN) { + cli_out ("snapshot create: description truncated: " + "Description provided is longer than 1024 characters"); + desc_len = MAX_SNAP_DESCRIPTION_LEN; + } else { + desc_len = strlen (words[desc_opt_loc]); + } + + strncpy (desc, words[desc_opt_loc], desc_len); + desc[desc_len] = '\0'; + /* Calculating the size of the description as given by the user */ + + ret = dict_set_dynstr (dict, "description", desc); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snap " + "description"); + goto out; + } + + ret = 0; +out: + if (ret && desc) + GF_FREE (desc); + + return ret; +} + +/* Function to check whether the Volume name is repeated */ +int +cli_check_if_volname_repeated (const char **words, unsigned int start_index, + uint64_t cur_index) { + uint64_t i = -1; + int ret = 0; + + GF_ASSERT (words); + + for (i = start_index ; i < cur_index ; i++) { + if (strcmp (words[i], words[cur_index]) == 0) { + ret = -1; + goto out; + } + } +out : + return ret; +} + +/* snapshot create <snapname> <vol-name(s)> [description <description>] + * [force] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_create_parse (dict_t *dict, const char **words, int wordcount) { + uint64_t i = 0; + int ret = -1; + uint64_t volcount = 0; + char key[PATH_MAX] = ""; + char *snapname = NULL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount <= cmdi + 1) { + cli_err ("Invalid Syntax."); + gf_log ("cli", GF_LOG_ERROR, + "Too less words for snap create command"); + goto out; + } + + if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { + cli_err ("snapshot create: failed: snapname cannot exceed " + "255 characters."); + gf_log ("cli", GF_LOG_ERROR, "Snapname too long"); + + goto out; + } + + snapname = (char *) words[cmdi]; + for (i = 0 ; i < strlen (snapname); i++) { + /* Following volume name convention */ + if (!isalnum (snapname[i]) && (snapname[i] != '_' + && (snapname[i] != '-'))) { + /* TODO : Is this message enough?? */ + cli_err ("Snapname can contain only alphanumeric, " + "\"-\" and \"_\" characters"); + goto out; + } + } + + ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save snap " + "name"); + goto out; + } + + /* Filling volume name in the dictionary */ + for (i = cmdi + 1 ; i < wordcount + && (strcmp (words[i], "description")) != 0 + && (strcmp (words[i], "force") != 0); i++) { + volcount++; + /* volume index starts from 1 */ + ret = snprintf (key, sizeof (key),"volname%ld", volcount); + if (ret < 0) { + goto out; + } + + ret = dict_set_str (dict, key, (char *)words[i]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not " + "save volume name"); + goto out; + } + + if (i >= cmdi + 2) { + ret = -1; + cli_err("Creating multiple volume snapshot is not " + "supported as of now"); + goto out; + } + /* TODO : remove this above condition check once + * multiple volume snapshot is supported */ + } + + if (volcount == 0) { + ret = -1; + cli_err ("Please provide the volume name"); + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_int32 (dict, "volcount", volcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save volcount"); + goto out; + } + + /* Verify how we got out of "for" loop, + * if it is by reaching wordcount limit then goto "out", + * because we need not parse for "description" and "force" + * after this. + */ + if (i == wordcount) { + goto out; + } + + if ((strcmp (words[i], "description")) == 0) { + ++i; + if (i > (wordcount - 1)) { + ret = -1; + cli_err ("Please provide a description"); + gf_log ("cli", GF_LOG_ERROR, + "Description not provided"); + goto out; + } + + ret = cli_snap_create_desc_parse(dict, words, wordcount, i); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save snap " + "description"); + goto out; + } + + if ( i == (wordcount - 1)) + goto out; + i++; + /* point the index to next word. + * As description might be follwed by force option. + * Before that, check if wordcount limit is reached + */ + } + + if ((strcmp (words[i], "force") != 0)) { + ret = -1; + cli_err ("Invalid Syntax."); + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + ret = dict_set_int8 (dict, "snap-force", 1); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save " + "snap force option"); + goto out; + } + + /* Check if the command has anything after "force" keyword */ + if (++i < wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = 0; + +out : + return ret; +} + +/* snapshot list [volname] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_list_parse (dict_t *dict, const char **words, int wordcount) { + int ret = -1; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount < 2 || wordcount > 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + if (wordcount == 2) { + ret = 0; + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to save volname in dictionary"); + goto out; + } +out : + return ret; +} + +/* snapshot info [(snapname | volume <volname>)] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_info_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + int32_t cmd = GF_SNAP_INFO_TYPE_ALL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount > 4 || wordcount < cmdi) { + gf_log ("", GF_LOG_ERROR, "Invalid syntax"); + goto out; + } + + if (wordcount == cmdi) { + ret = 0; + goto out; + } + + /* If 3rd word is not "volume", then it must + * be snapname. + */ + if (strcmp (words[cmdi], "volume") != 0) { + ret = dict_set_str (dict, "snapname", + (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save " + "snapname %s", words[cmdi]); + goto out; + } + + /* Once snap name is parsed, if we encounter any other + * word then fail it. Invalid Syntax. + * example : snapshot info <snapname> word + */ + if ((cmdi + 1) != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + cmd = GF_SNAP_INFO_TYPE_SNAP; + ret = 0; + goto out; + /* No need to continue the parsing once we + * get the snapname + */ + } + + /* If 3rd word is "volume", then check if next word + * is present. As, "snapshot info volume" is an + * invalid command. + */ + if ((cmdi + 1) == wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words[wordcount - 1]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Count not save " + "volume name %s", words[wordcount - 1]); + goto out; + } + cmd = GF_SNAP_INFO_TYPE_VOL; +out : + if (ret == 0) { + ret = dict_set_int32 (dict, "cmd", cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save " + "type of snapshot info"); + } + } + return ret; +} + + + +/* snapshot restore <snapname> + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_restore_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount != 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "snapname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", + words[2]); + goto out; + } +out : + return ret; +} + +/* snapshot delete <snapname> + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + * 1 if user cancel the operation + */ +int +cli_snap_delete_parse (dict_t *dict, const char **words, int wordcount, + struct cli_state *state) { + + int ret = -1; + const char *question = NULL; + gf_answer_t answer = GF_ANSWER_NO; + + question = "Deleting snap will erase all the information about " + "the snap. Do you still want to continue?"; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount != 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "snapname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snapname %s", + words[2]); + goto out; + } + + answer = cli_cmd_get_confirmation (state, question); + if (GF_ANSWER_NO == answer) { + ret = 1; + gf_log ("cli", GF_LOG_DEBUG, "User cancelled " + "snapshot delete operation"); + goto out; + } +out : + return ret; +} + +/* snapshot status [(snapname | volume <volname>)] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_status_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + int32_t cmd = GF_SNAP_STATUS_TYPE_ALL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount > 4 || wordcount < cmdi) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + if (wordcount == cmdi) { + ret = 0; + goto out; + } + + /* if 3rd word is not "volume", then it must be "snapname" + */ + if (strcmp (words[cmdi], "volume") != 0) { + ret = dict_set_str (dict, "snapname", + (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Count not save " + "snap name %s", words[cmdi]); + goto out; + } + + if ((cmdi + 1) != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = 0; + cmd = GF_SNAP_STATUS_TYPE_SNAP; + goto out; + } + + /* If 3rd word is "volume", then check if next word is present. + * As, "snapshot info volume" is an invalid command + */ + if ((cmdi + 1) == wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words [wordcount - 1]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Count not save " + "volume name %s", words[wordcount - 1]); + goto out; + } + cmd = GF_SNAP_STATUS_TYPE_VOL; + +out : + if (ret == 0) { + ret = dict_set_int32 (dict, "cmd", cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save cmd " + "of snapshot status"); + } + } + return ret; +} + + +int32_t +cli_snap_config_limit_parse (const char **words, dict_t *dict, + unsigned int wordcount, unsigned int index, + char *key) +{ + int ret = -1; + int limit = 0; + + GF_ASSERT (words); + GF_ASSERT (dict); + GF_ASSERT (key); + + if (index >= wordcount) { + ret = -1; + cli_err ("Please provide a value for %s.",key); + gf_log ("cli", GF_LOG_ERROR, "Value not provided for %s", key); + goto out; + } + + limit = strtol (words[index], NULL, 0); + if (limit <= 0) { + ret = -1; + cli_err ("%s should be greater than 0.", key); + goto out; + } + + ret = dict_set_int32 (dict, key, limit); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not set " + "%s in dictionary", key); + goto out; + } + +out : + return ret; +} + +/* function cli_snap_config_parse + * Config Syntax : gluster snapshot config [volname] + * [snap-max-hard-limit <count>] + * [snap-max-soft-limit <count>] + * + return value: <0 on failure + 1 if user cancels the operation + 0 on success + + NOTE : snap-max-soft-limit can only be set for system. +*/ +int32_t +cli_snap_config_parse (const char **words, int wordcount, dict_t *dict, + struct cli_state *state) +{ + int ret = -1; + gf_answer_t answer = GF_ANSWER_NO; + gf_boolean_t vol_presence = _gf_false; + struct snap_config_opt_vals_ *conf_vals = NULL; + int8_t hard_limit = 0; + int8_t soft_limit = 0; + int8_t config_type = -1; + const char *question = NULL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + GF_ASSERT (state); + + if ((wordcount < 2) || (wordcount > 7)) { + gf_log ("cli", GF_LOG_ERROR, + "Invalid wordcount(%d)", wordcount); + goto out; + } + + if (wordcount == 2) { + config_type = GF_SNAP_CONFIG_DISPLAY; + ret = 0; + goto set; + } + + /* Check whether the 3rd word is volname */ + if (strcmp (words[cmdi], "snap-max-hard-limit") != 0 + && strcmp (words[cmdi], "snap-max-soft-limit") != 0) { + ret = dict_set_str (dict, "volname", (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to set volname"); + goto out; + } + cmdi++; + vol_presence = _gf_true; + + if (cmdi == wordcount) { + config_type = GF_SNAP_CONFIG_DISPLAY; + ret = 0; + goto set; + } + } + + config_type = GF_SNAP_CONFIG_TYPE_SET; + + if (strcmp (words[cmdi], "snap-max-hard-limit") == 0) { + ret = cli_snap_config_limit_parse (words, dict, wordcount, + ++cmdi, "snap-max-hard-limit"); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " + "config hard limit"); + goto out; + } + hard_limit = 1; + + if (++cmdi == wordcount) { + ret = 0; + goto set; + } + } + + if (strcmp (words[cmdi], "snap-max-soft-limit") == 0) { + if (vol_presence == 1) { + ret = -1; + cli_err ("Soft limit cannot be set to individual " + "volumes."); + gf_log ("cli", GF_LOG_ERROR, "Soft limit cannot be " + "set to volumes"); + goto out; + } + + ret = cli_snap_config_limit_parse (words, dict, wordcount, + ++cmdi, "snap-max-soft-limit"); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " + "config soft limit"); + goto out; + } + + if (++cmdi != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + soft_limit = 1; + } else { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + ret = 0; /* Success */ + +set: + ret = dict_set_int32 (dict, "config-command", config_type); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to set " + "config-command"); + goto out; + } + + if (config_type == GF_SNAP_CONFIG_TYPE_SET) { + conf_vals = snap_confopt_vals; + if (hard_limit && soft_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question; + } else if (soft_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question; + } else if (hard_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question; + } + + answer = cli_cmd_get_confirmation (state, question); + if (GF_ANSWER_NO == answer) { + ret = 1; + gf_log ("cli", GF_LOG_DEBUG, "User cancelled " + "snapshot config operation"); + } + } + +out: + return ret; +} + +int +validate_snapname (const char *snapname, char **opwords) { + int ret = -1; + int i = 0; + + GF_ASSERT (snapname); + GF_ASSERT (opwords); + + for (i = 0 ; opwords[i] != NULL; i++) { + if (strcmp (opwords[i], snapname) == 0) { + cli_out ("\"%s\" cannot be a snapname", snapname); + goto out; + } + } + ret = 0; +out : + return ret; +} + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, + struct cli_state *state) +{ + int32_t ret = -1; + dict_t *dict = NULL; + gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE; + char *w = NULL; + char *opwords[] = {"create", "delete", "restore", "start", + "stop", "list", "status", "config", + "info", NULL}; + char *invalid_snapnames[] = {"description", "force", + "volume", NULL}; + + GF_ASSERT (words); + GF_ASSERT (options); + GF_ASSERT (state); + + dict = dict_new (); + if (!dict) + goto out; + + /* Lowest wordcount possible */ + if (wordcount < 2) { + gf_log ("", GF_LOG_ERROR, + "Invalid command: Not enough arguments"); + goto out; + } + + w = str_getunamb (words[1], opwords); + if (!w) { + /* Checks if the operation is a valid operation */ + gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); + goto out; + } + + if (!strcmp (w, "create")) { + type = GF_SNAP_OPTION_TYPE_CREATE; + } else if (!strcmp (w, "list")) { + type = GF_SNAP_OPTION_TYPE_LIST; + } else if (!strcmp (w, "info")) { + type = GF_SNAP_OPTION_TYPE_INFO; + } else if (!strcmp (w, "delete")) { + type = GF_SNAP_OPTION_TYPE_DELETE; + } else if (!strcmp (w, "config")) { + type = GF_SNAP_OPTION_TYPE_CONFIG; + } else if (!strcmp (w, "restore")) { + type = GF_SNAP_OPTION_TYPE_RESTORE; + } else if (!strcmp (w, "status")) { + type = GF_SNAP_OPTION_TYPE_STATUS; + } + + if (type != GF_SNAP_OPTION_TYPE_CONFIG) { + ret = dict_set_int32 (dict, "hold_snap_locks", _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to set hold-snap-locks value " + "as _gf_true"); + goto out; + } + } + + /* Check which op is intended */ + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + { + /* Syntax : + * gluster snapshot create <snapname> <vol-name(s)> + * [description <description>] + * [force] + */ + + /* In cases where the snapname is not given then + * parsing fails & snapname cannot be "description", + * "force" and "volume", that check is made here + */ + if (wordcount == 2){ + ret = -1; + gf_log ("cli", GF_LOG_ERROR, + "Invalid Syntax"); + goto out; + } + + ret = validate_snapname (words[2], invalid_snapnames); + if (ret) { + goto out; + } + + ret = cli_snap_create_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "create command parsing failed."); + goto out; + } + break; + } + case GF_SNAP_OPTION_TYPE_INFO: + { + /* Syntax : + * gluster snapshot info [(snapname] | [vol <volname>)] + */ + ret = cli_snap_info_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot info command"); + goto out; + } + break; + } + + case GF_SNAP_OPTION_TYPE_LIST: + { + /* Syntax : + * gluster snaphsot list [volname] + */ + + ret = cli_snap_list_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot list command"); + goto out; + } + break; + } + + case GF_SNAP_OPTION_TYPE_DELETE: + { + /* Syntax : + * gluster snapshot delete <snapname> + */ + ret = cli_snap_delete_parse (dict, words, wordcount, + state); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot delete command"); + goto out; + } + break; + } + + case GF_SNAP_OPTION_TYPE_CONFIG: + { + /* snapshot config [volname] [snap-max-hard-limit <count>] + * [snap-max-soft-limit <percent>] */ + ret = cli_snap_config_parse (words, wordcount, dict, + state); + if (ret) { + if (ret < 0) + gf_log ("cli", GF_LOG_ERROR, + "config command parsing failed."); + goto out; + } + + ret = dict_set_int32 (dict, "type", + GF_SNAP_OPTION_TYPE_CONFIG); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to set " + "config type"); + ret = -1; + goto out; + } + break; + } + case GF_SNAP_OPTION_TYPE_STATUS: + { + /* Syntax : + * gluster snapshot status [(snapname | + * volume <volname>)] + */ + ret = cli_snap_status_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot status command"); + goto out; + } + break; + } + + case GF_SNAP_OPTION_TYPE_RESTORE: + { + /* Syntax: + * snapshot restore <snapname> + */ + ret = cli_snap_restore_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "restore command"); + goto out; + } + break; + } + default: + gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); + goto out; + break; + } + + ret = dict_set_int32 (dict, "type", type); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Failed to set type."); + goto out; + } + /* If you got so far, input is valid */ + ret = 0; +out: + if (ret) { + if (dict) + dict_destroy (dict); + } else + *options = dict; + + return ret; +} diff --git a/cli/src/cli-cmd-peer.c b/cli/src/cli-cmd-peer.c index 08651e72f..551312411 100644 --- a/cli/src/cli-cmd-peer.c +++ b/cli/src/cli-cmd-peer.c @@ -41,6 +41,7 @@ cli_cmd_peer_probe_cbk (struct cli_state *state, struct cli_cmd_word *word, dict_t *dict = NULL; int sent = 0; int parse_error = 0; + cli_local_t *local = NULL; if (!(wordcount == 3)) { cli_usage_out (word->pattern); @@ -78,6 +79,9 @@ cli_cmd_peer_probe_cbk (struct cli_state *state, struct cli_cmd_word *word, goto out; } */ + + CLI_LOCAL_INIT (local, words, frame, dict); + if (proc->fn) { ret = proc->fn (frame, THIS, dict); } @@ -106,6 +110,7 @@ cli_cmd_peer_deprobe_cbk (struct cli_state *state, struct cli_cmd_word *word, int flags = 0; int sent = 0; int parse_error = 0; + cli_local_t *local = NULL; if ((wordcount < 3) || (wordcount > 4)) { cli_usage_out (word->pattern); @@ -145,6 +150,8 @@ cli_cmd_peer_deprobe_cbk (struct cli_state *state, struct cli_cmd_word *word, if (ret) goto out; + CLI_LOCAL_INIT (local, words, frame, dict); + if (proc->fn) { ret = proc->fn (frame, THIS, dict); } @@ -184,7 +191,7 @@ cli_cmd_peer_status_cbk (struct cli_state *state, struct cli_cmd_word *word, goto out; if (proc->fn) { - ret = proc->fn (frame, THIS, (char *)words[1] ); + ret = proc->fn (frame, THIS, (void *)GF_CLI_LIST_PEERS); } out: @@ -199,6 +206,45 @@ out: return ret; } +int +cli_cmd_pool_list_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int sent = 0; + int parse_error = 0; + + if (wordcount != 2) { + cli_usage_out (word->pattern); + parse_error = 1; + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LIST_FRIENDS]; + + frame = create_frame (THIS, THIS->ctx->pool); + if (!frame) + goto out; + + if (proc->fn) { + ret = proc->fn (frame, THIS, + (void *)GF_CLI_LIST_POOL_NODES); + } + +out: + if (ret) { + cli_cmd_sent_status_get (&sent); + if ((sent == 0) && (parse_error == 0)) + cli_err ("pool list: command execution failed"); + } + + CLI_STACK_DESTROY (frame); + + return ret; +} + struct cli_cmd cli_probe_cmds[] = { { "peer probe <HOSTNAME>", cli_cmd_peer_probe_cbk, @@ -216,6 +262,10 @@ struct cli_cmd cli_probe_cmds[] = { cli_cmd_peer_help_cbk, "Help command for peer "}, + { "pool list", + cli_cmd_pool_list_cbk, + "list all the nodes in the pool (including localhost)"}, + { NULL, NULL, NULL } }; diff --git a/cli/src/cli-cmd-snapshot.c b/cli/src/cli-cmd-snapshot.c new file mode 100644 index 000000000..de492d683 --- /dev/null +++ b/cli/src/cli-cmd-snapshot.c @@ -0,0 +1,146 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <pthread.h> + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "cli.h" +#include "cli-cmd.h" + +extern rpc_clnt_prog_t *cli_rpc_prog; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount); + +int +cli_cmd_snapshot_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = 0; + int parse_err = 0; + dict_t *options = NULL; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + cli_local_t *local = NULL; + + proc = &cli_rpc_prog->proctable [GLUSTER_CLI_SNAP]; + if (proc == NULL) { + ret = -1; + goto out; + } + + frame = create_frame (THIS, THIS->ctx->pool); + if (frame == NULL) { + ret = -1; + goto out; + } + + /* Parses the command entered by the user */ + ret = cli_cmd_snapshot_parse (words, wordcount, &options, state); + if (ret) { + if (ret < 0) { + cli_usage_out (word->pattern); + parse_err = 1; + } + else { + /* User might have cancelled the snapshot operation */ + ret = 0; + } + goto out; + } + + CLI_LOCAL_INIT (local, words, frame, options); + + if (proc->fn) + ret = proc->fn (frame, THIS, options); + +out: + if (ret && parse_err == 0) + cli_out ("Snapshot command failed"); + + CLI_STACK_DESTROY (frame); + + return ret; +} + +struct cli_cmd snapshot_cmds[] = { + { "snapshot help", + cli_cmd_snapshot_help_cbk, + "display help for snapshot commands" + }, + { "snapshot create <snapname> <volname(s)> [description <description>] [force]", + cli_cmd_snapshot_cbk, + "Snapshot Create." + }, + { "snapshot restore <snapname>", + cli_cmd_snapshot_cbk, + "Snapshot Restore." + }, + { "snapshot status [(snapname | volume <volname>)]", + cli_cmd_snapshot_cbk, + "Snapshot Status." + }, + { "snapshot info [(snapname | volume <volname>)]", + cli_cmd_snapshot_cbk, + "Snapshot Info." + }, + { "snapshot list [volname]", + cli_cmd_snapshot_cbk, + "Snapshot List." + }, + {"snapshot config [volname] [snap-max-hard-limit <count>] [snap-max-soft-limit <percent>]", + cli_cmd_snapshot_cbk, + "Snapshot Config." + }, + {"snapshot delete <snapname>", + cli_cmd_snapshot_cbk, + "Snapshot Delete." + }, + { NULL, NULL, NULL } +}; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, + struct cli_cmd_word *in_word, + const char **words, + int wordcount) +{ + struct cli_cmd *cmd = NULL; + + for (cmd = snapshot_cmds; cmd->pattern; cmd++) + if (_gf_false == cmd->disable) + cli_out ("%s - %s", cmd->pattern, cmd->desc); + + return 0; +} + +int +cli_cmd_snapshot_register (struct cli_state *state) +{ + int ret = 0; + struct cli_cmd *cmd = NULL; + + for (cmd = snapshot_cmds; cmd->pattern; cmd++) { + + ret = cli_cmd_register (&state->tree, cmd); + if (ret) + goto out; + } +out: + return ret; +} diff --git a/cli/src/cli-cmd-system.c b/cli/src/cli-cmd-system.c index 255eb605e..8cfa5e70c 100644 --- a/cli/src/cli-cmd-system.c +++ b/cli/src/cli-cmd-system.c @@ -31,6 +31,12 @@ extern rpc_clnt_prog_t *cli_rpc_prog; int cli_cmd_system_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount); +int cli_cmd_copy_file_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount); + +int cli_cmd_sys_exec_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount); + int cli_cmd_getspec_cbk (struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) @@ -278,6 +284,114 @@ cli_cmd_umount_cbk (struct cli_state *state, struct cli_cmd_word *word, return ret; } +int +cli_cmd_uuid_get_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + int sent = 0; + int parse_error = 0; + dict_t *dict = NULL; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + cli_local_t *local = NULL; + xlator_t *this = NULL; + + this = THIS; + if (wordcount != 3) { + cli_usage_out (word->pattern); + parse_error = 1; + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_UUID_GET]; + frame = create_frame (this, this->ctx->pool); + if (!frame) + goto out; + + dict = dict_new (); + if (!dict) + goto out; + + CLI_LOCAL_INIT (local, words, frame, dict); + if (proc->fn) + ret = proc->fn (frame, this, dict); + +out: + if (ret) { + cli_cmd_sent_status_get (&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out ("uuid get failed"); + } + + if (dict) + dict_unref (dict); + + CLI_STACK_DESTROY (frame); + return ret; +} + +int +cli_cmd_uuid_reset_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int sent = 0; + int parse_error = 0; + gf_answer_t answer = GF_ANSWER_NO; + char *question = NULL; + cli_local_t *local = NULL; + dict_t *dict = NULL; + xlator_t *this = NULL; + + question = "Resetting uuid changes the uuid of local glusterd. " + "Do you want to continue?"; + + if (wordcount != 3) { + cli_usage_out (word->pattern); + parse_error = 1; + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_UUID_RESET]; + + this = THIS; + frame = create_frame (this, this->ctx->pool); + if (!frame) + goto out; + + dict = dict_new (); + if (!dict) { + ret = -1; + goto out; + } + CLI_LOCAL_INIT (local, words, frame, dict); + answer = cli_cmd_get_confirmation (state, question); + + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } + + //send NULL as argument since no dictionary is sent to glusterd + if (proc->fn) { + ret = proc->fn (frame, this, dict); + } + +out: + if (ret) { + cli_cmd_sent_status_get (&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out ("uuid reset failed"); + } + + CLI_STACK_DESTROY (frame); + + return ret; +} + struct cli_cmd cli_system_cmds[] = { { "system:: getspec <VOLID>", cli_cmd_getspec_cbk, @@ -303,14 +417,163 @@ struct cli_cmd cli_system_cmds[] = { cli_cmd_umount_cbk, "request an umount"}, + { "system:: uuid get", + cli_cmd_uuid_get_cbk, + "get uuid of glusterd"}, + + { "system:: uuid reset", + cli_cmd_uuid_reset_cbk, + "reset the uuid of glusterd"}, + { "system:: help", cli_cmd_system_help_cbk, "display help for system commands"}, + { "system:: copy file [<filename>]", + cli_cmd_copy_file_cbk, + "Copy file from current node's $working_dir to " + "$working_dir of all cluster nodes"}, + + { "system:: execute <command> <args>", + cli_cmd_sys_exec_cbk, + "Execute the command on all the nodes " + "in the cluster and display their output."}, + { NULL, NULL, NULL } }; int +cli_cmd_sys_exec_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + char cmd_arg_name[PATH_MAX] = ""; + char *command = NULL; + char *saveptr = NULL; + char *tmp = NULL; + int ret = -1; + int i = -1; + int cmd_args_count = 0; + int in_cmd_args_count = 0; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + cli_local_t *local = NULL; + + if (wordcount < 3) { + cli_usage_out (word->pattern); + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + command = strtok_r ((char *)words[2], " ", &saveptr); + do { + tmp = strtok_r (NULL, " ", &saveptr); + if (tmp) { + in_cmd_args_count++; + memset (cmd_arg_name, '\0', sizeof(cmd_arg_name)); + snprintf (cmd_arg_name, sizeof(cmd_arg_name), + "cmd_arg_%d", in_cmd_args_count); + ret = dict_set_str (dict, cmd_arg_name, tmp); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to set " + "%s in dict", cmd_arg_name); + goto out; + } + } + } while (tmp); + + cmd_args_count = wordcount - 3; + + ret = dict_set_str (dict, "command", command); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to set command in dict"); + goto out; + } + + for (i=1; i <= cmd_args_count; i++) { + in_cmd_args_count++; + memset (cmd_arg_name, '\0', sizeof(cmd_arg_name)); + snprintf (cmd_arg_name, sizeof(cmd_arg_name), + "cmd_arg_%d", in_cmd_args_count); + ret = dict_set_str (dict, cmd_arg_name, + (char *)words[2+i]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to set %s in dict", + cmd_arg_name); + goto out; + } + } + + ret = dict_set_int32 (dict, "cmd_args_count", in_cmd_args_count); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set cmd_args_count in dict"); + goto out; + } + + ret = dict_set_str (dict, "volname", "N/A"); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to set volname in dict"); + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SYS_EXEC]; + if (proc && proc->fn) { + frame = create_frame (THIS, THIS->ctx->pool); + if (!frame) + goto out; + CLI_LOCAL_INIT (local, words, frame, dict); + ret = proc->fn (frame, THIS, (void*)dict); + } +out: + return ret; +} + +int +cli_cmd_copy_file_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + char *filename = ""; + dict_t *dict = NULL; + cli_local_t *local = NULL; + + if (wordcount != 4) { + cli_usage_out (word->pattern); + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + filename = (char*)words[3]; + ret = dict_set_str (dict, "source", filename); + if (ret) + gf_log ("", GF_LOG_ERROR, "Unable to set filename in dict"); + + ret = dict_set_str (dict, "volname", "N/A"); + if (ret) + gf_log ("", GF_LOG_ERROR, "Unable to set volname in dict"); + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_COPY_FILE]; + if (proc && proc->fn) { + frame = create_frame (THIS, THIS->ctx->pool); + if (!frame) + goto out; + CLI_LOCAL_INIT (local, words, frame, dict); + ret = proc->fn (frame, THIS, (void*)dict); + } +out: + return ret; +} + +int cli_cmd_system_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index f7586c44c..100be0b73 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -29,19 +29,6 @@ #include "cli1-xdr.h" #include "run.h" -#define CLI_LOCAL_INIT(local, words, frame, dictionary) \ - do { \ - local = cli_local_get (); \ - \ - if (local) { \ - local->words = words; \ - if (dictionary) \ - local->dict = dictionary; \ - if (frame) \ - frame->local = local; \ - } \ - } while (0) - extern struct rpc_clnt *global_rpc; extern rpc_clnt_prog_t *cli_rpc_prog; @@ -125,6 +112,11 @@ cli_cmd_sync_volume_cbk (struct cli_state *state, struct cli_cmd_word *word, int parse_error = 0; dict_t *dict = NULL; cli_local_t *local = NULL; + gf_answer_t answer = GF_ANSWER_NO; + const char *question = "Sync volume may make data " + "inaccessible while the sync " + "is in progress. Do you want " + "to continue?"; if ((wordcount < 3) || (wordcount > 4)) { cli_usage_out (word->pattern); @@ -159,6 +151,14 @@ cli_cmd_sync_volume_cbk (struct cli_state *state, struct cli_cmd_word *word, goto out; } + if (!(state->mode & GLUSTER_MODE_SCRIPT)) { + answer = cli_cmd_get_confirmation (state, question); + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } + } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SYNC_VOLUME]; frame = create_frame (THIS, THIS->ctx->pool); @@ -392,6 +392,15 @@ cli_cmd_volume_create_cbk (struct cli_state *state, struct cli_cmd_word *word, } } + if (state->mode & GLUSTER_MODE_SCRIPT) { + ret = dict_set_int32 (options, "force", _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to set force " + "option"); + goto out; + } + } + CLI_LOCAL_INIT (local, words, frame, options); if (proc->fn) { @@ -888,6 +897,7 @@ cli_cmd_volume_set_cbk (struct cli_state *state, struct cli_cmd_word *word, call_frame_t *frame = NULL; dict_t *options = NULL; cli_local_t *local = NULL; + char *op_errstr = NULL; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SET_VOLUME]; @@ -895,9 +905,14 @@ cli_cmd_volume_set_cbk (struct cli_state *state, struct cli_cmd_word *word, if (!frame) goto out; - ret = cli_cmd_volume_set_parse (words, wordcount, &options); + ret = cli_cmd_volume_set_parse (words, wordcount, &options, &op_errstr); if (ret) { - cli_usage_out (word->pattern); + if (op_errstr) { + cli_err ("%s", op_errstr); + GF_FREE (op_errstr); + } else + cli_usage_out (word->pattern); + parse_error = 1; goto out; } @@ -963,6 +978,15 @@ cli_cmd_volume_add_brick_cbk (struct cli_state *state, } } + if (state->mode & GLUSTER_MODE_SCRIPT) { + ret = dict_set_int32 (options, "force", _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to set force " + "option"); + goto out; + } + } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_ADD_BRICK]; CLI_LOCAL_INIT (local, words, frame, options); @@ -1131,6 +1155,15 @@ cli_cmd_volume_replace_brick_cbk (struct cli_state *state, goto out; } + if (state->mode & GLUSTER_MODE_SCRIPT) { + ret = dict_set_int32 (options, "force", _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to set force" + "option"); + goto out; + } + } + CLI_LOCAL_INIT (local, words, frame, options); if (proc->fn) { @@ -1795,7 +1828,13 @@ struct cli_cmd volume_cmds[] = { cli_cmd_volume_info_cbk, "list information of all volumes"}, - { "volume create <NEW-VOLNAME> [stripe <COUNT>] [replica <COUNT>] [transport <tcp|rdma|tcp,rdma>] <NEW-BRICK> ...", + { "volume create <NEW-VOLNAME> [stripe <COUNT>] [replica <COUNT>] " + "[transport <tcp|rdma|tcp,rdma>] <NEW-BRICK>" +#ifdef HAVE_BD_XLATOR + "?<vg_name>" +#endif + "... [force]", + cli_cmd_volume_create_cbk, "create a new volume of specified type with mentioned bricks"}, @@ -1815,11 +1854,11 @@ struct cli_cmd volume_cmds[] = { cli_cmd_volume_rename_cbk, "rename volume <VOLNAME> to <NEW-VOLNAME>"},*/ - { "volume add-brick <VOLNAME> [<stripe|replica> <COUNT>] <NEW-BRICK> ...", + { "volume add-brick <VOLNAME> [<stripe|replica> <COUNT>] <NEW-BRICK> ... [force]", cli_cmd_volume_add_brick_cbk, "add brick to volume <VOLNAME>"}, - { "volume remove-brick <VOLNAME> [replica <COUNT>] <BRICK> ... {start|stop|status|commit|force}", + { "volume remove-brick <VOLNAME> [replica <COUNT>] <BRICK> ... [start|stop|status|commit|force]", cli_cmd_volume_remove_brick_cbk, "remove brick from volume <VOLNAME>"}, @@ -1827,7 +1866,7 @@ struct cli_cmd volume_cmds[] = { cli_cmd_volume_defrag_cbk, "rebalance operations"}, - { "volume replace-brick <VOLNAME> <BRICK> <NEW-BRICK> {start|pause|abort|status|commit [force]}", + { "volume replace-brick <VOLNAME> <BRICK> <NEW-BRICK> {start [force]|pause|abort|status|commit [force]}", cli_cmd_volume_replace_brick_cbk, "replace-brick operations"}, @@ -1856,13 +1895,14 @@ struct cli_cmd volume_cmds[] = { "reset all the reconfigured options"}, #if (SYNCDAEMON_COMPILE) - {"volume "GEOREP" [<VOLNAME>] [<SLAVE-URL>] {start|stop|config|status|log-rotate} [options...]", + {"volume "GEOREP" [<VOLNAME>] [<SLAVE-URL>] {create [push-pem] [force]" + "|start [force]|stop [force]|config|status [detail]|delete} [options...]", cli_cmd_volume_gsync_set_cbk, "Geo-sync operations", cli_cmd_check_gsync_exists_cbk}, #endif - { "volume profile <VOLNAME> {start|info|stop} [nfs]", + { "volume profile <VOLNAME> {start|stop|info [nfs]}", cli_cmd_volume_profile_cbk, "volume profile operations"}, @@ -1870,18 +1910,17 @@ struct cli_cmd volume_cmds[] = { cli_cmd_quota_cbk, "quota translator specific operations"}, - { "volume top <VOLNAME> {[open|read|write|opendir|readdir [nfs]] " - "|[read-perf|write-perf [nfs|{bs <size> count <count>}]]" - "|[clear [nfs]]} [brick <brick>] [list-cnt <count>]", + { "volume top <VOLNAME> {open|read|write|opendir|readdir|clear} [nfs|brick <brick>] [list-cnt <value>] |\n" + "volume top <VOLNAME> {read-perf|write-perf} [bs <size> count <count>] [brick <brick>] [list-cnt <value>]", cli_cmd_volume_top_cbk, "volume top operations"}, { "volume status [all | <VOLNAME> [nfs|shd|<BRICK>]]" - " [detail|clients|mem|inode|fd|callpool]", + " [detail|clients|mem|inode|fd|callpool|tasks]", cli_cmd_volume_status_cbk, "display status of all or specified volume(s)/brick"}, - { "volume heal <VOLNAME> [{full | info {healed | heal-failed | split-brain}}]", + { "volume heal <VOLNAME> [{full | statistics {heal-count {replica <hostname:brickname>}} |info {healed | heal-failed | split-brain}}]", cli_cmd_volume_heal_cbk, "self-heal commands on volume specified by <VOLNAME>"}, diff --git a/cli/src/cli-cmd.c b/cli/src/cli-cmd.c index 64aba5d9f..b81f75b5b 100644 --- a/cli/src/cli-cmd.c +++ b/cli/src/cli-cmd.c @@ -231,6 +231,9 @@ cli_cmds_register (struct cli_state *state) if (ret) goto out; + ret = cli_cmd_snapshot_register (state); + if (ret) + goto out; out: return ret; } @@ -360,8 +363,11 @@ cli_cmd_submit (void *req, call_frame_t *frame, int ret = -1; unsigned timeout = 0; - timeout = (GLUSTER_CLI_PROFILE_VOLUME == procnum) ? - CLI_TOP_CMD_TIMEOUT : CLI_DEFAULT_CMD_TIMEOUT; + if ((GLUSTER_CLI_PROFILE_VOLUME == procnum) || + (GLUSTER_CLI_HEAL_VOLUME == procnum)) + timeout = CLI_TEN_MINUTES_TIMEOUT; + else + timeout = CLI_DEFAULT_CMD_TIMEOUT; cli_cmd_lock (); cmd_sent = 0; diff --git a/cli/src/cli-cmd.h b/cli/src/cli-cmd.h index 0ec316774..041729276 100644 --- a/cli/src/cli-cmd.h +++ b/cli/src/cli-cmd.h @@ -20,6 +20,19 @@ #include "cli.h" #include "list.h" +#define CLI_LOCAL_INIT(local, words, frame, dictionary) \ + do { \ + local = cli_local_get (); \ + \ + if (local) { \ + local->words = words; \ + if (dictionary) \ + local->dict = dictionary; \ + if (frame) \ + frame->local = local; \ + } \ + } while (0) + #define CLI_STACK_DESTROY(_frame) \ do { \ if (_frame) { \ @@ -80,6 +93,8 @@ int cli_cmd_probe_register (struct cli_state *state); int cli_cmd_system_register (struct cli_state *state); +int cli_cmd_snapshot_register (struct cli_state *state); + int cli_cmd_misc_register (struct cli_state *state); struct cli_cmd_word *cli_cmd_nextword (struct cli_cmd_word *word, @@ -105,4 +120,5 @@ cli_cmd_submit (void *req, call_frame_t *frame, gf_answer_t cli_cmd_get_confirmation (struct cli_state *state, const char *question); int cli_cmd_sent_status_get (int *status); + #endif /* __CLI_CMD_H__ */ diff --git a/cli/src/cli-rl.c b/cli/src/cli-rl.c index 6f75b6f4c..ade1c8ebb 100644 --- a/cli/src/cli-rl.c +++ b/cli/src/cli-rl.c @@ -365,7 +365,8 @@ cli_rl_input (void *_data) if (!line) exit(0); //break; - cli_rl_process_line (line); + if (*line) + cli_rl_process_line (line); free (line); } diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 2e0c58402..bfeb854ad 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -13,10 +13,6 @@ #include "config.h" #endif -#ifndef GSYNC_CONF -#define GSYNC_CONF GEOREP"/gsyncd.conf" -#endif - /* Widths of various columns in top read/write-perf output * Total width of top read/write-perf should be 80 chars * including one space between column @@ -26,6 +22,8 @@ #define VOL_TOP_PERF_SPEED_WIDTH 4 #define VOL_TOP_PERF_TIME_WIDTH 26 +#define INDENT_MAIN_HEAD "%-25s %s " + #include "cli.h" #include "compat-errno.h" #include "cli-cmd.h" @@ -62,6 +60,17 @@ char *cli_vol_status_str[] = {"Created", "Stopped", }; +char *cli_vol_task_status_str[] = {"not started", + "in progress", + "stopped", + "completed", + "failed", + "fix-layout in progress", + "fix-layout stopped", + "fix-layout completed", + "fix-layout failed", +}; + int32_t gf_cli_get_volume (call_frame_t *frame, xlator_t *this, void *data); @@ -87,7 +96,7 @@ int gf_cli_probe_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) { - gf1_cli_probe_rsp rsp = {0,}; + gf_cli_rsp rsp = {0,}; int ret = -1; char msg[1024] = {0,}; @@ -95,92 +104,28 @@ gf_cli_probe_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_probe_rsp); + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); //rsp.op_ret = -1; //rsp.op_errno = EINVAL; goto out; } gf_log ("cli", GF_LOG_INFO, "Received resp to probe"); - if (!rsp.op_ret) { - switch (rsp.op_errno) { - case GF_PROBE_SUCCESS: - snprintf (msg, sizeof (msg), - "success"); - break; - case GF_PROBE_LOCALHOST: - snprintf (msg, sizeof (msg), - "success: on localhost not needed"); - break; - case GF_PROBE_FRIEND: - snprintf (msg, sizeof (msg), - "success: host %s port %d already" - " in peer list", rsp.hostname, - rsp.port); - break; - default: - rsp.op_ret = -1; - snprintf (msg, sizeof (msg), - "Probe returned with unknown errno" - " %d", rsp.op_errno); - break; - } - } - if (rsp.op_ret) { - if (rsp.op_errstr && (strlen (rsp.op_errstr) > 0)) { - snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); - } else { - switch (rsp.op_errno) { - case GF_PROBE_ANOTHER_CLUSTER: - snprintf (msg, sizeof (msg), - "%s is already part of " - "another cluster", - rsp.hostname); - break; - case GF_PROBE_VOLUME_CONFLICT: - snprintf (msg, sizeof (msg), - "Atleast one volume on %s " - "conflicts with existing " - "volumes in the cluster", - rsp.hostname); - break; - case GF_PROBE_UNKNOWN_PEER: - snprintf (msg, sizeof (msg), - "%s responded with 'unknown " - "peer' error, this could " - "happen if %s doesn't have " - "localhost in its peer " - "database", rsp.hostname, - rsp.hostname); - break; - case GF_PROBE_ADD_FAILED: - snprintf (msg, sizeof (msg), - "Failed to add peer " - "information on %s" , - rsp.hostname); - break; - case GF_PROBE_SAME_UUID: - snprintf (msg, sizeof (msg), - "Peer uuid (host %s) is" - "same as local uuid", - rsp.hostname); - break; - default: - snprintf (msg, sizeof (msg), - "Probe returned with unknown " - "errno %d", rsp.op_errno); - break; - } - } - gf_log ("cli", GF_LOG_ERROR, "%s", msg); + if (rsp.op_errstr && (strlen (rsp.op_errstr) > 0)) { + snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); + if (rsp.op_ret) + gf_log ("cli", GF_LOG_ERROR, "%s", msg); } if (global_state->mode & GLUSTER_MODE_XML) { - ret = cli_xml_output_str ("peerProbe", msg, rsp.op_ret, - rsp.op_errno, NULL); + ret = cli_xml_output_str (NULL, + (rsp.op_ret)? NULL : msg, + rsp.op_ret, rsp.op_errno, + (rsp.op_ret)? msg : NULL); if (ret) gf_log ("cli", GF_LOG_ERROR, "Error outputting to xml"); @@ -188,7 +133,7 @@ gf_cli_probe_cbk (struct rpc_req *req, struct iovec *iov, } if (!rsp.op_ret) - cli_out ("peer probe: %s", msg); + cli_out ("peer probe: success. %s", msg); else cli_err ("peer probe: failed: %s", msg); @@ -203,67 +148,39 @@ int gf_cli_deprobe_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) { - gf1_cli_deprobe_rsp rsp = {0,}; + gf_cli_rsp rsp = {0,}; int ret = -1; - char msg[1024] = {0,}; + char msg[1024] = {0,}; if (-1 == req->rpc_status) { goto out; } - ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_deprobe_rsp); + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); //rsp.op_ret = -1; //rsp.op_errno = EINVAL; goto out; } gf_log ("cli", GF_LOG_INFO, "Received resp to deprobe"); + if (rsp.op_ret) { if (strlen (rsp.op_errstr) > 0) { snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); gf_log ("cli", GF_LOG_ERROR, "%s", rsp.op_errstr); - } else { - switch (rsp.op_errno) { - case GF_DEPROBE_LOCALHOST: - snprintf (msg, sizeof (msg), - "%s is localhost", - rsp.hostname); - break; - case GF_DEPROBE_NOT_FRIEND: - snprintf (msg, sizeof (msg), - "%s is not part of cluster", - rsp.hostname); - break; - case GF_DEPROBE_BRICK_EXIST: - snprintf (msg, sizeof (msg), - "Brick(s) with the peer %s " - "exist in cluster", - rsp.hostname); - break; - case GF_DEPROBE_FRIEND_DOWN: - snprintf (msg, sizeof (msg), - "One of the peers is probably" - " down. Check with 'peer " - "status'."); - break; - default: - snprintf (msg, sizeof (msg), - "Detach returned with unknown" - " errno %d", rsp.op_errno); - break; - } - gf_log ("cli", GF_LOG_ERROR,"Detach failed with op_ret " - "%d and op_errno %d", rsp.op_ret, rsp.op_errno); } } else { snprintf (msg, sizeof (msg), "success"); } if (global_state->mode & GLUSTER_MODE_XML) { - ret = cli_xml_output_str ("peerDetach", msg, rsp.op_ret, - rsp.op_errno, NULL); + ret = cli_xml_output_str (NULL, + (rsp.op_ret)? NULL : msg, + rsp.op_ret, rsp.op_errno, + (rsp.op_ret)? msg : NULL); if (ret) gf_log ("cli", GF_LOG_ERROR, "Error outputting to xml"); @@ -283,21 +200,128 @@ out: } int -gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +gf_cli_output_peer_status (dict_t *dict, int count) { - gf1_cli_peer_list_rsp rsp = {0,}; int ret = -1; - dict_t *dict = NULL; char *uuid_buf = NULL; char *hostname_buf = NULL; int32_t i = 1; char key[256] = {0,}; char *state = NULL; - int32_t port = 0; int32_t connected = 0; char *connected_str = NULL; + + cli_out ("Number of Peers: %d", count); + i = 1; + while ( i <= count) { + snprintf (key, 256, "friend%d.uuid", i); + ret = dict_get_str (dict, key, &uuid_buf); + if (ret) + goto out; + + snprintf (key, 256, "friend%d.hostname", i); + ret = dict_get_str (dict, key, &hostname_buf); + if (ret) + goto out; + + snprintf (key, 256, "friend%d.connected", i); + ret = dict_get_int32 (dict, key, &connected); + if (ret) + goto out; + if (connected) + connected_str = "Connected"; + else + connected_str = "Disconnected"; + + + snprintf (key, 256, "friend%d.state", i); + ret = dict_get_str (dict, key, &state); + if (ret) + goto out; + + cli_out ("\nHostname: %s\nUuid: %s\nState: %s (%s)", + hostname_buf, uuid_buf, state, connected_str); + i++; + } + + ret = 0; +out: + return ret; +} + +int +gf_cli_output_pool_list (dict_t *dict, int count) +{ + int ret = -1; + char *uuid_buf = NULL; + char *hostname_buf = NULL; + int32_t i = 1; + char key[256] = {0,}; + int32_t connected = 0; + char *connected_str = NULL; + + if (count >= 1) + cli_out ("UUID\t\t\t\t\tHostname\tState"); + + while ( i <= count) { + snprintf (key, 256, "friend%d.uuid", i); + ret = dict_get_str (dict, key, &uuid_buf); + if (ret) + goto out; + + snprintf (key, 256, "friend%d.hostname", i); + ret = dict_get_str (dict, key, &hostname_buf); + if (ret) + goto out; + + snprintf (key, 256, "friend%d.connected", i); + ret = dict_get_int32 (dict, key, &connected); + if (ret) + goto out; + if (connected) + connected_str = "Connected"; + else + connected_str = "Disconnected"; + + cli_out ("%s\t%-9s\t%s ", uuid_buf, hostname_buf, + connected_str); + i++; + } + + ret = 0; +out: + return ret; +} + +/* function pointer for gf_cli_output_{pool_list,peer_status} */ +typedef int (*cli_friend_output_fn) (dict_t*, int); + +int +gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + gf1_cli_peer_list_rsp rsp = {0,}; + int ret = -1; + dict_t *dict = NULL; char msg[1024] = {0,}; + char *cmd = NULL; + cli_friend_output_fn friend_output_fn; + call_frame_t *frame = NULL; + unsigned long flags = 0; + + frame = myframe; + flags = (long)frame->local; + + if (flags == GF_CLI_LIST_POOL_NODES) { + cmd = "pool list"; + friend_output_fn = &gf_cli_output_pool_list; + } else { + cmd = "peer status"; + friend_output_fn = &gf_cli_output_peer_status; + } + + /* 'free' the flags set by gf_cli_list_friends */ + frame->local = NULL; if (-1 == req->rpc_status) { goto out; @@ -305,13 +329,14 @@ gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_peer_list_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); //rsp.op_ret = -1; //rsp.op_errno = EINVAL; goto out; } - gf_log ("cli", GF_LOG_INFO, "Received resp to list: %d", + gf_log ("cli", GF_LOG_DEBUG, "Received resp to list: %d", rsp.op_ret); ret = rsp.op_ret; @@ -320,7 +345,7 @@ gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, if (!rsp.friends.friends_len) { snprintf (msg, sizeof (msg), - "peer status: No peers present"); + "%s: No peers present", cmd); if (global_state->mode & GLUSTER_MODE_XML) { ret = cli_xml_output_peer_status (dict, rsp.op_ret, @@ -367,49 +392,9 @@ gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - cli_out ("Number of Peers: %d", count); - i = 1; - while ( i <= count) { - snprintf (key, 256, "friend%d.uuid", i); - ret = dict_get_str (dict, key, &uuid_buf); - if (ret) - goto out; - - snprintf (key, 256, "friend%d.hostname", i); - ret = dict_get_str (dict, key, &hostname_buf); - if (ret) - goto out; - - snprintf (key, 256, "friend%d.connected", i); - ret = dict_get_int32 (dict, key, &connected); - if (ret) - goto out; - if (connected) - connected_str = "Connected"; - else - connected_str = "Disconnected"; - - snprintf (key, 256, "friend%d.port", i); - ret = dict_get_int32 (dict, key, &port); - if (ret) - goto out; - - snprintf (key, 256, "friend%d.state", i); - ret = dict_get_str (dict, key, &state); - if (ret) - goto out; - - if (!port) { - cli_out ("\nHostname: %s\nUuid: %s\nState: %s " - "(%s)", - hostname_buf, uuid_buf, state, - connected_str); - } else { - cli_out ("\nHostname: %s\nPort: %d\nUuid: %s\n" - "State: %s (%s)", hostname_buf, port, - uuid_buf, state, connected_str); - } - i++; + ret = friend_output_fn (dict, count); + if (ret) { + goto out; } } else { if (global_state->mode & GLUSTER_MODE_XML) { @@ -430,7 +415,7 @@ gf_cli_list_friends_cbk (struct rpc_req *req, struct iovec *iov, out: cli_cmd_broadcast_response (ret); if (ret) - cli_err ("peer status: failed"); + cli_err ("%s: failed", cmd); if (dict) dict_destroy (dict); @@ -464,6 +449,30 @@ cli_out_options ( char *substr, char *optstr, char *valstr) cli_out ("%s: %s",ptr2 , valstr); } +static int +_gf_cli_output_volinfo_opts (dict_t *d, char *k, + data_t *v, void *tmp) +{ + int ret = 0; + char *key = NULL; + char *ptr = NULL; + data_t *value = NULL; + + key = tmp; + + ptr = strstr (k, "option."); + if (ptr) { + value = v; + if (!value) { + ret = -1; + goto out; + } + cli_out_options (key, k, v->data); + } +out: + return ret; +} + int gf_cli_get_volume_cbk (struct rpc_req *req, struct iovec *iov, @@ -481,23 +490,27 @@ gf_cli_get_volume_cbk (struct rpc_req *req, struct iovec *iov, int32_t replica_count = 0; int32_t vol_type = 0; int32_t transport = 0; - char *ptr = NULL; char *volume_id_str = NULL; char *brick = NULL; char *volname = NULL; dict_t *dict = NULL; - data_t *value = NULL; cli_local_t *local = NULL; char key[1024] = {0}; char err_str[2048] = {0}; gf_cli_rsp rsp = {0}; + char *caps = NULL; + int k __attribute__((unused)) = 0; + // snap_volume variable helps in showing whether a volume is a normal + //volume or a volume for the snapshot + int32_t snap_volume = 0; if (-1 == req->rpc_status) goto out; ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("cli", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -611,6 +624,11 @@ xml_output: if (ret) goto out; + snprintf (key, sizeof (key), "volume%d.snap_volume", i); + ret = dict_get_int32 (dict, key, &snap_volume); + if (ret) + goto out; + snprintf (key, 256, "volume%d.brick_count", i); ret = dict_get_int32 (dict, key, &brick_count); if (ret) @@ -651,6 +669,44 @@ xml_output: cli_out ("Type: %s", cli_vol_type_str[vol_type]); cli_out ("Volume ID: %s", volume_id_str); cli_out ("Status: %s", cli_vol_status_str[status]); + if (snap_volume) + cli_out ("Snap Volume: %s", "yes"); + else + cli_out ("Snap Volume: %s", "no"); + +#ifdef HAVE_BD_XLATOR + k = 0; + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "volume%d.xlator%d", i, k); + ret = dict_get_str (dict, key, &caps); + if (ret) + goto next; + do { + j = 0; + cli_out ("Xlator %d: %s", k + 1, caps); + do { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), + "volume%d.xlator%d.caps%d", + i, k, j++); + ret = dict_get_str (dict, key, &caps); + if (ret) + break; + cli_out ("Capability %d: %s", j, caps); + } while (1); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), + "volume%d.xlator%d", i, ++k); + ret = dict_get_str (dict, key, &caps); + if (ret) + break; + } while (1); + +next: +#else + caps = 0; /* Avoid compiler warnings when BD not enabled */ +#endif if (type == GF_CLUSTER_TYPE_STRIPE_REPLICATE) { cli_out ("Number of Bricks: %d x %d x %d = %d", @@ -658,10 +714,8 @@ xml_output: stripe_count, replica_count, brick_count); - } else if (type == GF_CLUSTER_TYPE_NONE) { cli_out ("Number of Bricks: %d", brick_count); - } else { /* For both replicate and stripe, dist_count is good enough */ @@ -689,6 +743,12 @@ xml_output: goto out; cli_out ("Brick%d: %s", j, brick); +#ifdef HAVE_BD_XLATOR + snprintf (key, 256, "volume%d.vg%d", i, j); + ret = dict_get_str (dict, key, &caps); + if (!ret) + cli_out ("Brick%d VG: %s", j, caps); +#endif j++; } @@ -703,22 +763,8 @@ xml_output: cli_out ("Options Reconfigured:"); snprintf (key, 256, "volume%d.option.",i); - int _output_volinfo_opts (dict_t *d, char *k, - data_t *v, void *tmp) - { - ptr = strstr (k, "option."); - if (ptr) { - value = v; - if (!value) { - ret = -1; - goto internal_out; - } - cli_out_options (key, k, v->data); - } - internal_out: - return ret; - } - ret = dict_foreach (dict, _output_volinfo_opts, NULL); + + ret = dict_foreach (dict, _gf_cli_output_volinfo_opts, key); if (ret) goto out; @@ -762,7 +808,8 @@ gf_cli_create_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -829,20 +876,22 @@ gf_cli_delete_volume_cbk (struct rpc_req *req, struct iovec *iov, goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } - frame = myframe; local = frame->local; if (local) dict = local->dict; ret = dict_get_str (dict, "volname", &volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (frame->this->name, GF_LOG_ERROR, "dict get failed"); goto out; } @@ -890,6 +939,144 @@ out: } int +gf_cli3_1_uuid_get_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + char *uuid_str = NULL; + gf_cli_rsp rsp = {0,}; + int ret = -1; + cli_local_t *local = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + + if (-1 == req->rpc_status) + goto out; + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + local = frame->local; + frame->local = NULL; + + gf_log ("cli", GF_LOG_INFO, "Received resp to uuid get"); + + dict = dict_new (); + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, + &dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to unserialize " + "response for uuid get"); + goto out; + } + + ret = dict_get_str (dict, "uuid", &uuid_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get uuid " + "from dictionary"); + goto out; + } + + if (global_state->mode & GLUSTER_MODE_XML) { + ret = cli_xml_output_dict ("uuidGenerate", dict, rsp.op_ret, + rsp.op_errno, rsp.op_errstr); + if (ret) + gf_log ("cli", GF_LOG_ERROR, + "Error outputting to xml"); + goto out; + } + + if (rsp.op_ret) { + if (strcmp (rsp.op_errstr, "") == 0) + cli_err ("Get uuid was unsuccessful"); + else + cli_err ("%s", rsp.op_errstr); + + } else { + cli_out ("UUID: %s", uuid_str); + + } + ret = rsp.op_ret; + +out: + cli_cmd_broadcast_response (ret); + cli_local_wipe (local); + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + if (dict) + dict_unref (dict); + + gf_log ("", GF_LOG_DEBUG, "Returning with %d", ret); + return ret; +} + +int +gf_cli3_1_uuid_reset_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + gf_cli_rsp rsp = {0,}; + int ret = -1; + cli_local_t *local = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + + if (-1 == req->rpc_status) { + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + local = frame->local; + frame->local = NULL; + + gf_log ("cli", GF_LOG_INFO, "Received resp to uuid reset"); + + if (global_state->mode & GLUSTER_MODE_XML) { + ret = cli_xml_output_dict ("uuidReset", dict, rsp.op_ret, + rsp.op_errno, rsp.op_errstr); + if (ret) + gf_log ("cli", GF_LOG_ERROR, + "Error outputting to xml"); + goto out; + } + + if (rsp.op_ret && strcmp (rsp.op_errstr, "")) + cli_err ("%s", rsp.op_errstr); + else + cli_out ("resetting the peer uuid has been %s", + (rsp.op_ret) ? "unsuccessful": "successful"); + ret = rsp.op_ret; + +out: + cli_cmd_broadcast_response (ret); + cli_local_wipe (local); + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + if (dict) + dict_unref (dict); + + gf_log ("", GF_LOG_INFO, "Returning with %d", ret); + return ret; +} + +int gf_cli_start_volume_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) { @@ -905,14 +1092,15 @@ gf_cli_start_volume_cbk (struct rpc_req *req, struct iovec *iov, goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } - frame = myframe; - if (frame) local = frame->local; @@ -921,7 +1109,7 @@ gf_cli_start_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_str (dict, "volname", &volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "dict get failed"); + gf_log (frame->this->name, GF_LOG_ERROR, "dict get failed"); goto out; } @@ -982,14 +1170,15 @@ gf_cli_stop_volume_cbk (struct rpc_req *req, struct iovec *iov, goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } - frame = myframe; - if (frame) local = frame->local; @@ -997,7 +1186,7 @@ gf_cli_stop_volume_cbk (struct rpc_req *req, struct iovec *iov, dict = local->dict; ret = dict_get_str (dict, "volname", &volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (frame->this->name, GF_LOG_ERROR, "Unable to get volname from dict"); goto out; } @@ -1063,26 +1252,29 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, char msg[1024] = {0,}; gf_defrag_status_t status_rcd = GF_DEFRAG_STATUS_NOT_STARTED; int32_t counter = 0; - char *node_uuid = NULL; + char *node_name = NULL; char key[256] = {0,}; int32_t i = 1; uint64_t failures = 0; + uint64_t skipped = 0; double elapsed = 0; char *size_str = NULL; + char *task_id_str = NULL; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } - frame = myframe; - if (frame) local = frame->local; @@ -1091,14 +1283,14 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_str (local_dict, "volname", &volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (frame->this->name, GF_LOG_ERROR, "Failed to get volname"); goto out; } ret = dict_get_int32 (local_dict, "rebalance-command", (int32_t*)&cmd); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (frame->this->name, GF_LOG_ERROR, "Failed to get command"); goto out; } @@ -1118,15 +1310,24 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, } } - if (!((cmd == GF_DEFRAG_CMD_STOP) || (cmd == GF_DEFRAG_CMD_STATUS))) { - /* All other possibility is about starting a volume */ - if (rsp.op_ret && strcmp (rsp.op_errstr, "")) + if (!((cmd == GF_DEFRAG_CMD_STOP) || (cmd == GF_DEFRAG_CMD_STATUS)) && + !(global_state->mode & GLUSTER_MODE_XML)) { + /* All other possibilites are about starting a rebalance */ + ret = dict_get_str (dict, GF_REBALANCE_TID_KEY, &task_id_str); + if (rsp.op_ret && strcmp (rsp.op_errstr, "")) { snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); - else - snprintf (msg, sizeof (msg), - "Starting rebalance on volume %s has been %s", - volname, (rsp.op_ret) ? "unsuccessful": - "successful"); + } else { + if (!rsp.op_ret) { + snprintf (msg, sizeof (msg), + "Starting rebalance on volume %s has " + "been successful.\nID: %s", volname, + task_id_str); + } else { + snprintf (msg, sizeof (msg), + "Starting rebalance on volume %s has " + "been unsuccessful.", volname); + } + } goto done; } @@ -1142,8 +1343,12 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, goto done; } else { snprintf (msg, sizeof (msg), - "Stopped rebalance process on volume %s \n", - volname); + "rebalance process may be in the middle of a " + "file migration.\nThe process will be fully " + "stopped once the migration of the file is " + "complete.\nPlease check rebalance process " + "for completion before doing any further " + "brick related tasks on the volume."); } } if (cmd == GF_DEFRAG_CMD_STATUS) { @@ -1168,86 +1373,76 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_int32 (dict, "count", &counter); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "count not set"); + gf_log (frame->this->name, GF_LOG_ERROR, "count not set"); goto out; } - cli_out ("%40s %16s %13s %13s %13s %14s %s", "Node", "Rebalanced-files", - "size", "scanned", "failures", "status", "run time in secs"); - cli_out ("%40s %16s %13s %13s %13s %14s %16s", "---------", + cli_out ("%40s %16s %13s %13s %13s %13s %20s %18s", "Node", + "Rebalanced-files", "size", "scanned", "failures", "skipped", + "status", "run time in secs"); + cli_out ("%40s %16s %13s %13s %13s %13s %20s %18s", "---------", "-----------", "-----------", "-----------", "-----------", - "------------", "--------------"); + "-----------", "------------", "--------------"); do { - snprintf (key, 256, "node-uuid-%d", i); - ret = dict_get_str (dict, key, &node_uuid); + snprintf (key, 256, "node-name-%d", i); + ret = dict_get_str (dict, key, &node_name); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, - "failed to get node-uuid"); + gf_log (frame->this->name, GF_LOG_TRACE, + "failed to get node-name"); memset (key, 0, 256); snprintf (key, 256, "files-%d", i); ret = dict_get_uint64 (dict, key, &files); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get file count"); memset (key, 0, 256); snprintf (key, 256, "size-%d", i); ret = dict_get_uint64 (dict, key, &size); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get size of xfer"); memset (key, 0, 256); snprintf (key, 256, "lookups-%d", i); ret = dict_get_uint64 (dict, key, &lookup); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get lookedup file count"); memset (key, 0, 256); snprintf (key, 256, "status-%d", i); ret = dict_get_int32 (dict, key, (int32_t *)&status_rcd); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get status"); memset (key, 0, 256); snprintf (key, 256, "failures-%d", i); ret = dict_get_uint64 (dict, key, &failures); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get failures count"); memset (key, 0, 256); + snprintf (key, 256, "skipped-%d", i); + ret = dict_get_uint64 (dict, key, &skipped); + if (ret) + gf_log (frame->this->name, GF_LOG_TRACE, + "failed to get skipped count"); + memset (key, 0, 256); snprintf (key, 256, "run-time-%d", i); ret = dict_get_double (dict, key, &elapsed); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get run-time"); - switch (status_rcd) { - case GF_DEFRAG_STATUS_NOT_STARTED: - status = "not started"; - break; - case GF_DEFRAG_STATUS_STARTED: - status = "in progress"; - break; - case GF_DEFRAG_STATUS_STOPPED: - status = "stopped"; - break; - case GF_DEFRAG_STATUS_COMPLETE: - status = "completed"; - break; - case GF_DEFRAG_STATUS_FAILED: - status = "failed"; - break; - } - + status = cli_vol_task_status_str[status_rcd]; size_str = gf_uint64_2human_readable(size); cli_out ("%40s %16"PRIu64 " %13s" " %13"PRIu64 " %13"PRIu64 - " %14s %16.2f", node_uuid, files, size_str, lookup, - failures, status, elapsed); + " %13"PRIu64 " %20s %18.2f", node_name, files, + size_str, lookup, failures, skipped, status, elapsed); GF_FREE(size_str); i++; @@ -1255,10 +1450,18 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, done: - if (rsp.op_ret) - cli_err ("volume rebalance: %s: failed: %s", volname, msg); - else - cli_out ("volume rebalance: %s: success: %s", volname, msg); + if (global_state->mode & GLUSTER_MODE_XML) + cli_xml_output_str ("volRebalance", msg, + rsp.op_ret, rsp.op_errno, + rsp.op_errstr); + else { + if (rsp.op_ret) + cli_err ("volume rebalance: %s: failed: %s", volname, + msg); + else + cli_out ("volume rebalance: %s: success: %s", volname, + msg); + } ret = rsp.op_ret; out: @@ -1284,7 +1487,8 @@ gf_cli_rename_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1328,13 +1532,14 @@ gf_cli_reset_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } gf_log ("cli", GF_LOG_INFO, "Received resp to reset"); - if (rsp.op_ret && strcmp (rsp.op_errstr, "")) + if (strcmp (rsp.op_errstr, "")) snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); else snprintf (msg, sizeof (msg), "reset volume %s", @@ -1352,7 +1557,7 @@ gf_cli_reset_volume_cbk (struct rpc_req *req, struct iovec *iov, if (rsp.op_ret) cli_err ("volume reset: failed: %s", msg); else - cli_out ("volume reset: success"); + cli_out ("volume reset: success: %s", msg); ret = rsp.op_ret; @@ -1361,6 +1566,53 @@ out: return ret; } +char * +is_server_debug_xlator (void *myframe) +{ + call_frame_t *frame = NULL; + cli_local_t *local = NULL; + char **words = NULL; + char *key = NULL; + char *value = NULL; + char *debug_xlator = NULL; + + frame = myframe; + local = frame->local; + words = (char **)local->words; + + while (*words != NULL) { + if (strstr (*words, "trace") == NULL && + strstr (*words, "error-gen") == NULL) { + words++; + continue; + } + + key = *words; + words++; + value = *words; + if (value == NULL) + break; + if (strstr (value, "client")) { + words++; + continue; + } else { + if (!(strstr (value, "posix") || strstr (value, "acl") + || strstr (value, "locks") || + strstr (value, "io-threads") || + strstr (value, "marker") || + strstr (value, "index"))) { + words++; + continue; + } else { + debug_xlator = gf_strdup (key); + break; + } + } + } + + return debug_xlator; +} + int gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) @@ -1370,6 +1622,8 @@ gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, dict_t *dict = NULL; char *help_str = NULL; char msg[1024] = {0,}; + char *debug_xlator = _gf_false; + char tmp_str[512] = {0,}; if (-1 == req->rpc_status) { goto out; @@ -1377,7 +1631,8 @@ gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1392,9 +1647,21 @@ gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + /* For brick processes graph change does not happen on the fly. + * The proces has to be restarted. So this is a check from the + * volume set option such that if debug xlators such as trace/errorgen + * are provided in the set command, warn the user. + */ + debug_xlator = is_server_debug_xlator (myframe); + if (dict_get_str (dict, "help-str", &help_str) && !msg[0]) snprintf (msg, sizeof (msg), "Set volume %s", (rsp.op_ret) ? "unsuccessful": "successful"); + if (rsp.op_ret == 0 && debug_xlator) { + snprintf (tmp_str, sizeof (tmp_str), "\n%s translator has been " + "added to the server volume file. Please restart the" + " volume for enabling the translator", debug_xlator); + } if ((global_state->mode & GLUSTER_MODE_XML) && (help_str == NULL)) { ret = cli_xml_output_str ("volSet", msg, rsp.op_ret, @@ -1405,16 +1672,20 @@ gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - if (rsp.op_ret && strcmp (rsp.op_errstr, "")) - cli_err ("volume set: failed: %s", rsp.op_errstr); - - if (!rsp.op_ret) { - if (help_str == NULL) - cli_out ("volume set: success"); + if (rsp.op_ret) { + if (strcmp (rsp.op_errstr, "")) + cli_err ("volume set: failed: %s", rsp.op_errstr); else - cli_out ("%s", help_str); + cli_err ("volume set: failed"); } else { - cli_err ("volume set: failed"); + if (help_str == NULL) { + if (debug_xlator == NULL) + cli_out ("volume set: success"); + else + cli_out ("volume set: success%s", tmp_str); + }else { + cli_out ("%s", help_str); + } } ret = rsp.op_ret; @@ -1422,6 +1693,7 @@ gf_cli_set_volume_cbk (struct rpc_req *req, struct iovec *iov, out: if (dict) dict_unref (dict); + GF_FREE (debug_xlator); cli_cmd_broadcast_response (ret); return ret; } @@ -1440,7 +1712,8 @@ gf_cli_add_brick_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1490,31 +1763,58 @@ gf_cli3_remove_brick_status_cbk (struct rpc_req *req, struct iovec *iov, char key[256] = {0,}; int32_t i = 1; int32_t counter = 0; - char *node_uuid = 0; + char *node_name = 0; gf_defrag_status_t status_rcd = GF_DEFRAG_STATUS_NOT_STARTED; uint64_t failures = 0; + uint64_t skipped = 0; double elapsed = 0; char *size_str = NULL; + int32_t command = 0; + gf1_op_commands cmd = GF_OP_CMD_NONE; + cli_local_t *local = NULL; + call_frame_t *frame = NULL; + char *cmd_str = "unknown"; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + if (frame) + local = frame->local; + ret = dict_get_int32 (local->dict, "command", &command); + if (ret) goto out; + cmd = command; + + switch (cmd) { + case GF_OP_CMD_STOP: + cmd_str = "stop"; + break; + case GF_OP_CMD_STATUS: + cmd_str = "status"; + break; + default: + break; } ret = rsp.op_ret; if (rsp.op_ret == -1) { if (strcmp (rsp.op_errstr, "")) - snprintf (msg, sizeof (msg), "volume remove-brick: " - "failed: %s", rsp.op_errstr); + snprintf (msg, sizeof (msg), "volume remove-brick %s: " + "failed: %s", cmd_str, rsp.op_errstr); else - snprintf (msg, sizeof (msg), "volume remove-brick: " - "failed: status getting failed"); + snprintf (msg, sizeof (msg), "volume remove-brick %s: " + "failed", cmd_str); if (global_state->mode & GLUSTER_MODE_XML) goto xml_output; @@ -1562,63 +1862,69 @@ xml_output: ret = dict_get_int32 (dict, "count", &counter); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "count not set"); + gf_log (frame->this->name, GF_LOG_ERROR, "count not set"); goto out; } - cli_out ("%40s %16s %13s %13s %13s %14s %s", "Node", "Rebalanced-files", - "size", "scanned", "failures", "status", "run-time in secs"); - cli_out ("%40s %16s %13s %13s %13s %14s %16s", "---------", + cli_out ("%40s %16s %13s %13s %13s %13s %14s %s", "Node", + "Rebalanced-files", "size", "scanned", "failures", "skipped", + "status", "run-time in secs"); + cli_out ("%40s %16s %13s %13s %13s %13s %14s %16s", "---------", "-----------", "-----------", "-----------", "-----------", - "------------", "--------------"); + "-----------","------------", "--------------"); do { - snprintf (key, 256, "node-uuid-%d", i); - ret = dict_get_str (dict, key, &node_uuid); + snprintf (key, 256, "node-name-%d", i); + ret = dict_get_str (dict, key, &node_name); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, - "failed to get node-uuid"); + gf_log (frame->this->name, GF_LOG_TRACE, + "failed to get node-name"); memset (key, 0, 256); snprintf (key, 256, "files-%d", i); ret = dict_get_uint64 (dict, key, &files); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get file count"); memset (key, 0, 256); snprintf (key, 256, "size-%d", i); ret = dict_get_uint64 (dict, key, &size); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get size of xfer"); memset (key, 0, 256); snprintf (key, 256, "lookups-%d", i); ret = dict_get_uint64 (dict, key, &lookup); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get lookedup file count"); memset (key, 0, 256); snprintf (key, 256, "status-%d", i); ret = dict_get_int32 (dict, key, (int32_t *)&status_rcd); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get status"); snprintf (key, 256, "failures-%d", i); ret = dict_get_uint64 (dict, key, &failures); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "Failed to get failure on files"); + snprintf (key, 256, "failures-%d", i); + ret = dict_get_uint64 (dict, key, &skipped); + if (ret) + gf_log (frame->this->name, GF_LOG_TRACE, + "Failed to get skipped files"); memset (key, 0, 256); snprintf (key, 256, "run-time-%d", i); ret = dict_get_double (dict, key, &elapsed); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "Failed to get run-time"); switch (status_rcd) { @@ -1637,17 +1943,32 @@ xml_output: case GF_DEFRAG_STATUS_FAILED: status = "failed"; break; + default: + break; } size_str = gf_uint64_2human_readable(size); - cli_out ("%40s %16"PRIu64 " %13s" " %13"PRIu64 " %13"PRIu64 - " %14s %16.2f", node_uuid, files, size_str, lookup, - failures, status, elapsed); + + if (strcmp (status, "not started")) { + cli_out ("%40s %16"PRIu64 " %13s" " %13"PRIu64 " %13" + PRIu64 " %13"PRIu64 " %14s %16.2f", node_name, + files, size_str, lookup, failures, skipped, + status, elapsed); + } GF_FREE(size_str); i++; } while (i <= counter); + if ((cmd == GF_OP_CMD_STOP) && (rsp.op_ret == 0)) { + cli_out ("'remove-brick' process may be in the middle of a " + "file migration.\nThe process will be fully stopped " + "once the migration of the file is complete.\nPlease " + "check remove-brick process for completion before " + "doing any further brick related tasks on the " + "volume."); + } + out: free (rsp.dict.dict_val); //malloced by xdr if (dict) @@ -1668,6 +1989,8 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, char *cmd_str = "unknown"; cli_local_t *local = NULL; call_frame_t *frame = NULL; + char *task_id_str = NULL; + dict_t *rsp_dict = NULL; if (-1 == req->rpc_status) { goto out; @@ -1678,7 +2001,8 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1688,10 +2012,31 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - switch (cmd) { + if (rsp.dict.dict_len) { + rsp_dict = dict_new (); + if (!rsp_dict) { + ret = -1; + goto out; + } + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, + &rsp_dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to unserialize rsp_dict"); + goto out; + } + } + + switch (cmd) { case GF_OP_CMD_START: cmd_str = "start"; + + ret = dict_get_str (rsp_dict, GF_REMOVE_BRICK_TID_KEY, &task_id_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "remove-brick-id is not present in dict"); + } break; case GF_OP_CMD_COMMIT: cmd_str = "commit"; @@ -1713,19 +2058,23 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, (rsp.op_ret) ? "unsuccessful": "successful"); if (global_state->mode & GLUSTER_MODE_XML) { - ret = cli_xml_output_vol_remove_brick (_gf_false, NULL, + ret = cli_xml_output_vol_remove_brick (_gf_false, rsp_dict, rsp.op_ret, rsp.op_errno, - rsp.op_errstr); + msg); if (ret) gf_log ("cli", GF_LOG_ERROR, "Error outputting to xml"); goto out; } - if (rsp.op_ret) - cli_err ("volume remove-brick: failed: %s", rsp.op_errstr); - else - cli_out ("volume remove-brick: success"); + if (rsp.op_ret) { + cli_err ("volume remove-brick %s: failed: %s", cmd_str, + msg); + } else { + cli_out ("volume remove-brick %s: success", cmd_str); + if (GF_OP_CMD_START == cmd && task_id_str != NULL) + cli_out ("ID: %s", task_id_str); + } ret = rsp.op_ret; @@ -1754,7 +2103,8 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, gf1_cli_replace_op replace_op = 0; char *rb_operation_str = NULL; dict_t *rsp_dict = NULL; - char msg[1024] = {0,}; + char msg[1024] = {0,}; + char *task_id_str = NULL; if (-1 == req->rpc_status) { goto out; @@ -1764,7 +2114,8 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1779,58 +2130,77 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } + if (rsp.dict.dict_len) { + /* Unserialize the dictionary */ + rsp_dict = dict_new (); + + ret = dict_unserialize (rsp.dict.dict_val, + rsp.dict.dict_len, + &rsp_dict); + if (ret < 0) { + gf_log ("glusterd", GF_LOG_ERROR, + "failed to " + "unserialize rsp buffer to dictionary"); + goto out; + } + } + switch (replace_op) { case GF_REPLACE_OP_START: - if (rsp.op_ret) - rb_operation_str = "replace-brick failed to start"; - else - rb_operation_str = "replace-brick started successfully"; + if (rsp.op_ret) { + rb_operation_str = gf_strdup ("replace-brick failed to" + " start"); + } else { + ret = dict_get_str (rsp_dict, GF_REPLACE_BRICK_TID_KEY, + &task_id_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get " + "\"replace-brick-id\" from dict"); + goto out; + } + ret = gf_asprintf (&rb_operation_str, + "replace-brick started successfully" + "\nID: %s", task_id_str); + if (ret < 0) + goto out; + } break; case GF_REPLACE_OP_STATUS: - if (rsp.op_ret || ret) - rb_operation_str = "replace-brick status unknown"; - else { - if (rsp.dict.dict_len) { - /* Unserialize the dictionary */ - rsp_dict = dict_new (); - - ret = dict_unserialize (rsp.dict.dict_val, - rsp.dict.dict_len, - &rsp_dict); - if (ret < 0) { - gf_log ("glusterd", GF_LOG_ERROR, - "failed to " - "unserialize req-buffer to dictionary"); - goto out; - } - } + if (rsp.op_ret || ret) { + rb_operation_str = gf_strdup ("replace-brick status " + "unknown"); + } else { ret = dict_get_str (rsp_dict, "status-reply", &status_reply); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "failed to" + gf_log (frame->this->name, GF_LOG_ERROR, "failed to" "get status"); goto out; } - rb_operation_str = status_reply; + rb_operation_str = gf_strdup (status_reply); } break; case GF_REPLACE_OP_PAUSE: if (rsp.op_ret) - rb_operation_str = "replace-brick pause failed"; + rb_operation_str = gf_strdup ("replace-brick pause " + "failed"); else - rb_operation_str = "replace-brick paused successfully"; + rb_operation_str = gf_strdup ("replace-brick paused " + "successfully"); break; case GF_REPLACE_OP_ABORT: if (rsp.op_ret) - rb_operation_str = "replace-brick abort failed"; + rb_operation_str = gf_strdup ("replace-brick abort " + "failed"); else - rb_operation_str = "replace-brick aborted successfully"; + rb_operation_str = gf_strdup ("replace-brick aborted " + "successfully"); break; case GF_REPLACE_OP_COMMIT: @@ -1851,9 +2221,11 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, if (rsp.op_ret || ret) - rb_operation_str = "replace-brick commit failed"; + rb_operation_str = gf_strdup ("replace-brick commit " + "failed"); else - rb_operation_str = "replace-brick commit successful"; + rb_operation_str = gf_strdup ("replace-brick commit " + "successful"); break; @@ -1864,7 +2236,7 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, } if (rsp.op_ret && (strcmp (rsp.op_errstr, ""))) { - rb_operation_str = rsp.op_errstr; + rb_operation_str = gf_strdup (rsp.op_errstr); } gf_log ("cli", GF_LOG_INFO, "Received resp to replace brick"); @@ -1888,6 +2260,17 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, ret = rsp.op_ret; out: + if (frame) + frame->local = NULL; + + if (local) { + dict_unref (local->dict); + cli_local_wipe (local); + } + + if (rb_operation_str) + GF_FREE (rb_operation_str); + cli_cmd_broadcast_response (ret); free (rsp.dict.dict_val); if (rsp_dict) @@ -1911,7 +2294,8 @@ gf_cli_log_rotate_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -1959,7 +2343,8 @@ gf_cli_sync_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -2115,15 +2500,19 @@ gf_cli_quota_cbk (struct rpc_req *req, struct iovec *iov, char *volname = NULL; char *limit_list = NULL; int32_t type = 0; - char msg[1024] = {0,}; + char msg[1024] = {0,}; + call_frame_t *frame = NULL; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -2154,17 +2543,17 @@ gf_cli_quota_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_str (dict, "volname", &volname); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get volname"); ret = dict_get_str (dict, "limit_list", &limit_list); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get limit_list"); ret = dict_get_int32 (dict, "type", &type); if (ret) - gf_log (THIS->name, GF_LOG_TRACE, + gf_log (frame->this->name, GF_LOG_TRACE, "failed to get type"); if (type == GF_QUOTA_OPTION_TYPE_LIST) { @@ -2233,14 +2622,24 @@ gf_cli_getspec_cbk (struct rpc_req *req, struct iovec *iov, gf_getspec_rsp rsp = {0,}; int ret = -1; char *spec = NULL; + call_frame_t *frame = NULL; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_getspec_rsp); - if (ret < 0 || rsp.op_ret == -1) { - gf_log ("", GF_LOG_ERROR, "error"); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + if (rsp.op_ret == -1) { + gf_log (frame->this->name, GF_LOG_ERROR, + "getspec failed"); goto out; } @@ -2270,14 +2669,24 @@ gf_cli_pmap_b2p_cbk (struct rpc_req *req, struct iovec *iov, pmap_port_by_brick_rsp rsp = {0,}; int ret = -1; char *spec = NULL; + call_frame_t *frame = NULL; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_pmap_port_by_brick_rsp); - if (ret < 0 || rsp.op_ret == -1) { - gf_log ("", GF_LOG_ERROR, "error"); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + if (rsp.op_ret == -1) { + gf_log (frame->this->name, GF_LOG_ERROR, + "pump_b2p failed"); goto out; } @@ -2298,10 +2707,9 @@ int32_t gf_cli_probe (call_frame_t *frame, xlator_t *this, void *data) { - gf1_cli_probe_req req = {0,}; + gf_cli_req req = {{0,},}; int ret = 0; dict_t *dict = NULL; - char *hostname = NULL; int port = 0; if (!frame || !this || !data) { @@ -2310,24 +2718,22 @@ gf_cli_probe (call_frame_t *frame, xlator_t *this, } dict = data; - ret = dict_get_str (dict, "hostname", &hostname); - if (ret) - goto out; ret = dict_get_int32 (dict, "port", &port); - if (ret) - port = CLI_GLUSTERD_PORT; - - req.hostname = hostname; - req.port = port; + if (ret) { + ret = dict_set_int32 (dict, "port", CLI_GLUSTERD_PORT); + if (ret) + goto out; + } - ret = cli_cmd_submit (&req, frame, cli_rpc_prog, - GLUSTER_CLI_PROBE, NULL, - this, gf_cli_probe_cbk, - (xdrproc_t)xdr_gf1_cli_probe_req); + ret = cli_to_glusterd (&req, frame, gf_cli_probe_cbk, + (xdrproc_t) xdr_gf_cli_req, dict, + GLUSTER_CLI_PROBE, this, cli_rpc_prog, NULL); out: + GF_FREE (req.dict.dict_val); gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; } @@ -2335,10 +2741,9 @@ int32_t gf_cli_deprobe (call_frame_t *frame, xlator_t *this, void *data) { - gf1_cli_deprobe_req req = {0,}; + gf_cli_req req = {{0,},}; int ret = 0; dict_t *dict = NULL; - char *hostname = NULL; int port = 0; int flags = 0; @@ -2348,51 +2753,64 @@ gf_cli_deprobe (call_frame_t *frame, xlator_t *this, } dict = data; - ret = dict_get_str (dict, "hostname", &hostname); - if (ret) - goto out; - ret = dict_get_int32 (dict, "port", &port); - if (ret) - port = CLI_GLUSTERD_PORT; + if (ret) { + ret = dict_set_int32 (dict, "port", CLI_GLUSTERD_PORT); + if (ret) + goto out; + } ret = dict_get_int32 (dict, "flags", &flags); - if (ret) - flags = 0; + if (ret) { + ret = dict_set_int32 (dict, "flags", 0); + if (ret) + goto out; + } - req.hostname = hostname; - req.port = port; - req.flags = flags; - ret = cli_cmd_submit (&req, frame, cli_rpc_prog, - GLUSTER_CLI_DEPROBE, NULL, - this, gf_cli_deprobe_cbk, - (xdrproc_t)xdr_gf1_cli_deprobe_req); + ret = cli_to_glusterd (&req, frame, gf_cli_deprobe_cbk, + (xdrproc_t)xdr_gf_cli_req, dict, + GLUSTER_CLI_DEPROBE, this, cli_rpc_prog, NULL); out: + GF_FREE (req.dict.dict_val); gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; } int32_t gf_cli_list_friends (call_frame_t *frame, xlator_t *this, - void *data) + void *data) { gf1_cli_peer_list_req req = {0,}; int ret = 0; + unsigned long flags = 0; if (!frame || !this) { ret = -1; goto out; } - req.flags = GF_CLI_LIST_ALL; + GF_ASSERT (frame->local == NULL); + flags = (long)data; + req.flags = flags; + frame->local = (void*)flags; ret = cli_cmd_submit (&req, frame, cli_rpc_prog, GLUSTER_CLI_LIST_FRIENDS, NULL, this, gf_cli_list_friends_cbk, (xdrproc_t) xdr_gf1_cli_peer_list_req); out: + if (ret) { + /* + * If everything goes fine, gf_cli_list_friends_cbk() + * [invoked through cli_cmd_submit()]resets the + * frame->local to NULL. In case cli_cmd_submit() + * fails in between, RESET frame->local here. + */ + frame->local = NULL; + } gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } @@ -2485,7 +2903,7 @@ gf_cli_get_volume (call_frame_t *frame, xlator_t *this, flags = ctx->flags; ret = dict_set_int32 (dict, "flags", flags); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "failed to set flags"); + gf_log (frame->this->name, GF_LOG_ERROR, "failed to set flags"); goto out; } @@ -2507,6 +2925,51 @@ out: return ret; } +int32_t +gf_cli3_1_uuid_get (call_frame_t *frame, xlator_t *this, + void *data) +{ + gf_cli_req req = {{0,}}; + int ret = 0; + dict_t *dict = NULL; + + if (!frame || !this || !data) { + ret = -1; + goto out; + } + + dict = data; + ret = cli_to_glusterd (&req, frame, gf_cli3_1_uuid_get_cbk, + (xdrproc_t)xdr_gf_cli_req, dict, + GLUSTER_CLI_UUID_GET, this, cli_rpc_prog, + NULL); +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int32_t +gf_cli3_1_uuid_reset (call_frame_t *frame, xlator_t *this, + void *data) +{ + gf_cli_req req = {{0,}}; + int ret = 0; + dict_t *dict = NULL; + + if (!frame || !this || !data) { + ret = -1; + goto out; + } + + dict = data; + ret = cli_to_glusterd (&req, frame, gf_cli3_1_uuid_reset_cbk, + (xdrproc_t)xdr_gf_cli_req, dict, + GLUSTER_CLI_UUID_RESET, this, cli_rpc_prog, + NULL); +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} int32_t gf_cli_create_volume (call_frame_t *frame, xlator_t *this, @@ -2803,7 +3266,7 @@ gf_cli_remove_brick (call_frame_t *frame, xlator_t *this, GLUSTER_CLI_REMOVE_BRICK, this, cli_rpc_prog, NULL); } else { - /* Need rebalance status to e sent :-) */ + /* Need rebalance status to be sent :-) */ req_dict = dict_new (); if (!req_dict) { ret = -1; @@ -2812,7 +3275,7 @@ gf_cli_remove_brick (call_frame_t *frame, xlator_t *this, ret = dict_set_str (req_dict, "volname", volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (this->name, GF_LOG_ERROR, "Failed to set dict"); goto out; } @@ -2824,7 +3287,7 @@ gf_cli_remove_brick (call_frame_t *frame, xlator_t *this, ret = dict_set_int32 (req_dict, "rebalance-command", (int32_t) cmd); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, + gf_log (this->name, GF_LOG_ERROR, "Failed to set dict"); goto out; } @@ -2975,6 +3438,7 @@ gf_cli_getspec (call_frame_t *frame, xlator_t *this, gf_getspec_req req = {0,}; int ret = 0; dict_t *dict = NULL; + dict_t *op_dict = NULL; if (!frame || !this || !data) { ret = -1; @@ -2987,12 +3451,45 @@ gf_cli_getspec (call_frame_t *frame, xlator_t *this, if (ret) goto out; + op_dict = dict_new (); + if (!dict) { + ret = -1; + goto out; + } + + // Set the supported min and max op-versions, so glusterd can make a + // decision + ret = dict_set_int32 (op_dict, "min-op-version", GD_OP_VERSION_MIN); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to set min-op-version" + " in request dict"); + goto out; + } + + ret = dict_set_int32 (op_dict, "max-op-version", GD_OP_VERSION_MAX); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to set max-op-version" + " in request dict"); + goto out; + } + + ret = dict_allocate_and_serialize (op_dict, &req.xdata.xdata_val, + &req.xdata.xdata_len); + if (ret < 0) { + gf_log (THIS->name, GF_LOG_ERROR, + "Failed to serialize dictionary"); + goto out; + } + ret = cli_cmd_submit (&req, frame, &cli_handshake_prog, GF_HNDSK_GETSPEC, NULL, this, gf_cli_getspec_cbk, (xdrproc_t) xdr_gf_getspec_req); out: + if (op_dict) { + dict_unref(op_dict); + } gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -3073,7 +3570,8 @@ gf_cli_fsm_log_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_fsm_log_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -3173,8 +3671,11 @@ gf_cli_gsync_config_command (dict_t *dict) char *subop = NULL; char *gwd = NULL; char *slave = NULL; + char *confpath = NULL; char *master = NULL; char *op_name = NULL; + int ret = -1; + char conf_path[PATH_MAX] = ""; if (dict_get_str (dict, "subop", &subop) != 0) return -1; @@ -3193,9 +3694,17 @@ gf_cli_gsync_config_command (dict_t *dict) if (dict_get_str (dict, "op_name", &op_name) != 0) op_name = NULL; + ret = dict_get_str (dict, "conf_path", &confpath); + if (!confpath) { + ret = snprintf (conf_path, sizeof(conf_path) - 1, + "%s/"GEOREP"/gsyncd_template.conf", gwd); + conf_path[ret] = '\0'; + confpath = conf_path; + } + runinit (&runner); runner_add_args (&runner, GSYNCD_PREFIX"/gsyncd", "-c", NULL); - runner_argprintf (&runner, "%s/"GSYNC_CONF, gwd); + runner_argprintf (&runner, "%s", confpath); if (master) runner_argprintf (&runner, ":%s", master); runner_add_arg (&runner, slave); @@ -3207,60 +3716,639 @@ gf_cli_gsync_config_command (dict_t *dict) } int -gf_cli_gsync_out_status (dict_t *dict) +gf_cli_fetch_gsyncd_status_values (char *status, + gf_cli_gsync_status_t *sts_val) { - int gsync_count = 0; - int i = 0; - int ret = 0; - char mst[PATH_MAX] = {0, }; - char slv[PATH_MAX]= {0, }; - char sts[PATH_MAX] = {0, }; - char hyphens[81] = {0, }; - char *mst_val = NULL; - char *slv_val = NULL; - char *sts_val = NULL; - - cli_out ("%-20s %-50s %-10s", "MASTER", "SLAVE", "STATUS"); - - for (i=0; i<sizeof(hyphens)-1; i++) - hyphens[i] = '-'; + int32_t ret = -1; + char *tmp = NULL; + char *save_ptr = NULL; + char *key = NULL; + char *value = NULL; - cli_out ("%s", hyphens); + if (!status || !sts_val) { + gf_log ("", GF_LOG_ERROR, "status or sts_val is null"); + goto out; + } + tmp = strtok_r (status, "\n", &save_ptr); - ret = dict_get_int32 (dict, "gsync-count", &gsync_count); + if (tmp) + sts_val->health = gf_strdup (tmp); + + while (tmp) { + key = strtok_r (tmp, "=", &value); + + if ((key) && (!strcmp(key, "Uptime"))) + sts_val->uptime = gf_strdup (value); + + if ((key) && (!strcmp(key, "FilesSyncd"))) + sts_val->files_syncd = gf_strdup (value); + + if ((key) && (!strcmp(key, "FilesPending"))) + sts_val->files_pending = gf_strdup (value); + + if ((key) && (!strcmp(key, "BytesPending"))) { + value = gf_uint64_2human_readable(atol(value)); + sts_val->bytes_pending = gf_strdup (value); + } + + if ((key) && (!strcmp(key, "DeletesPending"))) + sts_val->deletes_pending = gf_strdup (value); + + tmp = strtok_r (NULL, ";", &save_ptr); + } + + if (sts_val->health) + ret = 0; + + if (!sts_val->uptime) + sts_val->uptime = gf_strdup ("N/A"); + + if (!sts_val->files_syncd) + sts_val->files_syncd = gf_strdup ("N/A"); + + if (!sts_val->files_pending) + sts_val->files_pending = gf_strdup ("N/A"); + + if (!sts_val->bytes_pending) + sts_val->bytes_pending = gf_strdup ("N/A"); + + if (!sts_val->deletes_pending) + sts_val->deletes_pending = gf_strdup ("N/A"); + +out: + gf_log ("", GF_LOG_DEBUG, "Returning %d.", ret); + return ret; +} + +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; + } + +out: + return NULL; +} + +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 < 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]; + } + total_spacing += 4; /* For the spacing between the fields */ + + /* char pointers for each field */ + output_values = GF_CALLOC (number_of_fields, sizeof (char *), + gf_common_mt_char); + if (!output_values) { + ret = -1; + 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]) { + ret = -1; + goto out; + } + } + + hyphens = GF_CALLOC (total_spacing + 1, sizeof (char), + gf_common_mt_char); + 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_INFO, "No active geo-replication sessions" - "present for the selected"); + if (ret < sizeof(heading)) + heading[ret] = '\0'; + else + heading[sizeof(heading) - 1] = '\0'; ret = 0; + } else { + ret = -1; goto out; } - for (i = 1; i <= gsync_count; i++) { - snprintf (mst, sizeof(mst), "master%d", i); - snprintf (slv, sizeof(slv), "slave%d", i); - snprintf (sts, sizeof(sts), "status%d", i); + 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 < 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 %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 = 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], tmp, strlen (tmp)); + output_values[j][spacing[j]] = '\0'; + } + + 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 < number_of_fields; i++) { + if (output_values[i]) + GF_FREE (output_values[i]); + } + GF_FREE (output_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, &mst_val); + ret = dict_get_str (dict, mst, &(sts_vals[i]->master)); if (ret) goto out; - ret = dict_get_str (dict, slv, &slv_val); + ret = dict_get_str (dict, slv, &(sts_vals[i]->slave)); if (ret) goto out; - ret = dict_get_str (dict, sts, &sts_val); + ret = dict_get_str (dict, sts, &status); if (ret) goto out; - cli_out ("%-20s %-50s %-10s", mst_val, - slv_val, sts_val); + /* 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: +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) +{ + char *workdir = NULL; + char common_pem_file[PATH_MAX] = ""; + char *output = NULL; + char output_name[PATH_MAX] = ""; + int bytes_writen = 0; + int fd = -1; + int ret = -1; + int i = -1; + + ret = dict_get_str (dict, "glusterd_workdir", &workdir); + if (ret || !workdir) { + gf_log ("", GF_LOG_ERROR, "Unable to fetch workdir"); + ret = -1; + goto out; + } + + snprintf (common_pem_file, sizeof(common_pem_file), + "%s/geo-replication/common_secret.pem.pub", + workdir); + + unlink (common_pem_file); + + fd = open (common_pem_file, O_WRONLY | O_CREAT, 0600); + if (fd == -1) { + gf_log ("", GF_LOG_ERROR, "Failed to open %s" + " Error : %s", common_pem_file, + strerror (errno)); + ret = -1; + goto out; + } + + for (i = 1; i <= output_count; i++) { + memset (output_name, '\0', sizeof (output_name)); + snprintf (output_name, sizeof (output_name), + "output_%d", i); + ret = dict_get_str (dict, output_name, &output); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Failed to get %s.", + output_name); + cli_out ("Unable to fetch output."); + } + if (output) { + bytes_writen = write (fd, output, strlen(output)); + if (bytes_writen != strlen(output)) { + gf_log ("", GF_LOG_ERROR, "Failed to write " + "to %s", common_pem_file); + ret = -1; + goto out; + } + /* Adding the new line character */ + bytes_writen = write (fd, "\n", strlen("\n")); + if (bytes_writen != strlen("\n")) { + gf_log ("", GF_LOG_ERROR, + "Failed to add new line char"); + ret = -1; + goto out; + } + output = NULL; + } + } + + cli_out ("Common secret pub file present at %s", common_pem_file); + ret = 0; +out: + if (fd) + close (fd); + + gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int +gf_cli_sys_exec_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int ret = -1; + int output_count = -1; + int i = -1; + char *output = NULL; + char *command = NULL; + char output_name[PATH_MAX] = ""; + gf_cli_rsp rsp = {0, }; + dict_t *dict = NULL; + call_frame_t *frame = NULL; + + if (req->rpc_status == -1) { + ret = -1; + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + dict = dict_new (); + + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) + goto out; + + if (rsp.op_ret) { + cli_err ("%s", rsp.op_errstr ? rsp.op_errstr : + "Command failed."); + ret = rsp.op_ret; + goto out; + } + + ret = dict_get_int32 (dict, "output_count", &output_count); + if (ret) { + cli_out ("Command executed successfully."); + ret = 0; + goto out; + } + + ret = dict_get_str (dict, "command", &command); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get command from dict"); + goto out; + } + + if (!strcmp (command, "gsec_create")) { + ret = write_contents_to_common_pem_file (dict, output_count); + if (!ret) + goto out; + } + + for (i = 1; i <= output_count; i++) { + memset (output_name, '\0', sizeof (output_name)); + snprintf (output_name, sizeof (output_name), + "output_%d", i); + ret = dict_get_str (dict, output_name, &output); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Failed to get %s.", + output_name); + cli_out ("Unable to fetch output."); + } + if (output) { + cli_out ("%s", output); + output = NULL; + } + } + + ret = 0; +out: + if (dict) + dict_unref (dict); + cli_cmd_broadcast_response (ret); + + free (rsp.dict.dict_val); + + return ret; +} + +int +gf_cli_copy_file_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int ret = -1; + gf_cli_rsp rsp = {0, }; + dict_t *dict = NULL; + call_frame_t *frame = NULL; + + if (req->rpc_status == -1) { + ret = -1; + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + dict = dict_new (); + + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) + goto out; + + if (rsp.op_ret) { + cli_err ("%s", rsp.op_errstr ? rsp.op_errstr : + "Copy unsuccessful"); + ret = rsp.op_ret; + goto out; + } + + cli_out ("Successfully copied file."); + +out: + if (dict) + dict_unref (dict); + cli_cmd_broadcast_response (ret); + + free (rsp.dict.dict_val); + + return ret; } int @@ -3273,17 +4361,22 @@ gf_cli_gsync_set_cbk (struct rpc_req *req, struct iovec *iov, char *gsync_status = NULL; char *master = NULL; char *slave = NULL; - int32_t type = 0; + int32_t type = 0; + call_frame_t *frame = NULL; + gf_boolean_t status_detail = _gf_false; + if (req->rpc_status == -1) { ret = -1; goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, - "Unable to get response structure"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -3300,8 +4393,8 @@ gf_cli_gsync_set_cbk (struct rpc_req *req, struct iovec *iov, goto out; if (global_state->mode & GLUSTER_MODE_XML) { - ret = cli_xml_output_dict ("volGeoRep", dict, rsp.op_ret, - rsp.op_errno, rsp.op_errstr); + ret = cli_xml_output_vol_gsync (dict, rsp.op_ret, rsp.op_errno, + rsp.op_errstr); if (ret) gf_log ("cli", GF_LOG_ERROR, "Error outputting to xml"); @@ -3323,7 +4416,7 @@ gf_cli_gsync_set_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_int32 (dict, "type", &type); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "failed to get type"); + gf_log (frame->this->name, GF_LOG_ERROR, "failed to get type"); goto out; } @@ -3347,8 +4440,30 @@ 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); - goto out; + 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: + if (dict_get_str (dict, "master", &master) != 0) + master = "???"; + if (dict_get_str (dict, "slave", &slave) != 0) + slave = "???"; + cli_out ("Deleting " GEOREP " session between %s & %s" + " has been successful", master, slave); + break; + + case GF_GSYNC_OPTION_TYPE_CREATE: + if (dict_get_str (dict, "master", &master) != 0) + master = "???"; + if (dict_get_str (dict, "slave", &slave) != 0) + slave = "???"; + cli_out ("Creating " GEOREP " session between %s & %s" + " has been successful", master, slave); + break; + default: cli_out (GEOREP" command executed successfully"); } @@ -3364,6 +4479,54 @@ out: } int32_t +gf_cli_sys_exec (call_frame_t *frame, xlator_t *this, void *data) +{ + int ret = 0; + dict_t *dict = NULL; + gf_cli_req req = {{0,}}; + + if (!frame || !this || !data) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid data"); + goto out; + } + + dict = data; + + ret = cli_to_glusterd (&req, frame, gf_cli_sys_exec_cbk, + (xdrproc_t) xdr_gf_cli_req, dict, + GLUSTER_CLI_SYS_EXEC, this, cli_rpc_prog, + NULL); +out: + GF_FREE (req.dict.dict_val); + return ret; +} + +int32_t +gf_cli_copy_file (call_frame_t *frame, xlator_t *this, void *data) +{ + int ret = 0; + dict_t *dict = NULL; + gf_cli_req req = {{0,}}; + + if (!frame || !this || !data) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid data"); + goto out; + } + + dict = data; + + ret = cli_to_glusterd (&req, frame, gf_cli_copy_file_cbk, + (xdrproc_t) xdr_gf_cli_req, dict, + GLUSTER_CLI_COPY_FILE, this, cli_rpc_prog, + NULL); +out: + GF_FREE (req.dict.dict_val); + return ret; +} + +int32_t gf_cli_gsync_set (call_frame_t *frame, xlator_t *this, void *data) { @@ -3597,7 +4760,8 @@ gf_cli_profile_volume_cbk (struct rpc_req *req, struct iovec *iov, gf_log ("cli", GF_LOG_DEBUG, "Received resp to profile"); ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -3785,7 +4949,8 @@ gf_cli_top_volume_cbk (struct rpc_req *req, struct iovec *iov, gf_log ("cli", GF_LOG_DEBUG, "Received resp to top"); ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "Unable to decode response"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -4021,8 +5186,15 @@ gf_cli_getwd_cbk (struct rpc_req *req, struct iovec *iov, } ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_getwd_rsp); - if (ret < 0 || rsp.op_ret == -1) { - gf_log ("", GF_LOG_ERROR, "error"); + if (ret < 0) { + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + if (rsp.op_ret == -1) { + cli_err ("getwd failed"); + ret = rsp.op_ret; goto out; } @@ -5038,6 +6210,123 @@ out: return; } +static void +cli_print_volume_status_tasks (dict_t *dict) +{ + int ret = -1; + int i = 0; + int j = 0; + int count = 0; + int task_count = 0; + int status = 0; + char *op = NULL; + char *task_id_str = NULL; + char *volname = NULL; + char key[1024] = {0,}; + char task[1024] = {0,}; + char *brick = NULL; + char *src_brick = NULL; + char *dest_brick = NULL; + + ret = dict_get_str (dict, "volname", &volname); + if (ret) + goto out; + + ret = dict_get_int32 (dict, "tasks", &task_count); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get tasks count"); + return; + } + + cli_out ("Task Status of Volume %s", volname); + cli_print_line (CLI_BRICK_STATUS_LINE_LEN); + + if (task_count == 0) { + cli_out ("There are no active volume tasks"); + cli_out (" "); + return; + } + + for (i = 0; i < task_count; i++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.type", i); + ret = dict_get_str(dict, key, &op); + if (ret) + return; + cli_out ("%-20s : %-20s", "Task", op); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.id", i); + ret = dict_get_str (dict, key, &task_id_str); + if (ret) + return; + cli_out ("%-20s : %-20s", "ID", task_id_str); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.status", i); + ret = dict_get_int32 (dict, key, &status); + if (ret) + return; + + snprintf (task, sizeof (task), "task%d", i); + + /* + Replace brick only has two states - In progress and Complete + Ref: xlators/mgmt/glusterd/src/glusterd-replace-brick.c + */ + + if (!strcmp (op, "Replace brick")) { + if (status) + status = GF_DEFRAG_STATUS_COMPLETE; + else + status = GF_DEFRAG_STATUS_STARTED; + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "%s.src-brick", task); + ret = dict_get_str (dict, key, &src_brick); + if (ret) + goto out; + + cli_out ("%-20s : %-20s", "Source Brick", src_brick); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "%s.dst-brick", task); + ret = dict_get_str (dict, key, &dest_brick); + if (ret) + goto out; + + cli_out ("%-20s : %-20s", "Destination Brick", + dest_brick); + + } else if (!strcmp (op, "Remove brick")) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "%s.count", task); + ret = dict_get_int32 (dict, key, &count); + if (ret) + goto out; + + cli_out ("%-20s", "Removed bricks:"); + + for (j = 1; j <= count; j++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key),"%s.brick%d", + task, j); + ret = dict_get_str (dict, key, &brick); + if (ret) + goto out; + + cli_out ("%-20s", brick); + } + } + cli_out ("%-20s : %-20s", "Status", + cli_vol_task_status_str[status]); + cli_out (" "); + } + +out: + return; +} + static int gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) @@ -5066,7 +6355,8 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("cli", GF_LOG_ERROR, "Volume status response error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -5091,10 +6381,12 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, "status information."); if (global_state->mode & GLUSTER_MODE_XML) { - cli_xml_output_str ("volStatus", msg, rsp.op_ret, - rsp.op_errno, rsp.op_errstr); - ret = 0; - goto out; + if (!local->all) + cli_xml_output_str ("volStatus", msg, + rsp.op_ret, rsp.op_errno, + rsp.op_errstr); + ret = 0; + goto out; } cli_err ("%s", msg); @@ -5136,23 +6428,6 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, if ((cmd & GF_CLI_STATUS_NFS) || (cmd & GF_CLI_STATUS_SHD)) notbrick = _gf_true; - ret = dict_get_int32 (dict, "count", &count); - if (ret) - goto out; - if (count == 0) { - ret = -1; - goto out; - } - - ret = dict_get_int32 (dict, "brick-index-max", &brick_index_max); - if (ret) - goto out; - ret = dict_get_int32 (dict, "other-count", &other_count); - if (ret) - goto out; - - index_max = brick_index_max + other_count; - if (global_state->mode & GLUSTER_MODE_XML) { if (!local->all) { ret = cli_xml_output_vol_status_begin (local, @@ -5165,11 +6440,21 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, goto out; } } - ret = cli_xml_output_vol_status (local, dict); - if (ret) { - gf_log ("cli", GF_LOG_ERROR, - "Error outputting to xml"); - goto out; + if (cmd & GF_CLI_STATUS_TASKS) { + ret = cli_xml_output_vol_status_tasks_detail (local, + dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR,"Error outputting " + "to xml"); + goto out; + } + } else { + ret = cli_xml_output_vol_status (local, dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Error outputting to xml"); + goto out; + } } if (!local->all) { @@ -5205,6 +6490,10 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, cli_print_volume_status_callpool (dict, notbrick); goto cont; break; + case GF_CLI_STATUS_TASKS: + cli_print_volume_status_tasks (dict); + goto cont; + break; default: break; } @@ -5213,6 +6502,17 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, if (ret) goto out; + ret = dict_get_int32 (dict, "brick-index-max", &brick_index_max); + if (ret) + goto out; + + ret = dict_get_int32 (dict, "other-count", &other_count); + if (ret) + goto out; + + index_max = brick_index_max + other_count; + + cli_out ("Status of volume: %s", volname); if ((cmd & GF_CLI_STATUS_DETAIL) == 0) { @@ -5284,6 +6584,9 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, } } cli_out (" "); + + if ((cmd & GF_CLI_STATUS_MASK) == GF_CLI_STATUS_NONE) + cli_print_volume_status_tasks (dict); cont: ret = rsp.op_ret; @@ -5359,12 +6662,6 @@ gf_cli_status_volume_all (call_frame_t *frame, xlator_t *this, void *data) goto out; } - if (vol_count == 0) { - cli_err ("No volumes present"); - ret = 0; - goto out; - } - /* remove the "all" flag in cmd */ cmd &= ~GF_CLI_STATUS_ALL; cmd |= GF_CLI_STATUS_VOL; @@ -5379,6 +6676,12 @@ gf_cli_status_volume_all (call_frame_t *frame, xlator_t *this, void *data) } } + if (vol_count == 0 && !(global_state->mode & GLUSTER_MODE_XML)) { + cli_err ("No volumes present"); + ret = 0; + goto out; + } + for (i = 0; i < vol_count; i++) { dict = dict_new (); @@ -5442,7 +6745,8 @@ gf_cli_mount_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_mount_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -5509,7 +6813,8 @@ gf_cli_umount_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf1_cli_umount_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -5559,6 +6864,97 @@ gf_cli_umount (call_frame_t *frame, xlator_t *this, void *data) } void +cmd_heal_volume_statistics_out (dict_t *dict, int brick) +{ + + uint64_t num_entries = 0; + int ret = 0; + char key[256] = {0}; + char *hostname = NULL; + uint64_t i = 0; + uint64_t healed_count = 0; + uint64_t split_brain_count = 0; + uint64_t heal_failed_count = 0; + char *start_time_str = NULL; + char *end_time_str = NULL; + char *crawl_type = NULL; + int progress = -1; + + snprintf (key, sizeof key, "%d-hostname", brick); + ret = dict_get_str (dict, key, &hostname); + if (ret) + goto out; + cli_out ("------------------------------------------------"); + cli_out ("\nCrawl statistics for brick no %d", brick); + cli_out ("Hostname of brick %s", hostname); + + snprintf (key, sizeof key, "statistics-%d-count", brick); + ret = dict_get_uint64 (dict, key, &num_entries); + if (ret) + goto out; + + for (i = 0; i < num_entries; i++) + { + snprintf (key, sizeof key, "statistics_crawl_type-%d-%"PRIu64, + brick, i); + ret = dict_get_str (dict, key, &crawl_type); + if (ret) + goto out; + + snprintf (key, sizeof key, "statistics_healed_cnt-%d-%"PRIu64, + brick,i); + ret = dict_get_uint64 (dict, key, &healed_count); + if (ret) + goto out; + + snprintf (key, sizeof key, "statistics_sb_cnt-%d-%"PRIu64, + brick, i); + ret = dict_get_uint64 (dict, key, &split_brain_count); + if (ret) + goto out; + snprintf (key, sizeof key, "statistics_heal_failed_cnt-%d-%"PRIu64, + brick, i); + ret = dict_get_uint64 (dict, key, &heal_failed_count); + if (ret) + goto out; + snprintf (key, sizeof key, "statistics_strt_time-%d-%"PRIu64, + brick, i); + ret = dict_get_str (dict, key, &start_time_str); + if (ret) + goto out; + snprintf (key, sizeof key, "statistics_end_time-%d-%"PRIu64, + brick, i); + ret = dict_get_str (dict, key, &end_time_str); + if (ret) + goto out; + snprintf (key, sizeof key, "statistics_inprogress-%d-%"PRIu64, + brick, i); + ret = dict_get_int32 (dict, key, &progress); + if (ret) + goto out; + + cli_out ("\nStarting time of crawl: %s", start_time_str); + if (progress == 1) + cli_out ("Crawl is in progress"); + else + cli_out ("Ending time of crawl: %s", end_time_str); + + cli_out ("Type of crawl: %s", crawl_type); + cli_out ("No. of entries healed: %"PRIu64, + healed_count); + cli_out ("No. of entries in split-brain: %"PRIu64, + split_brain_count); + cli_out ("No. of heal failed entries: %"PRIu64, + heal_failed_count); + + } + + +out: + return; +} + +void cmd_heal_volume_brick_out (dict_t *dict, int brick) { uint64_t num_entries = 0; @@ -5570,6 +6966,7 @@ cmd_heal_volume_brick_out (dict_t *dict, int brick) uint64_t i = 0; uint32_t time = 0; char timestr[32] = {0}; + char *shd_status = NULL; snprintf (key, sizeof key, "%d-hostname", brick); ret = dict_get_str (dict, key, &hostname); @@ -5580,37 +6977,96 @@ cmd_heal_volume_brick_out (dict_t *dict, int brick) if (ret) goto out; cli_out ("\nBrick %s:%s", hostname, path); - snprintf (key, sizeof key, "%d-count", brick); - ret = dict_get_uint64 (dict, key, &num_entries); - cli_out ("Number of entries: %"PRIu64, num_entries); + snprintf (key, sizeof key, "%d-status", brick); ret = dict_get_str (dict, key, &status); if (status && strlen (status)) cli_out ("Status: %s", status); - for (i = 0; i < num_entries; i++) { - snprintf (key, sizeof key, "%d-%"PRIu64, brick, i); - ret = dict_get_str (dict, key, &path); - if (ret) - continue; - time = 0; - snprintf (key, sizeof key, "%d-%"PRIu64"-time", brick, i); - ret = dict_get_uint32 (dict, key, &time); - if (!time) { - cli_out ("%s", path); - } else { - gf_time_fmt (timestr, sizeof timestr, - time, gf_timefmt_FT); - if (i == 0) { + + snprintf (key, sizeof key, "%d-shd-status",brick); + ret = dict_get_str (dict, key, &shd_status); + + if(!shd_status) + { + snprintf (key, sizeof key, "%d-count", brick); + ret = dict_get_uint64 (dict, key, &num_entries); + cli_out ("Number of entries: %"PRIu64, num_entries); + + + for (i = 0; i < num_entries; i++) { + snprintf (key, sizeof key, "%d-%"PRIu64, brick, i); + ret = dict_get_str (dict, key, &path); + if (ret) + continue; + time = 0; + snprintf (key, sizeof key, "%d-%"PRIu64"-time", + brick, i); + ret = dict_get_uint32 (dict, key, &time); + if (!time) { + cli_out ("%s", path); + } else { + gf_time_fmt (timestr, sizeof timestr, + time, gf_timefmt_FT); + if (i == 0) { cli_out ("at path on brick"); cli_out ("-----------------------------------"); + } + cli_out ("%s %s", timestr, path); } - cli_out ("%s %s", timestr, path); } } + out: return; } + +void +cmd_heal_volume_statistics_heal_count_out (dict_t *dict, int brick) +{ + uint64_t num_entries = 0; + int ret = 0; + char key[256] = {0}; + char *hostname = NULL; + char *path = NULL; + char *status = NULL; + char *shd_status = NULL; + + snprintf (key, sizeof key, "%d-hostname", brick); + ret = dict_get_str (dict, key, &hostname); + if (ret) + goto out; + snprintf (key, sizeof key, "%d-path", brick); + ret = dict_get_str (dict, key, &path); + if (ret) + goto out; + cli_out ("\nBrick %s:%s", hostname, path); + + snprintf (key, sizeof key, "%d-status", brick); + ret = dict_get_str (dict, key, &status); + if (status && strlen (status)) + cli_out ("Status: %s", status); + + snprintf (key, sizeof key, "%d-shd-status",brick); + ret = dict_get_str (dict, key, &shd_status); + + if(!shd_status) + { + snprintf (key, sizeof key, "%d-hardlinks", brick); + ret = dict_get_uint64 (dict, key, &num_entries); + if (ret) + cli_out ("No gathered input for this brick"); + else + cli_out ("Number of entries: %"PRIu64, num_entries); + + + } + +out: + return; +} + + int gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) @@ -5627,19 +7083,21 @@ gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, gf_xl_afr_op_t heal_op = GF_AFR_OP_INVALID; char *operation = NULL; char *substr = NULL; + char *heal_op_str = NULL; if (-1 == req->rpc_status) { goto out; } + frame = myframe; + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log ("", GF_LOG_ERROR, "error"); + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } - frame = myframe; - if (frame) local = frame->local; @@ -5662,18 +7120,51 @@ gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = dict_get_str (input_dict, "volname", &volname); if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "failed to get volname"); + gf_log (frame->this->name, GF_LOG_ERROR, "failed to get volname"); goto out; } gf_log ("cli", GF_LOG_INFO, "Received resp to heal volume"); + switch (heal_op) { + case GF_AFR_OP_HEAL_INDEX: + heal_op_str = "to perform index self heal"; + break; + case GF_AFR_OP_HEAL_FULL: + heal_op_str = "to perform full self heal"; + break; + case GF_AFR_OP_INDEX_SUMMARY: + heal_op_str = "list of entries to be healed"; + break; + case GF_AFR_OP_HEALED_FILES: + heal_op_str = "list of healed entries"; + break; + case GF_AFR_OP_HEAL_FAILED_FILES: + heal_op_str = "list of heal failed entries"; + break; + case GF_AFR_OP_SPLIT_BRAIN_FILES: + heal_op_str = "list of split brain entries"; + break; + case GF_AFR_OP_STATISTICS: + heal_op_str = "crawl statistics"; + break; + case GF_AFR_OP_STATISTICS_HEAL_COUNT: + heal_op_str = "count of entries to be healed"; + break; + case GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: + heal_op_str = "count of entries to be healed per replica"; + break; + case GF_AFR_OP_INVALID: + heal_op_str = "invalid heal op"; + break; + } + if ((heal_op == GF_AFR_OP_HEAL_FULL) || (heal_op == GF_AFR_OP_HEAL_INDEX)) { - operation = "Launching Heal operation"; + operation = "Launching heal operation"; substr = "\nUse heal info commands to check status"; } else { - operation = "Gathering Heal info"; + operation = "Gathering"; substr = ""; } @@ -5681,15 +7172,15 @@ gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, if (strcmp (rsp.op_errstr, "")) { cli_err ("%s", rsp.op_errstr); } else { - cli_err ("%s on volume %s has been unsuccessful", - operation, volname); + cli_err ("%s %s on volume %s has been unsuccessful", + operation, heal_op_str, volname); } ret = rsp.op_ret; goto out; } else { - cli_out ("%s on volume %s has been successful%s", operation, - volname, substr); + cli_out ("%s %s on volume %s has been successful %s", operation, + heal_op_str, volname, substr); } ret = rsp.op_ret; @@ -5723,8 +7214,28 @@ gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - for (i = 0; i < brick_count; i++) - cmd_heal_volume_brick_out (dict, i); + switch (heal_op) { + case GF_AFR_OP_STATISTICS: + for (i = 0; i < brick_count; i++) + cmd_heal_volume_statistics_out (dict, i); + break; + case GF_AFR_OP_STATISTICS_HEAL_COUNT: + case GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: + for (i = 0; i < brick_count; i++) + cmd_heal_volume_statistics_heal_count_out (dict, + i); + break; + case GF_AFR_OP_INDEX_SUMMARY: + case GF_AFR_OP_HEALED_FILES: + case GF_AFR_OP_HEAL_FAILED_FILES: + case GF_AFR_OP_SPLIT_BRAIN_FILES: + for (i = 0; i < brick_count; i++) + cmd_heal_volume_brick_out (dict, i); + break; + default: + break; + } + ret = rsp.op_ret; out: @@ -5776,7 +7287,8 @@ gf_cli_statedump_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log (THIS->name, GF_LOG_ERROR, "XDR decoding failed"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } gf_log ("cli", GF_LOG_DEBUG, "Received response to statedump"); @@ -5847,7 +7359,8 @@ gf_cli_list_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - gf_log (THIS->name, GF_LOG_ERROR, "XDR decoding failed"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } @@ -5934,8 +7447,8 @@ gf_cli_clearlocks_volume_cbk (struct rpc_req *req, struct iovec *iov, ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); if (ret < 0) { - - gf_log ("cli", GF_LOG_ERROR, "XDR decoding failed"); + gf_log (((call_frame_t *) myframe)->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); goto out; } gf_log ("cli", GF_LOG_DEBUG, "Received response to clear-locks"); @@ -6019,6 +7532,1024 @@ out: return ret; } +int32_t +cli_snapshot_remove_reply (gf_cli_rsp *rsp, dict_t *dict, call_frame_t *frame) +{ + int32_t ret = -1; + char *snap_name = NULL; + + GF_ASSERT (rsp); + GF_ASSERT (dict); + GF_ASSERT (frame); + + if (rsp->op_ret) { + cli_err("snapshot delete: failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get snapname"); + goto out; + } + + cli_out ("snapshot delete: %s: snap removed successfully", + snap_name); + ret = 0; + +out: + return ret; +} + +int +cli_snapshot_config_display (dict_t *dict, gf_cli_rsp *rsp) +{ + char buf[PATH_MAX] = ""; + char *volname = NULL; + int ret = -1; + int config_command = 0; + uint64_t value = 0; + uint64_t hard_limit = 0; + uint64_t soft_limit = 0; + uint64_t i = 0; + uint64_t voldisplaycount = 0; + + GF_ASSERT (dict); + GF_ASSERT (rsp); + + if (rsp->op_ret) { + cli_err ("Snapshot Config : failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_int32 (dict, "config-command", &config_command); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch config type"); + goto out; + } + + ret = dict_get_str (dict, "volname", &volname); + /* Ignore the error, as volname is optional */ + + if (!volname) { + volname = "System"; + } + + ret = dict_get_uint64 (dict, "snap-max-hard-limit", &hard_limit); + /* Ignore the error, as the key specified is optional */ + ret = dict_get_uint64 (dict, "snap-max-soft-limit", &soft_limit); + + if (!hard_limit && !soft_limit + && config_command != GF_SNAP_CONFIG_DISPLAY) { + ret = -1; + gf_log(THIS->name, GF_LOG_ERROR, + "Could not fetch config-key"); + goto out; + } + + switch (config_command) { + case GF_SNAP_CONFIG_TYPE_SET: + if (hard_limit && soft_limit) { + cli_out ("snapshot config: snap-max-hard-limit " + "& snap-max-soft-limit for system set " + "successfully"); + } else if (hard_limit){ + cli_out ("snapshot config: %s " + "for snap-max-hard-limit set successfully", + volname); + } else if (soft_limit) { + cli_out ("snapshot config: %s " + "for snap-max-soft-limit set successfully", + volname); + } + break; + + case GF_SNAP_CONFIG_DISPLAY : + cli_out ("\nSnapshot System Configuration:"); + ret = dict_get_uint64 (dict, "snap-max-hard-limit", + &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "snap_max_hard_limit for %s", volname); + ret = -1; + goto out; + } + cli_out ("snap-max-hard-limit : %"PRIu64, value); + + ret = dict_get_uint64 (dict, "snap-max-soft-limit", + &soft_limit); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "snap-max-soft-limit for %s", volname); + ret = -1; + goto out; + } + cli_out ("snap-max-soft-limit : %"PRIu64"%%\n", + soft_limit); + + cli_out ("Snapshot Volume Configuration:"); + + ret = dict_get_uint64 (dict, "voldisplaycount", + &voldisplaycount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Could not fetch voldisplaycount"); + ret = -1; + goto out; + } + + for (i = 0; i < voldisplaycount; i++) { + snprintf (buf, sizeof(buf), "volume%ld-volname", i); + ret = dict_get_str (dict, buf, &volname); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("\nVolume : %s", volname); + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-hard-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("snap-max-hard-limit : %"PRIu64, value); + + snprintf (buf, sizeof(buf), + "volume%ld-active-hard-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch" + " effective snap_max_hard_limit for " + "%s", volname); + ret = -1; + goto out; + } + cli_out ("Effective snap-max-hard-limit : %"PRIu64, + value); + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-soft-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("Effective snap-max-soft-limit : %"PRIu64" " + "(%"PRIu64"%%)", value, soft_limit); + } + break; + default : + break; + } + + ret = 0; +out: + return ret; +} + +/* This function is used to print the volume related information + * of a snap. + * + * arg - 0, dict : Response Dictionary. + * arg - 1, prefix str : snaplist.snap{0..}.vol{0..}.* + */ +int +cli_get_each_volinfo_in_snap (dict_t *dict, char *keyprefix, + gf_boolean_t snap_driven) { + char key[PATH_MAX] = ""; + char *get_buffer = NULL; + int value = 0; + int ret = -1; + char indent[5] = "\t"; + char *volname = NULL; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + if (snap_driven) { + ret = snprintf (key, sizeof (key), "%s.volname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Snap Volume Name", ":", get_buffer); + + ret = snprintf (key, sizeof (key), + "%s.origin-volname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log ("cli", GF_LOG_WARNING, "Failed to get %s", key); + cli_out ("%-12s", "Origin:"); + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Origin Volume name", ":", volname); + + + ret = snprintf (key, sizeof (key), "%s.snapcount", + keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s%s %s %s %d", indent, "Snaps taken for", + volname, ":", value); + + ret = snprintf (key, sizeof (key), "%s.snaps-available", + keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s%s %s %s %d", indent, "Snaps available for", + volname, ":", value); + } + + + ret = snprintf (key, sizeof (key), "%s.vol-status", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Status", + ":", get_buffer); +out : + return ret; +} + +/* This function is used to print snap related information + * arg - 0, dict : Response dictionary. + * arg - 1, prefix_str : snaplist.snap{0..}.* + */ +int +cli_get_volinfo_in_snap (dict_t *dict, char *keyprefix) { + + char key[PATH_MAX] = ""; + int i = 0; + int volcount = 0; + int ret = -1; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + ret = snprintf (key, sizeof (key), "%s.vol-count", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &volcount); + for (i = 1 ; i <= volcount ; i++) { + ret = snprintf (key, sizeof (key), + "%s.vol%d", keyprefix, i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_volinfo_in_snap (dict, key, _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not list " + "details of volume in a snap"); + goto out; + } + cli_out (" "); + } + +out : + return ret; +} + +int +cli_get_each_snap_info (dict_t *dict, char *prefix_str, + gf_boolean_t snap_driven) { + char key_buffer[PATH_MAX] = ""; + char *get_buffer = NULL; + int ret = -1; + char indent[5] = ""; + + GF_ASSERT (dict); + GF_ASSERT (prefix_str); + + if (!snap_driven) + strcat (indent, "\t"); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snapname", + prefix_str); + if (ret < 0 ) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snapname %s ", + key_buffer); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snapshot", + ":", get_buffer); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-id", + prefix_str); + if (ret < 0 ) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-id %s ", + key_buffer); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snap UUID", + ":", get_buffer); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-desc", + prefix_str); + if (ret < 0 ) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (!ret) { + /* Ignore error for description */ + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Description", ":", get_buffer); + } + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-time", + prefix_str); + if (ret < 0 ) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-time %s ", + prefix_str); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Created", + ":", get_buffer); + + if (snap_driven) { + cli_out ("%-12s", "Snap Volumes:\n"); + ret = cli_get_volinfo_in_snap (dict, prefix_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to list details " + "of the snaps"); + goto out; + } + } +out : + return ret; +} + +/* This is a generic function to print snap related information. + * arg - 0, dict : Response Dictionary + */ +int +cli_call_snapshot_info (dict_t *dict, gf_boolean_t bool_snap_driven) { + int snap_count = 0; + char key[PATH_MAX] = ""; + int ret = -1; + int i = 0; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "snap-count", &snap_count); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snap-count"); + goto out; + } + + if (snap_count == 0) { + cli_out ("No snapshots present"); + } + + for (i = 1 ; i <= snap_count ; i++) { + ret = snprintf (key, sizeof (key), "snap%d", i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_snap_info (dict, key, bool_snap_driven); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to print snap details"); + goto out; + } + } +out : + return ret; +} + +int +cli_get_snaps_in_volume (dict_t *dict) { + int ret = -1; + int i = 0; + int count = 0; + int avail = 0; + char key[PATH_MAX] = ""; + char *get_buffer = NULL; + + GF_ASSERT (dict); + + ret = dict_get_str (dict, "origin-volname", &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch origin-volname"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%s", "Volume Name", ":", get_buffer); + + ret = dict_get_int32 (dict, "snap-count", &avail); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap-count"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%d", "Snaps Taken", ":", avail); + + ret = dict_get_int32 (dict, "snaps-available", &count); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snaps-available"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%d", "Snaps Available", ":", count); + + for (i = 1 ; i <= avail ; i++) { + snprintf (key, sizeof (key), "snap%d", i); + ret = cli_get_each_snap_info (dict, key, _gf_false); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to print snap details"); + goto out; + } + + ret = snprintf (key, sizeof (key), "snap%d.vol1", i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_volinfo_in_snap (dict, key, _gf_false); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get volume " + "related information"); + goto out; + } + + cli_out (" "); + } +out : + return ret; +} + +int +cli_snapshot_list (dict_t *dict) { + int snapcount = 0; + char key[PATH_MAX] = ""; + int ret = -1; + int i = 0; + char *get_buffer = NULL; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "snap-count", &snapcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap count"); + goto out; + } + + if (snapcount == 0) { + cli_out ("No snapshots present"); + } + + for (i = 1 ; i <= snapcount ; i++) { + ret = snprintf (key, sizeof (key), "snapname%d",i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get %s ", key); + goto out; + } else { + cli_out ("%s", get_buffer); + } + } +out : + return ret; +} + +int +cli_get_snap_volume_status (dict_t *dict, char *key_prefix) +{ + int ret = -1; + char key[PATH_MAX] = ""; + char *buffer = NULL; + int brickcount = 0; + int i = 0; + int pid = 0; + + GF_ASSERT (dict); + GF_ASSERT (key_prefix); + + ret = snprintf (key, sizeof (key), "%s.brickcount", key_prefix); + if (ret < 0) { + goto out; + } + ret = dict_get_int32 (dict, key, &brickcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to fetch brickcount"); + goto out; + } + + for ( i = 0 ; i < brickcount ; i++ ) { + ret = snprintf (key, sizeof (key), "%s.brick%d.path", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Brick Path"); + continue; + } + cli_out ("\n\t%-17s %s %s", "Brick Path", ":", buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.vgname", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Volume Group"); + cli_out ("\t%-17s %s %s", "Volume Group", ":", "N/A"); + } else + cli_out ("\t%-17s %s %s", "Volume Group", ":", buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.status", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Brick Running"); + cli_out ("\t%-17s %s %s", "Brick Running", ":", "N/A"); + } else + cli_out ("\t%-17s %s %s", "Brick Running", ":", buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.pid", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &pid); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get pid"); + cli_out ("\t%-17s %s %s", "Brick PID", ":", "N/A"); + } else + cli_out ("\t%-17s %s %d", "Brick PID", ":", pid); + + ret = snprintf (key, sizeof (key), "%s.brick%d.data", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Data Percent"); + cli_out ("\t%-17s %s %s", "Data Percentage", ":", "N/A"); + } else + cli_out ("\t%-17s %s %s", "Data Percentage", ":", buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.lvsize", + key_prefix, i); + if (ret < 0) { + goto out; + } + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, "Unable to get LV Size"); + cli_out ("\t%-17s %s %s", "LV Size", ":", "N/A"); + } else + cli_out ("\t%-17s %s %s", "LV Size", ":", buffer); + + } +out : + return ret; +} + + + +int +cli_get_single_snap_status (dict_t *dict, char *keyprefix) +{ + int ret = -1; + char key[PATH_MAX] = ""; + int i = 0; + int volcount = 0; + char *get_buffer = NULL; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + ret = snprintf (key, sizeof (key), "%s.snapname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snapname"); + goto out; + } + cli_out ("\nSnap Name : %s", get_buffer); + + ret = snprintf (key, sizeof (key), "%s.uuid", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snap UUID"); + goto out; + } + cli_out ("Snap UUID : %s", get_buffer); + + ret = snprintf (key, sizeof (key), "%s.volcount", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &volcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get volume count"); + goto out; + } + + for (i = 0 ; i < volcount ; i++) { + ret = snprintf (key, sizeof (key), "%s.vol%d", keyprefix, i); + if (ret < 0) { + goto out; + } + + ret = cli_get_snap_volume_status (dict, key); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Could not get snap volume status"); + goto out; + } + } +out : + return ret; +} + +int +cli_snap_status_all (dict_t *dict) { + int ret = -1; + char key[PATH_MAX] = ""; + int snapcount = 0; + int i = 0; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "status.snapcount", &snapcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get snapcount"); + goto out; + } + + if (snapcount == 0) { + cli_out ("No snapshots present"); + } + + for (i = 0 ; i < snapcount; i++) { + ret = snprintf (key, sizeof (key), "status.snap%d",i); + if (ret < 0) { + goto out; + } + ret = cli_get_single_snap_status (dict, key); + } +out: + return ret; +} + + +int +cli_snapshot_status_display (dict_t *dict, gf_cli_rsp *rsp) +{ + char key[PATH_MAX] = ""; + int ret = -1; + int status_cmd = -1; + + GF_ASSERT (dict); + GF_ASSERT (rsp); + + if (rsp->op_ret) { + cli_err ("Snapshot Status : failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_int32 (dict, "cmd", &status_cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch status type"); + goto out; + } + switch (status_cmd) { + case GF_SNAP_STATUS_TYPE_ALL : + { + ret = cli_snap_status_all (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of all snap"); + goto out; + } + break; + } + + case GF_SNAP_STATUS_TYPE_SNAP : + { + ret = snprintf (key, sizeof (key), "status.snap0"); + if (ret < 0) { + goto out; + } + ret = cli_get_single_snap_status (dict, key); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of snap"); + goto out; + } + break; + } + + case GF_SNAP_STATUS_TYPE_VOL : + { + ret = cli_snap_status_all (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of snap in a volume"); + goto out; + } + break; + } + default : + break; + } +out : + return ret; +} + +int +gf_cli_snapshot_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int ret = -1; + gf_cli_rsp rsp = {0, }; + dict_t *dict = NULL; + char *snap_name = NULL; + int32_t type = 0; + call_frame_t *frame = NULL; + gf_boolean_t snap_driven = _gf_false; + + if (req->rpc_status == -1) { + ret = -1; + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + dict = dict_new (); + + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) + goto out; + + ret = dict_get_int32 (dict, "type", &type); + if (ret) { + gf_log (frame->this->name, GF_LOG_ERROR, "failed to get type"); + goto out; + } + + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + if (rsp.op_ret) { + cli_err("snapshot create: failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to get snap name"); + goto out; + } + cli_out ("snapshot create: %s: snap created successfully", + snap_name); + break; + + case GF_SNAP_OPTION_TYPE_RESTORE: + /* TODO: Check if rsp.op_ret needs to be checked here. Or is + * it ok to check this in the start of the function where we + * get rsp.*/ + if (rsp.op_ret) { + cli_err("snapshot restore: failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to get snap name"); + goto out; + } + + cli_out ("Snapshot restore: %s: Snap restored " + "successfully", snap_name); + + ret = 0; + break; + + case GF_SNAP_OPTION_TYPE_INFO: + if (rsp.op_ret) { + cli_err ("Snapshot info : failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + snap_driven = dict_get_str_boolean (dict, "snap-driven", + _gf_false); + if (snap_driven == _gf_true) { + ret = cli_call_snapshot_info (dict, snap_driven); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Snapshot info failed"); + goto out; + } + } else if (snap_driven == _gf_false) { + ret = cli_get_snaps_in_volume (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Snapshot info failed"); + goto out; + } + } + break; + + case GF_SNAP_OPTION_TYPE_CONFIG: + ret = cli_snapshot_config_display (dict, &rsp); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot config output."); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_LIST: + if (rsp.op_ret) { + cli_err ("Snapshot list : failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = cli_snapshot_list (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot list"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_DELETE: + ret = cli_snapshot_remove_reply (&rsp, dict, frame); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to delete snap"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_STATUS: + ret = cli_snapshot_status_display (dict, &rsp); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot status output."); + goto out; + } + break; + + default: + cli_err ("Unknown command executed"); + ret = -1; + goto out; + } +out: + if (dict) + dict_unref (dict); + cli_cmd_broadcast_response (ret); + + free (rsp.dict.dict_val); + free (rsp.op_errstr); + + return ret; +} + +int32_t +gf_cli_snapshot (call_frame_t *frame, xlator_t *this, + void *data) +{ + gf_cli_req req = {{0,}}; + dict_t *options = NULL; + int ret = -1; + + if (!frame || !this || !data) + goto out; + + options = data; + + ret = cli_to_glusterd (&req, frame, gf_cli_snapshot_cbk, + (xdrproc_t) xdr_gf_cli_req, options, + GLUSTER_CLI_SNAP, this, cli_rpc_prog, + NULL); +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + + GF_FREE (req.dict.dict_val); + return ret; +} + int cli_to_glusterd (gf_cli_req *req, call_frame_t *frame, fop_cbk_fn_t cbkfn, xdrproc_t xdrproc, dict_t *dict, @@ -6095,6 +8626,8 @@ struct rpc_clnt_procedure gluster_cli_actors[GLUSTER_CLI_MAXVALUE] = { [GLUSTER_CLI_PROBE] = {"PROBE_QUERY", gf_cli_probe}, [GLUSTER_CLI_DEPROBE] = {"DEPROBE_QUERY", gf_cli_deprobe}, [GLUSTER_CLI_LIST_FRIENDS] = {"LIST_FRIENDS", gf_cli_list_friends}, + [GLUSTER_CLI_UUID_RESET] = {"UUID_RESET", gf_cli3_1_uuid_reset}, + [GLUSTER_CLI_UUID_GET] = {"UUID_GET", gf_cli3_1_uuid_get}, [GLUSTER_CLI_CREATE_VOLUME] = {"CREATE_VOLUME", gf_cli_create_volume}, [GLUSTER_CLI_DELETE_VOLUME] = {"DELETE_VOLUME", gf_cli_delete_volume}, [GLUSTER_CLI_START_VOLUME] = {"START_VOLUME", gf_cli_start_volume}, @@ -6126,6 +8659,9 @@ struct rpc_clnt_procedure gluster_cli_actors[GLUSTER_CLI_MAXVALUE] = { [GLUSTER_CLI_STATEDUMP_VOLUME] = {"STATEDUMP_VOLUME", gf_cli_statedump_volume}, [GLUSTER_CLI_LIST_VOLUME] = {"LIST_VOLUME", gf_cli_list_volume}, [GLUSTER_CLI_CLRLOCKS_VOLUME] = {"CLEARLOCKS_VOLUME", gf_cli_clearlocks_volume}, + [GLUSTER_CLI_COPY_FILE] = {"COPY_FILE", gf_cli_copy_file}, + [GLUSTER_CLI_SYS_EXEC] = {"SYS_EXEC", gf_cli_sys_exec}, + [GLUSTER_CLI_SNAP] = {"SNAP", gf_cli_snapshot}, }; struct rpc_clnt_program cli_prog = { diff --git a/cli/src/cli-xml-output.c b/cli/src/cli-xml-output.c index 34b046cc9..d8884d44b 100644 --- a/cli/src/cli-xml-output.c +++ b/cli/src/cli-xml-output.c @@ -15,6 +15,39 @@ #include "syscall.h" +enum gf_task_types { + GF_TASK_TYPE_REBALANCE, + GF_TASK_TYPE_REMOVE_BRICK +}; + +/* + * IMPORTANT NOTE: + * All exported functions in this file which use libxml need use a + * #if (HAVE_LIB_XML), #else, #endif + * For eg, + * int exported_func () { + * #if (HAVE_LIB_XML) + * <Stuff using libxml> + * #else + * return 0; + * #endif + * } + * + * All other functions, which are called internally within this file need to be + * within #if (HAVE_LIB_XML), #endif statements + * For eg, + * #if (HAVE_LIB_XML) + * int internal_func () + * { + * } + * #endif + * + * Following the above formate ensures that all xml related code is compliled + * only when libxml2 is present, and also keeps the rest of the codebase free + * of #if (HAVE_LIB_XML) + */ + + #if (HAVE_LIB_XML) #include <libxml/encoding.h> @@ -30,18 +63,11 @@ }while (0) \ int -cli_begin_xml_output (xmlTextWriterPtr *writer, xmlBufferPtr *buf) +cli_begin_xml_output (xmlTextWriterPtr *writer, xmlDocPtr *doc) { int ret = -1; - *buf = xmlBufferCreateSize (8192); - if (*buf == NULL) { - ret = -1; - goto out; - } - xmlBufferSetAllocationScheme (*buf, XML_BUFFER_ALLOC_DOUBLEIT); - - *writer = xmlNewTextWriterMemory (*buf, 0); + *writer = xmlNewTextWriterDoc (doc, 0); if (writer == NULL) { ret = -1; goto out; @@ -60,7 +86,7 @@ out: } int -cli_end_xml_output (xmlTextWriterPtr writer, xmlBufferPtr buf) +cli_end_xml_output (xmlTextWriterPtr writer, xmlDocPtr doc) { int ret = -1; @@ -71,10 +97,12 @@ cli_end_xml_output (xmlTextWriterPtr writer, xmlBufferPtr buf) ret = xmlTextWriterEndDocument (writer); XML_RET_CHECK_AND_GOTO (ret, out); - cli_out ("%s", (const char *)buf->content); + + /* Dump xml document to stdout and pretty format it */ + xmlSaveFormatFileEnc ("-", doc, "UTF-8", 1); xmlFreeTextWriter (writer); - xmlBufferFree (buf); + xmlFreeDoc (doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -96,7 +124,7 @@ cli_xml_output_common (xmlTextWriterPtr writer, int op_ret, int op_errno, XML_RET_CHECK_AND_GOTO (ret, out); ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"opErrstr", - "%s", op_errstr); + "%s", op_errstr); XML_RET_CHECK_AND_GOTO (ret, out); out: @@ -112,9 +140,9 @@ cli_xml_output_str (char *op, char *str, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -122,15 +150,21 @@ cli_xml_output_str (char *op, char *str, int op_ret, int op_errno, if (ret) goto out; - ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"cliOp", - "%s", op); - XML_RET_CHECK_AND_GOTO (ret, out); + if (op) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"cliOp", + "%s", op); + XML_RET_CHECK_AND_GOTO (ret, out); + } - ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"output", - "%s", str); - XML_RET_CHECK_AND_GOTO (ret, out); + if (str) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"output", + "%s", str); + XML_RET_CHECK_AND_GOTO (ret, out); + } - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -164,9 +198,9 @@ cli_xml_output_dict ( char *op, dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -185,7 +219,7 @@ cli_xml_output_dict ( char *op, dict_t *dict, int op_ret, int op_errno, ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -1306,7 +1340,7 @@ cli_xml_output_vol_status_begin (cli_local_t *local, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; - ret = cli_begin_xml_output (&(local->writer), &(local->buf)); + ret = cli_begin_xml_output (&(local->writer), &(local->doc)); XML_RET_CHECK_AND_GOTO (ret, out); ret = cli_xml_output_common (local->writer, op_ret, op_errno, @@ -1344,7 +1378,7 @@ cli_xml_output_vol_status_end (cli_local_t *local) ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO(ret, out); - ret = cli_end_xml_output (local->writer, local->buf); + ret = cli_end_xml_output (local->writer, local->doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -1353,6 +1387,215 @@ out: #endif } +#if (HAVE_LIB_XML) +int +cli_xml_output_remove_brick_task_params (xmlTextWriterPtr writer, dict_t *dict, + char *prefix) +{ + int ret = -1; + char key[1024] = {0,}; + int count = 0; + int i = 0; + char *brick = NULL; + + /* <params> */ + ret = xmlTextWriterStartElement (writer, (xmlChar *)"params"); + XML_RET_CHECK_AND_GOTO (ret, out); + + snprintf (key, sizeof (key), "%s.count", prefix); + ret = dict_get_int32 (dict, key, &count); + if (ret) + goto out; + + for (i = 1; i <= count; i++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "%s.brick%d", prefix, i); + ret = dict_get_str (dict, key, &brick); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"brick", + "%s", brick); + XML_RET_CHECK_AND_GOTO (ret, out); + brick = NULL; + } + + /* </param> */ + ret = xmlTextWriterEndElement (writer); + +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int +cli_xml_output_replace_brick_task_params (xmlTextWriterPtr writer, dict_t *dict, + char *prefix) +{ + + int ret = -1; + char key[1024] = {0,}; + char *brick = NULL; + + /* <params> */ + ret = xmlTextWriterStartElement (writer, (xmlChar *)"params"); + XML_RET_CHECK_AND_GOTO (ret, out); + + snprintf (key, sizeof (key), "%s.src-brick", prefix); + ret = dict_get_str (dict, key, &brick); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"srcBrick", + "%s", brick); + XML_RET_CHECK_AND_GOTO (ret, out); + + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "%s.dst-brick", prefix); + ret = dict_get_str (dict, key, &brick); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"dstBrick", + "%s", brick); + XML_RET_CHECK_AND_GOTO (ret, out); + + + /* </param> */ + ret = xmlTextWriterEndElement (writer); + +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int +cli_xml_output_vol_status_tasks (cli_local_t *local, dict_t *dict) { + int ret = -1; + char *task_type = NULL; + char *task_id_str = NULL; + int status = 0; + int tasks = 0; + char key[1024] = {0,}; + int i = 0; + + /* <tasks> */ + ret = xmlTextWriterStartElement (local->writer, (xmlChar *)"tasks"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = dict_get_int32 (dict, "tasks", &tasks); + if (ret) + goto out; + + for (i = 0; i < tasks; i++) { + /* <task> */ + ret = xmlTextWriterStartElement (local->writer, + (xmlChar *)"task"); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.type", i); + ret = dict_get_str (dict, key, &task_type); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"type", + "%s", task_type); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.id", i); + ret = dict_get_str (dict, key, &task_id_str); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.status", i); + ret = dict_get_int32 (dict, key, &status); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"status", + "%d", status); + XML_RET_CHECK_AND_GOTO (ret, out); + + if (!strcmp (task_type, "Replace brick")) { + if (status) { + status = GF_DEFRAG_STATUS_COMPLETE; + } else { + status = GF_DEFRAG_STATUS_STARTED; + } + } + + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"statusStr", + "%s", + cli_vol_task_status_str[status]); + + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d", i); + if (!strcmp (task_type, "Replace brick")) { + ret = cli_xml_output_replace_brick_task_params + (local->writer, dict, key); + if (ret) + goto out; + } else if (!strcmp (task_type, "Remove brick")) { + ret = cli_xml_output_remove_brick_task_params + (local->writer, dict, key); + if (ret) + goto out; + } + + + /* </task> */ + ret = xmlTextWriterEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + } + + /* </tasks> */ + ret = xmlTextWriterEndElement (local->writer); + +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int +cli_xml_output_vol_status_tasks_detail (cli_local_t *local, dict_t *dict) +{ + int ret = -1; + char *volname = NULL; + + /*<volume>*/ + ret = xmlTextWriterStartElement (local->writer, (xmlChar *)"volume"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = dict_get_str (dict, "volname", &volname); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"volName", "%s", + volname); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = cli_xml_output_vol_status_tasks (local, dict); + if (ret) + goto out; + + /* </volume> */ + ret = xmlTextWriterEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + +out: + return ret; +} +#endif + int cli_xml_output_vol_status (cli_local_t *local, dict_t *dict) { @@ -1470,7 +1713,6 @@ cli_xml_output_vol_status (cli_local_t *local, dict_t *dict) goto out; } break; - default: break; @@ -1480,6 +1722,16 @@ cli_xml_output_vol_status (cli_local_t *local, dict_t *dict) XML_RET_CHECK_AND_GOTO (ret, out); } + /* Tasks are only present when a normal volume status call is done on a + * single volume or on all volumes + */ + if (((cmd & GF_CLI_STATUS_MASK) == GF_CLI_STATUS_NONE) && + (cmd & (GF_CLI_STATUS_VOL|GF_CLI_STATUS_ALL))) { + ret = cli_xml_output_vol_status_tasks (local, dict); + if (ret) + goto out; + } + /* </volume> */ ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO (ret, out); @@ -1606,7 +1858,7 @@ cli_xml_output_vol_top (dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; int brick_count = 0; int top_op = GF_CLI_TOP_NONE; char *brick_name = NULL; @@ -1620,7 +1872,7 @@ cli_xml_output_vol_top (dict_t *dict, int op_ret, int op_errno, int i = 0; int j = 0; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -1709,8 +1961,6 @@ cli_xml_output_vol_top (dict_t *dict, int op_ret, int op_errno, case GF_CLI_TOP_WRITE: case GF_CLI_TOP_OPENDIR: case GF_CLI_TOP_READDIR: - if (!members) - continue; break; @@ -1736,9 +1986,6 @@ cli_xml_output_vol_top (dict_t *dict, int op_ret, int op_errno, "%f", time_taken); } - if (!members) - continue; - break; default: @@ -1768,7 +2015,7 @@ cli_xml_output_vol_top (dict_t *dict, int op_ret, int op_errno, /* </volTop> */ ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -1963,7 +2210,7 @@ cli_xml_output_vol_profile (dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; char *volname = NULL; int op = GF_CLI_STATS_NONE; int brick_count = 0; @@ -1972,7 +2219,7 @@ cli_xml_output_vol_profile (dict_t *dict, int op_ret, int op_errno, char key[1024] = {0,}; int i = 0; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2052,7 +2299,7 @@ cont: ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -2068,13 +2315,13 @@ cli_xml_output_vol_list (dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; int count = 0; char *volname = NULL; char key[1024] = {0,}; int i = 0; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2109,7 +2356,7 @@ cli_xml_output_vol_list (dict_t *dict, int op_ret, int op_errno, ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -2163,15 +2410,44 @@ out: return ret; } +struct tmp_xml_option_logger { + char *key; + xmlTextWriterPtr writer; +}; + +static int +_output_vol_info_option (dict_t *d, char *k, data_t *v, + void *data) +{ + int ret = 0; + char *ptr = NULL; + struct tmp_xml_option_logger *tmp = NULL; + + tmp = data; + + ptr = strstr (k, "option."); + if (!ptr) + goto out; + + if (!v) { + ret = -1; + goto out; + } + ret = cli_xml_output_vol_info_option (tmp->writer, tmp->key, k, + v->data); + +out: + return ret; +} + int cli_xml_output_vol_info_options (xmlTextWriterPtr writer, dict_t *dict, char *prefix) { int ret = -1; int opt_count = 0; - data_t *value = 0; - char *ptr = NULL; char key[1024] = {0,}; + struct tmp_xml_option_logger tmp = {0,}; snprintf (key, sizeof (key), "%s.opt_count", prefix); ret = dict_get_int32 (dict, key, &opt_count); @@ -2186,26 +2462,9 @@ cli_xml_output_vol_info_options (xmlTextWriterPtr writer, dict_t *dict, XML_RET_CHECK_AND_GOTO (ret, out); snprintf (key, sizeof (key), "%s.option.", prefix); - int _output_vol_info_option (dict_t *d, char *k, data_t *v, - void *data) - { - int ret = 0; - ptr = strstr (k, "option."); - if (!ptr) - goto internal_out; - - value = v; - if (!value) { - ret = -1; - goto internal_out; - } - ret = cli_xml_output_vol_info_option (writer, key, k, - v->data); - - internal_out: - return ret; - } - ret = dict_foreach (dict, _output_vol_info_option, NULL); + tmp.key = key; + tmp.writer = writer; + ret = dict_foreach (dict, _output_vol_info_option, &tmp); if (ret) goto out; @@ -2226,6 +2485,7 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) int count = 0; char *volname = NULL; char *volume_id = NULL; + char *uuid = NULL; int type = 0; int status = 0; int brick_count = 0; @@ -2237,7 +2497,9 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) char key[1024] = {0,}; int i = 0; int j = 1; - + char *caps = NULL; + int k __attribute__((unused)) = 0; + char *snap_volume = NULL; ret = dict_get_int32 (dict, "count", &count); if (ret) @@ -2279,6 +2541,18 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) "%d", status); XML_RET_CHECK_AND_GOTO (ret, out); + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "volume%d.snap_volume", i); + ret = dict_get_str (dict, key, &snap_volume); + if (ret) + goto out; + if (snap_volume) { + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"snapVol", + "%s", snap_volume); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret =xmlTextWriterWriteFormatElement (local->writer, (xmlChar *)"statusStr", "%s", cli_vol_status_str[status]); @@ -2353,20 +2627,95 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) "%d", transport); XML_RET_CHECK_AND_GOTO (ret, out); +#ifdef HAVE_BD_XLATOR + /* <xlators> */ + ret = xmlTextWriterStartElement (local->writer, + (xmlChar *)"xlators"); + XML_RET_CHECK_AND_GOTO (ret, out); + + for (k = 0; ; k++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key),"volume%d.xlator%d", i, k); + ret = dict_get_str (dict, key, &caps); + if (ret) + break; + + /* <xlator> */ + ret = xmlTextWriterStartElement (local->writer, + (xmlChar *)"xlator"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement + (local->writer, (xmlChar *)"name", "%s", caps); + XML_RET_CHECK_AND_GOTO (ret, out); + + /* <capabilities> */ + ret = xmlTextWriterStartElement (local->writer, + (xmlChar *) + "capabilities"); + XML_RET_CHECK_AND_GOTO (ret, out); + + j = 0; + for (j = 0; ;j++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), + "volume%d.xlator%d.caps%d", i, k, j); + ret = dict_get_str (dict, key, &caps); + if (ret) + break; + ret = xmlTextWriterWriteFormatElement + (local->writer, (xmlChar *)"capability", + "%s", caps); + XML_RET_CHECK_AND_GOTO (ret, out); + } + /* </capabilities> */ + ret = xmlTextWriterEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + /* </xlator> */ + ret = xmlTextWriterEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret = xmlTextWriterFullEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + /* </xlators> */ +#else + caps = 0; /* Avoid compiler warnings when BD not enabled */ +#endif + j = 1; + /* <bricks> */ ret = xmlTextWriterStartElement (local->writer, (xmlChar *)"bricks"); XML_RET_CHECK_AND_GOTO (ret, out); while (j <= brick_count) { + ret = xmlTextWriterStartElement + (local->writer, (xmlChar *)"brick"); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "volume%d.brick%d.uuid", + i, j); + ret = dict_get_str (dict, key, &uuid); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatAttribute + (local->writer, (xmlChar *)"uuid", "%s", + uuid); + XML_RET_CHECK_AND_GOTO (ret, out); + memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "volume%d.brick%d", i, j); ret = dict_get_str (dict, key, &brick); if (ret) goto out; - ret = xmlTextWriterWriteFormatElement - (local->writer, (xmlChar *)"brick", "%s", - brick); + ret = xmlTextWriterWriteFormatString + (local->writer, "%s", brick); + XML_RET_CHECK_AND_GOTO (ret, out); + + /* </brick> */ + ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO (ret, out); + j++; } /* </bricks> */ @@ -2384,12 +2733,12 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO (ret, out); } - GF_FREE (local->get_vol.volname); + if (volname) { + GF_FREE (local->get_vol.volname); local->get_vol.volname = gf_strdup (volname); local->vol_count += count; } - out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -2407,7 +2756,7 @@ cli_xml_output_vol_info_begin (cli_local_t *local, int op_ret, int op_errno, GF_ASSERT (local); - ret = cli_begin_xml_output (&(local->writer), &(local->buf)); + ret = cli_begin_xml_output (&(local->writer), &(local->doc)); if (ret) goto out; @@ -2455,7 +2804,7 @@ cli_xml_output_vol_info_end (cli_local_t *local) ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (local->writer, local->buf); + ret = cli_end_xml_output (local->writer, local->doc); out: gf_log ("cli", GF_LOG_ERROR, "Returning %d", ret); @@ -2473,7 +2822,7 @@ cli_xml_output_vol_quota_limit_list (char *volname, char *limit_list, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; int64_t size = 0; int64_t limit_value = 0; int i = 0; @@ -2491,7 +2840,7 @@ cli_xml_output_vol_quota_limit_list (char *volname, char *limit_list, GF_ASSERT (volname); GF_ASSERT (limit_list); - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2601,7 +2950,7 @@ cont: ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: GF_FREE (size_str); @@ -2619,18 +2968,17 @@ cli_xml_output_peer_status (dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; int count = 0; char *uuid = NULL; char *hostname = NULL; int connected = 0; int state_id = 0; char *state_str = NULL; - int port = 0; int i = 1; char key[1024] = {0,}; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2690,34 +3038,23 @@ cli_xml_output_peer_status (dict_t *dict, int op_ret, int op_errno, memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "friend%d.stateId", i); ret = dict_get_int32 (dict, key, &state_id); - if (ret) - goto out; + if (!ret) { + /* ignore */ - ret = xmlTextWriterWriteFormatElement (writer, - (xmlChar *)"state", - "%d", state_id); - XML_RET_CHECK_AND_GOTO (ret, out); + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"state", "%d", state_id); + XML_RET_CHECK_AND_GOTO (ret, out); + } memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "friend%d.state", i); ret = dict_get_str (dict, key, &state_str); - if (ret) - goto out; + if (!ret) { + /* ignore */ - ret = xmlTextWriterWriteFormatElement (writer, - (xmlChar *)"stateStr", - "%s", state_str); - XML_RET_CHECK_AND_GOTO (ret, out); - - memset (key, 0, sizeof (key)); - snprintf (key, sizeof (key), "friend%d.port", i); - ret = dict_get_int32 (dict, key, &port); - if (port != 0) { - ret = xmlTextWriterWriteFormatElement - (writer, (xmlChar *)"port", "%d", port); + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"stateStr", "%s", state_str); XML_RET_CHECK_AND_GOTO (ret, out); - - port = 0; } /* </peer> */ @@ -2732,7 +3069,7 @@ cont: ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -2745,22 +3082,29 @@ out: #if (HAVE_LIB_XML) /* Used for rebalance stop/status, remove-brick status */ int -cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict) +cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict, + enum gf_task_types task_type) { int ret = -1; int count = 0; char *node_name = NULL; + char *node_uuid = NULL; uint64_t files = 0; uint64_t size = 0; uint64_t lookups = 0; int status_rcd = 0; uint64_t failures = 0; + uint64_t skipped = 0; uint64_t total_files = 0; uint64_t total_size = 0; uint64_t total_lookups = 0; uint64_t total_failures = 0; + uint64_t total_skipped = 0; char key[1024] = {0,}; int i = 0; + int overall_status = -1; + double elapsed = 0; + double overall_elapsed = 0; if (!dict) { ret = 0; @@ -2782,13 +3126,22 @@ cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict) XML_RET_CHECK_AND_GOTO (ret, out); memset (key, 0, sizeof (key)); - snprintf (key, sizeof (key), "node-uuid-%d", i); + snprintf (key, sizeof (key), "node-name-%d", i); ret = dict_get_str (dict, key, &node_name); if (ret) goto out; ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"nodeName", "%s", node_name); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "node-uuid-%d", i); + ret = dict_get_str (dict, key, &node_uuid); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"id", + "%s", node_uuid); XML_RET_CHECK_AND_GOTO (ret, out); memset (key, 0, sizeof (key)); @@ -2835,6 +3188,27 @@ cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict) "%"PRIu64, failures); XML_RET_CHECK_AND_GOTO (ret, out); + /* skipped-%d is not available for remove brick in dict, + so using failures as skipped count in case of remove-brick + similar to logic used in CLI(non xml output) */ + if (task_type == GF_TASK_TYPE_REBALANCE) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "skipped-%d", i); + } + else { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "failures-%d", i); + } + + ret = dict_get_uint64 (dict, key, &skipped); + if (ret) + goto out; + total_skipped += skipped; + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"skipped", + "%"PRIu64, skipped); + XML_RET_CHECK_AND_GOTO (ret, out); + memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "status-%d", i); ret = dict_get_int32 (dict, key, &status_rcd); @@ -2845,6 +3219,33 @@ cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict) "%d", status_rcd); XML_RET_CHECK_AND_GOTO (ret, out); + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"statusStr", + "%s", + cli_vol_task_status_str[status_rcd]); + + memset (key, 0, 256); + snprintf (key, 256, "run-time-%d", i); + ret = dict_get_double (dict, key, &elapsed); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"runtime", + "%.2f", elapsed); + XML_RET_CHECK_AND_GOTO (ret, out); + + if (elapsed > overall_elapsed) { + overall_elapsed = elapsed; + } + + if (-1 == overall_status) + overall_status = status_rcd; + else if ((GF_DEFRAG_STATUS_COMPLETE == overall_status || + status_rcd > overall_status) && + (status_rcd != GF_DEFRAG_STATUS_COMPLETE)) + overall_status = status_rcd; + XML_RET_CHECK_AND_GOTO (ret, out); + /* </node> */ ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); @@ -2871,7 +3272,22 @@ cli_xml_output_vol_rebalance_status (xmlTextWriterPtr writer, dict_t *dict) "%"PRIu64, total_failures); XML_RET_CHECK_AND_GOTO (ret, out); - // TODO : Aggregate status + ret = xmlTextWriterWriteFormatElement (writer,(xmlChar *)"skipped", + "%"PRIu64, total_skipped); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer,(xmlChar *)"status", + "%d", overall_status); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer,(xmlChar *)"statusStr", + "%s", + cli_vol_task_status_str[overall_status]); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer,(xmlChar *)"runtime", + "%.2f", overall_elapsed); + XML_RET_CHECK_AND_GOTO (ret, out); /* </aggregate> */ ret = xmlTextWriterEndElement (writer); @@ -2890,9 +3306,10 @@ cli_xml_output_vol_rebalance (gf_cli_defrag_type op, dict_t *dict, int op_ret, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; + char *task_id_str = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2904,12 +3321,21 @@ cli_xml_output_vol_rebalance (gf_cli_defrag_type op, dict_t *dict, int op_ret, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volRebalance"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REBALANCE_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"op", "%d", op); XML_RET_CHECK_AND_GOTO (ret, out); if ((GF_DEFRAG_CMD_STOP == op) || (GF_DEFRAG_CMD_STATUS == op)) { - ret = cli_xml_output_vol_rebalance_status (writer, dict); + ret = cli_xml_output_vol_rebalance_status (writer, dict, + GF_TASK_TYPE_REBALANCE); if (ret) goto out; } @@ -2919,7 +3345,7 @@ cli_xml_output_vol_rebalance (gf_cli_defrag_type op, dict_t *dict, int op_ret, XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -2936,9 +3362,10 @@ cli_xml_output_vol_remove_brick (gf_boolean_t status_op, dict_t *dict, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; + char *task_id_str = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2950,8 +3377,17 @@ cli_xml_output_vol_remove_brick (gf_boolean_t status_op, dict_t *dict, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volRemoveBrick"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REMOVE_BRICK_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + if (status_op) { - ret = cli_xml_output_vol_rebalance_status (writer, dict); + ret = cli_xml_output_vol_rebalance_status (writer, dict, + GF_TASK_TYPE_REMOVE_BRICK); if (ret) goto out; } @@ -2961,7 +3397,7 @@ cli_xml_output_vol_remove_brick (gf_boolean_t status_op, dict_t *dict, XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -2980,10 +3416,11 @@ cli_xml_output_vol_replace_brick (gf1_cli_replace_op op, dict_t *dict, int status = 0; uint64_t files = 0; char *current_file = 0; + char *task_id_str = NULL; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -2995,6 +3432,14 @@ cli_xml_output_vol_replace_brick (gf1_cli_replace_op op, dict_t *dict, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volReplaceBrick"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REPLACE_BRICK_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"op", "%d", op); XML_RET_CHECK_AND_GOTO (ret, out); @@ -3032,7 +3477,7 @@ cont: ret = xmlTextWriterEndElement (writer); XML_RET_CHECK_AND_GOTO (ret, out); - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -3049,11 +3494,11 @@ cli_xml_output_vol_create (dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; char *volname = NULL; char *volid = NULL; - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -3095,7 +3540,7 @@ cli_xml_output_vol_create (dict_t *dict, int op_ret, int op_errno, XML_RET_CHECK_AND_GOTO (ret, out); } - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -3112,13 +3557,13 @@ cli_xml_output_generic_volume (char *op, dict_t *dict, int op_ret, int op_errno, #if (HAVE_LIB_XML) int ret = -1; xmlTextWriterPtr writer = NULL; - xmlBufferPtr buf = NULL; + xmlDocPtr doc = NULL; char *volname = NULL; char *volid = NULL; GF_ASSERT (op); - ret = cli_begin_xml_output (&writer, &buf); + ret = cli_begin_xml_output (&writer, &doc); if (ret) goto out; @@ -3159,7 +3604,7 @@ cli_xml_output_generic_volume (char *op, dict_t *dict, int op_ret, int op_errno, XML_RET_CHECK_AND_GOTO (ret, out); } - ret = cli_end_xml_output (writer, buf); + ret = cli_end_xml_output (writer, doc); out: gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); @@ -3168,3 +3613,160 @@ out: return 0; #endif } + +#if (HAVE_LIB_XML) +int +cli_xml_output_vol_gsync_status (dict_t *dict, xmlTextWriterPtr writer) +{ + char master_key[PATH_MAX] = ""; + char slave_key[PATH_MAX] = ""; + char status_key[PATH_MAX] = ""; + char node_key[PATH_MAX] = ""; + char *master = NULL; + char *slave = NULL; + char *status = NULL; + char *node = NULL; + int ret = -1; + int gsync_count = 0; + int i = 1; + + ret = dict_get_int32 (dict, "gsync-count", &gsync_count); + if (ret) + goto out; + + for (i=1; i <= gsync_count; i++) { + snprintf (node_key, sizeof(node_key), "node%d", i); + snprintf (master_key, sizeof(master_key), "master%d", i); + snprintf (slave_key, sizeof(slave_key), "slave%d", i); + snprintf (status_key, sizeof(status_key), "status%d", i); + + ret = dict_get_str (dict, node_key, &node); + if (ret) + goto out; + + ret = dict_get_str (dict, master_key, &master); + if (ret) + goto out; + + ret = dict_get_str (dict, slave_key, &slave); + if (ret) + goto out; + + ret = dict_get_str (dict, status_key, &status); + if (ret) + goto out; + + /* <pair> */ + ret = xmlTextWriterStartElement (writer, (xmlChar *)"pair"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"node", + "%s", node); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"master", + "%s", master); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"slave", + "%s", slave); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"status", + "%s", status); + XML_RET_CHECK_AND_GOTO (ret, out); + + /* </pair> */ + ret = xmlTextWriterEndElement (writer); + XML_RET_CHECK_AND_GOTO (ret, out); + + } + +out: + gf_log ("cli",GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} +#endif + +int +cli_xml_output_vol_gsync (dict_t *dict, int op_ret, int op_errno, + char *op_errstr) +{ +#if (HAVE_LIB_XML) + int ret = -1; + xmlTextWriterPtr writer = NULL; + xmlDocPtr doc = NULL; + char *master = NULL; + char *slave = NULL; + int type = 0; + + GF_ASSERT (dict); + + ret = cli_begin_xml_output (&writer, &doc); + if (ret) + goto out; + + ret = cli_xml_output_common (writer, op_ret, op_errno, op_errstr); + if (ret) + goto out; + + /* <geoRep> */ + ret = xmlTextWriterStartElement (writer, (xmlChar *)"geoRep"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = dict_get_int32 (dict, "type", &type); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get type"); + goto out; + } + + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"type", + "%d", type); + XML_RET_CHECK_AND_GOTO (ret, out); + + switch (type) { + case GF_GSYNC_OPTION_TYPE_START: + case GF_GSYNC_OPTION_TYPE_STOP: + if (dict_get_str (dict, "master", &master) != 0) + master = "???"; + if (dict_get_str (dict, "slave", &slave) != 0) + slave = "???"; + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"master", + "%s", master); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"slave", + "%s", slave); + XML_RET_CHECK_AND_GOTO (ret, out); + + break; + + case GF_GSYNC_OPTION_TYPE_CONFIG: + break; + case GF_GSYNC_OPTION_TYPE_STATUS: + ret = cli_xml_output_vol_gsync_status(dict, writer); + break; + default: + ret = 0; + break; + } + + /* </geoRep> */ + ret = xmlTextWriterEndElement (writer); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = cli_end_xml_output (writer, doc); +out: + gf_log ("cli",GF_LOG_DEBUG, "Returning %d", ret); + return ret; +#else + return 0; +#endif +} diff --git a/cli/src/cli.c b/cli/src/cli.c index 7788ce389..91b315ff1 100644 --- a/cli/src/cli.c +++ b/cli/src/cli.c @@ -168,7 +168,8 @@ logging_init (glusterfs_ctx_t *ctx, struct cli_state *state) char *log_file = state->log_file ? state->log_file : DEFAULT_CLI_LOG_FILE_DIRECTORY "/cli.log"; - if (gf_log_init (ctx, log_file) == -1) { + /* passing ident as NULL means to use default ident for syslog */ + if (gf_log_init (ctx, log_file, NULL) == -1) { fprintf (stderr, "ERROR: failed to open logfile %s\n", log_file); return -1; @@ -298,7 +299,17 @@ cli_opt_parse (char *opt, struct cli_state *state) return 1; if (strcmp (opt, "version") == 0) { - puts (argp_program_version); + cli_out ("%s", argp_program_version); + exit (0); + } + + if (strcmp (opt, "print-logdir") == 0) { + cli_out ("%s", DEFAULT_LOG_FILE_DIRECTORY); + exit (0); + } + + if (strcmp (opt, "print-statedumpdir") == 0) { + cli_out ("%s", DEFAULT_VAR_RUN_DIRECTORY); exit (0); } @@ -342,6 +353,12 @@ cli_opt_parse (char *opt, struct cli_state *state) return 0; } + oarg = strtail (opt, "glusterd-sock="); + if (oarg) { + state->glusterd_sock = oarg; + return 0; + } + return -1; } @@ -377,6 +394,8 @@ parse_cmdline (int argc, char *argv[], struct cli_state *state) } } + state->argv[state->argc] = NULL; + return ret; } @@ -401,7 +420,6 @@ cli_state_init (struct cli_state *state) int ret = 0; - state->remote_host = "localhost"; state->log_level = -1; tree = &state->tree; @@ -488,23 +506,46 @@ cli_rpc_init (struct cli_state *state) if (!options) goto out; - ret = dict_set_str (options, "remote-host", state->remote_host); - if (ret) - goto out; + /* Connect using to glusterd using the specified method, giving + * preference to unix socket connection. If nothing is specified connect + * to the default glusterd socket + */ + if (state->glusterd_sock) { + gf_log ("cli", GF_LOG_INFO, "Connecting to glusterd using " + "sockfile %s", state->glusterd_sock); + ret = rpc_transport_unix_options_build (&options, + state->glusterd_sock, + 0); + if (ret) + goto out; + } else if (state->remote_host) { + gf_log ("cli", GF_LOG_INFO, "Connecting to remote glusterd at " + "%s", state->remote_host); + ret = dict_set_str (options, "remote-host", state->remote_host); + if (ret) + goto out; - if (state->remote_port) - port = state->remote_port; + if (state->remote_port) + port = state->remote_port; - ret = dict_set_int32 (options, "remote-port", port); - if (ret) - goto out; + ret = dict_set_int32 (options, "remote-port", port); + if (ret) + goto out; - ret = dict_set_str (options, "transport.address-family", "inet"); - if (ret) - goto out; + ret = dict_set_str (options, "transport.address-family", + "inet"); + if (ret) + goto out; + } else { + gf_log ("cli", GF_LOG_DEBUG, "Connecting to glusterd using " + "default socket"); + ret = rpc_transport_unix_options_build + (&options, DEFAULT_GLUSTERD_SOCKFILE, 0); + if (ret) + goto out; + } rpc = rpc_clnt_new (options, this->ctx, this->name, 16); - if (!rpc) goto out; @@ -514,7 +555,7 @@ cli_rpc_init (struct cli_state *state) goto out; } - rpc_clnt_start (rpc); + ret = rpc_clnt_start (rpc); out: if (ret) { if (rpc) @@ -560,7 +601,9 @@ main (int argc, char *argv[]) if (!ctx) return ENOMEM; +#ifdef DEBUG gf_mem_acct_enable_set (ctx); +#endif ret = glusterfs_globals_init (ctx); if (ret) diff --git a/cli/src/cli.h b/cli/src/cli.h index 0221f2e85..8daa4b741 100644 --- a/cli/src/cli.h +++ b/cli/src/cli.h @@ -18,6 +18,7 @@ #include "rpc-clnt.h" #include "glusterfs.h" #include "protocol-common.h" +#include "logging.h" #include "cli1-xdr.h" @@ -30,9 +31,8 @@ #define CLI_GLUSTERD_PORT 24007 #define CLI_DEFAULT_CONN_TIMEOUT 120 #define CLI_DEFAULT_CMD_TIMEOUT 120 -#define CLI_TOP_CMD_TIMEOUT 600 //Longer timeout for volume top +#define CLI_TEN_MINUTES_TIMEOUT 600 //Longer timeout for volume top #define DEFAULT_CLI_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" -#define DEFAULT_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" #define CLI_VOL_STATUS_BRICK_LEN 55 #define CLI_TAB_LENGTH 8 #define CLI_BRICK_STATUS_LINE_LEN 78 @@ -52,6 +52,7 @@ struct cli_cmd; extern char *cli_vol_type_str[]; extern char *cli_vol_status_str[]; +extern char *cli_vol_task_status_str[]; typedef int (cli_cmd_cbk_t)(struct cli_state *state, struct cli_cmd_word *word, @@ -114,6 +115,8 @@ struct cli_state { char *log_file; gf_loglevel_t log_level; + + char *glusterd_sock; }; struct cli_local { @@ -128,11 +131,23 @@ struct cli_local { gf_boolean_t all; #if (HAVE_LIB_XML) xmlTextWriterPtr writer; - xmlBufferPtr buf; + xmlDocPtr doc; int vol_count; #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; @@ -151,6 +166,13 @@ struct cli_volume_status { #endif }; +struct snap_config_opt_vals_ { + char *op_name; + char *question; +}; + +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; @@ -215,7 +237,7 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **opt); int32_t cli_cmd_volume_set_parse (const char **words, int wordcount, - dict_t **options); + dict_t **options, char **op_errstr); int32_t cli_cmd_volume_add_brick_parse (const char **words, int wordcount, @@ -361,4 +383,18 @@ cli_xml_output_vol_create (dict_t *dict, int op_ret, int op_errno, int cli_xml_output_generic_volume (char *op, dict_t *dict, int op_ret, int op_errno, char *op_errstr); + +int +cli_xml_output_vol_gsync (dict_t *dict, int op_ret, int op_errno, + char *op_errstr); +int +cli_xml_output_vol_status_tasks_detail (cli_local_t *local, dict_t *dict); + +char * +is_server_debug_xlator (void *myframe); + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, + struct cli_state *state); + #endif /* __CLI_H__ */ |
