diff options
| author | Avra Sengupta <asengupt@redhat.com> | 2014-02-19 16:30:11 +0530 | 
|---|---|---|
| committer | Vijay Bellur <vbellur@redhat.com> | 2014-04-11 16:29:17 -0700 | 
| commit | 29bccc2ed18eedc40e83d2f0d35327037a322384 (patch) | |
| tree | 207829c5a0535af28cbad6de90497d2f48093d1a /cli/src | |
| parent | 2045c9ea1c7c3aac9d377070df6f0ee99619f421 (diff) | |
gluster: GlusterFS Volume Snapshot Feature
This is the initial patch for the Snapshot feature. Current patch
includes following features:
* Snapshot create
* Snapshot delete
* Snapshot restore
* Snapshot list
* Snapshot info
* Snapshot status
* Snapshot config
Change-Id: I2f46920c0d61c515f6a60e0f8b46fff886d9f6a9
BUG: 1061685
Signed-off-by: shishir gowda <sgowda@redhat.com>
Signed-off-by: Sachin Pandit <spandit@redhat.com>
Signed-off-by: Vijaikumar M <vmallika@redhat.com>
Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com>
Signed-off-by: Rajesh Joseph <rjoseph@redhat.com>
Signed-off-by: Joseph Fernandes <josferna@redhat.com>
Signed-off-by: Avra Sengupta <asengupt@redhat.com>
Reviewed-on: http://review.gluster.org/7128
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'cli/src')
| -rw-r--r-- | cli/src/Makefile.am | 2 | ||||
| -rw-r--r-- | cli/src/cli-cmd-misc.c | 3 | ||||
| -rw-r--r-- | cli/src/cli-cmd-parser.c | 938 | ||||
| -rw-r--r-- | cli/src/cli-cmd-snapshot.c | 145 | ||||
| -rw-r--r-- | cli/src/cli-cmd.c | 3 | ||||
| -rw-r--r-- | cli/src/cli-cmd.h | 2 | ||||
| -rw-r--r-- | cli/src/cli-rpc-ops.c | 1041 | ||||
| -rw-r--r-- | cli/src/cli-xml-output.c | 13 | ||||
| -rw-r--r-- | cli/src/cli.h | 10 | 
9 files changed, 2152 insertions, 5 deletions
diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index 300c00ee922..4f8f239351a 100644 --- a/cli/src/Makefile.am +++ b/cli/src/Makefile.am @@ -2,7 +2,7 @@ sbin_PROGRAMS = gluster  gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c \  	 cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\ -	 cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c +	 cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c cli-cmd-snapshot.c  gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\  		$(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ diff --git a/cli/src/cli-cmd-misc.c b/cli/src/cli-cmd-misc.c index 4aec8dd35b4..566d7c978d9 100644 --- a/cli/src/cli-cmd-misc.c +++ b/cli/src/cli-cmd-misc.c @@ -32,6 +32,7 @@ extern struct cli_cmd cli_probe_cmds[];  extern struct cli_cmd cli_log_cmds[];  extern struct cli_cmd cli_system_cmds[];  extern struct cli_cmd cli_bd_cmds[]; +extern struct cli_cmd snapshot_cmds[];  struct cli_cmd cli_misc_cmds[];  int @@ -46,7 +47,7 @@ cli_cmd_display_help (struct cli_state *state, struct cli_cmd_word *in_word,                        const char **words, int wordcount)  {          struct cli_cmd        *cmd[] = {volume_cmds, cli_probe_cmds, -                                       cli_misc_cmds, +                                       cli_misc_cmds, snapshot_cmds,                                         NULL};          struct cli_cmd        *cmd_ind = NULL;          int                   i = 0; diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index afa16110490..9714449c19d 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1,5 +1,5 @@  /* -   Copyright (c) 2010-2012 Red Hat, Inc. <http://www.redhat.com> +   Copyright (c) 2010-2013 Red Hat, Inc. <http://www.redhat.com>     This file is part of GlusterFS.     This file is licensed to you under your choice of the GNU Lesser @@ -26,6 +26,38 @@  #include "protocol-common.h"  #include "cli1-xdr.h" +#define MAX_SNAP_DESCRIPTION_LEN 1024 + +struct snap_config_opt_vals_ snap_confopt_vals[] = { +        {.op_name        = "snap-max-hard-limit", +         .question       = "Changing snapshot-max-hard-limit " +                           "will lead to deletion of snapshots " +                           "if they exceed the new limit.\n" +                           "Do you want to continue?" +        }, +        {.op_name        = "snap-max-soft-limit", +         .question       = "Changing snapshot-max-soft-limit " +                           "will lead to deletion of snapshots " +                           "if they exceed the new limit.\n" +                           "Do you want to continue?" +        }, +        {.op_name        = "both", +        .question        = "Changing snapshot-max-hard-limit & " +                           "snapshot-max-soft-limit will lead to " +                           "deletion of snapshots if they exceed " +                           "the new limit.\nDo you want to continue?" +        }, +        {.op_name        = NULL, +        } +}; + +enum cli_snap_config_set_types { +        GF_SNAP_CONFIG_SET_HARD = 0, +        GF_SNAP_CONFIG_SET_SOFT = 1, +        GF_SNAP_CONFIG_SET_BOTH = 2, +}; +typedef enum cli_snap_config_set_types cli_snap_config_set_types; +  static const char *  id_sel (void *wcon)  { @@ -163,7 +195,9 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options          char    *invalid_volnames[] = {"volume", "type", "subvolumes", "option",                                         "end-volume", "all", "volume_not_in_ring", -                                       NULL}; +                                       "description", "force", +                                       "snap-max-hard-limit", +                                       "snap-max-soft-limit", NULL};          char    *w = NULL;          int      op_count = 0;          int32_t  replica_count = 1; @@ -2866,3 +2900,903 @@ out:          return ret;  } + +int32_t +cli_snap_create_desc_parse (dict_t *dict, const char **words, +                            size_t wordcount, int32_t desc_opt_loc) +{ +        int32_t        ret      = -1; +        char          *desc     = NULL; +        int32_t        desc_len = 0; + +        desc = GF_CALLOC (MAX_SNAP_DESCRIPTION_LEN + 1, sizeof(char), +                          gf_common_mt_char); +        if (!desc) { +                ret = -1; +                goto out; +        } + + +        if (strlen (words[desc_opt_loc]) >= MAX_SNAP_DESCRIPTION_LEN) { +                cli_out ("snapshot create: description truncated: " +                         "Description provided is longer than 1024 characters"); +                desc_len = MAX_SNAP_DESCRIPTION_LEN; +        } else { +                desc_len = strlen (words[desc_opt_loc]); +        } + +        strncpy (desc, words[desc_opt_loc], desc_len); +        desc[desc_len] = '\0'; +        /* Calculating the size of the description as given by the user */ + +        ret = dict_set_dynstr (dict, "description", desc); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to save snap " +                        "description"); +                goto out; +        } + +        ret = 0; +out: +        if (ret && desc) +                GF_FREE (desc); + +        return ret; +} + +/* Function to check whether the Volume name is repeated */ +int +cli_check_if_volname_repeated (const char **words, unsigned int start_index, +                           uint64_t cur_index) { +        uint64_t        i       =       -1; +        int             ret     =        0; + +        GF_ASSERT (words); + +        for (i = start_index ; i < cur_index ; i++) { +                if (strcmp (words[i], words[cur_index]) == 0) { +                        ret = -1; +                        goto out; +                } +        } +out: +        return ret; +} + +/* snapshot create <snapname> <vol-name(s)> [description <description>] + *                                           [force] + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_create_parse (dict_t *dict, const char **words, int wordcount) { +        uint64_t        i               =        0; +        int             ret             =       -1; +        uint64_t        volcount        =        0; +        char            key[PATH_MAX]   =        ""; +        char            *snapname       =       NULL; +        unsigned int    cmdi            =        2; +        /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/ + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount <= cmdi + 1) { +                cli_err ("Invalid Syntax."); +                gf_log ("cli", GF_LOG_ERROR, +                        "Too less words for snap create command"); +                goto out; +        } + +        if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { +                cli_err ("snapshot create: failed: snapname cannot exceed " +                         "255 characters."); +                gf_log ("cli", GF_LOG_ERROR, "Snapname too long"); + +                goto out; +        } + +        snapname = (char *) words[cmdi]; +        for (i = 0 ; i < strlen (snapname); i++) { +                /* Following volume name convention */ +                if (!isalnum (snapname[i]) && (snapname[i] != '_' +                                           && (snapname[i] != '-'))) { +                        /* TODO : Is this message enough?? */ +                        cli_err ("Snapname can contain only alphanumeric, " +                                 "\"-\" and \"_\" characters"); +                        goto out; +                } +        } + +        ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not save snap " +                        "name(%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); i++) { +                volcount++; +                /* volume index starts from 1 */ +                ret = snprintf (key, sizeof (key), "volname%ld", volcount); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_set_str (dict, key, (char *)words[i]); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not " +                                "save volume name(%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" and "force" +         * after this. +         */ +        if (i == wordcount) { +                goto out; +        } + +        if ((strcmp (words[i], "description")) == 0) { +                ++i; +                if (i > (wordcount - 1)) { +                        ret = -1; +                        cli_err ("Please provide a description"); +                        gf_log ("cli", GF_LOG_ERROR, +                                "Description not provided"); +                        goto out; +                } + +                ret = cli_snap_create_desc_parse(dict, words, wordcount, i); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not save snap " +                                "description"); +                        goto out; +                } + +                if (i == (wordcount - 1)) +                        goto out; +                i++; +                /* point the index to next word. +                 * As description might be follwed by force option. +                 * Before that, check if wordcount limit is reached +                 */ +        } + +        if ((strcmp (words[i], "force") != 0)) { +                ret = -1; +                cli_err ("Invalid Syntax."); +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } +        ret = dict_set_int8 (dict, "snap-force", 1); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not save " +                        "snap force option"); +                goto out; +        } + +        /* Check if the command has anything after "force" keyword */ +        if (++i < wordcount) { +                ret = -1; +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        ret = 0; + +out: +        return ret; +} + +/* snapshot list [volname] + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_list_parse (dict_t *dict, const char **words, int wordcount) { +        int             ret     =       -1; + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount < 2 || wordcount > 3) { +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        if (wordcount == 2) { +                ret = 0; +                goto out; +        } + +        ret = dict_set_str (dict, "volname", (char *)words[2]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, +                        "Failed to save volname in dictionary"); +                goto out; +        } +out: +        return ret; +} + +/* snapshot info [(snapname |  volume <volname>)] + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_info_parse (dict_t *dict, const char **words, int wordcount) +{ + +        int             ret             =       -1; +        int32_t         cmd             =       GF_SNAP_INFO_TYPE_ALL; +        unsigned int    cmdi            =        2; +        /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/ + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount > 4 || wordcount < cmdi) { +                gf_log ("", GF_LOG_ERROR, "Invalid syntax"); +                goto out; +        } + +        if (wordcount == cmdi) { +                ret = 0; +                goto out; +        } + +        /* If 3rd word is not "volume", then it must +         * be snapname. +         */ +        if (strcmp (words[cmdi], "volume") != 0) { +                ret = dict_set_str (dict, "snapname", +                                   (char *)words[cmdi]); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Unable to save " +                                "snapname %s", words[cmdi]); +                        goto out; +                } + +                /* Once snap name is parsed, if we encounter any other +                 * word then fail it. Invalid Syntax. +                 * example : snapshot info <snapname> word +                 */ +                if ((cmdi + 1) != wordcount) { +                        ret = -1; +                        gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                        goto out; +                } + +                cmd = GF_SNAP_INFO_TYPE_SNAP; +                ret = 0; +                goto out; +                /* No need to continue the parsing once we +                 * get the snapname +                 */ +        } + +        /* If 3rd word is "volume", then check if next word +         * is present. As, "snapshot info volume" is an +         * invalid command. +         */ +        if ((cmdi + 1) == wordcount) { +                ret = -1; +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        ret = dict_set_str (dict, "volname", (char *)words[wordcount - 1]); +        if (ret) { +                gf_log ("", GF_LOG_ERROR, "Count not save " +                        "volume name %s", words[wordcount - 1]); +                goto out; +        } +        cmd = GF_SNAP_INFO_TYPE_VOL; +out: +        if (ret == 0) { +                ret = dict_set_int32 (dict, "cmd", cmd); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not save " +                                "type of snapshot info"); +                } +        } +        return ret; +} + + + +/* snapshot restore <snapname> + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_restore_parse (dict_t *dict, const char **words, int wordcount) +{ + +        int             ret             =       -1; + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount != 3) { +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        ret = dict_set_str (dict, "snapname", (char *)words[2]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", +                        words[2]); +                goto out; +        } +out: +        return ret; +} + +/* snapshot delete <snapname> + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + *                 1 if user cancel the operation + */ +int +cli_snap_delete_parse (dict_t *dict, const char **words, int wordcount, +                       struct cli_state *state) { + +        int             ret             =       -1; +        const char      *question       =       NULL; +        gf_answer_t     answer          =       GF_ANSWER_NO; + +        question = "Deleting snap will erase all the information about " +                   "the snap. Do you still want to continue?"; + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount != 3) { +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        ret = dict_set_str (dict, "snapname", (char *)words[2]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to save snapname %s", +                        words[2]); +                goto out; +        } + +        answer = cli_cmd_get_confirmation (state, question); +        if (GF_ANSWER_NO == answer) { +                ret = 1; +                gf_log ("cli", GF_LOG_DEBUG, "User cancelled " +                        "snapshot delete operation for snap %s", +                        (char *)words[2]); +                goto out; +        } +out: +        return ret; +} + +/* snapshot status [(snapname | volume <volname>)] + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_status_parse (dict_t *dict, const char **words, int wordcount) +{ + +        int             ret  =        -1; +        int32_t         cmd  =       GF_SNAP_STATUS_TYPE_ALL; +        unsigned int    cmdi =        2; +        /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/ + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount > 4 || wordcount < cmdi) { +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        if (wordcount == cmdi) { +                ret = 0; +                goto out; +        } + +        /* if 3rd word is not "volume", then it must be "snapname" +        */ +        if (strcmp (words[cmdi], "volume") != 0) { +                ret = dict_set_str (dict, "snapname", +                                   (char *)words[cmdi]); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Count not save " +                                "snap name %s", words[cmdi]); +                        goto out; +                } + +                if ((cmdi + 1) != wordcount) { +                        ret = -1; +                        gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                        goto out; +                } + +                ret = 0; +                cmd = GF_SNAP_STATUS_TYPE_SNAP; +                goto out; +        } + +        /* If 3rd word is "volume", then check if next word is present. +         * As, "snapshot info volume" is an invalid command +         */ +        if ((cmdi + 1) == wordcount) { +                ret = -1; +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } + +        ret = dict_set_str (dict, "volname", (char *)words [wordcount - 1]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Count not save " +                        "volume name %s", words[wordcount - 1]); +                goto out; +        } +        cmd = GF_SNAP_STATUS_TYPE_VOL; + +out: +        if (ret == 0) { +                ret = dict_set_int32 (dict, "cmd", cmd); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not save cmd " +                                "of snapshot status"); +                } +        } +        return ret; +} + + +int32_t +cli_snap_config_limit_parse (const char **words, dict_t *dict, +                             unsigned int wordcount, unsigned int index, +                             char *key) +{ +        int             ret             =       -1; +        int             limit           =       0; + +        GF_ASSERT (words); +        GF_ASSERT (dict); +        GF_ASSERT (key); + +        if (index >= wordcount) { +                ret = -1; +                cli_err ("Please provide a value for %s.", key); +                gf_log ("cli", GF_LOG_ERROR, "Value not provided for %s", key); +                goto out; +        } + +        limit = strtol (words[index], NULL, 0); +        if (limit <= 0) { +                ret = -1; +                cli_err ("%s should be greater than 0.", key); +                goto out; +        } + +        ret = dict_set_int32 (dict, key, limit); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not set " +                        "%s in dictionary", key); +                goto out; +        } + +out: +        return ret; +} + +/* function cli_snap_config_parse + * Config Syntax : gluster snapshot config [volname] + *                                         [snap-max-hard-limit <count>] + *                                         [snap-max-soft-limit <count>] + * +   return value: <0  on failure +                  1  if user cancels the operation +                  0  on success + +  NOTE : snap-max-soft-limit can only be set for system. +*/ +int32_t +cli_snap_config_parse (const char **words, int wordcount, dict_t *dict, +                       struct cli_state *state) +{ +        int                            ret                 = -1; +        gf_answer_t                    answer              = GF_ANSWER_NO; +        gf_boolean_t                   vol_presence        = _gf_false; +        struct snap_config_opt_vals_  *conf_vals           = NULL; +        int8_t                         hard_limit          = 0; +        int8_t                         soft_limit          = 0; +        int8_t                         config_type         = -1; +        const char                    *question            = NULL; +        unsigned int                    cmdi               = 2; +        /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/ + +        GF_ASSERT (words); +        GF_ASSERT (dict); +        GF_ASSERT (state); + +        if ((wordcount < 2) || (wordcount > 7)) { +                gf_log ("cli", GF_LOG_ERROR, +                        "Invalid wordcount(%d)", wordcount); +                goto out; +        } + +        if (wordcount == 2) { +                config_type = GF_SNAP_CONFIG_DISPLAY; +                ret = 0; +                goto set; +        } + +        /* Check whether the 3rd word is volname */ +        if (strcmp (words[cmdi], "snap-max-hard-limit") != 0 +             && strcmp (words[cmdi], "snap-max-soft-limit") != 0) { +                ret = dict_set_str (dict, "volname", (char *)words[cmdi]); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to set volname"); +                        goto out; +                } +                cmdi++; +                vol_presence = _gf_true; + +                if (cmdi == wordcount) { +                        config_type = GF_SNAP_CONFIG_DISPLAY; +                        ret = 0; +                        goto set; +                } +        } + +        config_type = GF_SNAP_CONFIG_TYPE_SET; + +        if (strcmp (words[cmdi], "snap-max-hard-limit") == 0) { +                ret = cli_snap_config_limit_parse (words, dict, wordcount, +                                                ++cmdi, "snap-max-hard-limit"); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " +                                "config hard limit"); +                        goto out; +                } +                hard_limit = 1; + +                if (++cmdi == wordcount) { +                        ret = 0; +                        goto set; +                } +        } + +        if (strcmp (words[cmdi], "snap-max-soft-limit") == 0) { +                if (vol_presence == 1) { +                        ret = -1; +                        cli_err ("Soft limit cannot be set to individual " +                                  "volumes."); +                        gf_log ("cli", GF_LOG_ERROR, "Soft limit cannot be " +                                "set to volumes"); +                        goto out; +                } + +                ret = cli_snap_config_limit_parse (words, dict, wordcount, +                                                ++cmdi, "snap-max-soft-limit"); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " +                                "config soft limit"); +                        goto out; +                } + +                if (++cmdi != wordcount) { +                        ret = -1; +                        gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                        goto out; +                } +                soft_limit = 1; +        } else { +                ret = -1; +                gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                goto out; +        } +        ret = 0; /* Success */ + +set: +        ret = dict_set_int32 (dict, "config-command", config_type); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to set " +                        "config-command"); +                goto out; +        } + +        if (config_type == GF_SNAP_CONFIG_TYPE_SET) { +                conf_vals = snap_confopt_vals; +                if (hard_limit && soft_limit) { +                        question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question; +                } else if (soft_limit) { +                        question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question; +                } else if (hard_limit) { +                        question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question; +                } + +                answer = cli_cmd_get_confirmation (state, question); +                if (GF_ANSWER_NO == answer) { +                        ret = 1; +                        gf_log ("cli", GF_LOG_DEBUG, "User cancelled " +                        "snapshot config operation"); +                } +        } + +out: +        return ret; +} + +int +validate_snapname (const char *snapname, char **opwords) { +        int     ret     =       -1; +        int     i       =       0; + +        GF_ASSERT (snapname); +        GF_ASSERT (opwords); + +        for (i = 0 ; opwords[i] != NULL; i++) { +                if (strcmp (opwords[i], snapname) == 0) { +                        cli_out ("\"%s\" cannot be a snapname", snapname); +                        goto out; +                } +        } +        ret = 0; +out: +        return ret; +} + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, +                        struct cli_state *state) +{ +        int32_t            ret        = -1; +        dict_t             *dict      = NULL; +        gf1_cli_snapshot   type       = GF_SNAP_OPTION_TYPE_NONE; +        char               *w         = NULL; +        char               *opwords[] = {"create", "delete", "restore", "start", +                                         "stop", "list", "status", "config", +                                         "info", NULL}; +        char               *invalid_snapnames[] = {"description", "force", +                                                  "volume", NULL}; + +        GF_ASSERT (words); +        GF_ASSERT (options); +        GF_ASSERT (state); + +        dict = dict_new (); +        if (!dict) +                goto out; + +        /* Lowest wordcount possible */ +        if (wordcount < 2) { +                gf_log ("", GF_LOG_ERROR, +                        "Invalid command: Not enough arguments"); +                goto out; +        } + +        w = str_getunamb (words[1], opwords); +        if (!w) { +                /* Checks if the operation is a valid operation */ +                gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); +                goto out; +        } + +        if (!strcmp (w, "create")) { +                type = GF_SNAP_OPTION_TYPE_CREATE; +        } else if (!strcmp (w, "list")) { +                type = GF_SNAP_OPTION_TYPE_LIST; +        } else if (!strcmp (w, "info")) { +                type = GF_SNAP_OPTION_TYPE_INFO; +        } else if (!strcmp (w, "delete")) { +                type = GF_SNAP_OPTION_TYPE_DELETE; +        } else if (!strcmp (w, "config")) { +                type = GF_SNAP_OPTION_TYPE_CONFIG; +        } else if (!strcmp (w, "restore")) { +                type = GF_SNAP_OPTION_TYPE_RESTORE; +        } else if (!strcmp (w, "status")) { +                type = GF_SNAP_OPTION_TYPE_STATUS; +        } + +        if (type != GF_SNAP_OPTION_TYPE_CONFIG) { +                ret = dict_set_int32 (dict, "hold_snap_locks", _gf_true); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Unable to set hold-snap-locks value " +                                "as _gf_true"); +                        goto out; +                } +        } + +        /* Check which op is intended */ +        switch (type) { +        case GF_SNAP_OPTION_TYPE_CREATE: +                /* Syntax : +                 * gluster snapshot create <snapname> <vol-name(s)> +                 *                         [description <description>] +                 *                         [force] +                 */ +                /* In cases where the snapname is not given then +                 * parsing fails & snapname cannot be "description", +                 * "force" and "volume", that check is made here +                 */ +                if (wordcount == 2){ +                        ret = -1; +                        gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                        goto out; +                } + +                ret = validate_snapname (words[2], invalid_snapnames); +                if (ret) { +                        goto out; +                } + +                ret = cli_snap_create_parse (dict, words, wordcount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "create command parsing failed."); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_INFO: +                /* Syntax : +                 * gluster snapshot info [(snapname] | [vol <volname>)] +                 */ +                ret = cli_snap_info_parse (dict, words, wordcount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse " +                                "snapshot info command"); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_LIST: +                /* Syntax : +                 * gluster snaphsot list [volname] +                 */ + +                ret = cli_snap_list_parse (dict, words, wordcount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse " +                                "snapshot list command"); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_DELETE: +                /* Syntax : +                 * gluster snapshot delete <snapname> +                 */ +                ret = cli_snap_delete_parse (dict, words, wordcount, state); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse " +                                "snapshot delete command"); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_CONFIG: +                /* snapshot config [volname]  [snap-max-hard-limit <count>] +                 *                            [snap-max-soft-limit <percent>] */ +                ret = cli_snap_config_parse (words, wordcount, dict, state); +                if (ret) { +                        if (ret < 0) +                                gf_log ("cli", GF_LOG_ERROR, +                                        "config command parsing failed."); +                        goto out; +                } + +                ret = dict_set_int32 (dict, "type", GF_SNAP_OPTION_TYPE_CONFIG); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Unable to set " +                                "config type"); +                        ret = -1; +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_STATUS: +                { +                        /* Syntax : +                         * gluster snapshot status [(snapname | +                         *                         volume <volname>)] +                         */ +                        ret = cli_snap_status_parse (dict, words, wordcount); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Failed to parse " +                                        "snapshot status command"); +                                goto out; +                        } +                        break; +                } + +        case GF_SNAP_OPTION_TYPE_RESTORE: +                /* Syntax: +                 * snapshot restore <snapname> +                 */ +                ret = cli_snap_restore_parse (dict, words, wordcount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to parse " +                                "restore command"); +                        goto out; +                } +                break; + +        default: +                gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); +                goto out; +        } + +        ret = dict_set_int32 (dict, "type", type); +        if (ret) { +                gf_log ("", GF_LOG_ERROR, +                        "Failed to set type."); +                goto out; +        } +        /* If you got so far, input is valid */ +        ret = 0; +out: +        if (ret) { +                if (dict) +                        dict_destroy (dict); +        } else +                *options = dict; + +        return ret; +} diff --git a/cli/src/cli-cmd-snapshot.c b/cli/src/cli-cmd-snapshot.c new file mode 100644 index 00000000000..941dcbdd2a3 --- /dev/null +++ b/cli/src/cli-cmd-snapshot.c @@ -0,0 +1,145 @@ +/* +   Copyright (c) 2013-2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <pthread.h> + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "cli.h" +#include "cli-cmd.h" + +extern rpc_clnt_prog_t *cli_rpc_prog; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, +                           const char **words, int wordcount); + +int +cli_cmd_snapshot_cbk (struct cli_state *state, struct cli_cmd_word *word, +                      const char **words, int wordcount) +{ +        int                      ret       = 0; +        int                      parse_err = 0; +        dict_t                  *options   = NULL; +        rpc_clnt_procedure_t    *proc      = NULL; +        call_frame_t            *frame     = NULL; +        cli_local_t             *local     = NULL; + +        proc = &cli_rpc_prog->proctable [GLUSTER_CLI_SNAP]; +        if (proc == NULL) { +               ret = -1; +                goto out; +        } + +        frame = create_frame (THIS, THIS->ctx->pool); +        if (frame == NULL) { +                ret = -1; +                goto out; +        } + +        /* Parses the command entered by the user */ +        ret = cli_cmd_snapshot_parse (words, wordcount, &options, state); +        if (ret) { +                if (ret < 0) { +                        cli_usage_out (word->pattern); +                        parse_err = 1; +                } else { +                        /* User might have cancelled the snapshot operation */ +                        ret = 0; +                } +                goto out; +        } + +        CLI_LOCAL_INIT (local, words, frame, options); + +        if (proc->fn) +                ret = proc->fn (frame, THIS, options); + +out: +        if (ret && parse_err == 0) +                cli_out ("Snapshot command failed"); + +        CLI_STACK_DESTROY (frame); + +        return ret; +} + +struct cli_cmd snapshot_cmds[] = { +        { "snapshot help", +          cli_cmd_snapshot_help_cbk, +          "display help for snapshot commands" +        }, +        { "snapshot create <snapname> <volname(s)> [description <description>] [force]", +          cli_cmd_snapshot_cbk, +          "Snapshot Create." +        }, +        { "snapshot restore <snapname>", +          cli_cmd_snapshot_cbk, +          "Snapshot Restore." +        }, +        { "snapshot status [(snapname | volume <volname>)]", +          cli_cmd_snapshot_cbk, +          "Snapshot Status." +        }, +        { "snapshot info [(snapname | volume <volname>)]", +          cli_cmd_snapshot_cbk, +          "Snapshot Info." +        }, +        { "snapshot list [volname]", +          cli_cmd_snapshot_cbk, +          "Snapshot List." +        }, +        {"snapshot config [volname] [snap-max-hard-limit <count>] [snap-max-soft-limit <percent>]", +          cli_cmd_snapshot_cbk, +          "Snapshot Config." +        }, +        {"snapshot delete <snapname>", +          cli_cmd_snapshot_cbk, +          "Snapshot Delete." +        }, +        { NULL, NULL, NULL } +}; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, +                           struct cli_cmd_word *in_word, +                           const char **words, +                           int wordcount) +{ +        struct cli_cmd        *cmd = NULL; + +        for (cmd = snapshot_cmds; cmd->pattern; cmd++) +                if (_gf_false == cmd->disable) +                        cli_out ("%s - %s", cmd->pattern, cmd->desc); + +        return 0; +} + +int +cli_cmd_snapshot_register (struct cli_state *state) +{ +        int  ret = 0; +        struct cli_cmd *cmd = NULL; + +        for (cmd = snapshot_cmds; cmd->pattern; cmd++) { + +                ret = cli_cmd_register (&state->tree, cmd); +                if (ret) +                        goto out; +        } +out: +        return ret; +} diff --git a/cli/src/cli-cmd.c b/cli/src/cli-cmd.c index 63b939282fa..ec2d58ae91c 100644 --- a/cli/src/cli-cmd.c +++ b/cli/src/cli-cmd.c @@ -231,6 +231,9 @@ cli_cmds_register (struct cli_state *state)          if (ret)                  goto out; +        ret = cli_cmd_snapshot_register (state); +        if (ret) +                goto out;  out:          return ret;  } diff --git a/cli/src/cli-cmd.h b/cli/src/cli-cmd.h index 541b4ff7332..91d15b7e170 100644 --- a/cli/src/cli-cmd.h +++ b/cli/src/cli-cmd.h @@ -93,6 +93,8 @@ int cli_cmd_probe_register (struct cli_state *state);  int cli_cmd_system_register (struct cli_state *state); +int cli_cmd_snapshot_register (struct cli_state *state); +  int cli_cmd_misc_register (struct cli_state *state);  struct cli_cmd_word *cli_cmd_nextword (struct cli_cmd_word *word, diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 239a29ba831..ebabf6b3dba 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -22,6 +22,8 @@  #define VOL_TOP_PERF_SPEED_WIDTH        4  #define VOL_TOP_PERF_TIME_WIDTH         26 +#define INDENT_MAIN_HEAD "%-25s %s " +  #include "cli.h"  #include "compat-errno.h"  #include "cli-cmd.h" @@ -291,7 +293,7 @@ gf_cli_output_pool_list (dict_t *dict, int count)                  else                          connected_str = "Disconnected"; -                cli_out ("%s\t%-8s\t%s ", uuid_buf, hostname_buf, +                cli_out ("%s\t%-9s\t%s ", uuid_buf, hostname_buf,                           connected_str);                  i++;          } @@ -508,6 +510,9 @@ gf_cli_get_volume_cbk (struct rpc_req *req, struct iovec *iov,          gf_cli_rsp                 rsp                  = {0};          char                      *caps                 = NULL;          int                        k __attribute__((unused)) = 0; +        /* snap_volume variable helps in showing whether a volume is a normal +         * volume or a volume for the snapshot */ +        int32_t                    snap_volume          = 0;          if (-1 == req->rpc_status)                  goto out; @@ -629,6 +634,11 @@ xml_output:                  if (ret)                          goto out; +                snprintf (key, sizeof (key), "volume%d.snap_volume", i); +                ret = dict_get_int32 (dict, key, &snap_volume); +                if (ret) +                        goto out; +                  snprintf (key, 256, "volume%d.brick_count", i);                  ret = dict_get_int32 (dict, key, &brick_count);                  if (ret) @@ -669,6 +679,10 @@ xml_output:                  cli_out ("Type: %s", cli_vol_type_str[vol_type]);                  cli_out ("Volume ID: %s", volume_id_str);                  cli_out ("Status: %s", cli_vol_status_str[status]); +                if (snap_volume) +                        cli_out ("Snap Volume: %s", "yes"); +                else +                        cli_out ("Snap Volume: %s", "no");  #ifdef HAVE_BD_XLATOR                  k = 0; @@ -7619,6 +7633,1030 @@ out:          return ret;  } +int32_t +cli_snapshot_remove_reply (gf_cli_rsp *rsp, dict_t *dict, call_frame_t *frame) +{ +        int32_t       ret        = -1; +        char         *snap_name  = NULL; + +        GF_ASSERT (rsp); +        GF_ASSERT (dict); +        GF_ASSERT (frame); + +        if (rsp->op_ret) { +                cli_err("snapshot delete: failed: %s", +                        rsp->op_errstr ? rsp->op_errstr : +                        "Please check log file for details"); +                ret = rsp->op_ret; +                goto out; +        } + +        ret = dict_get_str (dict, "snapname", &snap_name); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Failed to get snapname"); +                goto out; +        } + +        cli_out ("snapshot delete: %s: snap removed successfully", +                 snap_name); +        ret = 0; + +out: +        return ret; +} + +int +cli_snapshot_config_display (dict_t *dict, gf_cli_rsp *rsp) +{ +        char                buf[PATH_MAX]        = ""; +        char               *volname              = NULL; +        int                 ret                  = -1; +        int                 config_command       = 0; +        uint64_t            value                = 0; +        uint64_t            hard_limit           = 0; +        uint64_t            soft_limit           = 0; +        uint64_t            i                    = 0; +        uint64_t            voldisplaycount      = 0; + +        GF_ASSERT (dict); +        GF_ASSERT (rsp); + +        if (rsp->op_ret) { +                cli_err ("Snapshot Config : failed: %s", +                         rsp->op_errstr ? rsp->op_errstr : +                         "Please check log file for details"); +                ret = rsp->op_ret; +                goto out; +        } + +        ret = dict_get_int32 (dict, "config-command", &config_command); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch config type"); +                goto out; +        } + +        ret = dict_get_str (dict, "volname", &volname); +        /* Ignore the error, as volname is optional */ + +        if (!volname) { +                volname = "System"; +        } + +        ret = dict_get_uint64 (dict, "snap-max-hard-limit", &hard_limit); +        /* Ignore the error, as the key specified is optional */ +        ret = dict_get_uint64 (dict, "snap-max-soft-limit", &soft_limit); + +        if (!hard_limit && !soft_limit +                        && config_command != GF_SNAP_CONFIG_DISPLAY) { +                ret = -1; +                gf_log(THIS->name, GF_LOG_ERROR, +                       "Could not fetch config-key"); +                goto out; +        } + +        switch (config_command) { +        case GF_SNAP_CONFIG_TYPE_SET: +                if (hard_limit && soft_limit) { +                        cli_out ("snapshot config: snap-max-hard-limit " +                                "& snap-max-soft-limit for system set " +                                 "successfully"); +                } else if (hard_limit) { +                        cli_out ("snapshot config: %s " +                                 "for snap-max-hard-limit set successfully", +                                 volname); +                } else if (soft_limit) { +                        cli_out ("snapshot config: %s " +                                 "for snap-max-soft-limit set successfully", +                                 volname); +                } +                break; + +        case GF_SNAP_CONFIG_DISPLAY: +                cli_out ("\nSnapshot System Configuration:"); +                ret = dict_get_uint64 (dict, "snap-max-hard-limit", +                                       &value); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                "snap_max_hard_limit for %s", volname); +                        ret = -1; +                        goto out; +                } +                cli_out ("snap-max-hard-limit : %"PRIu64, value); + +                ret = dict_get_uint64 (dict, "snap-max-soft-limit", +                                       &soft_limit); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                               "snap-max-soft-limit for %s", volname); +                        ret = -1; +                        goto out; +                } +                cli_out ("snap-max-soft-limit : %"PRIu64"%%\n", +                         soft_limit); + +                cli_out ("Snapshot Volume Configuration:"); + +                ret = dict_get_uint64 (dict, "voldisplaycount", +                                       &voldisplaycount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                               "Could not fetch voldisplaycount"); +                        ret = -1; +                        goto out; +                } + +                for (i = 0; i < voldisplaycount; i++) { +                        snprintf (buf, sizeof(buf), "volume%ld-volname", i); +                        ret = dict_get_str (dict, buf, &volname); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                       " %s", buf); +                                ret = -1; +                                goto out; +                        } +                        cli_out ("\nVolume : %s", volname); + +                        snprintf (buf, sizeof(buf), +                                  "volume%ld-snap-max-hard-limit", i); +                        ret = dict_get_uint64 (dict, buf, &value); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                       " %s", buf); +                                ret = -1; +                                goto out; +                        } +                        cli_out ("snap-max-hard-limit : %"PRIu64, value); + +                        snprintf (buf, sizeof(buf), +                                  "volume%ld-active-hard-limit", i); +                        ret = dict_get_uint64 (dict, buf, &value); +                        if (ret) { +                                 gf_log ("cli", GF_LOG_ERROR, "Could not fetch" +                                        " effective snap_max_hard_limit for " +                                        "%s", volname); +                                ret = -1; +                                goto out; +                        } +                        cli_out ("Effective snap-max-hard-limit : %"PRIu64, +                                 value); + +                        snprintf (buf, sizeof(buf), +                                      "volume%ld-snap-max-soft-limit", i); +                        ret = dict_get_uint64 (dict, buf, &value); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                       " %s", buf); +                                ret = -1; +                                goto out; +                        } +                        cli_out ("Effective snap-max-soft-limit : %"PRIu64" " +                                 "(%"PRIu64"%%)", value, soft_limit); +                } +                break; +        default: +                break; +        } + +        ret = 0; +out: +        return ret; +} + +/* This function is used to print the volume related information + * of a snap. + * + * arg - 0, dict       : Response Dictionary. + * arg - 1, prefix str : snaplist.snap{0..}.vol{0..}.* + */ +int +cli_get_each_volinfo_in_snap (dict_t *dict, char *keyprefix, +                              gf_boolean_t snap_driven) { +        char             key[PATH_MAX]  =  ""; +        char            *get_buffer     =  NULL; +        int              value          =  0; +        int              ret            = -1; +        char             indent[5]      = "\t"; +        char            *volname        =  NULL; + +        GF_ASSERT (dict); +        GF_ASSERT (keyprefix); + +        if (snap_driven) { +                ret = snprintf (key, sizeof (key), "%s.volname", keyprefix); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &get_buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); +                        goto out; +                } +                cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, +                        "Snap Volume Name", ":", get_buffer); + +                ret = snprintf (key, sizeof (key), +                                "%s.origin-volname", keyprefix); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &volname); +                if (ret) { +                        gf_log ("cli", GF_LOG_WARNING, "Failed to get %s", key); +                        cli_out ("%-12s", "Origin:"); +                } +                cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, +                        "Origin Volume name", ":", volname); + + +                ret = snprintf (key, sizeof (key), "%s.snapcount", +                                keyprefix); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_int32 (dict, key, &value); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); +                        goto out; +                } +                cli_out ("%s%s %s      %s %d", indent, "Snaps taken for", +                        volname, ":", value); + +                ret = snprintf (key, sizeof (key), "%s.snaps-available", +                                keyprefix); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_int32 (dict, key, &value); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); +                        goto out; +                } +                cli_out ("%s%s %s  %s %d", indent, "Snaps available for", +                         volname, ":", value); +        } + + +        ret = snprintf (key, sizeof (key), "%s.vol-status", keyprefix); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); +                goto out; +        } +        cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Status", +                 ":", get_buffer); +out: +        return ret; +} + +/* This function is used to print snap related information + * arg - 0, dict       : Response dictionary. + * arg - 1, prefix_str : snaplist.snap{0..}.* + */ +int +cli_get_volinfo_in_snap (dict_t *dict, char *keyprefix) { + +        char            key[PATH_MAX]   = ""; +        int             i               = 0; +        int             volcount        = 0; +        int             ret             = -1; + +        GF_ASSERT (dict); +        GF_ASSERT (keyprefix); + +        ret = snprintf (key, sizeof (key), "%s.vol-count", keyprefix); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_int32 (dict, key, &volcount); +        for (i = 1 ; i <= volcount ; i++) { +                ret = snprintf (key, sizeof (key), +                                "%s.vol%d", keyprefix, i); +                if (ret < 0) { +                        goto out; +                } +                ret = cli_get_each_volinfo_in_snap (dict, key, _gf_true); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not list " +                                "details of volume in a snap"); +                        goto out; +                } +                cli_out (" "); +        } + +out: +        return ret; +} + +int +cli_get_each_snap_info (dict_t *dict, char *prefix_str, +                        gf_boolean_t snap_driven) { +        char            key_buffer[PATH_MAX] = ""; +        char           *get_buffer           = NULL; +        int             ret                  = -1; +        char            indent[5]            = ""; + +        GF_ASSERT (dict); +        GF_ASSERT (prefix_str); + +        if (!snap_driven) +                strcat (indent, "\t"); + +        ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snapname", +                        prefix_str); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key_buffer, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snapname %s ", +                        key_buffer); +                goto out; +        } +        cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snapshot", +                ":", get_buffer); + +        ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-id", +                        prefix_str); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key_buffer, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-id %s ", +                        key_buffer); +                goto out; +        } +        cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snap UUID", +                ":", get_buffer); + +        ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-desc", +                        prefix_str); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key_buffer, &get_buffer); +        if (!ret) { +                /* Ignore error for description */ +                cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, +                         "Description", ":", get_buffer); +        } + +        ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-time", +                        prefix_str); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key_buffer, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-time %s ", +                        prefix_str); +                goto out; +        } +        cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Created", +                ":", get_buffer); + +        if (snap_driven) { +                cli_out ("%-12s", "Snap Volumes:\n"); +                ret = cli_get_volinfo_in_snap (dict, prefix_str); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Unable to list details " +                                "of the snaps"); +                        goto out; +                } +        } +out: +        return ret; +} + +/* This is a generic function to print snap related information. + * arg - 0, dict : Response Dictionary + */ +int +cli_call_snapshot_info (dict_t *dict, gf_boolean_t bool_snap_driven) { +        int             snap_count       =  0; +        char            key[PATH_MAX]    =  ""; +        int             ret              =  -1; +        int             i                =   0; + +        GF_ASSERT (dict); + +        ret = dict_get_int32 (dict, "snap-count", &snap_count); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to get snap-count"); +                goto out; +        } + +        if (snap_count == 0) { +                cli_out ("No snapshots present"); +        } + +        for (i = 1 ; i <= snap_count ; i++) { +                ret = snprintf (key, sizeof (key), "snap%d", i); +                if (ret < 0) { +                        goto out; +                } +                ret = cli_get_each_snap_info (dict, key, bool_snap_driven); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Unable to print snap details"); +                        goto out; +                } +        } +out: +        return ret; +} + +int +cli_get_snaps_in_volume (dict_t *dict) { +        int      ret             =  -1; +        int      i               =  0; +        int      count           =  0; +        int      avail           =  0; +        char     key[PATH_MAX]   =  ""; +        char    *get_buffer      =  NULL; + +        GF_ASSERT (dict); + +        ret = dict_get_str (dict, "origin-volname", &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch origin-volname"); +                goto out; +        } +        cli_out (INDENT_MAIN_HEAD "%s", "Volume Name", ":", get_buffer); + +        ret = dict_get_int32 (dict, "snap-count", &avail); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap-count"); +                goto out; +        } +        cli_out (INDENT_MAIN_HEAD "%d", "Snaps Taken", ":", avail); + +        ret = dict_get_int32 (dict, "snaps-available", &count); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch snaps-available"); +                goto out; +        } +        cli_out (INDENT_MAIN_HEAD "%d", "Snaps Available", ":", count); + +        for (i = 1 ; i <= avail ; i++) { +                snprintf (key, sizeof (key), "snap%d", i); +                ret = cli_get_each_snap_info (dict, key, _gf_false); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Unable to print snap details"); +                        goto out; +                } + +                ret = snprintf (key, sizeof (key), "snap%d.vol1", i); +                if (ret < 0) { +                        goto out; +                } +                ret = cli_get_each_volinfo_in_snap (dict, key, _gf_false); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not get volume " +                                "related information"); +                        goto out; +                } + +                cli_out (" "); +        } +out: +        return ret; +} + +int +cli_snapshot_list (dict_t *dict) { +        int     snapcount       =       0; +        char    key[PATH_MAX]   =       ""; +        int     ret             =       -1; +        int     i               =       0; +        char    *get_buffer     =       NULL; + +        GF_ASSERT (dict); + +        ret = dict_get_int32 (dict, "snap-count", &snapcount); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap count"); +                goto out; +        } + +        if (snapcount == 0) { +                cli_out ("No snapshots present"); +        } + +        for (i = 1 ; i <= snapcount ; i++) { +                ret = snprintf (key, sizeof (key), "snapname%d", i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &get_buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Could not get %s ", key); +                        goto out; +                } else { +                        cli_out ("%s", get_buffer); +                } +        } +out: +        return ret; +} + +int +cli_get_snap_volume_status (dict_t *dict, char *key_prefix) +{ +        int             ret             =       -1; +        char            key[PATH_MAX]   =       ""; +        char            *buffer         =       NULL; +        int             brickcount      =       0; +        int             i               =       0; +        int             pid             =       0; + +        GF_ASSERT (dict); +        GF_ASSERT (key_prefix); + +        ret = snprintf (key, sizeof (key), "%s.brickcount", key_prefix); +        if (ret < 0) { +                goto out; +        } +        ret = dict_get_int32 (dict, key, &brickcount); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Failed to fetch brickcount"); +                goto out; +        } + +        for (i = 0 ; i < brickcount ; i++) { +                ret = snprintf (key, sizeof (key), "%s.brick%d.path", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, +                                "Unable to get Brick Path"); +                        continue; +                } +                cli_out ("\n\t%-17s %s   %s", "Brick Path", ":", buffer); + +                ret = snprintf (key, sizeof (key), "%s.brick%d.vgname", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, +                                "Unable to get Volume Group"); +                        cli_out ("\t%-17s %s   %s", "Volume Group", ":", +                                 "N/A"); +                } else +                        cli_out ("\t%-17s %s   %s", "Volume Group", ":", +                                 buffer); + +                ret = snprintf (key, sizeof (key), "%s.brick%d.status", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, +                                "Unable to get Brick Running"); +                        cli_out ("\t%-17s %s   %s", "Brick Running", ":", +                                 "N/A"); +                } else +                        cli_out ("\t%-17s %s   %s", "Brick Running", ":", +                                 buffer); + +                ret = snprintf (key, sizeof (key), "%s.brick%d.pid", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_int32 (dict, key, &pid); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, +                                "Unable to get pid"); +                        cli_out ("\t%-17s %s   %s", "Brick PID", ":", "N/A"); +                } else +                        cli_out ("\t%-17s %s   %d", "Brick PID", ":", pid); + +                ret = snprintf (key, sizeof (key), "%s.brick%d.data", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = dict_get_str (dict, key, &buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, +                                "Unable to get Data Percent"); +                        cli_out ("\t%-17s %s   %s", "Data Percentage", ":", +                                 "N/A"); +                } else +                        cli_out ("\t%-17s %s   %s", "Data Percentage", ":", +                                 buffer); + +                ret = snprintf (key, sizeof (key), "%s.brick%d.lvsize", +                                key_prefix, i); +                if (ret < 0) { +                        goto out; +                } +                ret = dict_get_str (dict, key, &buffer); +                if (ret) { +                        gf_log ("cli", GF_LOG_INFO, "Unable to get LV Size"); +                        cli_out ("\t%-17s %s   %s", "LV Size", ":", "N/A"); +                } else +                        cli_out ("\t%-17s %s   %s", "LV Size", ":", buffer); + +        } +out: +        return ret; +} + + + +int +cli_get_single_snap_status (dict_t *dict, char *keyprefix) +{ +        int             ret             =       -1; +        char            key[PATH_MAX]   =       ""; +        int             i               =       0; +        int             volcount        =       0; +        char            *get_buffer     =       NULL; + +        GF_ASSERT (dict); +        GF_ASSERT (keyprefix); + +        ret = snprintf (key, sizeof (key), "%s.snapname", keyprefix); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to get snapname"); +                goto out; +        } +        cli_out ("\nSnap Name : %s", get_buffer); + +        ret = snprintf (key, sizeof (key), "%s.uuid", keyprefix); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_str (dict, key, &get_buffer); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to get snap UUID"); +                goto out; +        } +        cli_out ("Snap UUID : %s", get_buffer); + +        ret = snprintf (key, sizeof (key), "%s.volcount", keyprefix); +        if (ret < 0) { +                goto out; +        } + +        ret = dict_get_int32 (dict, key, &volcount); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Unable to get volume count"); +                goto out; +        } + +        for (i = 0 ; i < volcount ; i++) { +                ret = snprintf (key, sizeof (key), "%s.vol%d", keyprefix, i); +                if (ret < 0) { +                        goto out; +                } + +                ret = cli_get_snap_volume_status (dict, key); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Could not get snap volume status"); +                        goto out; +                } +        } +out: +        return ret; +} + +int +cli_snap_status_all (dict_t *dict) { +        int             ret             =       -1; +        char            key[PATH_MAX]   =       ""; +        int             snapcount       =       0; +        int             i               =       0; + +        GF_ASSERT (dict); + +        ret = dict_get_int32 (dict, "status.snapcount", &snapcount); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not get snapcount"); +                goto out; +        } + +        if (snapcount == 0) { +                cli_out ("No snapshots present"); +        } + +        for (i = 0 ; i < snapcount; i++) { +                ret = snprintf (key, sizeof (key), "status.snap%d",i); +                if (ret < 0) { +                        goto out; +                } +                ret = cli_get_single_snap_status (dict, key); +        } +out: +        return ret; +} + + +int +cli_snapshot_status_display (dict_t *dict, gf_cli_rsp *rsp) +{ +        char            key[PATH_MAX]   =       ""; +        int             ret             =       -1; +        int             status_cmd      =       -1; + +        GF_ASSERT (dict); +        GF_ASSERT (rsp); + +        if (rsp->op_ret) { +                cli_err ("Snapshot Status : failed: %s", +                        rsp->op_errstr ? rsp->op_errstr : +                        "Please check log file for details"); +                ret = rsp->op_ret; +                goto out; +        } + +        ret = dict_get_int32 (dict, "cmd", &status_cmd); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not fetch status type"); +                goto out; +        } +        switch (status_cmd) { +                case GF_SNAP_STATUS_TYPE_ALL: +                { +                        ret = cli_snap_status_all (dict); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                        "status of all snap"); +                                goto out; +                        } +                break; +                } + +                case GF_SNAP_STATUS_TYPE_SNAP: +                { +                        ret = snprintf (key, sizeof (key), "status.snap0"); +                        if (ret < 0) { +                                goto out; +                        } +                        ret = cli_get_single_snap_status (dict, key); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                        "status of snap"); +                                goto out; +                        } +                break; +                } + +                case GF_SNAP_STATUS_TYPE_VOL: +                { +                        ret = cli_snap_status_all (dict); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, "Could not fetch " +                                        "status of snap in a volume"); +                                goto out; +                        } +                        break; +                } +                default: +                        break; +        } +out: +        return ret; +} + +int +gf_cli_snapshot_cbk (struct rpc_req *req, struct iovec *iov, +                     int count, void *myframe) +{ +        int                   ret                      = -1; +        gf_cli_rsp            rsp                      = {0, }; +        dict_t               *dict                     = NULL; +        char                 *snap_name                = NULL; +        int32_t               type                     =  0; +        call_frame_t         *frame                    = NULL; +        gf_boolean_t         snap_driven               = _gf_false; + +        if (req->rpc_status == -1) { +                ret = -1; +                goto out; +        } + +        frame = myframe; + +        ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); +        if (ret < 0) { +                gf_log (frame->this->name, GF_LOG_ERROR, +                        "Failed to decode xdr response"); +                goto out; +        } + +        dict = dict_new (); + +        if (!dict) { +                ret = -1; +                goto out; +        } + +        ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + +        if (ret) +                goto out; + +        ret = dict_get_int32 (dict, "type", &type); +        if (ret) { +                gf_log (frame->this->name, GF_LOG_ERROR, "failed to get type"); +                goto out; +        } + +        switch (type) { +        case GF_SNAP_OPTION_TYPE_CREATE: +                if (rsp.op_ret) { +                        cli_err("snapshot create: failed: %s", +                                 rsp.op_errstr ? rsp.op_errstr : +                                 "Please check log file for details"); +                                 ret = rsp.op_ret; +                                 goto out; +                } + +                ret = dict_get_str (dict, "snapname", &snap_name); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Failed to get snap name"); +                        goto out; +                } +                cli_out ("snapshot create: %s: snap created successfully", +                         snap_name); +                break; + +        case GF_SNAP_OPTION_TYPE_RESTORE: +                /* TODO: Check if rsp.op_ret needs to be checked here. Or is +                 * it ok to check this in the start of the function where we +                 * get rsp.*/ +                if (rsp.op_ret) { +                        cli_err("snapshot restore: failed: %s", +                                 rsp.op_errstr ? rsp.op_errstr : +                                 "Please check log file for details"); +                                 ret = rsp.op_ret; +                                 goto out; +                } + +                ret = dict_get_str (dict, "snapname", &snap_name); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Failed to get snap name"); +                        goto out; +                } + +                cli_out ("Snapshot restore: %s: Snap restored " +                                 "successfully", snap_name); + +                ret = 0; +                break; + +        case GF_SNAP_OPTION_TYPE_INFO: +                if (rsp.op_ret) { +                        cli_err ("Snapshot info : failed: %s", +                                  rsp.op_errstr ? rsp.op_errstr : +                                  "Please check log file for details"); +                        ret = rsp.op_ret; +                        goto out; +                } + +                snap_driven = dict_get_str_boolean (dict, "snap-driven", +                                                    _gf_false); +                if (snap_driven == _gf_true) { +                        ret = cli_call_snapshot_info  (dict, snap_driven); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, +                                        "Snapshot info failed"); +                                goto out; +                        } +                } else if (snap_driven == _gf_false) { +                        ret = cli_get_snaps_in_volume (dict); +                        if (ret) { +                                gf_log ("cli", GF_LOG_ERROR, +                                        "Snapshot info failed"); +                                goto out; +                        } +                } +                break; + +        case GF_SNAP_OPTION_TYPE_CONFIG: +                ret = cli_snapshot_config_display (dict, &rsp); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to display " +                                "snapshot config output."); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_LIST: +                if (rsp.op_ret) { +                        cli_err ("Snapshot list : failed: %s", +                                 rsp.op_errstr ? rsp.op_errstr : +                                "Please check log file for details"); +                        ret = rsp.op_ret; +                        goto out; +                } + +                ret = cli_snapshot_list (dict); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to display " +                                "snapshot list"); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_DELETE: +                ret = cli_snapshot_remove_reply (&rsp, dict, frame); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Failed to delete snap"); +                        goto out; +                } +                break; + +        case GF_SNAP_OPTION_TYPE_STATUS: +                ret = cli_snapshot_status_display (dict, &rsp); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, "Failed to display " +                                "snapshot status output."); +                        goto out; +                } +                break; + +        default: +                cli_err ("Unknown command executed"); +                ret = -1; +                goto out; +        } +out: +        if (dict) +                dict_unref (dict); +        cli_cmd_broadcast_response (ret); + +        free (rsp.dict.dict_val); +        free (rsp.op_errstr); + +        return ret; +} + +int32_t +gf_cli_snapshot (call_frame_t *frame, xlator_t *this, +                 void *data) +{ +        gf_cli_req        req     = {{0,}}; +        dict_t           *options = NULL; +        int               ret     = -1; + +        if (!frame || !this || !data) +                goto out; + +        options = data; + +        ret = cli_to_glusterd (&req, frame, gf_cli_snapshot_cbk, +                               (xdrproc_t) xdr_gf_cli_req, options, +                               GLUSTER_CLI_SNAP, this, cli_rpc_prog, +                               NULL); +out: +        gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + +        GF_FREE (req.dict.dict_val); +        return ret; +} +  int  cli_to_glusterd (gf_cli_req *req, call_frame_t *frame,                   fop_cbk_fn_t cbkfn, xdrproc_t xdrproc, dict_t *dict, @@ -7730,6 +8768,7 @@ struct rpc_clnt_procedure gluster_cli_actors[GLUSTER_CLI_MAXVALUE] = {          [GLUSTER_CLI_CLRLOCKS_VOLUME]  = {"CLEARLOCKS_VOLUME", gf_cli_clearlocks_volume},          [GLUSTER_CLI_COPY_FILE]        = {"COPY_FILE", gf_cli_copy_file},          [GLUSTER_CLI_SYS_EXEC]         = {"SYS_EXEC", gf_cli_sys_exec}, +        [GLUSTER_CLI_SNAP]             = {"SNAP", gf_cli_snapshot},  };  struct rpc_clnt_program cli_prog = { diff --git a/cli/src/cli-xml-output.c b/cli/src/cli-xml-output.c index ae6b051ed54..863e8a4a6c6 100644 --- a/cli/src/cli-xml-output.c +++ b/cli/src/cli-xml-output.c @@ -2535,6 +2535,7 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict)          int                     j = 1;          char                    *caps = NULL;          int                     k __attribute__((unused)) = 0; +        char                    *snap_volume = NULL;          ret = dict_get_int32 (dict, "count", &count);          if (ret) @@ -2576,6 +2577,18 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict)                                                         "%d", status);                  XML_RET_CHECK_AND_GOTO (ret, out); +                memset (key, 0, sizeof (key)); +                snprintf (key, sizeof (key), "volume%d.snap_volume", i); +                ret = dict_get_str (dict, key, &snap_volume); +                if (ret) +                        goto out; +                if (snap_volume) { +                        ret = xmlTextWriterWriteFormatElement (local->writer, +                                                         (xmlChar *)"snapVol", +                                                          "%s", snap_volume); +                        XML_RET_CHECK_AND_GOTO (ret, out); +                } +                  ret =xmlTextWriterWriteFormatElement                          (local->writer, (xmlChar *)"statusStr", "%s",                           cli_vol_status_str[status]); diff --git a/cli/src/cli.h b/cli/src/cli.h index 53537c6425d..69a7e82bf63 100644 --- a/cli/src/cli.h +++ b/cli/src/cli.h @@ -166,6 +166,11 @@ struct cli_volume_status {  #endif  }; +struct snap_config_opt_vals_ { +        char           *op_name; +        char           *question; +}; +  typedef struct cli_volume_status cli_volume_status_t;  typedef struct cli_local cli_local_t; @@ -385,4 +390,9 @@ cli_xml_output_vol_status_tasks_detail (cli_local_t *local, dict_t *dict);  char *  is_server_debug_xlator (void *myframe); + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, +                        struct cli_state *state); +  #endif /* __CLI_H__ */  | 
