/*
   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>
#include <stdint.h>
#include <pthread.h>
#include <fnmatch.h>
#include <time.h>

#include "cli.h"
#include "cli-cmd.h"
#include "cli-mem-types.h"
#include <glusterfs/dict.h>
#include <glusterfs/list.h>

#include "protocol-common.h"
#include "cli1-xdr.h"

#define MAX_SNAP_DESCRIPTION_LEN 1024

static struct snap_config_opt_vals_ snap_confopt_vals[] = {
    {.op_name = "snap-max-hard-limit",
     .question = "Changing snapshot-max-hard-limit "
                 "will limit the creation of new snapshots "
                 "if they exceed the new limit.\n"
                 "Do you want to continue?"},
    {.op_name = "snap-max-soft-limit",
     .question = "If Auto-delete is enabled, snap-max-soft-limit will"
                 " trigger deletion of oldest snapshot, on the "
                 "creation of new snapshot, when the "
                 "snap-max-soft-limit is reached.\n"
                 "Do you want to change the snap-max-soft-limit?"},
    {.op_name = "both",
     .question = "Changing snapshot-max-hard-limit "
                 "will limit the creation of new snapshots "
                 "if they exceed the new snapshot-max-hard-limit.\n"
                 "If Auto-delete is enabled, snap-max-soft-limit will"
                 " trigger deletion of oldest snapshot, on the "
                 "creation of new snapshot, when the "
                 "snap-max-soft-limit is reached.\n"
                 "Do 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;

typedef struct _cli_brick {
    struct list_head list;
    const char *name;
    int32_t len;
} cli_brick_t;

int
cli_cmd_validate_volume(char *volname);

static const char *
id_sel(void *wcon)
{
    return (const char *)wcon;
}

static char *
str_getunamb(const char *tok, char **opwords)
{
    return (char *)cli_getunamb(tok, (void **)opwords, id_sel);
}

int32_t
cli_cmd_ta_brick_parse(const char **words, int wordcount, char **ta_brick)
{
    char *host_name = NULL;
    char *tmp_host = NULL;
    char *delimiter = NULL;
    cli_brick_t *brick = NULL;
    int ret = 0;

    GF_ASSERT(words);
    GF_ASSERT(wordcount);

    if (validate_brick_name((char *)words[wordcount - 1])) {
        cli_err(
            "Wrong brick type: %s, use <HOSTNAME>:"
            "<export-dir-abs-path>",
            words[wordcount - 1]);
        ret = -1;
        goto out;
    } else {
        delimiter = strrchr(words[wordcount - 1], ':');
        ret = gf_canonicalize_path(delimiter + 1);
        if (ret)
            goto out;
    }

    tmp_host = gf_strdup((char *)words[wordcount - 1]);
    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 retrieve "
               "hostname");
        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, _gf_false)) {
        cli_err(
            "internet address '%s' does not conform to "
            "standards",
            host_name);
    }

    brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node);
    if (brick == NULL) {
        ret = -1;
        gf_log("cli", GF_LOG_ERROR, "Out of memory");
        goto out;
    }

    brick->name = words[wordcount - 1];
    brick->len = strlen(words[wordcount - 1]);
    *ta_brick = GF_MALLOC(brick->len + 3, gf_common_mt_char);
    if (*ta_brick == NULL) {
        ret = -1;
        gf_log("cli", GF_LOG_ERROR, "Out of memory");
        goto out;
    }

    strcat(*ta_brick, " ");
    strcat(*ta_brick, brick->name);
    strcat(*ta_brick, " ");
out:
    if (tmp_host) {
        GF_FREE(tmp_host);
        tmp_host = NULL;
    }
    if (brick) {
        GF_FREE(brick);
        brick = NULL;
    }

    return ret;
}

int32_t
cli_cmd_bricks_parse(const char **words, int wordcount, int brick_index,
                     char **bricks, int *brick_count)
{
    int ret = 0;
    char *delimiter = NULL;
    char *host_name = NULL;
    char *tmp_host = NULL;
    char *bricks_str = NULL;
    int len = 0;
    int brick_list_len = 1; /* For initial space */
    struct list_head brick_list = {
        0,
    };
    cli_brick_t *brick = NULL;

    GF_ASSERT(words);
    GF_ASSERT(wordcount);
    GF_ASSERT(bricks);
    GF_ASSERT(brick_index > 0);
    GF_ASSERT(brick_index < wordcount);

    INIT_LIST_HEAD(&brick_list);

    while (brick_index < wordcount) {
        if (validate_brick_name((char *)words[brick_index])) {
            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 = gf_canonicalize_path(delimiter + 1);
            if (ret)
                goto out;
        }

        tmp_host = gf_strdup((char *)words[brick_index]);
        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");
            GF_FREE(tmp_host);
            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;
            GF_FREE(tmp_host);
            goto out;
        }
        if (!valid_internet_address(host_name, _gf_false, _gf_false)) {
            cli_err(
                "internet address '%s' does not conform to "
                "standards",
                host_name);
        }
        GF_FREE(tmp_host);
        list_for_each_entry(brick, &brick_list, list)
        {
            if (strcmp(brick->name, words[brick_index]) == 0) {
                ret = -1;
                cli_err("Found duplicate exports %s", words[brick_index]);
                goto out;
            }
        }

        brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node);
        if (brick == NULL) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Out of memory");
            goto out;
        }
        len = strlen(words[brick_index]);
        brick->name = words[brick_index];
        brick->len = len;
        list_add_tail(&brick->list, &brick_list);

        brick_list_len += len + 1; /* Brick name + space */
        ++(*brick_count);
        ++brick_index;
    }

    /* If brick count is not valid exit here */
    if (!*brick_count) {
        cli_err("No bricks specified");
        ret = -1;
        goto out;
    }

    brick_list_len++; /* For terminating null char */

    bricks_str = GF_MALLOC(brick_list_len, gf_common_mt_char);
    if (bricks_str == NULL) {
        ret = -1;
        gf_log("cli", GF_LOG_ERROR, "Out of memory");
        goto out;
    }
    *bricks = bricks_str;
    *bricks_str = ' ';
    bricks_str++;
    while (!list_empty(&brick_list)) {
        brick = list_first_entry(&brick_list, cli_brick_t, list);
        list_del_init(&brick->list);
        memcpy(bricks_str, brick->name, brick->len);
        bricks_str[brick->len] = ' ';
        bricks_str += brick->len + 1;
        GF_FREE(brick);
    }
    *bricks_str = 0;

out:
    while (!list_empty(&brick_list)) {
        brick = list_first_entry(&brick_list, cli_brick_t, list);
        list_del_init(&brick->list);
        GF_FREE(brick);
    }

    return ret;
}

int32_t
cli_cmd_create_disperse_check(struct cli_state *state, int *disperse,
                              int *redundancy, int *data, int count)
{
    int i = 0;
    int tmp = 0;
    gf_answer_t answer = GF_ANSWER_NO;
    char question[128];

    const char *question1 =
        "There isn't an optimal redundancy value "
        "for this configuration. Do you want to "
        "create the volume with redundancy 1 ?";

    const char *question2 =
        "The optimal redundancy for this "
        "configuration is %d. Do you want to create "
        "the volume with this value ?";

    const char *question3 =
        "This configuration is not optimal on most "
        "workloads. Do you want to use it ?";

    const char *question4 =
        "Redundancy for this configuration is %d. "
        "Do you want to create "
        "the volume with this value ?";

    if (*data > 0) {
        if (*disperse > 0 && *redundancy > 0) {
            if (*disperse != (*data + *redundancy)) {
                cli_err(
                    "Disperse count(%d) should be equal "
                    "to sum of disperse-data count(%d) and "
                    "redundancy count(%d)",
                    *disperse, *data, *redundancy);
                return -1;
            }
        } else if (*redundancy > 0) {
            *disperse = *data + *redundancy;
        } else if (*disperse > 0) {
            *redundancy = *disperse - *data;
        } else {
            if ((count - *data) >= *data) {
                cli_err(
                    "Please provide redundancy count "
                    "along with disperse-data count");
                return -1;
            } else {
                sprintf(question, question4, count - *data);
                answer = cli_cmd_get_confirmation(state, question);
                if (answer == GF_ANSWER_NO)
                    return -1;
                *redundancy = count - *data;
                *disperse = count;
            }
        }
    }

    if (*disperse <= 0) {
        if (count < 3) {
            cli_err(
                "number of bricks must be greater "
                "than 2");

            return -1;
        }
        *disperse = count;
    }

    if (*redundancy == -1) {
        tmp = *disperse - 1;
        for (i = tmp / 2; (i > 0) && ((tmp & -tmp) != tmp); i--, tmp--)
            ;

        if (i == 0) {
            answer = cli_cmd_get_confirmation(state, question1);
            if (answer == GF_ANSWER_NO)
                return -1;

            *redundancy = 1;
        } else {
            *redundancy = *disperse - tmp;
            if (*redundancy > 1) {
                sprintf(question, question2, *redundancy);
                answer = cli_cmd_get_confirmation(state, question);
                if (answer == GF_ANSWER_NO)
                    return -1;
            }
        }

        tmp = 0;
    } else {
        tmp = *disperse - *redundancy;
    }

    if ((*redundancy < 1) || (*redundancy > (*disperse - 1) / 2)) {
        cli_err(
            "redundancy must be greater than or equal to 1 and "
            "less than %d for a disperse %d volume",
            (*disperse + 1) / 2, *disperse);

        return -1;
    }

    if ((tmp & -tmp) != tmp) {
        answer = cli_cmd_get_confirmation(state, question3);
        if (answer == GF_ANSWER_NO)
            return -1;
    }

    return 0;
}

static int32_t
cli_validate_disperse_volume(char *word, gf1_cluster_type type,
                             const char **words, int32_t wordcount,
                             int32_t index, int32_t *disperse_count,
                             int32_t *redundancy_count, int32_t *data_count)
{
    int ret = -1;

    switch (type) {
        case GF_CLUSTER_TYPE_NONE:
        case GF_CLUSTER_TYPE_DISPERSE:
            if (strcmp(word, "disperse") == 0) {
                if (*disperse_count >= 0) {
                    cli_err("disperse option given twice");
                    goto out;
                }
                if (wordcount < (index + 2)) {
                    goto out;
                }
                ret = gf_string2int(words[index + 1], disperse_count);
                if (ret == -1 && errno == EINVAL) {
                    *disperse_count = 0;
                    ret = 1;
                } else if (ret == -1) {
                    goto out;
                } else {
                    if (*disperse_count < 3) {
                        cli_err(
                            "disperse count must "
                            "be greater than 2");
                        goto out;
                    }
                    ret = 2;
                }
            } else if (strcmp(word, "disperse-data") == 0) {
                if (*data_count >= 0) {
                    cli_err("disperse-data option given twice");
                    goto out;
                }
                if (wordcount < (index + 2)) {
                    goto out;
                }
                ret = gf_string2int(words[index + 1], data_count);
                if (ret == -1 || *data_count < 2) {
                    cli_err("disperse-data must be greater than 1");
                    goto out;
                }
                ret = 2;
            } else if (strcmp(word, "redundancy") == 0) {
                if (*redundancy_count >= 0) {
                    cli_err("redundancy option given twice");
                    goto out;
                }
                if (wordcount < (index + 2)) {
                    goto out;
                }
                ret = gf_string2int(words[index + 1], redundancy_count);
                if (ret == -1 || *redundancy_count < 1) {
                    cli_err("redundancy must be greater than 0");
                    goto out;
                }
                ret = 2;
            }
            break;
        case GF_CLUSTER_TYPE_REPLICATE:
            cli_err(
                "replicated-dispersed volume is not "
                "supported");
            goto out;
        default:
            cli_err("Invalid type given");
            break;
    }
out:
    return ret;
}

int32_t
cli_validate_volname(const char *volname)
{
    int32_t ret = -1;
    int32_t i = -1;
    int volname_len;
    static const char *const invalid_volnames[] = {"volume",
                                                   "type",
                                                   "help",
                                                   "subvolumes",
                                                   "option",
                                                   "end-volume",
                                                   "all",
                                                   "volume_not_in_ring",
                                                   "description",
                                                   "force",
                                                   "snap-max-hard-limit",
                                                   "snap-max-soft-limit",
                                                   "auto-delete",
                                                   "activate-on-create",
                                                   NULL};

    if (volname[0] == '-')
        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, '/'))
        goto out;

    volname_len = strlen(volname);
    if (volname_len > GD_VOLUME_NAME_MAX) {
        cli_err("Volume name exceeds %d characters.", GD_VOLUME_NAME_MAX);
        goto out;
    }

    for (i = 0; i < volname_len; i++) {
        if (!isalnum(volname[i]) && (volname[i] != '_') &&
            (volname[i] != '-')) {
            cli_err(
                "Volume name should not contain \"%c\""
                " character.\nVolume names can only"
                "contain alphanumeric, '-' and '_' "
                "characters.",
                volname[i]);
            goto out;
        }
    }

    ret = 0;
out:
    return ret;
}

