diff options
Diffstat (limited to 'cli/src/cli-cmd-parser.c')
| -rw-r--r-- | cli/src/cli-cmd-parser.c | 2242 |
1 files changed, 1946 insertions, 296 deletions
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index b169b77c6..5ab208b8f 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1,22 +1,12 @@ /* - Copyright (c) 2010-2011 Gluster, Inc. <http://www.gluster.com> - This file is part of GlusterFS. - - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. -*/ + 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 + 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> @@ -36,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) { @@ -74,21 +96,20 @@ cli_cmd_bricks_parse (const char **words, int wordcount, int brick_index, brick_list_len++; while (brick_index < wordcount) { if (validate_brick_name ((char *)words[brick_index])) { - cli_out ("Wrong brick type: %s, use <HOSTNAME>:" + cli_err ("Wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr (words[brick_index], ':'); - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } if ((brick_list_len + strlen (words[brick_index]) + 1) > sizeof (brick_list)) { - gf_log ("cli", GF_LOG_ERROR, - "total brick list is larger than a request " - "can take (brick_count %d)", *brick_count); + cli_err ("Total brick list is larger than a request. " + "Can take (brick_count %d)", *brick_count); ret = -1; goto out; } @@ -108,16 +129,18 @@ cli_cmd_bricks_parse (const char **words, int wordcount, int brick_index, } if (!(strcmp (host_name, "localhost") && - strcmp (host_name, "127.0.0.1"))) { - cli_out ("Please provide a valid hostname/ip other " - "than localhost or 127.0.0.1"); + 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; GF_FREE (tmp_host); goto out; } - if (!valid_internet_address (host_name)) { - cli_out ("internet address '%s' does not comform to " - "standards", host_name); + if (!valid_internet_address (host_name, _gf_false)) { + cli_err ("internet address '%s' does not conform to " + "standards", host_name); } GF_FREE (tmp_host); tmp_list = gf_strdup (brick_list + 1); @@ -131,7 +154,7 @@ cli_cmd_bricks_parse (const char **words, int wordcount, int brick_index, strtok_r (tmp_list, " ", &tmpptr); if (!(strcmp (tmp_list, words[brick_index]))) { ret = -1; - cli_out ("Found duplicate" + cli_err ("Found duplicate" " exports %s",words[brick_index]); goto out; } @@ -149,8 +172,7 @@ cli_cmd_bricks_parse (const char **words, int wordcount, int brick_index, if (!*bricks) ret = -1; out: - if (free_list_ptr) - GF_FREE (free_list_ptr); + GF_FREE (free_list_ptr); return ret; } @@ -170,10 +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", "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); @@ -195,9 +225,12 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options if (volname[0] == '-') goto out; - if (!strcmp (volname, "all")) { - cli_out ("\"all\" cannot be the name of a volume."); - goto out; + for (i = 0; invalid_volnames[i]; i++) { + if (!strcmp (volname, invalid_volnames[i])) { + cli_err ("\"%s\" cannot be the name of a volume.", + volname); + goto out; + } } if (strchr (volname, '/')) @@ -228,7 +261,7 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options switch (type) { case GF_CLUSTER_TYPE_STRIPE_REPLICATE: case GF_CLUSTER_TYPE_REPLICATE: - cli_out ("replica option given twice"); + cli_err ("replica option given twice"); goto out; case GF_CLUSTER_TYPE_NONE: type = GF_CLUSTER_TYPE_REPLICATE; @@ -244,7 +277,8 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options } replica_count = strtol (words[index+1], NULL, 0); if (replica_count < 2) { - cli_out ("replica count should be greater than 1"); + cli_err ("replica count should be greater" + " than 1"); ret = -1; goto out; } @@ -258,7 +292,7 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options switch (type) { case GF_CLUSTER_TYPE_STRIPE_REPLICATE: case GF_CLUSTER_TYPE_STRIPE: - cli_out ("stripe option given twice"); + cli_err ("stripe option given twice"); goto out; case GF_CLUSTER_TYPE_NONE: type = GF_CLUSTER_TYPE_STRIPE; @@ -273,7 +307,8 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options } stripe_count = strtol (words[index+1], NULL, 0); if (stripe_count < 2) { - cli_out ("stripe count should be greater than 1"); + cli_err ("stripe count should be greater" + " than 1"); ret = -1; goto out; } @@ -285,7 +320,7 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options } else if ((strcmp (w, "transport")) == 0) { if (trans_type) { - cli_out ("'transport' option given more" + cli_err ("'transport' option given more" " than one time"); goto out; } @@ -303,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; @@ -326,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; @@ -334,20 +374,20 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options /* If brick-count is not valid when replica or stripe is given, exit here */ if (!brick_count) { - cli_out ("No bricks specified"); + cli_err ("No bricks specified"); ret = -1; goto out; } if (brick_count % sub_count) { if (type == GF_CLUSTER_TYPE_STRIPE) - cli_out ("number of bricks is not a multiple of " + cli_err ("number of bricks is not a multiple of " "stripe count"); else if (type == GF_CLUSTER_TYPE_REPLICATE) - cli_out ("number of bricks is not a multiple of " + cli_err ("number of bricks is not a multiple of " "replica count"); else - cli_out ("number of bricks given doesn't match " + cli_err ("number of bricks given doesn't match " "required count"); ret = -1; @@ -366,7 +406,7 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options ret = dict_set_dynstr (dict, "transport", trans_type); if (ret) goto out; - + trans_type = NULL; ret = dict_set_dynstr (dict, "bricks", bricks); if (ret) @@ -376,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: @@ -384,6 +428,9 @@ out: if (dict) dict_destroy (dict); } + + GF_FREE (trans_type); + return ret; } @@ -497,7 +544,7 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) goto out; if (!strcmp (volname, "all")) { - cli_out ("\"all\" cannot be the name of a volume."); + cli_err ("\"all\" cannot be the name of a volume."); goto out; } @@ -522,16 +569,26 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) goto out; } - if ((strcmp (w, "enable")) == 0 && wordcount == 4) { - type = GF_QUOTA_OPTION_TYPE_ENABLE; - ret = 0; - goto set_type; + if (strcmp (w, "enable") == 0) { + if (wordcount == 4) { + type = GF_QUOTA_OPTION_TYPE_ENABLE; + ret = 0; + goto set_type; + } else { + ret = -1; + goto out; + } } - if (strcmp (w, "disable") == 0 && wordcount == 4) { - type = GF_QUOTA_OPTION_TYPE_DISABLE; - ret = 0; - goto set_type; + if (strcmp (w, "disable") == 0) { + if (wordcount == 4) { + type = GF_QUOTA_OPTION_TYPE_DISABLE; + ret = 0; + goto set_type; + } else { + ret = -1; + goto out; + } } if (strcmp (w, "limit-usage") == 0) { @@ -543,7 +600,7 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) type = GF_QUOTA_OPTION_TYPE_LIMIT_USAGE; if (words[4][0] != '/') { - cli_out ("Please enter absolute path"); + cli_err ("Please enter absolute path"); return -2; } @@ -552,14 +609,14 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) goto out; if (!words[5]) { - cli_out ("Please enter the limit value to be set"); + cli_err ("Please enter the limit value to be set"); return -2; } ret = gf_string2bytesize (words[5], &value); if (ret != 0) { - cli_out ("Please enter a correct value"); + cli_err ("Please enter a correct value"); return -1; } @@ -578,7 +635,7 @@ cli_cmd_quota_parse (const char **words, int wordcount, dict_t **options) type = GF_QUOTA_OPTION_TYPE_REMOVE; if (words[4][0] != '/') { - cli_out ("Please enter absolute path"); + cli_err ("Please enter absolute path"); return -2; } @@ -634,54 +691,117 @@ out: return ret; } -int32_t -cli_cmd_valid_ip_list (char *iplist) +static inline gf_boolean_t +cli_is_key_spl (char *key) { - int ret = 0; - char *duplist = NULL; - char *addr = NULL; - char *saveptr = NULL; - - GF_ASSERT (iplist); - duplist = gf_strdup (iplist); + return (strcmp (key, "group") == 0); +} - if (!duplist) { - ret = -1; +#define GLUSTERD_DEFAULT_WORKDIR "/var/lib/glusterd" +static int +cli_add_key_group (dict_t *dict, char *key, char *value, char **op_errstr) +{ + int ret = -1; + int opt_count = 0; + char iter_key[1024] = {0,}; + char iter_val[1024] = {0,}; + char *saveptr = NULL; + char *tok_key = NULL; + char *tok_val = NULL; + char *dkey = NULL; + char *dval = NULL; + 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", + GLUSTERD_DEFAULT_WORKDIR, value); + if (ret == -1) { + tagpath = NULL; goto out; } - addr = strtok_r (duplist, ",", &saveptr); - if (!addr) { + 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; } - while (addr) { - if (!valid_internet_address (addr) && - !valid_wildcard_internet_address (addr)) { - cli_out ("Invalid ip or wildcard : %s", addr); - ret= -1; + + opt_count = 0; + buf = line; + while (fscanf (fp, "%s", buf) != EOF) { + + opt_count++; + tok_key = strtok_r (line, "=", &saveptr); + 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; } - addr = strtok_r (NULL, ",", &saveptr); + + snprintf (iter_key, sizeof (iter_key), "key%d", opt_count); + dkey = gf_strdup (tok_key); + ret = dict_set_dynstr (dict, iter_key, dkey); + if (ret) + goto out; + dkey = NULL; + + snprintf (iter_val, sizeof (iter_val), "value%d", opt_count); + dval = gf_strdup (tok_val); + ret = dict_set_dynstr (dict, iter_val, dval); + if (ret) + goto out; + dval = NULL; + + } + + 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); out: - if (duplist) - GF_FREE (duplist); - gf_log ("cli", GF_LOG_INFO, "Returning %d", ret); + + GF_FREE (tagpath); + + if (ret) { + GF_FREE (dkey); + GF_FREE (dval); + } + + if (fp) + fclose (fp); + return ret; } +#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; - int ret = -1; - int count = 0; - char *key = NULL; - char *value = NULL; - int i = 0; - char str[50] = {0,}; + dict_t *dict = NULL; + char *volname = NULL; + int ret = -1; + int count = 0; + char *key = NULL; + char *value = NULL; + int i = 0; + char str[50] = {0,}; GF_ASSERT (words); GF_ASSERT (options); @@ -703,42 +823,65 @@ cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options) if (ret) goto out; - if (wordcount == 3) { - if (!strcmp (volname, "help")) { - ret = dict_set_str (dict, "help", volname); - if (ret) - goto out; - } else if (!strcmp (volname, "help-xml")) { - ret = dict_set_str (dict, "help-xml", volname); - if (ret) - goto out; - } else { + if ((!strcmp (volname, "help") || !strcmp (volname, "help-xml")) + && wordcount == 3 ) { + ret = dict_set_str (dict, volname, volname); + if (ret) + goto out; + + } else if (wordcount < 5) { + ret = -1; + goto out; + + } else if (wordcount == 5 && cli_is_key_spl ((char *)words[3])) { + key = (char *) words[3]; + value = (char *) words[4]; + if ( !key || !value) { + ret = -1; + goto out; + } + + ret = gf_strip_whitespace (value, strlen (value)); + if (ret == -1) + goto out; + + 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; + } for (i = 3; i < wordcount; i+=2) { - key = (char *) words[i]; - value = (char *) words[i+1]; + key = (char *) words[i]; + value = (char *) words[i+1]; - if ( !key || !value) { - ret = -1; - goto out; - } + if ( !key || !value) { + ret = -1; + goto out; + } count++; - if (!strncmp ("auth.allow", key, sizeof (key)) || - !strncmp ("auth.reject", key, sizeof (key))) { - ret = cli_cmd_valid_ip_list (value); - if (ret) { - gf_log ("cli", GF_LOG_ERROR, - "invalid ips given"); - goto out; - } + + ret = gf_strip_whitespace (value, strlen (value)); + if (ret == -1) + goto out; + + if (strlen (value) == 0) { + ret = -1; + goto out; + } + + if (cli_is_key_spl (key)) { + ret = -1; + goto out; } + sprintf (str, "key%d", count); ret = dict_set_str (dict, str, key); if (ret) @@ -759,10 +902,8 @@ cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options) *options = dict; out: - if (ret) { - if (dict) - dict_destroy (dict); - } + if (ret) + dict_destroy (dict); return ret; } @@ -781,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); @@ -825,7 +968,7 @@ cli_cmd_volume_add_brick_parse (const char **words, int wordcount, } count = strtol (words[4], NULL, 0); if (!count || (count < 2)) { - cli_out ("replica count should be greater than 1"); + cli_err ("replica count should be greater than 1"); ret = -1; goto out; } @@ -841,7 +984,7 @@ cli_cmd_volume_add_brick_parse (const char **words, int wordcount, } count = strtol (words[4], NULL, 0); if (!count || (count < 2)) { - cli_out ("stripe count should be greater than 1"); + cli_err ("stripe count should be greater than 1"); ret = -1; goto out; } @@ -858,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; @@ -872,6 +1021,10 @@ parse_bricks: if (ret) goto out; + ret = dict_set_int32 (dict, "force", is_force); + if (ret) + goto out; + *options = dict; out: @@ -900,7 +1053,7 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, char *tmp_brick = NULL; char *tmp_brick1 = NULL; char *type_opword[] = { "replica", NULL }; - char *opwords[] = { "start", "commit", "pause", "abort", "status", + char *opwords[] = { "start", "commit", "stop", "status", "force", NULL }; char *w = NULL; int32_t command = GF_OP_CMD_NONE; @@ -933,7 +1086,7 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, } count = strtol (words[4], NULL, 0); if (count < 1) { - cli_out ("replica count should be greater than 0 in " + cli_err ("replica count should be greater than 0 in " "case of remove-brick"); ret = -1; goto out; @@ -962,10 +1115,8 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, command = GF_OP_CMD_COMMIT; if (question) *question = 1; - } else if (!strcmp ("pause", w)) { - command = GF_OP_CMD_PAUSE; - } else if (!strcmp ("abort", w)) { - command = GF_OP_CMD_ABORT; + } else if (!strcmp ("stop", w)) { + command = GF_OP_CMD_STOP; } else if (!strcmp ("status", w)) { command = GF_OP_CMD_STATUS; } else if (!strcmp ("force", w)) { @@ -1011,13 +1162,13 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, while (brick_index < wordcount) { if (validate_brick_name ((char *)words[brick_index])) { - cli_out ("wrong brick type: %s, use <HOSTNAME>:" + cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr(words[brick_index], ':'); - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1029,7 +1180,7 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, if (!(strcmp (tmp_brick, tmp_brick1))) { gf_log("",GF_LOG_ERROR, "Duplicate bricks" " found %s", words[brick_index]); - cli_out("Duplicate bricks found %s", + cli_err("Duplicate bricks found %s", words[brick_index]); ret = -1; goto out; @@ -1056,10 +1207,8 @@ out: dict_destroy (dict); } - if (tmp_brick) - GF_FREE (tmp_brick); - if (tmp_brick1) - GF_FREE (tmp_brick1); + GF_FREE (tmp_brick); + GF_FREE (tmp_brick1); return ret; } @@ -1078,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); @@ -1105,13 +1255,13 @@ cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, } if (validate_brick_name ((char *)words[3])) { - cli_out ("wrong brick type: %s, use " + cli_err ("wrong brick type: %s, use " "<HOSTNAME>:<export-dir-abs-path>", words[3]); ret = -1; goto out; } else { delimiter = strrchr ((char *)words[3], ':'); - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1126,13 +1276,13 @@ cli_cmd_volume_replace_brick_parse (const char **words, int wordcount, } if (validate_brick_name ((char *)words[4])) { - cli_out ("wrong brick type: %s, use " + cli_err ("wrong brick type: %s, use " "<HOSTNAME>:<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { delimiter = strrchr ((char *)words[4], ':'); - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1175,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; } } @@ -1194,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; @@ -1235,12 +1393,12 @@ cli_cmd_log_filename_parse (const char **words, int wordcount, dict_t **options) delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { - cli_out ("wrong brick type: %s, use <HOSTNAME>:" + cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1290,8 +1448,8 @@ cli_cmd_log_level_parse (const char **words, int worcount, dict_t **options) ret = glusterd_check_log_level(words[5]); if (ret == -1) { - cli_out("Invalid log level [%s] specified", words[5]); - cli_out("Valid values for loglevel: (DEBUG|WARNING|ERROR" + cli_err("Invalid log level [%s] specified", words[5]); + cli_err("Valid values for loglevel: (DEBUG|WARNING|ERROR" "|CRITICAL|NONE|TRACE)"); goto out; } @@ -1351,12 +1509,12 @@ cli_cmd_log_locate_parse (const char **words, int wordcount, dict_t **options) delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { - cli_out ("wrong brick type: %s, use <HOSTNAME>:" + cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1402,12 +1560,12 @@ cli_cmd_log_rotate_parse (const char **words, int wordcount, dict_t **options) delimiter = strchr (words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter+1) != '/') { - cli_out ("wrong brick type: %s, use <HOSTNAME>:" + cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", words[4]); ret = -1; goto out; } else { - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1438,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); @@ -1465,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) @@ -1499,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 @@ -1525,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) @@ -1545,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; @@ -1561,72 +1890,27 @@ 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'; - - ret = dict_set_dynstr (dict, "op_value", append_str); - } - - if (!subop || dict_set_dynstr (dict, "subop", subop) != 0) - ret = -1; - } + if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) + ret = config_parse (words, wordcount, dict, cmdi, glob); out: if (ret) { if (dict) dict_destroy (dict); - if (append_str) - GF_FREE (append_str); } else *options = dict; + return ret; } @@ -1648,7 +1932,7 @@ cli_cmd_volume_profile_parse (const char **words, int wordcount, if (!dict) goto out; - if (wordcount != 4) + if (wordcount < 4 || wordcount >5) goto out; volname = (char *)words[2]; @@ -1670,7 +1954,19 @@ cli_cmd_volume_profile_parse (const char **words, int wordcount, op = GF_CLI_STATS_INFO; } else GF_ASSERT (!"opword mismatch"); + ret = dict_set_int32 (dict, "op", (int32_t)op); + if (ret) + goto out; + + if (wordcount == 5) { + if (!strcmp (words[4], "nfs")) { + ret = dict_set_int32 (dict, "nfs", _gf_true); + if (ret) + goto out; + } + } + *options = dict; out: if (ret && dict) @@ -1692,12 +1988,13 @@ cli_cmd_volume_top_parse (const char **words, int wordcount, int32_t list_cnt = -1; int index = 0; int perf = 0; - int32_t blk_size = 0; - int32_t count = 0; + uint32_t blk_size = 0; + uint32_t count = 0; + gf_boolean_t nfs = _gf_false; char *delimiter = NULL; char *opwords[] = { "open", "read", "write", "opendir", "readdir", "read-perf", "write-perf", - NULL }; + "clear", NULL }; char *w = NULL; GF_ASSERT (words); @@ -1742,13 +2039,30 @@ cli_cmd_volume_top_parse (const char **words, int wordcount, } else if (strcmp (w, "write-perf") == 0) { top_op = GF_CLI_TOP_WRITE_PERF; perf = 1; + } else if (strcmp (w, "clear") == 0) { + ret = dict_set_int32 (dict, "clear-stats", 1); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Could not set clear-stats in dict"); + goto out; + } } else GF_ASSERT (!"opword mismatch"); ret = dict_set_int32 (dict, "top-op", (int32_t)top_op); if (ret) goto out; - for (index = 4; index < wordcount; index+=2) { + if ((wordcount > 4) && !strcmp (words[4], "nfs")) { + nfs = _gf_true; + ret = dict_set_int32 (dict, "nfs", nfs); + if (ret) + goto out; + index = 5; + } else { + index = 4; + } + + for (; index < wordcount; index+=2) { key = (char *) words[index]; value = (char *) words[index+1]; @@ -1759,14 +2073,14 @@ cli_cmd_volume_top_parse (const char **words, int wordcount, } if (!strcmp (key, "brick")) { delimiter = strchr (value, ':'); - if (!delimiter || delimiter == value + if (!delimiter || delimiter == value || *(delimiter+1) != '/') { - cli_out ("wrong brick type: %s, use <HOSTNAME>:" + cli_err ("wrong brick type: %s, use <HOSTNAME>:" "<export-dir-abs-path>", value); ret = -1; goto out; } else { - ret = cli_canonicalize_path (delimiter + 1); + ret = gf_canonicalize_path (delimiter + 1); if (ret) goto out; } @@ -1777,39 +2091,40 @@ cli_cmd_volume_top_parse (const char **words, int wordcount, if (!ret) list_cnt = atoi (value); if (ret || (list_cnt < 0) || (list_cnt > 100)) { - cli_out ("list-cnt should be between 0 to 100"); + cli_err ("list-cnt should be between 0 to 100"); ret = -1; goto out; } - } else if (perf && !strcmp (key, "bs")) { + } else if (perf && !nfs && !strcmp (key, "bs")) { ret = gf_is_str_int (value); if (!ret) blk_size = atoi (value); if (ret || (blk_size <= 0)) { if (blk_size < 0) - cli_out ("block size is an invalid number"); + cli_err ("block size is an invalid" + " number"); else - cli_out ("block size should be an integer " - "greater than zero"); + cli_err ("block size should be an " + "integer greater than zero"); ret = -1; goto out; } - ret = dict_set_int32 (dict, "blk-size", blk_size); - } else if (perf && !strcmp (key, "count")) { + ret = dict_set_uint32 (dict, "blk-size", blk_size); + } else if (perf && !nfs && !strcmp (key, "count")) { ret = gf_is_str_int (value); if (!ret) count = atoi(value); if (ret || (count <= 0)) { if (count < 0) - cli_out ("count is an invalid number"); - else - cli_out ("count should be an integer " + cli_err ("count is an invalid number"); + else + cli_err ("count should be an integer " "greater than zero"); ret = -1; goto out; } - ret = dict_set_int32 (dict, "blk-cnt", count); + ret = dict_set_uint32 (dict, "blk-cnt", count); } else { ret = -1; goto out; @@ -1829,9 +2144,17 @@ cli_cmd_volume_top_parse (const char **words, int wordcount, } if ((blk_size > 0) ^ (count > 0)) { + cli_err ("Need to give both 'bs' and 'count'"); + ret = -1; + goto out; + } else if (((uint64_t)blk_size * count) > (10 * GF_UNIT_GB)) { + cli_err ("'bs * count' value %"PRIu64" is greater than " + "maximum allowed value of 10GB", + ((uint64_t)blk_size * count)); ret = -1; goto out; } + *options = dict; out: if (ret && dict) @@ -1839,39 +2162,43 @@ out: return ret; } -gf_boolean_t -cli_cmd_validate_statusop (const char *arg) +uint32_t +cli_cmd_get_statusop (const char *arg) { - char *opwords[] = {"misc-details", "mem", "clients", "fd", "inode", - "callpool", NULL}; - char *w = NULL; + int i = 0; + uint32_t ret = GF_CLI_STATUS_NONE; + char *w = NULL; + char *opwords[] = {"detail", "mem", "clients", "fd", + "inode", "callpool", "tasks", NULL}; + struct { + char *opname; + uint32_t opcode; + } optable[] = { + { "detail", GF_CLI_STATUS_DETAIL }, + { "mem", GF_CLI_STATUS_MEM }, + { "clients", GF_CLI_STATUS_CLIENTS }, + { "fd", GF_CLI_STATUS_FD }, + { "inode", GF_CLI_STATUS_INODE }, + { "callpool", GF_CLI_STATUS_CALLPOOL }, + { "tasks", GF_CLI_STATUS_TASKS }, + { NULL } + }; w = str_getunamb (arg, opwords); if (!w) { - gf_log ("cli", GF_LOG_ERROR, "Unknown status op %s", - arg); - return _gf_false; + gf_log ("cli", GF_LOG_DEBUG, + "Not a status op %s", arg); + goto out; } - return _gf_true; -} -int -cli_cmd_get_statusop (const char *arg) -{ - int ret = GF_CLI_STATUS_INVAL; - if (!strcmp (arg, "misc-details")) - ret = GF_CLI_STATUS_DETAIL; - else if (!strcmp (arg, "mem")) - ret = GF_CLI_STATUS_MEM; - else if (!strcmp (arg, "clients")) - ret = GF_CLI_STATUS_CLIENTS; - else if (!strcmp (arg, "inode")) - ret = GF_CLI_STATUS_INODE; - else if (!strcmp (arg, "fd")) - ret = GF_CLI_STATUS_FD; - else if (!strcmp (arg, "callpool")) - ret = GF_CLI_STATUS_CALLPOOL; + for (i = 0; optable[i].opname; i++) { + if (!strcmp (w, optable[i].opname)) { + ret = optable[i].opcode; + break; + } + } + out: return ret; } @@ -1879,9 +2206,9 @@ int cli_cmd_volume_status_parse (const char **words, int wordcount, dict_t **options) { - dict_t *dict = NULL; - int ret = -1; - int cmd = 0; + dict_t *dict = NULL; + int ret = -1; + uint32_t cmd = 0; GF_ASSERT (options); @@ -1898,61 +2225,105 @@ cli_cmd_volume_status_parse (const char **words, int wordcount, case 3: if (!strcmp (words[2], "all")) { - cmd = GF_CLI_STATUS_ALL; ret = 0; + } else { cmd = GF_CLI_STATUS_VOL; ret = dict_set_str (dict, "volname", (char *)words[2]); } + break; case 4: + cmd = cli_cmd_get_statusop (words[3]); + if (!strcmp (words[2], "all")) { - cli_out ("Cannot specify brick/status-type for \"all\""); - ret = -1; - goto out; + if (cmd == GF_CLI_STATUS_NONE) { + cli_err ("%s is not a valid status option", + words[3]); + ret = -1; + goto out; + } + cmd |= GF_CLI_STATUS_ALL; + ret = 0; + } else { - cmd = GF_CLI_STATUS_VOL; - ret = dict_set_str (dict, "volname", (char *)words[2]); + ret = dict_set_str (dict, "volname", + (char *)words[2]); if (ret) goto out; - } - if (cli_cmd_validate_statusop (words[3])) { - ret = cli_cmd_get_statusop (words[3]); - if (GF_CLI_STATUS_INVAL == ret) - goto out; - cmd |= ret; - ret = 0; - } else { - cmd = GF_CLI_STATUS_BRICK; - ret = dict_set_str (dict, "brick", (char *)words[3]); + if (cmd == GF_CLI_STATUS_NONE) { + if (!strcmp (words[3], "nfs")) { + cmd |= GF_CLI_STATUS_NFS; + } else if (!strcmp (words[3], "shd")) { + cmd |= GF_CLI_STATUS_SHD; + } else { + cmd = GF_CLI_STATUS_BRICK; + ret = dict_set_str (dict, "brick", + (char *)words[3]); + } + + } else { + cmd |= GF_CLI_STATUS_VOL; + ret = 0; + } } + break; case 5: - if (!cli_cmd_validate_statusop (words[4])) { + if (!strcmp (words[2], "all")) { + cli_err ("Cannot specify brick/nfs for \"all\""); ret = -1; goto out; } - cmd = GF_CLI_STATUS_BRICK; - ret = cli_cmd_get_statusop (words[4]); - if (GF_CLI_STATUS_INVAL == ret) + cmd = cli_cmd_get_statusop (words[4]); + if (cmd == GF_CLI_STATUS_NONE) { + cli_err ("%s is not a valid status option", + words[4]); + ret = -1; goto out; - cmd |= ret; + } - if (!strcmp (words[2], "all")) { - cli_out ("Cannot specify brick/status-type for \"all\""); - ret = -1; + + ret = dict_set_str (dict, "volname", (char *)words[2]); + if (ret) goto out; + + if (!strcmp (words[3], "nfs")) { + if (cmd == GF_CLI_STATUS_FD || + 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; + } + cmd |= GF_CLI_STATUS_NFS; + } else if (!strcmp (words[3], "shd")){ + if (cmd == GF_CLI_STATUS_FD || + cmd == GF_CLI_STATUS_CLIENTS || + 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 { - ret = dict_set_str (dict, "volname", (char *)words[2]); - if (ret) + 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]); } - ret = dict_set_str (dict, "brick", (char *)words[3]); break; default: @@ -1976,18 +2347,20 @@ cli_cmd_volume_status_parse (const char **words, int wordcount, } gf_boolean_t -cli_cmd_validate_dumpoption (const char *option) +cli_cmd_validate_dumpoption (const char *arg, char **option) { char *opwords[] = {"all", "nfs", "mem", "iobuf", "callpool", "priv", - "fd", "inode", NULL}; + "fd", "inode", "history", "inodectx", "fdctx", + NULL}; char *w = NULL; - w = str_getunamb (option, opwords); + w = str_getunamb (arg, opwords); if (!w) { gf_log ("cli", GF_LOG_DEBUG, "Unknown statedump option %s", - option); + arg); return _gf_false; } + *option = w; return _gf_true; } @@ -1999,14 +2372,15 @@ cli_cmd_volume_statedump_options_parse (const char **words, int wordcount, int i = 0; dict_t *dict = NULL; int option_cnt = 0; + char *option = NULL; char option_str[100] = {0,}; for (i = 3; i < wordcount; i++, option_cnt++) { - if (!cli_cmd_validate_dumpoption (words[i])) { + if (!cli_cmd_validate_dumpoption (words[i], &option)) { ret = -1; goto out; } - strncat (option_str, words[i], sizeof (words [i])); + strncat (option_str, option, strlen (option)); strncat (option_str, " ", 1); } @@ -2014,7 +2388,7 @@ cli_cmd_volume_statedump_options_parse (const char **words, int wordcount, if (!dict) goto out; - ret = dict_set_str (dict, "options", gf_strdup (option_str)); + ret = dict_set_dynstr (dict, "options", gf_strdup (option_str)); if (ret) goto out; @@ -2030,3 +2404,1279 @@ out: gf_log ("cli", GF_LOG_ERROR, "Error parsing dumpoptions"); return ret; } + +int +cli_cmd_volume_clrlks_opts_parse (const char **words, int wordcount, + dict_t **options) +{ + int ret = -1; + int i = 0; + dict_t *dict = NULL; + char *kind_opts[4] = {"blocked", "granted", "all", NULL}; + char *types[4] = {"inode", "entry", "posix", NULL}; + char *free_ptr = NULL; + + dict = dict_new (); + if (!dict) + goto out; + + if (strcmp (words[4], "kind")) + goto out; + + for (i = 0; kind_opts[i]; i++) { + if (!strcmp (words[5], kind_opts[i])) { + free_ptr = gf_strdup (words[5]); + ret = dict_set_dynstr (dict, "kind", free_ptr); + if (ret) + goto out; + free_ptr = NULL; + break; + } + } + if (i == 3) + goto out; + + ret = -1; + for (i = 0; types[i]; i++) { + if (!strcmp (words[6], types[i])) { + free_ptr = gf_strdup (words[6]); + ret = dict_set_dynstr (dict, "type", free_ptr); + if (ret) + goto out; + free_ptr = NULL; + break; + } + } + if (i == 3) + goto out; + + if (wordcount == 8) { + free_ptr = gf_strdup (words[7]); + ret = dict_set_dynstr (dict, "opts", free_ptr); + if (ret) + goto out; + free_ptr = NULL; + } + + ret = 0; + *options = dict; +out: + if (ret) { + GF_FREE (free_ptr); + dict_unref (dict); + } + + 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) + goto out; + + ret = dict_set_str (dict, "volname", (char *) words[2]); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "failed to set volname"); + goto out; + } + + if (wordcount == 3) { + ret = dict_set_int32 (dict, "heal-op", GF_AFR_OP_HEAL_INDEX); + goto done; + } + + if (wordcount == 4) { + if (!strcmp (words[3], "full")) { + 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); + goto done; + } else { + ret = -1; + goto out; + } + } + if (wordcount == 5) { + if (strcmp (words[3], "info") && + strcmp (words[3], "statistics")) { + ret = -1; + goto out; + } + + 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[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: + *options = dict; +out: + if (ret && dict) { + dict_unref (dict); + *options = NULL; + } + + return ret; +} + +int +cli_cmd_volume_defrag_parse (const char **words, int wordcount, + dict_t **options) +{ + dict_t *dict = NULL; + int ret = -1; + char *option = NULL; + char *volname = NULL; + char *command = NULL; + gf_cli_defrag_type cmd = 0; + + GF_ASSERT (words); + GF_ASSERT (options); + + dict = dict_new (); + if (!dict) + goto out; + + if (!((wordcount == 4) || (wordcount == 5))) + goto out; + + if (wordcount == 4) { + if (strcmp (words[3], "start") && strcmp (words[3], "stop") && + strcmp (words[3], "status")) + goto out; + } else { + if (strcmp (words[3], "fix-layout") && + strcmp (words[3], "start")) + goto out; + } + + volname = (char *) words[2]; + + if (wordcount == 4) { + command = (char *) words[3]; + } + if (wordcount == 5) { + if ((strcmp (words[3], "fix-layout") || + strcmp (words[4], "start")) && + (strcmp (words[3], "start") || + strcmp (words[4], "force"))) { + ret = -1; + goto out; + } + command = (char *) words[3]; + option = (char *) words[4]; + } + + if (strcmp (command, "start") == 0) { + cmd = GF_DEFRAG_CMD_START; + if (option && strcmp (option, "force") == 0) { + cmd = GF_DEFRAG_CMD_START_FORCE; + } + goto done; + } + + if (strcmp (command, "fix-layout") == 0) { + cmd = GF_DEFRAG_CMD_START_LAYOUT_FIX; + goto done; + } + if (strcmp (command, "stop") == 0) { + cmd = GF_DEFRAG_CMD_STOP; + goto done; + } + if (strcmp (command, "status") == 0) { + cmd = GF_DEFRAG_CMD_STATUS; + } + +done: + ret = dict_set_str (dict, "volname", volname); + + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "failed to set dict"); + goto out; + } + + ret = dict_set_int32 (dict, "rebalance-command", (int32_t) cmd); + + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "failed to set dict"); + goto out; + } + + *options = dict; + +out: + if (ret && dict) + dict_destroy (dict); + + 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; +} |
