From f8a158134d89b25063b059cd4241ffc84c48f469 Mon Sep 17 00:00:00 2001 From: Avra Sengupta Date: Mon, 30 Sep 2013 16:26:33 +0530 Subject: cli: snapshot create cli interface. $ gluster snapshot help snapshot help - display help for snapshot commands snapshot create [-n ] [-d ] - Snapshot Create. $ gluster snapshot create vol1 snapshot create: ???: snap created successfully $ gluster snapshot create vol1 vol2 snapshot create: ???: consistency group created successfully (The ??? will be replaced by the glusterd snap create command with the generated snap-name or cg-name) $ gluster snapshot create vol1 vol2 -n CG1 snapshot create: CG1: consistency group created successfully $ gluster snapshot create vol1 -n snap1 -d Description snapshot create: snap1: snap created successfully $ gluster snapshot create vol1 -n snap1 -d "Description can have -d within quotes" snapshot create: snap1: snap created successfully $ gluster snapshot create vol1 -n snap1 -d Description cant have -d without quotes snapshot create: failed: Options(-n/-d) are not valid descriptions Usage: snapshot create [-n ] [-d ] $ gluster snapshot create vol1 -n "Multi word snap name" -d Description snapshot create: failed: Invalid snap name Usage: snapshot create [-n ] [-d ] $ gluster snapshot create vol1 -d Description -n "-d" snapshot create: failed: Options(-n/-d) are not valid snap names Usage: snapshot create [-n ] [-d ] $ gluster snapshot create vol1 -d -n snap1 snapshot create: failed: No description provided Usage: snapshot create [-n ] [-d ] Change-Id: I74b5a8406d72282fbb7ba7d07e0c7fe395148d38 Signed-off-by: Avra Sengupta --- cli/src/Makefile.am | 2 +- cli/src/cli-cmd-parser.c | 325 +++++++++++++++++++++++++++++++++++++++++++++ cli/src/cli-cmd-snapshot.c | 117 ++++++++++++++++ cli/src/cli-cmd.c | 3 + cli/src/cli-cmd.h | 2 + cli/src/cli-rpc-ops.c | 122 +++++++++++++++++ cli/src/cli.h | 4 + 7 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 cli/src/cli-cmd-snapshot.c (limited to 'cli') diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index d5189da5e..216d1bb55 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-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-cmd-snapshot.c gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\ $(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index cd0370acc..3d8ec5d8f 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -26,6 +26,8 @@ #include "protocol-common.h" #include "cli1-xdr.h" +#define MAX_SNAP_DESCRIPTION_LEN 1024 + static const char * id_sel (void *wcon) { @@ -2732,3 +2734,326 @@ out: return ret; } + +int32_t +cli_snap_create_desc_parse (dict_t *dict, const char **words, int wordcount, + int32_t desc_opt_loc, int32_t no_of_wrds_in_desc) +{ + int32_t ret = -1; + char *desc = NULL; + int32_t i = 0; + int32_t desc_len = 0; + + desc = GF_CALLOC (MAX_SNAP_DESCRIPTION_LEN + 1, sizeof(char), + gf_common_mt_char); + if (!desc) { + gf_log ("", GF_LOG_ERROR, "Out Of Memory"); + ret = -1; + goto out; + } + + /* Creating the description string */ + for (i = 0; i < no_of_wrds_in_desc; i++) { + if ((strcmp (words[desc_opt_loc + 1 + i], "-n") == 0) || + (strcmp (words[desc_opt_loc + 1 + i], "-d") == 0)) { + cli_out ("snapshot create: failed: Options(-n/-d) " + "are not valid descriptions"); + ret = -1; + goto out; + } + + strcat (desc, words[desc_opt_loc + 1 + i]); + strcat (desc, " "); + /* Calculating the size of the description as given by the user */ + desc_len += strlen(words[desc_opt_loc + 1 + i]); + desc_len++; + } + + /* Removing the last space in the string */ + desc[--desc_len] = '\0'; + + if (desc_len > MAX_SNAP_DESCRIPTION_LEN) { + cli_out ("snapshot create: description truncated: " + "Description provided is longer than 1024 characters"); + desc[MAX_SNAP_DESCRIPTION_LEN] = '\0'; + } + + ret = dict_set_dynstr (dict, "snap-description", desc); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save snap description"); + goto out; + } + + ret = 0; +out: + + if (ret) { + if (desc) + GF_FREE (desc); + } + + return ret; +} + +int32_t +cli_snap_create_parse (dict_t *dict, const char **words, int wordcount, + int32_t cmdi) +{ + int32_t volcount = -1; + int32_t no_of_wrds_in_desc = -1; + int32_t name_opt_loc = -1; + int32_t desc_opt_loc = -1; + char volname_buf[PATH_MAX] = ""; + int32_t ret = -1; + int32_t i = -1; + + /* Finding the "-n" and "-d" in the cli */ + for (i = cmdi + 1; i < wordcount; i++) { + if ((strcmp (words[i], "-n") == 0) && + (name_opt_loc == -1)) + name_opt_loc = i; + + if ((strcmp (words[i], "-d") == 0) && + (desc_opt_loc == -1)) + desc_opt_loc = i; + } + + if ((name_opt_loc == -1) && (desc_opt_loc == -1)) { + /* No snap-name and description has been given */ + + volcount = (wordcount - 1) - cmdi; + } else if ((name_opt_loc > cmdi + 1) && (desc_opt_loc == -1)) { + /* If only name and no description is given */ + + /* if more than one or no snap name is given */ + if (((wordcount - 1) - name_opt_loc) != 1) { + cli_out ("snapshot create: failed: " + "Invalid snap name arguments"); + ret = -1; + goto out; + } + + volcount = (name_opt_loc - 1) - cmdi; + } else if ((name_opt_loc == -1) && (desc_opt_loc > cmdi + 1)) { + /* If no name and only description is given */ + + /* Description should not be blank */ + no_of_wrds_in_desc = (wordcount - 1) - desc_opt_loc; + if (no_of_wrds_in_desc == 0) { + cli_out ("snapshot create: failed: " + "No description provided"); + ret = -1; + goto out; + } + + volcount = (desc_opt_loc - 1) - cmdi; + } else if ((name_opt_loc > cmdi + 1) && (desc_opt_loc > cmdi + 1)) { + /* Both name and description is given */ + + /* Figuring out which comes first */ + if (name_opt_loc < desc_opt_loc) { + /* if more than one or no snap name is given */ + if ((desc_opt_loc - name_opt_loc) != 2) { + cli_out ("snapshot create: failed: " + "Invalid snap name arguments"); + ret = -1; + goto out; + } + + /* Description should not be blank */ + no_of_wrds_in_desc = (wordcount - 1) - desc_opt_loc; + if (no_of_wrds_in_desc == 0) { + cli_out ("snapshot create: failed: " + "No description provided"); + ret = -1; + goto out; + } + + volcount = (name_opt_loc - 1) - cmdi; + } else if (desc_opt_loc < name_opt_loc) { + /* if more than one or no snap name is given */ + if (((wordcount - 1) - name_opt_loc) != 1) { + cli_out ("snapshot create: failed: " + "Invalid snap name arguments"); + ret = -1; + goto out; + } + + /* Description should not be blank */ + no_of_wrds_in_desc = (name_opt_loc) - desc_opt_loc -1; + if (no_of_wrds_in_desc == 0) { + cli_out ("snapshot create: failed: " + "No description provided"); + ret = -1; + goto out; + } + + volcount = (desc_opt_loc - 1) - cmdi; + } + } + + /*At least one volume name should be present */ + if (volcount < 1) { + cli_out ("snapshot create: failed: No volume name provided"); + ret = -1; + goto out; + } + + /* Only one volume is present. volname + * should be set and not volname%d. */ + if (volcount == 1) { + ret = dict_set_str (dict, "volname", (char *)words[2]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save volname"); + goto out; + } + } else { + /* Saving the volume names */ + for (i = 2; i < volcount + 2; i++) { + ret = snprintf (volname_buf, sizeof(volname_buf) - 1, + "volname%d", i - 1); + volname_buf[ret] = '\0'; + + ret = dict_set_str (dict, volname_buf, + (char *)words[i]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save %s", + volname_buf); + goto out; + } + } + } + + /* Saving the volcount */ + ret = dict_set_int32 (dict, "volcount", volcount); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save volcount"); + goto out; + } + + /* Saving snap-name/cg-name in dict */ + if (name_opt_loc > cmdi + 1) { + if (strstr((char *)words[name_opt_loc + 1], " ")) { + cli_out ("snapshot create: failed: Invalid snap name"); + ret = -1; + goto out; + } + + if ((strcmp ((char *)words[name_opt_loc + 1], "-n") == 0) || + (strcmp ((char *)words[name_opt_loc + 1], "-d") == 0)) { + cli_out ("snapshot create: failed: Options(-n/-d) " + "are not valid snap names"); + ret = -1; + goto out; + } + + /* Decide if it's a cg-name or a snap-name */ + if (volcount > 1) { + ret = dict_set_str (dict, "cg-name", + (char *)words[name_opt_loc + 1]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save cg-name"); + goto out; + } + } else { + ret = dict_set_str (dict, "snap-name", + (char *)words[name_opt_loc + 1]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to save snap-name"); + goto out; + } + } + } + + /* Parsing the description and saving it in the dict */ + if (desc_opt_loc > cmdi + 1) { + ret = cli_snap_create_desc_parse (dict, words, + wordcount, + desc_opt_loc, + no_of_wrds_in_desc); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to parse snap-description"); + goto out; + } + } + +out: + return ret; +} + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options) +{ + int32_t ret = -1; + dict_t *dict = NULL; + gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE; + int32_t cmdi = 0; + char *opwords[] = {"create", NULL}; + char *w = NULL; + + GF_ASSERT (words); + GF_ASSERT (options); + + dict = dict_new (); + if (!dict) + goto out; + + /* syntax: + * snapshot create [-n ] [-d ] + */ + + /* Lowest wordcount possible */ + if (wordcount < 2) { + gf_log ("", GF_LOG_ERROR, "Invalid command: Not enough arguments"); + goto out; + } + + /* In cases where the vol-name is not given + * parsing fails. volname cannot be an opword. + * and that is what this check verifies */ + w = str_getunamb (words[2], opwords); + if (w) + 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; + } + + /* Check which op is intended */ + if (strcmp (w, "create") == 0) { + type = GF_SNAP_OPTION_TYPE_CREATE; + cmdi = 1; + ret = dict_set_int32 (dict, "type", type); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Failed to set type."); + goto out; + } + + ret = cli_snap_create_parse (dict, words, + wordcount, cmdi); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "create command parsing failed."); + goto out; + } + } else { + gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); + 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 000000000..cc1fb3c90 --- /dev/null +++ b/cli/src/cli-cmd-snapshot.c @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + 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 +#include +#include +#include +#include + +#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); + if (ret) { + cli_usage_out (word->pattern); + parse_err = 1; + 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 [-n ] [-d ]", + cli_cmd_snapshot_cbk, + "Snapshot Create." + }, + + { 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 1045f34f0..b81f75b5b 100644 --- a/cli/src/cli-cmd.c +++ b/cli/src/cli-cmd.c @@ -231,6 +231,9 @@ cli_cmds_register (struct cli_state *state) if (ret) goto out; + ret = cli_cmd_snapshot_register (state); + if (ret) + goto out; out: return ret; } diff --git a/cli/src/cli-cmd.h b/cli/src/cli-cmd.h index 06a1ed32a..041729276 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 808145015..b100f2d62 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -7518,6 +7518,127 @@ 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; + char *cg_name = NULL; + int32_t type = 0; + int32_t volcount = 0; + call_frame_t *frame = NULL; + + if (req->rpc_status == -1) { + ret = -1; + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + dict = dict_new (); + + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) + goto out; + + 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_int32 (dict, "volcount", &volcount); + if (ret) { + gf_log (frame->this->name, GF_LOG_ERROR, + "failed to get volcount"); + goto out; + } + + if (volcount > 1) { + if (dict_get_str (dict, "cg-name", + &cg_name) != 0) + cg_name = "???"; + + cli_out ("snapshot create: %s: consistency " + "group created successfully", + cg_name); + } else { + if (dict_get_str (dict, "snap-name", + &snap_name) != 0) + snap_name = "???"; + + cli_out ("snapshot create: %s: " + "snap created successfully", + snap_name); + } + 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); + + 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_ERROR, "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, @@ -7629,6 +7750,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.h b/cli/src/cli.h index bc71ee2b4..65f9b857f 100644 --- a/cli/src/cli.h +++ b/cli/src/cli.h @@ -387,4 +387,8 @@ 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); + #endif /* __CLI_H__ */ -- cgit