int32_t
cli_cmd_volume_create_parse(struct cli_state *state, const char **words,
                            int wordcount, dict_t **options, char **brick_list)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;
    gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
    int sub_count = 1;
    int brick_index = 0;
    char *trans_type = NULL;
    int32_t index = 0;
    char *bricks = NULL;
    char *ta_brick = NULL;
    int32_t brick_count = 0;
    static char *opwords[] = {
        "replica",       "transport", "disperse",     "redundancy",
        "disperse-data", "arbiter",   "thin-arbiter", NULL};

    char *w = NULL;
    int op_count = 0;
    int32_t replica_count = 1;
    int32_t arbiter_count = 0;
    int32_t thin_arbiter_count = 0;
    int32_t disperse_count = -1;
    int32_t redundancy_count = -1;
    int32_t disperse_data_count = -1;
    gf_boolean_t is_force = _gf_false;
    int wc = wordcount;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();

    if (!dict)
        goto out;

    if (wordcount < 3)
        goto out;

    volname = (char *)words[2];

    GF_ASSERT(volname);

    /* Validate the volume name here itself */
    if (cli_validate_volname(volname) < 0)
        goto out;

    if (wordcount < 4) {
        ret = -1;
        goto out;
    }

    type = GF_CLUSTER_TYPE_NONE;
    index = 3;

    while (op_count < 3) {
        ret = -1;
        w = str_getunamb(words[index], opwords);
        if (!w) {
            break;
        } else if ((strcmp(w, "replica")) == 0) {
            switch (type) {
                case GF_CLUSTER_TYPE_REPLICATE:
                    cli_err("replica option given twice");
                    goto out;
                case GF_CLUSTER_TYPE_NONE:
                    type = GF_CLUSTER_TYPE_REPLICATE;
                    break;
                case GF_CLUSTER_TYPE_DISPERSE:
                    cli_err(
                        "replicated-dispersed volume is not "
                        "supported");
                    goto out;
                default:
                    cli_err("Invalid type given");
                    goto out;
            }

            if (wordcount < (index + 2)) {
                ret = -1;
                goto out;
            }

            replica_count = strtol(words[index + 1], NULL, 0);
            if (replica_count < 2) {
                cli_err(
                    "replica count should be greater"
                    " than 1");
                ret = -1;
                goto out;
            }

            index += 2;
            if (words[index]) {
                if (!strcmp(words[index], "arbiter")) {
                    ret = gf_string2int(words[index + 1], &arbiter_count);
                    if ((ret == -1) || (arbiter_count != 1) ||
                        ((replica_count < 2) || (replica_count > 3))) {
                        cli_err(
                            "For arbiter "
                            "configuration, "
                            "replica count must be"
                            " 2 and arbiter count "
                            "must be 1. The 3rd "
                            "brick of the replica "
                            "will be the arbiter");
                        ret = -1;
                        goto out;
                    }
                    ret = dict_set_int32(dict, "arbiter-count", arbiter_count);
                    if (ret)
                        goto out;
                    index += 2;
                } else if (!strcmp(words[index], "thin-arbiter")) {
                    ret = gf_string2int(words[index + 1], &thin_arbiter_count);
                    if ((ret == -1) || (thin_arbiter_count != 1) ||
                        (replica_count != 2)) {
                        cli_err(
                            "For thin-arbiter "
                            "configuration, "
                            "replica count must be"
                            " 2 and thin-arbiter count "
                            "must be 1. The 3rd "
                            "brick of the replica "
                            "will be the thin-arbiter brick");
                        ret = -1;
                        goto out;
                    }
                    ret = dict_set_int32(dict, "thin-arbiter-count",
                                         thin_arbiter_count);
                    if (ret)
                        goto out;
                    index += 2;
                }
            }

            /* Do this to keep glusterd happy with sending
               "replica 3 arbiter 1" options to server */
            if ((arbiter_count == 1) && (replica_count == 2))
                replica_count += arbiter_count;

            if (replica_count == 2 && thin_arbiter_count == 0) {
                if (strcmp(words[wordcount - 1], "force")) {
                    question =
                        "Replica 2 volumes are prone"
                        " to split-brain. Use "
                        "Arbiter or Replica 3 to "
                        "avoid this. See: "
                        "http://docs.gluster.org/en/latest/Administrator-Guide/"
                        "Split-brain-and-ways-to-deal-with-it/."
                        "\nDo you still want to "
                        "continue?\n";
                    answer = cli_cmd_get_confirmation(state, question);
                    if (GF_ANSWER_NO == answer) {
                        gf_log("cli", GF_LOG_ERROR,
                               "Volume create "
                               "cancelled, exiting");
                        ret = -1;
                        goto out;
                    }
                }
            }
            ret = dict_set_int32(dict, "replica-count", replica_count);
            if (ret)
                goto out;

        } else if ((strcmp(w, "transport")) == 0) {
            if (trans_type) {
                cli_err(
                    "'transport' option given more"
                    " than one time");
                goto out;
            }
            if ((strcasecmp(words[index + 1], "tcp") == 0)) {
                trans_type = gf_strdup("tcp");
            } else if ((strcasecmp(words[index + 1], "rdma") == 0)) {
                trans_type = gf_strdup("rdma");
            } else if ((strcasecmp(words[index + 1], "tcp,rdma") == 0) ||
                       (strcasecmp(words[index + 1], "rdma,tcp") == 0)) {
                trans_type = gf_strdup("tcp,rdma");
            } else {
                gf_log("", GF_LOG_ERROR,
                       "incorrect transport"
                       " protocol specified");
                ret = -1;
                goto out;
            }
            index += 2;

        } else if ((strcmp(w, "disperse") == 0) ||
                   (strcmp(w, "redundancy") == 0) ||
                   (strcmp(w, "disperse-data") == 0)) {
            ret = cli_validate_disperse_volume(
                w, type, words, wordcount, index, &disperse_count,
                &redundancy_count, &disperse_data_count);
            if (ret < 0)
                goto out;
            index += ret;
            type = GF_CLUSTER_TYPE_DISPERSE;
        } else if ((strcmp(w, "arbiter") == 0)) {
            cli_err(
                "arbiter option must be preceded by replica "
                "option.");
            ret = -1;
            goto out;
        } else if ((strcmp(w, "thin-arbiter") == 0)) {
            cli_err(
                "thin-arbiter option must be preceded by replica "
                "option.");
            ret = -1;
            goto out;
        } else {
            GF_ASSERT(!"opword mismatch");
            ret = -1;
            goto out;
        }
        op_count++;
    }

    if (!trans_type)
        trans_type = gf_strdup("tcp");

    if (index >= wordcount) {
        ret = -1;
        goto out;
    }

    brick_index = index;

    if (strcmp(words[wordcount - 1], "force") == 0) {
        is_force = _gf_true;
        wc = wordcount - 1;
    }

    // Exclude the thin-arbiter-brick i.e. last brick in the bricks list
    if (thin_arbiter_count == 1) {
        ret = cli_cmd_bricks_parse(words, wc - 1, brick_index, &bricks,
                                   &brick_count);
        if (ret)
            goto out;

        ret = cli_cmd_ta_brick_parse(words, wc, &ta_brick);

    } else {
        ret = cli_cmd_bricks_parse(words, wc, brick_index, &bricks,
                                   &brick_count);
    }

    if (ret)
        goto out;

    if (type == GF_CLUSTER_TYPE_DISPERSE) {
        ret = cli_cmd_create_disperse_check(state, &disperse_count,
                                            &redundancy_count,
                                            &disperse_data_count, brick_count);
        if (!ret)
            ret = dict_set_int32(dict, "disperse-count", disperse_count);
        if (!ret)
            ret = dict_set_int32(dict, "redundancy-count", redundancy_count);
        if (ret)
            goto out;

        sub_count = disperse_count;
    }
    if (brick_count % sub_count) {
        if (type == GF_CLUSTER_TYPE_REPLICATE)
            cli_err(
                "number of bricks is not a multiple of "
                "replica count");
        else if (type == GF_CLUSTER_TYPE_DISPERSE)
            cli_err(
                "number of bricks is not a multiple of "
                "disperse count");
        else
            cli_err(
                "number of bricks given doesn't match "
                "required count");

        ret = -1;
        goto out;
    }

    /* Everything is parsed fine. start setting info in dict */
    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    ret = dict_set_int32(dict, "type", type);
    if (ret)
        goto out;

    ret = dict_set_dynstr(dict, "transport", trans_type);
    if (ret)
        goto out;
    trans_type = NULL;

    ret = dict_set_dynstr(dict, "bricks", bricks);
    if (ret)
        goto out;

    if (thin_arbiter_count == 1) {
        ret = dict_set_dynstr(dict, "ta-brick", ta_brick);
        if (ret)
            goto out;
    }

    ret = dict_set_int32(dict, "count", brick_count);
    if (ret)
        goto out;

    ret = dict_set_int32(dict, "force", is_force);
    if (ret)
        goto out;

    *options = dict;
    *brick_list = bricks;
out:
    if (ret) {
        GF_FREE(bricks);
        GF_FREE(ta_brick);
        gf_log("cli", GF_LOG_ERROR, "Unable to parse create volume CLI");
        if (dict)
            dict_unref(dict);
    }

    GF_FREE(trans_type);

    return ret;
}

int32_t
cli_cmd_volume_reset_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();

    if (!dict)
        goto out;

    if (wordcount < 3)
        goto out;

    if (wordcount > 5)
        goto out;

    volname = (char *)words[2];

    if (!volname) {
        ret = -1;
        goto out;
    }

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    if (wordcount == 3) {
        ret = dict_set_str(dict, "key", "all");
        if (ret)
            goto out;
    }

    if (wordcount >= 4) {
        if (!strcmp("force", (char *)words[3])) {
            ret = dict_set_int32(dict, "force", 1);
            if (ret)
                goto out;
            ret = dict_set_str(dict, "key", "all");
            if (ret)
                goto out;
        } else {
            ret = dict_set_str(dict, "key", (char *)words[3]);
            if (ret)
                goto out;
        }
    }

    if (wordcount == 5) {
        if (strcmp("force", (char *)words[4])) {
            ret = -1;
            goto out;
        } else {
            ret = dict_set_int32(dict, "force", 1);
            if (ret)
                goto out;
        }
    }

    *options = dict;

out:
    if (ret && dict) {
        dict_unref(dict);
    }

    return ret;
}

int32_t
cli_cmd_get_state_parse(struct cli_state *state, const char **words,
                        int wordcount, dict_t **options, char **op_errstr)
{
    dict_t *dict = NULL;
    int ret = -1;
    char *odir = NULL;
    char *filename = NULL;
    char *daemon_name = NULL;
    int count = 0;
    uint32_t cmd = 0;

    GF_VALIDATE_OR_GOTO("cli", options, out);
    GF_VALIDATE_OR_GOTO("cli", words, out);

    dict = dict_new();
    if (!dict)
        goto out;

    if (wordcount < 1 || wordcount > 7) {
        *op_errstr = gf_strdup(
            "Problem parsing arguments."
            " Check usage.");
        goto out;
    }

    if (wordcount >= 1) {
        gf_asprintf(&daemon_name, "%s", "glusterd");

        for (count = 1; count < wordcount; count++) {
            if (strcmp(words[count], "odir") == 0 ||
                strcmp(words[count], "file") == 0) {
                if (strcmp(words[count], "odir") == 0) {
                    if (++count < wordcount) {
                        odir = (char *)words[count];
                        continue;
                    } else {
                        ret = -1;
                        goto out;
                    }
                } else if (strcmp(words[count], "file") == 0) {
                    if (++count < wordcount) {
                        filename = (char *)words[count];
                        continue;
                    } else {
                        ret = -1;
                        goto out;
                    }
                }
            } else {
                if (count > 1) {
                    if (count == wordcount - 1) {
                        if (strcmp(words[count], "detail") == 0) {
                            cmd = GF_CLI_GET_STATE_DETAIL;
                            continue;
                        } else if (strcmp(words[count], "volumeoptions") == 0) {
                            cmd = GF_CLI_GET_STATE_VOLOPTS;
                            continue;
                        }
                    } else {
                        *op_errstr = gf_strdup(
                            "Problem"
                            " parsing arguments. "
                            "Check usage.");
                        ret = -1;
                        goto out;
                    }
                }
                if (strcmp(words[count], "glusterd") == 0) {
                    continue;
                } else {
                    if (count == wordcount - 1) {
                        if (strcmp(words[count], "detail") == 0) {
                            cmd = GF_CLI_GET_STATE_DETAIL;
                            continue;
                        } else if (strcmp(words[count], "volumeoptions") == 0) {
                            cmd = GF_CLI_GET_STATE_VOLOPTS;
                            continue;
                        }
                    }

                    *op_errstr = gf_strdup(
                        "glusterd is "
                        "the only supported daemon.");
                    ret = -1;
                    goto out;
                }
            }
        }

        ret = dict_set_dynstr(dict, "daemon", daemon_name);
        if (ret) {
            *op_errstr = gf_strdup(
                "Command failed. Please check "
                " log file for more details.");
            gf_log(THIS->name, GF_LOG_ERROR,
                   "Setting daemon name to dictionary failed");
            goto out;
        }
        daemon_name = NULL;

        if (odir) {
            ret = dict_set_str(dict, "odir", odir);
            if (ret) {
                *op_errstr = gf_strdup(
                    "Command failed. Please"
                    " check log file for"
                    " more details.");
                gf_log(THIS->name, GF_LOG_ERROR,
                       "Setting output directory to"
                       "dictionary failed");
                goto out;
            }
        }

        if (filename) {
            ret = dict_set_str(dict, "filename", filename);
            if (ret) {
                *op_errstr = gf_strdup(
                    "Command failed. Please"
                    " check log file for"
                    " more  details.");
                gf_log(THIS->name, GF_LOG_ERROR,
                       "Setting filename to dictionary failed");
                goto out;
            }
        }

        if (cmd) {
            ret = dict_set_uint32(dict, "getstate-cmd", cmd);
            if (ret) {
                *op_errstr = gf_strdup(
                    "Command failed. Please"
                    " check log file for"
                    " more details.");
                gf_log(THIS->name, GF_LOG_ERROR,
                       "Setting "
                       "get-state command type to dictionary "
                       "failed");
                goto out;
            }
        }
    }

out:
    if (dict)
        *options = dict;

    if (ret && dict)
        dict_unref(dict);

    GF_FREE(daemon_name);

    return ret;
}

int32_t
cli_cmd_inode_quota_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict) {
        gf_log("cli", GF_LOG_ERROR, "dict_new failed");
        goto out;
    }

    if (wordcount != 4)
        goto out;

    volname = (char *)words[2];
    if (!volname) {
        ret = -1;
        goto out;
    }

    /* Validate the volume name here itself */
    if (cli_validate_volname(volname) < 0)
        goto out;

    ret = dict_set_str(dict, "volname", volname);
    if (ret < 0)
        goto out;

    if (strcmp(words[3], "enable") != 0) {
        cli_out("Invalid quota option : %s", words[3]);
        ret = -1;
        goto out;
    }

    ret = dict_set_int32(dict, "type", GF_QUOTA_OPTION_TYPE_ENABLE_OBJECTS);
    if (ret < 0)
        goto out;

    *options = dict;
out:
    if (ret < 0) {
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

int32_t
cli_cmd_quota_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;
    int i = -1;
    char key[20] = {
        0,
    };
    int64_t value = 0;
    gf_quota_type type = GF_QUOTA_OPTION_TYPE_NONE;
    static char *opwords[] = {"enable",
                              "disable",
                              "limit-usage",
                              "remove",
                              "list",
                              "alert-time",
                              "soft-timeout",
                              "hard-timeout",
                              "default-soft-limit",
                              "limit-objects",
                              "list-objects",
                              "remove-objects",
                              NULL};
    char *w = NULL;
    time_t time = 0;
    double percent = 0;
    char *end_ptr = NULL;
    int64_t limit = 0;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict) {
        gf_log("cli", GF_LOG_ERROR, "dict_new failed");
        goto out;
    }

    if (wordcount < 4) {
        if ((wordcount == 3) && !(strcmp(words[2], "help"))) {
            ret = 1;
        }
        goto out;
    }

    volname = (char *)words[2];
    if (!volname) {
        ret = -1;
        goto out;
    }

    /* Validate the volume name here itself */
    if (cli_validate_volname(volname) < 0)
        goto out;

    ret = dict_set_str(dict, "volname", volname);
    if (ret < 0)
        goto out;

    w = str_getunamb(words[3], opwords);
    if (!w) {
        cli_out("Invalid quota option : %s", words[3]);
        ret = -1;
        goto out;
    }

    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) {
        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) {
        type = GF_QUOTA_OPTION_TYPE_LIMIT_USAGE;
    } else if (strcmp(w, "limit-objects") == 0) {
        type = GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS;
    }

    if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE ||
        type == GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS) {
        if (wordcount < 6 || wordcount > 7) {
            ret = -1;
            goto out;
        }

        if (words[4][0] != '/') {
            cli_err("Please enter absolute path");
            ret = -1;
            goto out;
        }
        ret = dict_set_str(dict, "path", (char *)words[4]);
        if (ret)
            goto out;

        if (!words[5]) {
            cli_err("Please enter the limit value to be set");
            ret = -1;
            goto out;
        }

        if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE) {
            ret = gf_string2bytesize_int64(words[5], &value);
            if (ret != 0 || value <= 0) {
                if (errno == ERANGE || value <= 0) {
                    ret = -1;
                    cli_err(
                        "Please enter an integer "
                        "value in the range of "
                        "(1 - %" PRId64 ")",
                        INT64_MAX);
                } else
                    cli_err(
                        "Please enter a correct "
                        "value");
                goto out;
            }
        } else {
            errno = 0;
            limit = strtol(words[5], &end_ptr, 10);
            if (errno == ERANGE || errno == EINVAL || limit <= 0 ||
                strcmp(end_ptr, "") != 0) {
                ret = -1;
                cli_err(
                    "Please enter an integer value in "
                    "the range 1 - %" PRId64,
                    INT64_MAX);
                goto out;
            }
        }

        ret = dict_set_str(dict, "hard-limit", (char *)words[5]);
        if (ret < 0)
            goto out;

        if (wordcount == 7) {
            ret = gf_string2percent(words[6], &percent);
            if (ret != 0 || percent > 100) {
                ret = -1;
                cli_err(
                    "Please enter a correct value "
                    "in the range of 0 to 100");
                goto out;
            }

            ret = dict_set_str(dict, "soft-limit", (char *)words[6]);
            if (ret < 0)
                goto out;
        }

        goto set_type;
    }

    if (strcmp(w, "remove") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }

        type = GF_QUOTA_OPTION_TYPE_REMOVE;

        if (words[4][0] != '/') {
            cli_err("Please enter absolute path");
            ret = -1;
            goto out;
        }

        ret = dict_set_str(dict, "path", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    }

    if (strcmp(w, "remove-objects") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }

        type = GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS;

        if (words[4][0] != '/') {
            cli_err("Please enter absolute path");
            ret = -1;
            goto out;
        }

        ret = dict_set_str(dict, "path", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    }

    if (strcmp(w, "list") == 0) {
        type = GF_QUOTA_OPTION_TYPE_LIST;

        if (words[4] && words[4][0] != '/') {
            cli_err("Please enter absolute path");
            ret = -1;
            goto out;
        }

        i = 4;
        while (i < wordcount) {
            snprintf(key, 20, "path%d", i - 4);

            ret = dict_set_str(dict, key, (char *)words[i++]);
            if (ret < 0)
                goto out;
        }

        ret = dict_set_int32(dict, "count", i - 4);
        if (ret < 0)
            goto out;

        goto set_type;
    }

    if (strcmp(w, "list-objects") == 0) {
        type = GF_QUOTA_OPTION_TYPE_LIST_OBJECTS;

        i = 4;
        while (i < wordcount) {
            snprintf(key, 20, "path%d", i - 4);

            ret = dict_set_str(dict, key, (char *)words[i++]);
            if (ret < 0) {
                gf_log("cli", GF_LOG_ERROR,
                       "Failed to set "
                       "quota patch in request dictionary");
                goto out;
            }
        }

        ret = dict_set_int32(dict, "count", i - 4);
        if (ret < 0) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set quota "
                   "limit count in request dictionary");
            goto out;
        }

        goto set_type;
    }

    if (strcmp(w, "alert-time") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }
        type = GF_QUOTA_OPTION_TYPE_ALERT_TIME;

        ret = gf_string2time(words[4], &time);
        if (ret) {
            cli_err(
                "Invalid argument %s. Please enter a valid "
                "string",
                words[4]);
            goto out;
        }

        ret = dict_set_str(dict, "value", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    }

    if (strcmp(w, "soft-timeout") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }
        type = GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT;

        ret = gf_string2time(words[4], &time);
        if (ret) {
            cli_err(
                "Invalid argument %s. Please enter a valid "
                "string",
                words[4]);
            goto out;
        }

        ret = dict_set_str(dict, "value", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    }

    if (strcmp(w, "hard-timeout") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }
        type = GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT;

        ret = gf_string2time(words[4], &time);
        if (ret) {
            cli_err(
                "Invalid argument %s. Please enter a valid "
                "string",
                words[4]);
            goto out;
        }

        ret = dict_set_str(dict, "value", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    }
    if (strcmp(w, "default-soft-limit") == 0) {
        if (wordcount != 5) {
            ret = -1;
            goto out;
        }
        type = GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT;

        ret = dict_set_str(dict, "value", (char *)words[4]);
        if (ret < 0)
            goto out;
        goto set_type;
    } else {
        GF_ASSERT(!"opword mismatch");
    }

set_type:
    ret = dict_set_int32(dict, "type", type);
    if (ret < 0)
        goto out;

    *options = dict;
out:
    if (ret < 0) {
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

static gf_boolean_t
cli_is_key_spl(char *key)
{
    return (strcmp(key, "group") == 0);
}

static int32_t
cli_add_key_group_value(dict_t *dict, const char *name, const char *value,
                        int32_t id, char **op_errstr)
{
    char *key = NULL;
    char *data = NULL;
    int32_t ret = -1;

    ret = gf_asprintf(&key, "%s%d", name, id);
    if (ret < 0) {
        goto out;
    }
    data = gf_strdup(value);
    if (data == NULL) {
        gf_log(THIS->name, GF_LOG_ERROR, "Failed to allocate memory for data");
        ret = -1;
        goto out;
    }

    ret = dict_set_dynstr(dict, key, data);
    if (ret == 0) {
        data = NULL;
    }

out:
    GF_FREE(key);
    GF_FREE(data);

    if ((ret != 0) && (op_errstr != NULL)) {
        *op_errstr = gf_strdup("Failed to allocate memory");
    }

    return ret;
}

static int
cli_add_key_group(dict_t *dict, char *key, char *value, char **op_errstr)
{
    int ret = -1;
    int opt_count = 0;
    char *saveptr = NULL;
    char *tok_key = NULL;
    char *tok_val = NULL;
    char *tagpath = NULL;
    char line[PATH_MAX + 256] = {
        0,
    };
    FILE *fp = NULL;

    ret = gf_asprintf(&tagpath, "%s/groups/%s", GLUSTERD_DEFAULT_WORKDIR,
                      value);
    if (ret == -1) {
        tagpath = NULL;
        goto out;
    }

    fp = fopen(tagpath, "r");
    if (!fp) {
        ret = -1;
        if (op_errstr) {
            gf_asprintf(op_errstr,
                        "Unable to open file '%s'. "
                        "Error: %s",
                        tagpath, strerror(errno));
        }
        goto out;
    }

    opt_count = 0;
    while (fgets(line, sizeof(line), fp) != NULL) {
        if (strlen(line) >= sizeof(line) - 1) {
            ret = -1;
            if (op_errstr != NULL) {
                *op_errstr = gf_strdup("Line too long");
            }
            goto out;
        }

        /* Treat line that start with "#" as comments */
        if ('#' == line[0])
            continue;

        opt_count++;
        tok_key = strtok_r(line, "=", &saveptr);
        tok_val = strtok_r(NULL, "\r\n", &saveptr);
        if (!tok_key || !tok_val) {
            ret = -1;
            if (op_errstr) {
                gf_asprintf(op_errstr,
                            "'%s' file format "
                            "not valid.",
                            tagpath);
            }
            goto out;
        }

        ret = cli_add_key_group_value(dict, "key", tok_key, opt_count,
                                      op_errstr);
        if (ret != 0) {
            goto out;
        }
        ret = cli_add_key_group_value(dict, "value", tok_val, opt_count,
                                      op_errstr);
        if (ret != 0) {
            goto out;
        }
    }

    if (!opt_count) {
        ret = -1;
        if (op_errstr) {
            gf_asprintf(op_errstr, "'%s' file format not valid.", tagpath);
        }
        goto out;
    }
    ret = dict_set_int32(dict, "count", opt_count);
out:

    GF_FREE(tagpath);

    if (fp)
        fclose(fp);

    return ret;
}

int32_t
cli_cmd_volume_set_parse(struct cli_state *state, 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,
    };
    const char *question = NULL;
    gf_answer_t answer = GF_ANSWER_NO;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();

    if (!dict)
        goto out;

    if (wordcount < 3)
        goto out;

    volname = (char *)words[2];

    GF_ASSERT(volname);

    ret = dict_set_str(dict, "volname", volname);

    if (ret)
        goto out;

    if (!strcmp(volname, "all")) {
        ret = dict_set_str(dict, "globalname", "All");
        if (ret) {
            gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed.");
            goto out;
        }

        ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
        if (ret) {
            gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed.");
            goto out;
        }
    }

    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];

        if (!key || !value) {
            ret = -1;
            goto out;
        }

        count++;

        if (fnmatch("user.*", key, FNM_NOESCAPE) != 0) {
            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)
            goto out;

        sprintf(str, "value%d", count);
        ret = dict_set_str(dict, str, value);

        if (ret)
            goto out;

        if ((!strcmp(key, "cluster.enable-shared-storage")) &&
            (!strcmp(value, "disable"))) {
            question =
                "Disabling cluster.enable-shared-storage "
                "will delete the shared storage volume"
                "(gluster_shared_storage), which is used "
                "by snapshot scheduler, geo-replication "
                "and NFS-Ganesha. Do you still want to "
                "continue?";
            answer = cli_cmd_get_confirmation(state, question);
            if (GF_ANSWER_NO == answer) {
                gf_log("cli", GF_LOG_ERROR,
                       "Operation "
                       "cancelled, exiting");
                *op_errstr = gf_strdup("Aborted by user.");
                ret = -1;
                goto out;
            }
        }
        if ((!strcmp(key, "nfs.disable")) && (!strcmp(value, "off"))) {
            question =
                "Gluster NFS is being deprecated in favor "
                "of NFS-Ganesha Enter \"yes\" to continue "
                "using Gluster NFS";
            answer = cli_cmd_get_confirmation(state, question);
            if (GF_ANSWER_NO == answer) {
                gf_log("cli", GF_LOG_ERROR,
                       "Operation "
                       "cancelled, exiting");
                *op_errstr = gf_strdup("Aborted by user.");
                ret = -1;
                goto out;
            }
        }
    }

    ret = dict_set_int32(dict, "count", wordcount - 3);

    if (ret)
        goto out;

    *options = dict;

out:
    if (ret && dict)
        dict_unref(dict);

    return ret;
}

int32_t
cli_cmd_volume_add_brick_parse(struct cli_state *state, const char **words,
                               int wordcount, dict_t **options, int *ret_type)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;
    int brick_count = 0, brick_index = 0;
    char *bricks = NULL;
    static char *opwords_cl[] = {"replica", NULL};
    gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
    int count = 1;
    int arbiter_count = 0;
    char *w = NULL;
    int index;
    gf_boolean_t is_force = _gf_false;
    int wc = wordcount;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();

    if (!dict)
        goto out;

    if (wordcount < 3)
        goto out;

    volname = (char *)words[2];

    GF_ASSERT(volname);

    ret = dict_set_str(dict, "volname", volname);

    if (ret)
        goto out;

    if (wordcount < 4) {
        ret = -1;
        goto out;
    }
    if (wordcount < 6) {
        /* seems no options are given, go directly to the parse_brick */
        brick_index = 3;
        type = GF_CLUSTER_TYPE_NONE;
        goto parse_bricks;
    }

    w = str_getunamb(words[3], opwords_cl);
    if (!w) {
        type = GF_CLUSTER_TYPE_NONE;
        index = 3;
    } else if ((strcmp(w, "replica")) == 0) {
        type = GF_CLUSTER_TYPE_REPLICATE;
        count = strtol(words[4], NULL, 0);
        if (!count || (count < 2)) {
            cli_err("replica count should be greater than 1");
            ret = -1;
            goto out;
        }
        ret = dict_set_int32(dict, "replica-count", count);
        if (ret)
            goto out;
        index = 5;
        if (words[index] && !strcmp(words[index], "arbiter")) {
            arbiter_count = strtol(words[6], NULL, 0);
            if (arbiter_count != 1 || count != 3) {
                cli_err(
                    "For arbiter configuration, replica "
                    "count must be 3 and arbiter count "
                    "must be 1. The 3rd brick of the "
                    "replica will be the arbiter");
                ret = -1;
                goto out;
            }
            ret = dict_set_int32(dict, "arbiter-count", arbiter_count);
            if (ret)
                goto out;
            index = 7;
        }

        if (count == 2) {
            if (strcmp(words[wordcount - 1], "force")) {
                question =
                    "Replica 2 volumes are prone to "
                    "split-brain. Use Arbiter or "
                    "Replica 3 to avoid this. See: "
                    "http://docs.gluster.org/en/latest/Administrator-Guide/"
                    "Split-brain-and-ways-to-deal-with-it/."
                    "\nDo you still want to continue?\n";
                answer = cli_cmd_get_confirmation(state, question);
                if (GF_ANSWER_NO == answer) {
                    gf_log("cli", GF_LOG_ERROR,
                           "Add brick"
                           " cancelled, exiting");
                    ret = -1;
                    goto out;
                }
            }
        }
    } else {
        GF_ASSERT(!"opword mismatch");
        ret = -1;
        goto out;
    }

    brick_index = index;

parse_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;

    ret = dict_set_dynstr(dict, "bricks", bricks);
    if (ret)
        goto out;

    ret = dict_set_int32(dict, "count", brick_count);

    if (ret)
        goto out;

    ret = dict_set_int32(dict, "force", is_force);
    if (ret)
        goto out;

    *options = dict;

out:
    if (ret_type)
        *ret_type = type;

    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to parse add-brick CLI");
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

int32_t
cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words,
                                  int wordcount, dict_t **options,
                                  int *question, int *brick_count,
                                  int32_t *comm)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    char *delimiter = NULL;
    int ret = -1;
    char key[50];
    int brick_index = 0;
    int32_t tmp_index = 0;
    int32_t j = 0;
    char *tmp_brick = NULL;
    char *tmp_brick1 = NULL;
    static char *type_opword[] = {"replica", NULL};
    static char *opwords[] = {"start",  "commit", "stop",
                              "status", "force",  NULL};
    char *w = NULL;
    int32_t command = GF_OP_CMD_NONE;
    long count = 0;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *ques = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    if (wordcount < 5)
        goto out;

    dict = dict_new();
    if (!dict)
        goto out;

    volname = (char *)words[2];

    GF_ASSERT(volname);

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    brick_index = 3;
    w = str_getunamb(words[3], type_opword);
    if (w && !strcmp("replica", w)) {
        if (wordcount < 6) {
            ret = -1;
            goto out;
        }
        count = strtol(words[4], NULL, 0);
        if (count < 1) {
            cli_err(
                "replica count should be greater than 0 in "
                "case of remove-brick");
            ret = -1;
            goto out;
        }

        if (count == 2) {
            if (strcmp(words[wordcount - 1], "force")) {
                ques =
                    "Replica 2 volumes are prone to "
                    "split-brain. Use Arbiter or Replica 3 "
                    "to avoid this. See: "
                    "http://docs.gluster.org/en/latest/Administrator-Guide/"
                    "Split-brain-and-ways-to-deal-with-it/."
                    "\nDo you still want to continue?\n";
                answer = cli_cmd_get_confirmation(state, ques);
                if (GF_ANSWER_NO == answer) {
                    gf_log("cli", GF_LOG_ERROR,
                           "Remove "
                           "brick cancelled, exiting");
                    ret = -1;
                    goto out;
                }
            }
        }

        ret = dict_set_int32(dict, "replica-count", count);
        if (ret)
            goto out;
        brick_index = 5;
    } else if (w) {
        GF_ASSERT(!"opword mismatch");
    }

    w = str_getunamb(words[wordcount - 1], opwords);
    if (!w) {
        ret = -1;
        goto out;
    } else {
        /* handled this option */
        wordcount--;
        if (!strcmp("start", w)) {
            command = GF_OP_CMD_START;
            if (question)
                *question = 1;
        } else if (!strcmp("commit", w)) {
            command = GF_OP_CMD_COMMIT;
        } 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)) {
            command = GF_OP_CMD_COMMIT_FORCE;
            if (question)
                *question = 1;
        } else {
            GF_ASSERT(!"opword mismatch");
            ret = -1;
            goto out;
        }
    }

    ret = dict_set_int32(dict, "command", command);
    if (ret)
        gf_log("cli", GF_LOG_INFO, "failed to set 'command' %d", command);

    tmp_index = brick_index;
    tmp_brick = GF_MALLOC(2048 * sizeof(*tmp_brick), gf_common_mt_char);

    if (!tmp_brick) {
        gf_log("", GF_LOG_ERROR,
               "cli_cmd_volume_remove_brick_parse: "
               "Unable to get memory");
        ret = -1;
        goto out;
    }

    tmp_brick1 = GF_MALLOC(2048 * sizeof(*tmp_brick1), gf_common_mt_char);

    if (!tmp_brick1) {
        gf_log("", GF_LOG_ERROR,
               "cli_cmd_volume_remove_brick_parse: "
               "Unable to get memory");
        ret = -1;
        goto out;
    }

    while (brick_index < wordcount) {
        if (validate_brick_name((char *)words[brick_index])) {
            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 = gf_canonicalize_path(delimiter + 1);
            if (ret)
                goto out;
        }

        j = tmp_index;
        strcpy(tmp_brick, words[brick_index]);
        while (j < brick_index) {
            strcpy(tmp_brick1, words[j]);
            if (!(strcmp(tmp_brick, tmp_brick1))) {
                gf_log("", GF_LOG_ERROR,
                       "Duplicate bricks"
                       " found %s",
                       words[brick_index]);
                cli_err("Duplicate bricks found %s", words[brick_index]);
                ret = -1;
                goto out;
            }
            j++;
        }
        snprintf(key, 50, "brick%d", ++(*brick_count));
        ret = dict_set_str(dict, key, (char *)words[brick_index++]);

        if (ret)
            goto out;
    }

    if (command != GF_OP_CMD_STATUS && command != GF_OP_CMD_STOP) {
        ret = dict_set_int32(dict, "count", *brick_count);
        if (ret)
            goto out;
    }

    *options = dict;

out:
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI");
        if (dict)
            dict_unref(dict);
    }

    GF_FREE(tmp_brick);
    GF_FREE(tmp_brick1);

    *comm = command;

    return ret;
}

int32_t
cli_cmd_brick_op_validate_bricks(const char **words, dict_t *dict, int src,
                                 int dst)
{
    int ret = -1;
    char *delimiter = NULL;

    if (validate_brick_name((char *)words[src])) {
        cli_err(
            "wrong brick type: %s, use "
            "<HOSTNAME>:<export-dir-abs-path>",
            words[3]);
        ret = -1;
        goto out;
    } else {
        delimiter = strrchr((char *)words[src], '/');
        ret = gf_canonicalize_path(delimiter);
        if (ret)
            goto out;
    }

    ret = dict_set_str(dict, "src-brick", (char *)words[src]);
    if (ret)
        goto out;

    if (dst == -1) {
        ret = 0;
        goto out;
    }

    if (validate_brick_name((char *)words[dst])) {
        cli_err(
            "wrong brick type: %s, use "
            "<HOSTNAME>:<export-dir-abs-path>",
            words[dst]);
        ret = -1;
        goto out;
    } else {
        delimiter = strrchr((char *)words[dst], '/');
        ret = gf_canonicalize_path(delimiter);
        if (ret)
            goto out;
    }

    ret = dict_set_str(dict, "dst-brick", (char *)words[dst]);
    if (ret)
        goto out;
    ret = 0;
out:
    return ret;
}

int32_t
cli_cmd_volume_reset_brick_parse(const char **words, int wordcount,
                                 dict_t **options)
{
    int ret = -1;
    char *volname = NULL;
    dict_t *dict = NULL;

    if (wordcount < 5 || wordcount > 7)
        goto out;

    dict = dict_new();

    if (!dict)
        goto out;

    volname = (char *)words[2];

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    if (wordcount == 5) {
        if (strcmp(words[4], "start")) {
            cli_err(
                "Invalid option '%s' for reset-brick. Please "
                "enter valid reset-brick command",
                words[4]);
            ret = -1;
            goto out;
        }

        ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, -1);
        if (ret)
            goto out;

        ret = dict_set_str(dict, "operation", "GF_RESET_OP_START");
        if (ret)
            goto out;
    } else if (wordcount == 6) {
        if (strcmp(words[5], "commit")) {
            cli_err(
                "Invalid option '%s' for reset-brick. Please "
                "enter valid reset-brick command",
                words[5]);
            ret = -1;
            goto out;
        }

        ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
        if (ret)
            goto out;

        ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT");
        if (ret)
            goto out;
    } else if (wordcount == 7) {
        if (strcmp(words[5], "commit") || strcmp(words[6], "force")) {
            cli_err(
                "Invalid option '%s %s' for reset-brick. Please "
                "enter valid reset-brick command",
                words[5], words[6]);
            ret = -1;
            goto out;
        }

        ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
        if (ret)
            goto out;

        ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT_FORCE");
        if (ret)
            goto out;
    }

    *options = dict;

out:
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI");
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

int32_t
cli_cmd_volume_replace_brick_parse(const char **words, int wordcount,
                                   dict_t **options)
{
    int ret = -1;
    char *volname = NULL;
    dict_t *dict = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    if (wordcount != 7) {
        ret = -1;
        goto out;
    }

    dict = dict_new();

    if (!dict) {
        gf_log("cli", GF_LOG_ERROR, "Failed to allocate dictionary");
        goto out;
    }

    volname = (char *)words[2];

    GF_ASSERT(volname);

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
    if (ret)
        goto out;

    /* commit force option */
    if (strcmp("commit", words[5]) || strcmp("force", words[6])) {
        cli_err(
            "Invalid option '%s' '%s' for replace-brick. Please "
            "enter valid replace-brick command",
            words[5], words[6]);
        ret = -1;
        goto out;
    }

    ret = dict_set_str(dict, "operation", "GF_REPLACE_OP_COMMIT_FORCE");
    if (ret)
        goto out;

    *options = dict;

out:
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI");
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

int32_t
cli_cmd_log_rotate_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    char *str = NULL;
    int ret = -1;
    char *delimiter = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict)
        goto out;

    if (strcmp("rotate", words[3]) == 0)
        volname = (char *)words[2];
    GF_ASSERT(volname);

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    if (words[4]) {
        delimiter = strchr(words[4], ':');
        if (!delimiter || delimiter == words[4] || *(delimiter + 1) != '/') {
            cli_err(
                "wrong brick type: %s, use <HOSTNAME>:"
                "<export-dir-abs-path>",
                words[4]);
            ret = -1;
            goto out;
        } else {
            ret = gf_canonicalize_path(delimiter + 1);
            if (ret)
                goto out;
        }
        str = (char *)words[4];
        ret = dict_set_str(dict, "brick", str);
        if (ret)
            goto out;
    }

    *options = dict;

out:
    if (ret && dict)
        dict_unref(dict);

    return ret;
}

static gf_boolean_t
gsyncd_url_check(const char *w)
{
    return !!strpbrk(w, ":/");
}

static gf_boolean_t
valid_secondary_gsyncd_url(const char *w)
{
    if (strstr(w, ":::"))
        return _gf_false;
    else if (strstr(w, "::"))
        return _gf_true;
    else
        return _gf_false;
}

static gf_boolean_t
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;
    char *ret_chkpt = NULL;
    struct tm checkpoint_time;
    char chkpt_buf[20] = "";

    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++;

            /* strcat is used on this allocation and hence expected to be
             * initiatlized to 0. So GF_CALLOC is used.
             */
            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;

                GF_FREE(append_str);
                append_str = GF_MALLOC(300, cli_mt_append_str);
                if (!append_str) {
                    ret = -1;
                    goto out;
                }
                snprintf(append_str, 300, "%" GF_PRI_SECOND, tv.tv_sec);
            } else if ((strcmp(words[cmdi + 1], "checkpoint") == 0) &&
                       (strcmp(append_str, "now") != 0)) {
                memset(&checkpoint_time, 0, sizeof(struct tm));
                ret_chkpt = strptime(append_str, "%Y-%m-%d %H:%M:%S",
                                     &checkpoint_time);

                if (ret_chkpt == NULL || *ret_chkpt != '\0') {
                    ret = -1;
                    cli_err(
                        "Invalid Checkpoint label. Use format "
                        "\"Y-m-d H:M:S\", Example: 2016-10-25 15:30:45");
                    goto out;
                }
                GF_FREE(append_str);
                append_str = GF_MALLOC(300, cli_mt_append_str);
                if (!append_str) {
                    ret = -1;
                    goto out;
                }
                strftime(chkpt_buf, sizeof(chkpt_buf), "%s", &checkpoint_time);
                snprintf(append_str, 300, "%s", chkpt_buf);
            }

            ret = dict_set_dynstr(dict, "op_value", append_str);
            if (ret != 0) {
                goto out;
            }
            append_str = NULL;
    }

    ret = -1;
    if (subop) {
        ret = dict_set_dynstr(dict, "subop", subop);
        if (!ret)
            subop = NULL;
    }

out:
    GF_FREE(append_str);
    GF_FREE(subop);

    gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
    return ret;
}

/* ssh_port_parse: Parses and validates when ssh_port is given.
 *                 ssh_index refers to index of ssh_port and
 *                 type refers to either push-pem or no-verify
 */

static int32_t
parse_ssh_port(const char **words, int wordcount, dict_t *dict, unsigned *cmdi,
               int ssh_index, char *type)
{
    int ret = 0;
    char *end_ptr = NULL;
    int64_t limit = 0;

    if (!strcmp((char *)words[ssh_index], "ssh-port")) {
        if (strcmp((char *)words[ssh_index - 1], "create")) {
            ret = -1;
            goto out;
        }
        (*cmdi)++;
        limit = strtol(words[ssh_index + 1], &end_ptr, 10);
        if (errno == ERANGE || errno == EINVAL || limit <= 0 ||
            strcmp(end_ptr, "") != 0) {
            ret = -1;
            cli_err("Please enter an integer value for ssh_port ");
            goto out;
        }

        ret = dict_set_int32(dict, "ssh_port", limit);
        if (ret)
            goto out;
        (*cmdi)++;
    } else if (strcmp((char *)words[ssh_index + 1], "create")) {
        ret = -1;
        goto out;
    }

    ret = dict_set_int32(dict, type, 1);
    if (ret)
        goto out;
    (*cmdi)++;

out:
    return ret;
}

static int32_t
force_push_pem_no_verify_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], "no-verify")) &&
            (strcmp((char *)words[wordcount - 2], "push-pem")) &&
            (strcmp((char *)words[wordcount - 2], "pause")) &&
            (strcmp((char *)words[wordcount - 2], "resume"))) {
            ret = -1;
            goto out;
        }
        ret = dict_set_int32n(dict, "force", SLEN("force"), 1);
        if (ret)
            goto out;
        (*cmdi)++;

        if (!strcmp((char *)words[wordcount - 2], "push-pem")) {
            ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4,
                                 "push_pem");
            if (ret)
                goto out;
        } else if (!strcmp((char *)words[wordcount - 2], "no-verify")) {
            ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4,
                                 "no_verify");
            if (ret)
                goto out;
        }
    } else if (!strcmp((char *)words[wordcount - 1], "push-pem")) {
        ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3,
                             "push_pem");
        if (ret)
            goto out;
    } else if (!strcmp((char *)words[wordcount - 1], "no-verify")) {
        ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3,
                             "no_verify");
        if (ret)
            goto out;
    }

out:
    gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
    return ret;
}

int32_t
cli_cmd_gsync_set_parse(struct cli_state *state, const char **words,
                        int wordcount, dict_t **options, char **errstr)
{
    int32_t ret = -1;
    dict_t *dict = NULL;
    gf1_cli_gsync_set type = GF_GSYNC_OPTION_TYPE_NONE;
    int i = 0;
    unsigned primary_idx = 0;
    unsigned secondary_idx = 0;
    unsigned glob = 0;
    unsigned cmdi = 0;
    static char *opwords[] = {"create",    "status",   "start",  "stop",
                              "config",    "force",    "delete", "ssh-port",
                              "no-verify", "push-pem", "detail", "pause",
                              "resume",    NULL};
    char *w = NULL;
    char *save_ptr = NULL;
    char *secondary_temp = NULL;
    char *token = NULL;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict)
        goto out;

    /* new syntax:
     *
     * volume geo-replication $m $s create [[ssh-port n] [[no-verify] |
     * [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 [force]
     * volume geo-replication $m $s delete [reset-sync-time]
     * volume geo-replication $m $s pause [force]
     * volume geo-replication $m $s resume [force]
     */

    if (wordcount < 3)
        goto out;

    for (i = 2; i <= 3 && i < wordcount - 1; i++) {
        if (gsyncd_glob_check(words[i]))
            glob = i;
        if (gsyncd_url_check(words[i])) {
            secondary_idx = i;
            break;
        }
    }

    if (glob && !secondary_idx)
        /* glob is allowed only for config, thus it implies there is a
         * secondary argument; but that might have not been recognized on
         * the first scan as it's url characteristics has been covered
         * by the glob syntax.
         *
         * In this case, the secondary is perforce the last glob-word -- the
         * upcoming one is neither glob, nor url, so it's definitely not
         * the secondary.
         */
        secondary_idx = glob;
    if (secondary_idx) {
        cmdi = secondary_idx + 1;
        if (secondary_idx == 3)
            primary_idx = 2;
    } else if (i <= 4) {
        if (strtail("detail", (char *)words[wordcount - 1])) {
            cmdi = wordcount - 2;
            if (i == 4)
                primary_idx = 2;
        } else {
            /* no $s, can only be status cmd
             * (with either a single $m before it or nothing)
             * -- these conditions imply that i <= 3 after
             * the iteration and that i is the successor of
             * the (0 or 1 length) sequence of $m-s.
             */
            cmdi = i;
            if (i == 3)
                primary_idx = 2;
        }
    } else
        goto out;

    /* now check if input really complies syntax
     * (in a somewhat redundant way, in favor
     * transparent soundness)
     */

    if (primary_idx && gsyncd_url_check(words[primary_idx]))
        goto out;

    if (secondary_idx && !glob &&
        !valid_secondary_gsyncd_url(words[secondary_idx])) {
        gf_asprintf(errstr, "Invalid secondary url: %s", words[secondary_idx]);
        goto out;
    }

    w = str_getunamb(words[cmdi], opwords);
    if (!w)
        goto out;

    if (strcmp(w, "create") == 0) {
        type = GF_GSYNC_OPTION_TYPE_CREATE;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else if (strcmp(w, "status") == 0) {
        type = GF_GSYNC_OPTION_TYPE_STATUS;

        if (secondary_idx && !primary_idx)
            goto out;
    } else if (strcmp(w, "config") == 0) {
        type = GF_GSYNC_OPTION_TYPE_CONFIG;

        if (!secondary_idx)
            goto out;
    } else if (strcmp(w, "start") == 0) {
        type = GF_GSYNC_OPTION_TYPE_START;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else if (strcmp(w, "stop") == 0) {
        type = GF_GSYNC_OPTION_TYPE_STOP;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else if (strcmp(w, "delete") == 0) {
        type = GF_GSYNC_OPTION_TYPE_DELETE;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else if (strcmp(w, "pause") == 0) {
        type = GF_GSYNC_OPTION_TYPE_PAUSE;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else if (strcmp(w, "resume") == 0) {
        type = GF_GSYNC_OPTION_TYPE_RESUME;

        if (!primary_idx || !secondary_idx)
            goto out;
    } else
        GF_ASSERT(!"opword mismatch");

    ret = force_push_pem_no_verify_parse(words, wordcount, dict, &cmdi);
    if (ret)
        goto out;

    if (strtail("detail", (char *)words[wordcount - 1])) {
        if (!strtail("status", (char *)words[wordcount - 2])) {
            ret = -1;
            goto out;
        }

        ret = dict_set_uint32(dict, "status-detail", _gf_true);
        if (ret)
            goto out;
        cmdi++;
    }

    if (type == GF_GSYNC_OPTION_TYPE_DELETE &&
        !strcmp((char *)words[wordcount - 1], "reset-sync-time")) {
        if (strcmp((char *)words[wordcount - 2], "delete")) {
            ret = -1;
            goto out;
        }
        ret = dict_set_uint32(dict, "reset-sync-time", _gf_true);
        if (ret)
            goto out;
        cmdi++;
    }

    if (type != GF_GSYNC_OPTION_TYPE_CONFIG && (cmdi < wordcount - 1 || glob)) {
        ret = -1;
        goto out;
    }

    /* If got so far, input is valid, assemble the message */

    ret = 0;

    if (primary_idx) {
        ret = dict_set_str(dict, "primary", (char *)words[primary_idx]);
        if (!ret)
            ret = dict_set_str(dict, "volname", (char *)words[primary_idx]);
    }
    if (!ret && secondary_idx) {
        /* If geo-rep is created with root user using the syntax
         * gluster vol geo-rep <primaryvol> root@<secondaryhost> ...
         * pass down only <secondaryhost> else pass as it is.
         */
        secondary_temp = gf_strdup(words[secondary_idx]);
        if (secondary_temp == NULL) {
            ret = -1;
            goto out;
        }
        token = strtok_r(secondary_temp, "@", &save_ptr);
        if (token && !strcmp(token, "root")) {
            ret = dict_set_str(dict, "secondary",
                               (char *)words[secondary_idx] + 5);
        } else {
            ret = dict_set_str(dict, "secondary", (char *)words[secondary_idx]);
        }
    }
    if (!ret)
        ret = dict_set_int32(dict, "type", type);
    if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) {
        if (!strcmp((char *)words[wordcount - 2], "ignore-deletes") &&
            !strcmp((char *)words[wordcount - 1], "true")) {
            question =
                "There exists ~15 seconds delay for the option to take"
                " effect from stime of the corresponding brick. Please"
                " check the log for the time, the option is effective."
                " Proceed";

            answer = cli_cmd_get_confirmation(state, question);

            if (GF_ANSWER_NO == answer) {
                gf_log("cli", GF_LOG_INFO,
                       "Operation "
                       "cancelled, exiting");
                *errstr = gf_strdup("Aborted by user.");
                ret = -1;
                goto out;
            }
        }

        ret = config_parse(words, wordcount, dict, cmdi, glob);
    }

out:
    if (secondary_temp)
        GF_FREE(secondary_temp);
    if (ret && dict)
        dict_unref(dict);
    else
        *options = dict;

    return ret;
}

int32_t
cli_cmd_volume_profile_parse(const char **words, int wordcount,
                             dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    int ret = -1;
    gf1_cli_stats_op op = GF_CLI_STATS_NONE;
    gf1_cli_info_op info_op = GF_CLI_INFO_NONE;
    gf_boolean_t is_peek = _gf_false;

    static char *opwords[] = {"start", "stop", "info", NULL};
    char *w = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict)
        goto out;

    if (wordcount < 4)
        goto out;

    volname = (char *)words[2];

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    w = str_getunamb(words[3], opwords);
    if (!w) {
        ret = -1;
        goto out;
    }

    if ((strcmp(w, "start") == 0 || strcmp(w, "stop") == 0) && wordcount > 5) {
        ret = -1;
        goto out;
    }

    if (strcmp(w, "info") == 0 && wordcount > 7) {
        ret = -1;
        goto out;
    }

    if (strcmp(w, "start") == 0) {
        op = GF_CLI_STATS_START;
    } else if (strcmp(w, "stop") == 0) {
        op = GF_CLI_STATS_STOP;
    } else if (strcmp(w, "info") == 0) {
        op = GF_CLI_STATS_INFO;
        info_op = GF_CLI_INFO_ALL;
        if (wordcount > 4) {
            if (strcmp(words[4], "incremental") == 0) {
                info_op = GF_CLI_INFO_INCREMENTAL;
                if (wordcount > 5 && strcmp(words[5], "peek") == 0) {
                    is_peek = _gf_true;
                }
            } else if (strcmp(words[4], "cumulative") == 0) {
                info_op = GF_CLI_INFO_CUMULATIVE;
            } else if (strcmp(words[4], "clear") == 0) {
                info_op = GF_CLI_INFO_CLEAR;
            } else if (strcmp(words[4], "peek") == 0) {
                is_peek = _gf_true;
            }
        }
    } else
        GF_ASSERT(!"opword mismatch");

    ret = dict_set_int32(dict, "op", (int32_t)op);
    if (ret)
        goto out;

    ret = dict_set_int32(dict, "info-op", (int32_t)info_op);
    if (ret)
        goto out;

    ret = dict_set_int32(dict, "peek", is_peek);
    if (ret)
        goto out;

    if (!strcmp(words[wordcount - 1], "nfs")) {
        ret = dict_set_int32(dict, "nfs", _gf_true);
        if (ret)
            goto out;
    }

    *options = dict;
out:
    if (ret && dict)
        dict_unref(dict);
    return ret;
}

int32_t
cli_cmd_volume_top_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    char *volname = NULL;
    char *value = NULL;
    char *key = NULL;
    int ret = -1;
    gf1_cli_stats_op op = GF_CLI_STATS_NONE;
    gf1_cli_top_op top_op = GF_CLI_TOP_NONE;
    int32_t list_cnt = -1;
    int index = 0;
    int perf = 0;
    int32_t blk_size = 0;
    int count = 0;
    gf_boolean_t nfs = _gf_false;
    char *delimiter = NULL;
    static char *opwords[] = {"open",       "read",    "write",
                              "opendir",    "readdir", "read-perf",
                              "write-perf", "clear",   NULL};
    char *w = NULL;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();
    if (!dict)
        goto out;

    if (wordcount < 4)
        goto out;

    volname = (char *)words[2];

    ret = dict_set_str(dict, "volname", volname);
    if (ret)
        goto out;

    op = GF_CLI_STATS_TOP;
    ret = dict_set_int32(dict, "op", (int32_t)op);
    if (ret)
        goto out;

    w = str_getunamb(words[3], opwords);
    if (!w) {
        ret = -1;
        goto out;
    }
    if (strcmp(w, "open") == 0) {
        top_op = GF_CLI_TOP_OPEN;
    } else if (strcmp(w, "read") == 0) {
        top_op = GF_CLI_TOP_READ;
    } else if (strcmp(w, "write") == 0) {
        top_op = GF_CLI_TOP_WRITE;
    } else if (strcmp(w, "opendir") == 0) {
        top_op = GF_CLI_TOP_OPENDIR;
    } else if (strcmp(w, "readdir") == 0) {
        top_op = GF_CLI_TOP_READDIR;
    } else if (strcmp(w, "read-perf") == 0) {
        top_op = GF_CLI_TOP_READ_PERF;
        perf = 1;
    } 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;

    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];

        if (!key || !value) {
            ret = -1;
            goto out;
        }
        if (!strcmp(key, "brick")) {
            delimiter = strchr(value, ':');
            if (!delimiter || delimiter == value || *(delimiter + 1) != '/') {
                cli_err(
                    "wrong brick type: %s, use <HOSTNAME>:"
                    "<export-dir-abs-path>",
                    value);
                ret = -1;
                goto out;
            } else {
                ret = gf_canonicalize_path(delimiter + 1);
                if (ret)
                    goto out;
            }
            ret = dict_set_str(dict, "brick", value);

        } else if (!strcmp(key, "list-cnt")) {
            ret = gf_is_str_int(value);
            if (!ret)
                list_cnt = atoi(value);
            if (ret || (list_cnt < 0) || (list_cnt > 100)) {
                cli_err("list-cnt should be between 0 to 100");
                ret = -1;
                goto out;
            }
        } 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_err(
                        "block size is an invalid"
                        " number");
                else
                    cli_err(
                        "block size should be an "
                        "integer greater than zero");
                ret = -1;
                goto out;
            }
            ret = dict_set_uint32(dict, "blk-size", (uint32_t)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_err("count is an invalid number");
                else
                    cli_err(
                        "count should be an integer "
                        "greater than zero");

                ret = -1;
                goto out;
            }
            ret = dict_set_uint32(dict, "blk-cnt", count);
        } else {
            ret = -1;
            goto out;
        }
        if (ret) {
            gf_log("", GF_LOG_WARNING,
                   "Dict set failed for "
                   "key %s",
                   key);
            goto out;
        }
    }
    if (list_cnt == -1)
        list_cnt = 100;
    ret = dict_set_int32(dict, "list-cnt", list_cnt);
    if (ret) {
        gf_log("", GF_LOG_WARNING, "Dict set failed for list_cnt");
        goto out;
    }

    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)
        dict_unref(dict);
    return ret;
}

uint32_t
cli_cmd_get_statusop(const char *arg)
{
    int i = 0;
    uint32_t ret = GF_CLI_STATUS_NONE;
    char *w = NULL;
    static char *opwords[] = {"detail",   "mem",   "clients",     "fd", "inode",
                              "callpool", "tasks", "client-list", NULL};
    static 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},
                   {"client-list", GF_CLI_STATUS_CLIENT_LIST},
                   {NULL}};

    w = str_getunamb(arg, opwords);
    if (!w) {
        gf_log("cli", GF_LOG_DEBUG, "Not a status op  %s", arg);
        goto out;
    }

    for (i = 0; optable[i].opname; i++) {
        if (!strcmp(w, optable[i].opname)) {
            ret = optable[i].opcode;
            break;
        }
    }

out:
    return ret;
}

int
cli_cmd_volume_status_parse(const char **words, int wordcount, dict_t **options)
{
    dict_t *dict = NULL;
    int ret = -1;
    uint32_t cmd = 0;

    GF_ASSERT(options);

    dict = dict_new();
    if (!dict)
        goto out;

    switch (wordcount) {
        case 2:
            cmd = GF_CLI_STATUS_ALL;
            ret = 0;
            break;

        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")) {
                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 {
                ret = dict_set_str(dict, "volname", (char *)words[2]);
                if (ret)
                    goto out;

                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 if (!strcmp(words[3], "quotad")) {
                        cmd |= GF_CLI_STATUS_QUOTAD;
                    } else if (!strcmp(words[3], "snapd")) {
                        cmd |= GF_CLI_STATUS_SNAPD;
                    } else if (!strcmp(words[3], "bitd")) {
                        cmd |= GF_CLI_STATUS_BITD;
                    } else if (!strcmp(words[3], "scrub")) {
                        cmd |= GF_CLI_STATUS_SCRUB;
                    } 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 (!strcmp(words[2], "all")) {
                cli_err("Cannot specify brick/nfs for \"all\"");
                ret = -1;
                goto out;
            }

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

            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 if (!strcmp(words[3], "quotad")) {
                if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS ||
                    cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) {
                    cli_err(
                        "Detail/FD/Clients/Inode status not "
                        "available for Quota Daemon");
                    ret = -1;
                    goto out;
                }
                cmd |= GF_CLI_STATUS_QUOTAD;
            } else if (!strcmp(words[3], "snapd")) {
                if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS ||
                    cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) {
                    cli_err(
                        "Detail/FD/Clients/Inode status not "
                        "available for snap daemon");
                    ret = -1;
                    goto out;
                }
                cmd |= GF_CLI_STATUS_SNAPD;
            } 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]);
            }
            break;

        default:
            goto out;
    }

    if (ret)
        goto out;

    ret = dict_set_int32(dict, "cmd", cmd);
    if (ret)
        goto out;

    *options = dict;

out:
    if (ret && dict)
        dict_unref(dict);

    return ret;
}

gf_boolean_t
cli_cmd_validate_dumpoption(const char *arg, char **option)
{
    static char *opwords[] = {"all",   "nfs",    "mem",   "iobuf",   "callpool",
                              "priv",  "fd",     "inode", "history", "inodectx",
                              "fdctx", "quotad", NULL};
    char *w = NULL;

    w = str_getunamb(arg, opwords);
    if (!w) {
        gf_log("cli", GF_LOG_DEBUG, "Unknown statedump option  %s", arg);
        return _gf_false;
    }
    *option = w;
    return _gf_true;
}

int
cli_cmd_volume_statedump_options_parse(const char **words, int wordcount,
                                       dict_t **options)
{
    int ret = 0;
    int i = 0;
    dict_t *dict = NULL;
    int option_cnt = 0;
    char *option = NULL;
    char *option_str = NULL;
    char *tmp_str = NULL;
    char *tmp = NULL;
    char *ip_addr = NULL;
    char *pid = NULL;

    if ((wordcount >= 5) && ((strcmp(words[3], "client")) == 0)) {
        tmp = gf_strdup(words[4]);
        if (!tmp) {
            ret = -1;
            goto out;
        }
        ip_addr = strtok(tmp, ":");
        pid = strtok(NULL, ":");
        if (valid_internet_address(ip_addr, _gf_true, _gf_false) && pid &&
            gf_valid_pid(pid, strlen(pid))) {
            ret = gf_asprintf(&option_str, "%s %s %s", words[3], ip_addr, pid);
            if (ret < 0) {
                goto out;
            }
            option_cnt = 3;
        } else {
            ret = -1;
            goto out;
        }
    } else {
        for (i = 3; i < wordcount; i++, option_cnt++) {
            if (!cli_cmd_validate_dumpoption(words[i], &option)) {
                ret = -1;
                goto out;
            }
            tmp_str = option_str;
            option_str = NULL;
            ret = gf_asprintf(&option_str, "%s%s ", tmp_str ? tmp_str : "",
                              option);
            GF_FREE(tmp_str);
            if (ret < 0) {
                goto out;
            }
        }
        if (option_str && (strstr(option_str, "nfs")) &&
            strstr(option_str, "quotad")) {
            ret = -1;
            goto out;
        }
    }

    dict = dict_new();
    if (!dict) {
        ret = -1;
        goto out;
    }

    /* dynamic string in dict is freed up when dict is freed up, and hence
    if option_str is NULL pass in an duplicate empty string to the same */
    ret = dict_set_dynstr(dict, "options",
                          (option_str ? option_str : gf_strdup("")));
    if (ret)
        goto out;
    option_str = NULL;

    ret = dict_set_int32(dict, "option_cnt", option_cnt);
    if (ret)
        goto out;

    *options = dict;
out:
    GF_FREE(tmp);
    GF_FREE(option_str);
    if (ret && dict)
        dict_unref(dict);
    if (ret)
        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;
    int str_len = 0;
    *hostname = NULL;
    *path = NULL;

    str_len = strlen(tmp_words) + 1;
    words = GF_MALLOC(str_len, gf_common_mt_char);
    if (!words) {
        ret = -1;
        goto out;
    }

    snprintf(words, str_len, "%s", tmp_words);

    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 {
            str_len = strlen(delimiter + 1) + 1;
            *path = GF_MALLOC(str_len, gf_common_mt_char);
            if (!*path) {
                ret = -1;
                goto out;
            }
            snprintf(*path, str_len, "%s", delimiter + 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, _gf_false)) {
        cli_err(
            "internet address '%s' does not conform to "
            "standards",
            host_name);
        ret = -1;
        goto out;
    }

    str_len = strlen(host_name) + 1;
    *hostname = GF_MALLOC(str_len, gf_common_mt_char);
    if (!*hostname) {
        ret = -1;
        goto out;
    }
    snprintf(*hostname, str_len, "%s", host_name);
    ret = 0;

out:
    GF_FREE(words);
    GF_FREE(tmp_host);
    return ret;
}

static int
set_hostname_path_in_dict(const char *token, dict_t *dict, int heal_op)
{
    char *hostname = NULL;
    char *path = NULL;
    int ret = 0;

    ret = extract_hostname_path_from_token(token, &hostname, &path);
    if (ret)
        goto out;

    switch (heal_op) {
        case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK:
            ret = dict_set_dynstr(dict, "heal-source-hostname", hostname);
            if (ret)
                goto out;
            hostname = NULL;
            ret = dict_set_dynstr(dict, "heal-source-brickpath", path);
            if (ret) {
                goto out;
            }
            path = NULL;
            break;
        case GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA:
            ret = dict_set_dynstr(dict, "per-replica-cmd-hostname", hostname);
            if (ret)
                goto out;
            hostname = NULL;
            ret = dict_set_dynstr(dict, "per-replica-cmd-path", path);
            if (ret) {
                goto out;
            }
            path = NULL;
            break;
        default:
            ret = -1;
            break;
    }

out:
    GF_FREE(hostname);
    GF_FREE(path);
    return ret;
}

static int
heal_command_type_get(const char *command)
{
    int i = 0;
    /* subcommands are set as NULL */
    char *heal_cmds[GF_SHD_OP_HEAL_DISABLE + 1] = {
        [GF_SHD_OP_INVALID] = NULL,
        [GF_SHD_OP_HEAL_INDEX] = NULL,
        [GF_SHD_OP_HEAL_FULL] = "full",
        [GF_SHD_OP_INDEX_SUMMARY] = "info",
        [GF_SHD_OP_SPLIT_BRAIN_FILES] = NULL,
        [GF_SHD_OP_STATISTICS] = "statistics",
        [GF_SHD_OP_STATISTICS_HEAL_COUNT] = NULL,
        [GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA] = NULL,
        [GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE] = NULL,
        [GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK] = NULL,
        [GF_SHD_OP_HEAL_ENABLE] = "enable",
        [GF_SHD_OP_HEAL_DISABLE] = "disable",
    };

    for (i = 0; i <= GF_SHD_OP_HEAL_DISABLE; i++) {
        if (heal_cmds[i] && (strcmp(heal_cmds[i], command) == 0))
            return i;
    }

    return GF_SHD_OP_INVALID;
}

int
cli_cmd_volume_heal_options_parse(const char **words, int wordcount,
                                  dict_t **options)
{
    int ret = 0;
    dict_t *dict = NULL;
    gf_xl_afr_op_t op = GF_SHD_OP_INVALID;

    dict = dict_new();
    if (!dict) {
        gf_log(THIS->name, GF_LOG_ERROR, "Failed to create the dict");
        ret = -1;
        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_SHD_OP_HEAL_INDEX);
        goto done;
    }

    if (wordcount == 4) {
        op = heal_command_type_get(words[3]);
        if (op == GF_SHD_OP_INVALID) {
            ret = -1;
            goto out;
        }

        ret = dict_set_int32(dict, "heal-op", op);
        goto done;
    }

    if (wordcount == 5) {
        if (strcmp(words[3], "info") && strcmp(words[3], "statistics") &&
            strcmp(words[3], "granular-entry-heal")) {
            ret = -1;
            goto out;
        }

        if (!strcmp(words[3], "info")) {
            if (!strcmp(words[4], "split-brain")) {
                ret = dict_set_int32(dict, "heal-op",
                                     GF_SHD_OP_SPLIT_BRAIN_FILES);
                goto done;
            }
            if (!strcmp(words[4], "summary")) {
                ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_HEAL_SUMMARY);
                goto done;
            }
        }

        if (!strcmp(words[3], "statistics")) {
            if (!strcmp(words[4], "heal-count")) {
                ret = dict_set_int32(dict, "heal-op",
                                     GF_SHD_OP_STATISTICS_HEAL_COUNT);
                goto done;
            }
        }

        if (!strcmp(words[3], "granular-entry-heal")) {
            if (!strcmp(words[4], "enable")) {
                ret = dict_set_int32(dict, "heal-op",
                                     GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE);
                goto done;
            } else if (!strcmp(words[4], "disable")) {
                ret = dict_set_int32(dict, "heal-op",
                                     GF_SHD_OP_GRANULAR_ENTRY_HEAL_DISABLE);
                goto done;
            }
        }

        ret = -1;
        goto out;
    }
    if (wordcount == 6) {
        if (strcmp(words[3], "split-brain")) {
            ret = -1;
            goto out;
        }
        if (!strcmp(words[4], "bigger-file")) {
            ret = dict_set_int32(dict, "heal-op",
                                 GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE);
            if (ret)
                goto out;
            ret = dict_set_str(dict, "file", (char *)words[5]);
            if (ret)
                goto out;
            goto done;
        }
        if (!strcmp(words[4], "latest-mtime")) {
            ret = dict_set_int32(dict, "heal-op",
                                 GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME);
            if (ret)
                goto out;
            ret = dict_set_str(dict, "file", (char *)words[5]);
            if (ret)
                goto out;
            goto done;
        }
        if (!strcmp(words[4], "source-brick")) {
            ret = dict_set_int32(dict, "heal-op",
                                 GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
            if (ret)
                goto out;
            ret = set_hostname_path_in_dict(words[5], dict,
                                            GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
            if (ret)
                goto out;
            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_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA);
            if (ret)
                goto out;
            ret = set_hostname_path_in_dict(
                words[6], dict, GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA);
            if (ret)
                goto out;
            goto done;
        }
        if (!strcmp(words[3], "split-brain") &&
            !strcmp(words[4], "source-brick")) {
            ret = dict_set_int32(dict, "heal-op",
                                 GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
            ret = set_hostname_path_in_dict(words[5], dict,
                                            GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
            if (ret)
                goto out;
            ret = dict_set_str(dict, "file", (char *)words[6]);
            if (ret)
                goto out;
            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_unref(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;
    int len;

    desc = GF_MALLOC(MAX_SNAP_DESCRIPTION_LEN + 1, gf_common_mt_char);
    if (!desc) {
        ret = -1;
        goto out;
    }

    len = strlen(words[desc_opt_loc]);
    if (len >= 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 = len;
    }

    snprintf(desc, desc_len + 1, "%s", words[desc_opt_loc]);
    /* 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;
}

/* snapshot clone <clonename> <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_clone_parse(dict_t *dict, const char **words, int wordcount)
{
    uint64_t i = 0;
    int ret = -1;
    char *clonename = NULL;
    unsigned int cmdi = 2;
    /* cmdi is command index, here cmdi is "2" (gluster snapshot clone)*/

    GF_ASSERT(words);
    GF_ASSERT(dict);

    if (wordcount == cmdi + 1) {
        cli_err("Invalid Syntax.");
        gf_log("cli", GF_LOG_ERROR,
               "Invalid number of  words for snap clone command");
        goto out;
    }

    if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) {
        cli_err(
            "snapshot clone: failed: clonename cannot exceed "
            "255 characters.");
        gf_log("cli", GF_LOG_ERROR, "Clone name too long");

        goto out;
    }

    clonename = (char *)words[cmdi];
    for (i = 0; i < strlen(clonename); i++) {
        /* Following volume name convention */
        if (!isalnum(clonename[i]) &&
            (clonename[i] != '_' && (clonename[i] != '-'))) {
            /* TODO : Is this message enough?? */
            cli_err(
                "Clonename can contain only alphanumeric, "
                "\"-\" and \"_\" characters");
            goto out;
        }
    }

    ret = dict_set_int32(dict, "volcount", 1);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Could not save volcount");
        goto out;
    }

    ret = dict_set_str(dict, "clonename", (char *)words[cmdi]);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR,
               "Could not save clone "
               "name(%s)",
               (char *)words[cmdi]);
        goto out;
    }

    /* Filling snap name in the dictionary */
    ret = dict_set_str(dict, "snapname", (char *)words[cmdi + 1]);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR,
               "Could not "
               "save snap name(%s)",
               (char *)words[cmdi + 1]);
        goto out;
    }

    ret = 0;

out:
    return ret;
}

/* snapshot create <snapname> <vol-name(s)> [description <description>]
 * @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;
    int flags = 0;
    /* 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(%s)",
               (char *)words[cmdi]);
        goto out;
    }

    /* Filling volume name in the dictionary */
    for (i = cmdi + 1;
         i < wordcount && (strcmp(words[i], "description")) != 0 &&
         (strcmp(words[i], "force") != 0) &&
         (strcmp(words[i], "no-timestamp") != 0);
         i++) {
        volcount++;
        /* volume index starts from 1 */
        ret = snprintf(key, sizeof(key), "volname%" PRIu64, 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(%s)",
                   (char *)words[i]);
            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","force" and
     * "no-timestamp" after this.
     */
    if (i == wordcount) {
        goto out;
    }

    if (strcmp(words[i], "no-timestamp") == 0) {
        ret = dict_set_int32n(dict, "no-timestamp", SLEN("no-timestamp"), 1);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Could not save "
                   "time-stamp option");
        }
        if (i == (wordcount - 1))
            goto out;
        i++;
    }

    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 which is deprecated.
         * Before that, check if wordcount limit is reached
         */
    }

    /*TODO: the below force option should be completely removed after a
            couple of releases as it is deprecated.*/
    if (strcmp(words[i], "force") == 0) {
        cli_out(
            "Warning: \'force\' option is deprecated and "
            "should not be used in the future while "
            "creating snapshot. Snapshot create command will "
            "only execute if all the bricks used in creating "
            "the snapshot are online.");
    } else {
        ret = -1;
        cli_err("Invalid Syntax.");
        gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
        goto out;
    }

    /* Check if the command has anything after the force */
    if (++i < wordcount) {
        ret = -1;
        gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
        goto out;
    }

    ret = 0;

out:
    if (ret == 0) {
        /*Adding force flag in either of the case i.e force set
         * or unset*/
        ret = dict_set_int32(dict, "flags", flags);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Could not save "
                   "snap force option");
        }
    }
    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("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,
                   "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("cli", GF_LOG_ERROR,
               "Could 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, "sub-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,
                       struct cli_state *state)
{
    int ret = -1;
    const char *question = NULL;
    gf_answer_t answer = GF_ANSWER_NO;

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

    question =
        "Restore operation will replace the "
        "original volume with the snapshotted volume. "
        "Do you still want to continue?";

    answer = cli_cmd_get_confirmation(state, question);
    if (GF_ANSWER_NO == answer) {
        ret = 1;
        gf_log("cli", GF_LOG_ERROR,
               "User cancelled a snapshot "
               "restore operation for snap %s",
               (char *)words[2]);
        goto out;
    }
out:
    return ret;
}

/* snapshot activate <snapname> [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_activate_parse(dict_t *dict, const char **words, int wordcount)
{
    int ret = -1;
    int flags = 0;

    GF_ASSERT(words);
    GF_ASSERT(dict);

    if ((wordcount < 3) || (wordcount > 4)) {
        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;
    }

    if (wordcount == 4) {
        if (!strcmp("force", (char *)words[3])) {
            flags = GF_CLI_FLAG_OP_FORCE;
        } else {
            gf_log("cli", GF_LOG_ERROR, "Invalid option");
            ret = -1;
            goto out;
        }
    }
    ret = dict_set_int32(dict, "flags", flags);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to save force option");
        goto out;
    }
out:
    return ret;
}

/* snapshot deactivate <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 cancelled the request
 */
int
cli_snap_deactivate_parse(dict_t *dict, const char **words, int wordcount,
                          struct cli_state *state)
{
    int ret = -1;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question =
        "Deactivating snap will make its "
        "data inaccessible. Do you 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 snap-name %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 deactivate operation");
        goto out;
    }

out:
    return ret;
}

/* snapshot delete (all | 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
 *                 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;
    int32_t cmd = -1;
    unsigned int cmdi = 2;
    gf_answer_t answer = GF_ANSWER_NO;

    GF_ASSERT(words);
    GF_ASSERT(dict);

    if (wordcount > 4 || wordcount <= cmdi) {
        gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
        goto out;
    }

    question =
        "Deleting snap will erase all the information about "
        "the snap. Do you still want to continue?";

    if (strcmp(words[cmdi], "all") == 0) {
        ret = 0;
        cmd = GF_SNAP_DELETE_TYPE_ALL;
    } else if (strcmp(words[cmdi], "volume") == 0) {
        if (++cmdi == wordcount) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
            goto out;
        }

        ret = dict_set_str(dict, "volname", (char *)words[cmdi]);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Could not save "
                   "volume name %s",
                   words[wordcount - 1]);
            goto out;
        }
        cmd = GF_SNAP_DELETE_TYPE_VOL;
    } else {
        ret = dict_set_str(dict, "snapname", (char *)words[cmdi]);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Unable to save "
                   "snapname %s",
                   words[2]);
            goto out;
        }
        cmd = GF_SNAP_DELETE_TYPE_SNAP;
    }

    if ((cmdi + 1) != wordcount) {
        ret = -1;
        gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
        goto out;
    }

    if (cmd == GF_SNAP_DELETE_TYPE_SNAP) {
        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 for snap %s",
                   (char *)words[2]);
            goto out;
        }
    }

    ret = dict_set_int32(dict, "sub-cmd", cmd);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR,
               "Could not save "
               "type of snapshot delete");
    }
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, "sub-cmd", cmd);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Could not save cmd "
                   "of snapshot status");
        }
    }

    return ret;
}

/* return value:
 *      -1  in case of failure.
 *       0  in case of success.
 */
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;
    char *end_ptr = NULL;

    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], &end_ptr, 10);

    if (limit <= 0 || strcmp(end_ptr, "") != 0) {
        ret = -1;
        cli_err(
            "Please enter an integer value "
            "greater than zero for %s",
            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;
    }

    ret = dict_set_dynstr_with_alloc(dict, "globalname", "All");
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Could not set global key");
        goto out;
    }
    ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Could not set global locks");
        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, or limit value is out of
                                                                      range
                  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;
    }

    /* auto-delete cannot be a volume name */
    /* Check whether the 3rd word is volname */
    if (strcmp(words[cmdi], "snap-max-hard-limit") != 0 &&
        strcmp(words[cmdi], "snap-max-soft-limit") != 0 &&
        strcmp(words[cmdi], "auto-delete") != 0 &&
        strcmp(words[cmdi], "activate-on-create") != 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;
    }

    if (hard_limit || soft_limit)
        goto set;

    if (strcmp(words[cmdi], "auto-delete") == 0) {
        if (vol_presence == 1) {
            ret = -1;
            cli_err(
                "As of now, auto-delete option cannot be set "
                "to volumes");
            gf_log("cli", GF_LOG_ERROR,
                   "auto-delete option "
                   "cannot be set to volumes");
            goto out;
        }

        if (++cmdi >= wordcount) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
            goto out;
        }

        ret = dict_set_str(dict, "auto-delete", (char *)words[cmdi]);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set "
                   "value of auto-delete in request "
                   "dictionary");
            goto out;
        }

        if (++cmdi != wordcount) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
            goto out;
        }
    } else if (strcmp(words[cmdi], "activate-on-create") == 0) {
        if (vol_presence == 1) {
            ret = -1;
            cli_err(
                "As of now, activate-on-create option "
                "cannot be set to volumes");
            gf_log("cli", GF_LOG_ERROR,
                   "activate-on-create "
                   "option cannot be set to volumes");
            goto out;
        }

        if (++cmdi >= wordcount) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
            goto out;
        }

        ret = dict_set_str(dict, "snap-activate-on-create",
                           (char *)words[cmdi]);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set value "
                   "of activate-on-create in request dictionary");
            goto out;
        }

        if (++cmdi != wordcount) {
            ret = -1;
            gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
            goto out;
        }
    } 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 && (hard_limit || soft_limit)) {
        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_op_name(const char *op, const char *opname, char **opwords)
{
    int ret = -1;
    int i = 0;

    GF_ASSERT(opname);
    GF_ASSERT(opwords);

    for (i = 0; opwords[i] != NULL; i++) {
        if (strcmp(opwords[i], opname) == 0) {
            cli_out("\"%s\" cannot be a %s", opname, op);
            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;
    static char *opwords[] = {"create",     "delete", "restore", "activate",
                              "deactivate", "list",   "status",  "config",
                              "info",       "clone",  NULL};
    static char *invalid_snapnames[] = {"description", "force", "volume", "all",
                                        NULL};
    static char *invalid_volnames[] = {"volume",
                                       "type",
                                       "subvolumes",
                                       "option",
                                       "end-volume",
                                       "all",
                                       "volume_not_in_ring",
                                       "description",
                                       "force",
                                       "snap-max-hard-limit",
                                       "snap-max-soft-limit",
                                       "auto-delete",
                                       "activate-on-create",
                                       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;
    } else if (!strcmp(w, "activate")) {
        type = GF_SNAP_OPTION_TYPE_ACTIVATE;
    } else if (!strcmp(w, "deactivate")) {
        type = GF_SNAP_OPTION_TYPE_DEACTIVATE;
    } else if (!strcmp(w, "clone")) {
        type = GF_SNAP_OPTION_TYPE_CLONE;
    }

    if (type != GF_SNAP_OPTION_TYPE_CONFIG &&
        type != GF_SNAP_OPTION_TYPE_STATUS) {
        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;
        }
    }

    /* Following commands does not require volume locks  */
    if (type == GF_SNAP_OPTION_TYPE_STATUS ||
        type == GF_SNAP_OPTION_TYPE_ACTIVATE ||
        type == GF_SNAP_OPTION_TYPE_DEACTIVATE) {
        ret = dict_set_int32(dict, "hold_vol_locks", _gf_false);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Setting volume lock "
                   "flag failed");
            goto out;
        }
    }

    /* Check which op is intended */
    switch (type) {
        case GF_SNAP_OPTION_TYPE_CREATE:
            /* Syntax :
             * gluster snapshot create <snapname> <vol-name(s)>
             *                         [no-timestamp]
             *                         [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_op_name("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_CLONE:
            /* Syntax :
             * gluster snapshot clone <clonename> <snapname>
             */
            /* In cases where the clonename 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_op_name("clonename", words[2], invalid_volnames);
            if (ret) {
                goto out;
            }

            ret = cli_snap_clone_parse(dict, words, wordcount);
            if (ret) {
                gf_log("cli", GF_LOG_ERROR, "clone 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 :
             * snapshot delete (all | snapname | volume <volname>)
             */
            ret = cli_snap_delete_parse(dict, words, wordcount, state);
            if (ret) {
                /* A positive ret value means user cancelled
                 * the command */
                if (ret < 0) {
                    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, state);
            if (ret) {
                gf_log("cli", GF_LOG_ERROR,
                       "Failed to parse "
                       "restore command");
                goto out;
            }
            break;

        case GF_SNAP_OPTION_TYPE_ACTIVATE:
            /* Syntax:
             * snapshot activate <snapname> [force]
             */
            ret = cli_snap_activate_parse(dict, words, wordcount);
            if (ret) {
                gf_log("cli", GF_LOG_ERROR,
                       "Failed to parse "
                       "start command");
                goto out;
            }
            break;
        case GF_SNAP_OPTION_TYPE_DEACTIVATE:
            /* Syntax:
             * snapshot deactivate <snapname>
             */
            ret = cli_snap_deactivate_parse(dict, words, wordcount, state);
            if (ret) {
                /* A positive ret value means user cancelled
                 * the command */
                if (ret < 0) {
                    gf_log("cli", GF_LOG_ERROR,
                           "Failed to parse deactivate "
                           "command");
                }
                goto out;
            }
            break;

        default:
            ret = -1;
            gf_log("", GF_LOG_ERROR, "Opword Mismatch");
            goto out;
    }

    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_unref(dict);
    } else
        *options = dict;

    return ret;
}

int
cli_cmd_validate_volume(char *volname)
{
    int i = 0;
    int ret = -1;
    int volname_len;

    if (volname[0] == '-')
        return ret;

    if (!strcmp(volname, "all")) {
        cli_err("\"all\" cannot be the name of a volume.");
        return ret;
    }

    if (strchr(volname, '/')) {
        cli_err("Volume name should not contain \"/\" character.");
        return ret;
    }

    volname_len = strlen(volname);
    if (volname_len > GD_VOLUME_NAME_MAX) {
        cli_err("Volname can not exceed %d characters.", GD_VOLUME_NAME_MAX);
        return ret;
    }

    for (i = 0; i < volname_len; i++)
        if (!isalnum(volname[i]) && (volname[i] != '_') &&
            (volname[i] != '-')) {
            cli_err(
                "Volume name should not contain \"%c\""
                " character.\nVolume names can only"
                "contain alphanumeric, '-' and '_' "
                "characters.",
                volname[i]);
            return ret;
        }

    ret = 0;

    return ret;
}

int32_t
cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options)
{
    int32_t ret = -1;
    char *w = NULL;
    char *volname = NULL;
    static char *opwords[] = {"enable",          "disable", "scrub-throttle",
                              "scrub-frequency", "scrub",   "signing-time",
                              "signer-threads",  NULL};
    static char *scrub_throt_values[] = {"lazy", "normal", "aggressive", NULL};
    static char *scrub_freq_values[] = {
        "hourly", "daily", "weekly", "biweekly", "monthly", "minute", NULL};
    static char *scrub_values[] = {"pause", "resume", "status", "ondemand",
                                   NULL};
    dict_t *dict = NULL;
    gf_bitrot_type type = GF_BITROT_OPTION_TYPE_NONE;
    int32_t expiry_time = 0;
    int32_t signer_th_count = 0;

    GF_ASSERT(words);
    GF_ASSERT(options);

    /* Hack to print out bitrot help properly */
    if ((wordcount == 3) && !(strcmp(words[2], "help"))) {
        ret = 1;
        return ret;
    }

    if (wordcount < 4 || wordcount > 5) {
        gf_log("cli", GF_LOG_ERROR, "Invalid syntax");
        goto out;
    }

    dict = dict_new();
    if (!dict)
        goto out;

    volname = (char *)words[2];
    if (!volname) {
        ret = -1;
        goto out;
    }

    ret = cli_cmd_validate_volume(volname);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to validate volume name");
        goto out;
    }

    ret = dict_set_str(dict, "volname", volname);
    if (ret) {
        cli_out("Failed to set volume name in dictionary ");
        goto out;
    }

    w = str_getunamb(words[3], opwords);
    if (!w) {
        cli_out("Invalid bit rot option : %s", words[3]);
        ret = -1;
        goto out;
    }

    if (strcmp(w, "enable") == 0) {
        if (wordcount == 4) {
            type = GF_BITROT_OPTION_TYPE_ENABLE;
            ret = 0;
            goto set_type;
        } else {
            ret = -1;
            goto out;
        }
    }

    if (strcmp(w, "disable") == 0) {
        if (wordcount == 4) {
            type = GF_BITROT_OPTION_TYPE_DISABLE;
            ret = 0;
            goto set_type;
        } else {
            ret = -1;
            goto out;
        }
    }

    if (!strcmp(w, "scrub-throttle")) {
        if (!words[4]) {
            cli_err(
                "Missing scrub-throttle value for bitrot "
                "option");
            ret = -1;
            goto out;
        } else {
            w = str_getunamb(words[4], scrub_throt_values);
            if (!w) {
                cli_err(
                    "Invalid scrub-throttle option for "
                    "bitrot");
                ret = -1;
                goto out;
            } else {
                type = GF_BITROT_OPTION_TYPE_SCRUB_THROTTLE;
                ret = dict_set_str(dict, "scrub-throttle-value",
                                   (char *)words[4]);
                if (ret) {
                    cli_out(
                        "Failed to set scrub-throttle "
                        "value in the dict");
                    goto out;
                }
                goto set_type;
            }
        }
    }

    if (!strcmp(words[3], "scrub-frequency")) {
        if (!words[4]) {
            cli_err("Missing scrub-frequency value");
            ret = -1;
            goto out;
        } else {
            w = str_getunamb(words[4], scrub_freq_values);
            if (!w) {
                cli_err("Invalid frequency option for bitrot");
                ret = -1;
                goto out;
            } else {
                type = GF_BITROT_OPTION_TYPE_SCRUB_FREQ;
                ret = dict_set_str(dict, "scrub-frequency-value",
                                   (char *)words[4]);
                if (ret) {
                    cli_out(
                        "Failed to set dict for "
                        "bitrot");
                    goto out;
                }
                goto set_type;
            }
        }
    }

    if (!strcmp(words[3], "scrub")) {
        if (!words[4]) {
            cli_err("Missing scrub value for bitrot option");
            ret = -1;
            goto out;
        } else {
            w = str_getunamb(words[4], scrub_values);
            if (!w) {
                cli_err("Invalid scrub option for bitrot");
                ret = -1;
                goto out;
            } else {
                if (strcmp(words[4], "status") == 0) {
                    type = GF_BITROT_CMD_SCRUB_STATUS;
                } else if (strcmp(words[4], "ondemand") == 0) {
                    type = GF_BITROT_CMD_SCRUB_ONDEMAND;
                } else {
                    type = GF_BITROT_OPTION_TYPE_SCRUB;
                }
                ret = dict_set_str(dict, "scrub-value", (char *)words[4]);
                if (ret) {
                    cli_out(
                        "Failed to set dict for "
                        "bitrot");
                    goto out;
                }
                goto set_type;
            }
        }
    }

    if (!strcmp(words[3], "signing-time")) {
        if (!words[4]) {
            cli_err(
                "Missing signing-time value for bitrot "
                "option");
            ret = -1;
            goto out;
        } else {
            type = GF_BITROT_OPTION_TYPE_EXPIRY_TIME;

            expiry_time = strtol(words[4], NULL, 0);
            if (expiry_time < 1) {
                cli_err(
                    "Expiry time  value should not be less"
                    " than 1");
                ret = -1;
                goto out;
            }

            ret = dict_set_uint32(dict, "expiry-time",
                                  (unsigned int)expiry_time);
            if (ret) {
                cli_out("Failed to set dict for bitrot");
                goto out;
            }
            goto set_type;
        }
    } else if (!strcmp(words[3], "signer-threads")) {
        if (!words[4]) {
            cli_err(
                "Missing signer-thread value for bitrot "
                "option");
            ret = -1;
            goto out;
        } else {
            type = GF_BITROT_OPTION_TYPE_SIGNER_THREADS;

            signer_th_count = strtol(words[4], NULL, 0);
            if (signer_th_count < 1) {
                cli_err("signer-thread count should not be less than 1");
                ret = -1;
                goto out;
            }

            ret = dict_set_uint32(dict, "signer-threads",
                                  (unsigned int)signer_th_count);
            if (ret) {
                cli_out("Failed to set dict for bitrot");
                goto out;
            }
            goto set_type;
        }
    } else {
        cli_err(
            "Invalid option %s for bitrot. Please enter valid "
            "bitrot option",
            words[3]);
        ret = -1;
        goto out;
    }
set_type:
    ret = dict_set_int32(dict, "type", type);
    if (ret < 0)
        goto out;

    *options = dict;

out:
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to parse bitrot command");
        if (dict)
            dict_unref(dict);
    }

    return ret;
}

/* Parsing global option for NFS-Ganesha config
 *  gluster nfs-ganesha enable/disable */

int32_t
cli_cmd_ganesha_parse(struct cli_state *state, const char **words,
                      int wordcount, dict_t **options, char **op_errstr)
{
    dict_t *dict = NULL;
    int ret = -1;
    char *key = NULL;
    char *value = NULL;
    char *w = NULL;
    static char *opwords[] = {"enable", "disable", NULL};
    const char *question = NULL;
    gf_answer_t answer = GF_ANSWER_NO;

    GF_ASSERT(words);
    GF_ASSERT(options);

    dict = dict_new();

    if (!dict)
        goto out;

    if (wordcount != 2)
        goto out;

    key = (char *)words[0];
    value = (char *)words[1];

    if (!key || !value) {
        cli_out("Usage : nfs-ganesha <enable/disable>");
        ret = -1;
        goto out;
    }

    ret = gf_strip_whitespace(value, strlen(value));
    if (ret == -1)
        goto out;

    if (strcmp(key, "nfs-ganesha")) {
        gf_asprintf(op_errstr,
                    "Global option: error: ' %s '"
                    "is not a valid global option.",
                    key);
        ret = -1;
        goto out;
    }

    w = str_getunamb(value, opwords);
    if (!w) {
        cli_out(
            "Invalid global option \n"
            "Usage : nfs-ganesha <enable/disable>");
        ret = -1;
        goto out;
    }

    if (strcmp(value, "enable") == 0) {
        question =
            "Enabling NFS-Ganesha requires Gluster-NFS to be "
            "disabled across the trusted pool. Do you "
            "still want to continue?\n";
    } else if (strcmp(value, "disable") == 0) {
        question =
            "Disabling NFS-Ganesha will tear down the entire "
            "ganesha cluster across the trusted pool. Do you "
            "still want to continue?\n";
    } else {
        ret = -1;
        goto out;
    }
    answer = cli_cmd_get_confirmation(state, question);
    if (GF_ANSWER_NO == answer) {
        gf_log("cli", GF_LOG_ERROR,
               "Global operation "
               "cancelled, exiting");
        ret = -1;
        goto out;
    }
    cli_out("This will take a few minutes to complete. Please wait ..");

    ret = dict_set_str(dict, "key", key);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set on key failed");
        goto out;
    }

    ret = dict_set_str(dict, "value", value);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set on value failed");
        goto out;
    }

    ret = dict_set_str(dict, "globalname", "All");
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR,
               "dict set on global"
               " key failed.");
        goto out;
    }

    ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR,
               "dict set on global key "
               "failed.");
        goto out;
    }

    *options = dict;
out:
    if (ret)
        dict_unref(dict);

    return ret;
}
