diff options
Diffstat (limited to 'cli/src/cli-cmd-volume.c')
| -rw-r--r-- | cli/src/cli-cmd-volume.c | 3493 |
1 files changed, 2992 insertions, 501 deletions
diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index 09f89137445..f238851586e 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -1,787 +1,3278 @@ /* - Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> - This file is part of GlusterFS. - - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. -*/ + Copyright (c) 2010-2012 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 <sys/socket.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <netinet/in.h> #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include "cli1-xdr.h" +#include <glusterfs/run.h> +#include <glusterfs/syscall.h> +#include <glusterfs/common-utils.h> +#include <glusterfs/events.h> -extern struct rpc_clnt *global_rpc; +extern rpc_clnt_prog_t cli_quotad_clnt; -extern rpc_clnt_prog_t *cli_rpc_prog; +static int +gf_asprintf_append(char **string_ptr, const char *format, ...); int -cli_cmd_volume_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, - const char **words, int wordcount); +cli_cmd_volume_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount); -void -cli_cmd_volume_start_usage () +int +cli_cmd_bitrot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount); + +int +cli_cmd_quota_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount); + +int +cli_cmd_volume_info_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume start <VOLNAME>"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + cli_cmd_volume_get_ctx_t ctx = { + 0, + }; + cli_local_t *local = NULL; + int sent = 0; + int parse_error = 0; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOLUME]; + + if ((wordcount == 2) || (wordcount == 3 && !strcmp(words[2], "all"))) { + ctx.flags = GF_CLI_GET_NEXT_VOLUME; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_NEXT_VOLUME]; + } else if (wordcount == 3) { + ctx.flags = GF_CLI_GET_VOLUME; + ctx.volname = (char *)words[2]; + if (strlen(ctx.volname) > GD_VOLUME_NAME_MAX) { + cli_out("Invalid volume name"); + goto out; + } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOLUME]; + } else { + cli_usage_out(word->pattern); + parse_error = 1; + return -1; + } + + local = cli_local_get(); + + if (!local) + goto out; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) + goto out; + + local->get_vol.flags = ctx.flags; + if (ctx.volname) + local->get_vol.volname = gf_strdup(ctx.volname); + + frame->local = local; + + if (proc->fn) { + ret = proc->fn(frame, THIS, &ctx); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Getting Volume information failed!"); + } + + CLI_STACK_DESTROY(frame); + + return ret; } -void -cli_cmd_volume_stop_usage () +int +cli_cmd_sync_volume_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume stop <VOLNAME> [force]"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int sent = 0; + int parse_error = 0; + dict_t *dict = NULL; + cli_local_t *local = NULL; + gf_answer_t answer = GF_ANSWER_NO; + const char *question = + "Sync volume may make data " + "inaccessible while the sync " + "is in progress. Do you want " + "to continue?"; + + if ((wordcount < 3) || (wordcount > 4)) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + dict = dict_new(); + if (!dict) + goto out; + + if ((wordcount == 3) || !strcmp(words[3], "all")) { + ret = dict_set_int32(dict, "flags", (int32_t)GF_CLI_SYNC_ALL); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, + "failed to set" + "flag"); + goto out; + } + } else { + ret = dict_set_str(dict, "volname", (char *)words[3]); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, + "failed to set " + "volume"); + goto out; + } + } + + ret = dict_set_str(dict, "hostname", (char *)words[2]); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to set hostname"); + goto out; + } + + if (!(state->mode & GLUSTER_MODE_SCRIPT)) { + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SYNC_VOLUME]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, dict); + + if (proc->fn) { + ret = proc->fn(frame, THIS, dict); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume sync failed"); + } + + CLI_STACK_DESTROY(frame); + + return ret; } -void -cli_cmd_volume_rename_usage () +int +cli_cmd_volume_create_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume rename <VOLNAME> <NEW-VOLNAME>"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + char *trans_type = NULL; + char *bricks = NULL; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_CREATE_VOLUME]; + + ret = cli_cmd_volume_create_parse(state, words, wordcount, &options, + &bricks); + + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + ret = dict_get_str(options, "transport", &trans_type); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Unable to get transport type"); + goto out; + } + + if (state->mode & GLUSTER_MODE_WIGNORE) { + ret = dict_set_int32(options, "force", _gf_true); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Failed to set force " + "option"); + goto out; + } + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume create failed"); + } + + if (ret == 0) { + gf_event(EVENT_VOLUME_CREATE, "name=%s;bricks=%s", (char *)words[2], + bricks); + } + + CLI_STACK_DESTROY(frame); + return ret; } -void -cli_cmd_volume_delete_usage () +int +cli_cmd_volume_delete_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume delete <VOLNAME>"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + char *volname = NULL; + gf_answer_t answer = GF_ANSWER_NO; + const char *question = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + dict_t *dict = NULL; + + question = + "Deleting volume will erase all information about the volume. " + "Do you want to continue?"; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DELETE_VOLUME]; + + if (wordcount != 3) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + volname = (char *)words[2]; + + dict = dict_new(); + if (!dict) + goto out; + + ret = dict_set_str(dict, "volname", volname); + if (ret) { + gf_log(THIS->name, GF_LOG_WARNING, "dict set failed"); + goto out; + } + + if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) { + question = + "Deleting the shared storage volume" + "(gluster_shared_storage), will affect features " + "like snapshot scheduler, geo-replication " + "and NFS-Ganesha. Do you still want to " + "continue?"; + } + + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, dict); + + if (proc->fn) { + ret = proc->fn(frame, THIS, dict); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume delete failed"); + } + + CLI_STACK_DESTROY(frame); + + if (ret == 0 && GF_ANSWER_YES == answer) { + gf_event(EVENT_VOLUME_DELETE, "name=%s", (char *)words[2]); + } + + return ret; } int -cli_cmd_volume_info_cbk (struct cli_state *state, struct cli_cmd_word *word, +cli_cmd_volume_start_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - cli_cmd_volume_get_ctx_t ctx = {0,}; - cli_local_t *local = NULL; - - proc = &cli_rpc_prog->proctable[GF1_CLI_GET_VOLUME]; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int sent = 0; + int parse_error = 0; + dict_t *dict = NULL; + int flags = 0; + cli_local_t *local = NULL; + + if (wordcount < 3 || wordcount > 4) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + if (!words[2]) + goto out; + + if (wordcount == 4) { + if (!strcmp("force", words[3])) { + flags |= GF_CLI_FLAG_OP_FORCE; + } else { + ret = -1; + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + } - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + dict = dict_new(); + if (!dict) { + goto out; + } - if ((wordcount == 2) || (wordcount == 3 && - !strcmp (words[2], "all"))) { - ctx.flags = GF_CLI_GET_NEXT_VOLUME; - proc = &cli_rpc_prog->proctable[GF1_CLI_GET_NEXT_VOLUME]; - } else if (wordcount == 3) { - ctx.flags = GF_CLI_GET_VOLUME; - ctx.volname = (char *)words[2]; - if (strlen (ctx.volname) > 1024) { - cli_out ("Invalid volume name"); - goto out; - } - proc = &cli_rpc_prog->proctable[GF1_CLI_GET_VOLUME]; - } + ret = dict_set_str(dict, "volname", (char *)words[2]); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set failed"); + goto out; + } - local = cli_local_get (); + ret = dict_set_int32(dict, "flags", flags); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set failed"); + goto out; + } - if (!local) - goto out; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_START_VOLUME]; - local->u.get_vol.flags = ctx.flags; - if (ctx.volname) - local->u.get_vol.volname = gf_strdup (ctx.volname); + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } - frame->local = local; + CLI_LOCAL_INIT(local, words, frame, dict); - if (proc->fn) { - ret = proc->fn (frame, THIS, &ctx); - } + if (proc->fn) { + ret = proc->fn(frame, THIS, dict); + } out: - if (ret) - cli_out ("Getting Volume information failed!"); - return ret; + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume start failed"); + } + + CLI_STACK_DESTROY(frame); + if (ret == 0) { + gf_event(EVENT_VOLUME_START, "name=%s;force=%d", (char *)words[2], + (flags & GF_CLI_FLAG_OP_FORCE)); + } + + return ret; } -void -cli_cmd_volume_create_usage () +gf_answer_t +cli_cmd_get_confirmation(struct cli_state *state, const char *question) { - cli_out ("Usage: volume create <NEW-VOLNAME> " - "[stripe <COUNT>] [replica <COUNT>] [transport <tcp|rdma>] " - "<NEW-BRICK> ..."); + char answer[5] = { + '\0', + }; + int flush = '\0'; + size_t len; + + if (state->mode & GLUSTER_MODE_SCRIPT) + return GF_ANSWER_YES; + + printf("%s (y/n) ", question); + + if (fgets(answer, 4, stdin) == NULL) { + cli_out("gluster cli read error"); + goto out; + } + + len = strlen(answer); + + if (len && answer[len - 1] == '\n') { + answer[--len] = '\0'; + } else { + do { + flush = getchar(); + } while (flush != '\n'); + } + + if (len > 3) + goto out; + + if (!strcasecmp(answer, "y") || !strcasecmp(answer, "yes")) + return GF_ANSWER_YES; + + else if (!strcasecmp(answer, "n") || !strcasecmp(answer, "no")) + return GF_ANSWER_NO; + +out: + cli_out("Invalid input, please enter y/n"); + + return GF_ANSWER_NO; } int -cli_cmd_volume_create_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_volume_stop_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int flags = 0; + gf_answer_t answer = GF_ANSWER_NO; + int sent = 0; + int parse_error = 0; + dict_t *dict = NULL; + char *volname = NULL; + cli_local_t *local = NULL; + + const char *question = + "Stopping volume will make its data inaccessible. " + "Do you want to continue?"; + + if (wordcount < 3 || wordcount > 4) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + volname = (char *)words[2]; + + dict = dict_new(); + ret = dict_set_str(dict, "volname", volname); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set failed"); + goto out; + } + + if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) { + question = + "Stopping the shared storage volume" + "(gluster_shared_storage), will affect features " + "like snapshot scheduler, geo-replication " + "and NFS-Ganesha. Do you still want to " + "continue?"; + } + + if (wordcount == 4) { + if (!strcmp("force", words[3])) { + flags |= GF_CLI_FLAG_OP_FORCE; + } else { + ret = -1; + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + } - proc = &cli_rpc_prog->proctable[GF1_CLI_CREATE_VOLUME]; + ret = dict_set_int32(dict, "flags", flags); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set failed"); + goto out; + } - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + answer = cli_cmd_get_confirmation(state, question); - ret = cli_cmd_volume_create_parse (words, wordcount, &options); + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } - if (ret) { - cli_cmd_volume_create_usage (); - goto out; - } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STOP_VOLUME]; - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } -out: - if (ret) { - if (wordcount > 2) { - char *volname = (char *) words[2]; - cli_out ("Creating Volume %s failed",volname ); - } - } - if (options) - dict_unref (options); + CLI_LOCAL_INIT(local, words, frame, dict); - return ret; -} + if (proc->fn) { + ret = proc->fn(frame, THIS, dict); + } +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume stop on '%s' failed", volname); + } + + CLI_STACK_DESTROY(frame); + if (dict) + dict_unref(dict); + + if (ret == 0 && GF_ANSWER_YES == answer) { + gf_event(EVENT_VOLUME_STOP, "name=%s;force=%d", (char *)words[2], + (flags & GF_CLI_FLAG_OP_FORCE)); + } + + return ret; +} int -cli_cmd_volume_delete_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_volume_rename_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - char *volname = NULL; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + int sent = 0; + int parse_error = 0; - proc = &cli_rpc_prog->proctable[GF1_CLI_DELETE_VOLUME]; + if (wordcount != 4) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + dict = dict_new(); + if (!dict) + goto out; - if (wordcount != 3) { - cli_cmd_volume_delete_usage (); - goto out; - } + ret = dict_set_str(dict, "old-volname", (char *)words[2]); - volname = (char *)words[2]; + if (ret) + goto out; - if (proc->fn) { - ret = proc->fn (frame, THIS, volname); + ret = dict_set_str(dict, "new-volname", (char *)words[3]); + + if (ret) + goto out; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RENAME_VOLUME]; + + if (proc->fn) { + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; } + ret = proc->fn(frame, THIS, dict); + } out: - if (ret && volname) - cli_out ("Deleting Volume %s failed", volname); + if (dict) + dict_unref(dict); - return ret; -} + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume rename on '%s' failed", (char *)words[2]); + } + CLI_STACK_DESTROY(frame); + + return ret; +} int -cli_cmd_volume_start_cbk (struct cli_state *state, struct cli_cmd_word *word, +cli_cmd_volume_defrag_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - char *volname = NULL; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; +#if (USE_EVENTS) + eventtypes_t event = EVENT_LAST; +#endif +#ifdef GF_SOLARIS_HOST_OS + cli_out("Command not supported on Solaris"); + goto out; +#endif - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + ret = cli_cmd_volume_defrag_parse(words, wordcount, &dict); - if (wordcount != 3) { - cli_cmd_volume_start_usage (); - goto out; - } + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + } - volname = (char *)words[2]; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DEFRAG_VOLUME]; - proc = &cli_rpc_prog->proctable[GF1_CLI_START_VOLUME]; + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } - if (proc->fn) { - ret = proc->fn (frame, THIS, volname); - } + CLI_LOCAL_INIT(local, words, frame, dict); + + if (proc->fn) { + ret = proc->fn(frame, THIS, dict); + } out: - if (!proc && ret && volname) - cli_out ("Starting Volume %s failed", volname); + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume rebalance failed"); + } else { +#if (USE_EVENTS) + if (!(strcmp(words[wordcount - 1], "start")) || + !(strcmp(words[wordcount - 1], "force"))) { + event = EVENT_VOLUME_REBALANCE_START; + } else if (!strcmp(words[wordcount - 1], "stop")) { + event = EVENT_VOLUME_REBALANCE_STOP; + } - return ret; + if (event != EVENT_LAST) + gf_event(event, "volume=%s", (char *)words[2]); +#endif + } + + CLI_STACK_DESTROY(frame); + + return ret; } +int +cli_cmd_volume_reset_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int sent = 0; + int parse_error = 0; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + cli_local_t *local = NULL; +#if (USE_EVENTS) + int ret1 = -1; + char *tmp_opt = NULL; +#endif + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RESET_VOLUME]; + + ret = cli_cmd_volume_reset_parse(words, wordcount, &options); + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume reset failed"); + } + +#if (USE_EVENTS) + if (ret == 0) { + ret1 = dict_get_str(options, "key", &tmp_opt); + if (ret1) + tmp_opt = ""; + + gf_event(EVENT_VOLUME_RESET, "name=%s;option=%s", (char *)words[2], + tmp_opt); + } +#endif + + CLI_STACK_DESTROY(frame); + + return ret; +} int -cli_cmd_volume_stop_cbk (struct cli_state *state, struct cli_cmd_word *word, +cli_cmd_volume_profile_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - int flags = 0; - gf1_cli_stop_vol_req req = {0,}; + int sent = 0; + int parse_error = 0; - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + cli_local_t *local = NULL; - if (wordcount < 3 || wordcount > 4) { - cli_cmd_volume_stop_usage (); - goto out; - } + ret = cli_cmd_volume_profile_parse(words, wordcount, &options); - req.volname = (char *)words[2]; - if (!req.volname) - goto out; + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } - if (wordcount == 4) { - if (!strcmp("force", words[3])) { - flags |= GF_CLI_FLAG_OP_FORCE; - } else { - ret = -1; - cli_cmd_volume_stop_usage (); - goto out; - } - } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_PROFILE_VOLUME]; - req.flags = flags; - proc = &cli_rpc_prog->proctable[GF1_CLI_STOP_VOLUME]; + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); + ret = -1; + goto out; + } - if (proc->fn) { - ret = proc->fn (frame, THIS, &req); - } + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (!proc && ret && req.volname) - cli_out ("Stopping Volume %s failed", req.volname); + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume profile failed"); + } - return ret; -} + CLI_STACK_DESTROY(frame); + return ret; +} int -cli_cmd_volume_rename_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_volume_set_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *dict = NULL; + int sent = 0; + int parse_error = 0; + + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + cli_local_t *local = NULL; + char *op_errstr = NULL; + +#if (USE_EVENTS) + int ret1 = -1; + int i = 1; + char dict_key[50] = { + 0, + }; + char *tmp_opt = NULL; + char *opts_str = NULL; + int num_options = 0; +#endif + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SET_VOLUME]; - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + ret = cli_cmd_volume_set_parse(state, words, wordcount, &options, + &op_errstr); + if (ret) { + if (op_errstr) { + cli_err("%s", op_errstr); + GF_FREE(op_errstr); + } else + cli_usage_out(word->pattern); - dict = dict_new (); - if (!dict) - goto out; + parse_error = 1; + goto out; + } - if (wordcount != 4) { - cli_cmd_volume_rename_usage (); - goto out; + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume set failed"); + } + +#if (USE_EVENTS) + if (ret == 0 && strcmp(words[2], "help") != 0) { + ret1 = dict_get_int32(options, "count", &num_options); + if (ret1) { + num_options = 0; + goto end; + } else { + num_options = num_options / 2; } - ret = dict_set_str (dict, "old-volname", (char *)words[2]); + char *free_list_key[num_options]; + char *free_list_val[num_options]; + for (i = 0; i < num_options; i++) { + free_list_key[i] = NULL; + free_list_val[i] = NULL; + } + /* Initialize opts_str */ + opts_str = ""; + + /* Prepare String in format options=KEY1,VALUE1,KEY2,VALUE2 */ + for (i = 1; i <= num_options; i++) { + sprintf(dict_key, "key%d", i); + ret1 = dict_get_str(options, dict_key, &tmp_opt); + if (ret1) + tmp_opt = ""; + + gf_asprintf(&opts_str, "%s,%s", opts_str, tmp_opt); + free_list_key[i - 1] = opts_str; + + sprintf(dict_key, "value%d", i); + ret1 = dict_get_str(options, dict_key, &tmp_opt); + if (ret1) + tmp_opt = ""; + + gf_asprintf(&opts_str, "%s,%s", opts_str, tmp_opt); + free_list_val[i - 1] = opts_str; + } - if (ret) - goto out; + gf_event(EVENT_VOLUME_SET, "name=%s;options=%s", (char *)words[2], + opts_str); - ret = dict_set_str (dict, "new-volname", (char *)words[3]); + /* Allocated by gf_strdup and gf_asprintf */ + for (i = 0; i < num_options; i++) { + GF_FREE(free_list_key[i]); + GF_FREE(free_list_val[i]); + } + } +#endif - if (ret) - goto out; +end: + CLI_STACK_DESTROY(frame); - proc = &cli_rpc_prog->proctable[GF1_CLI_RENAME_VOLUME]; + return ret; +} - if (proc->fn) { - ret = proc->fn (frame, THIS, dict); +static int +cli_event_remove_brick_str(dict_t *options, char **event_str, + eventtypes_t *event) +{ + int ret = -1; + char *bricklist = NULL; + char *brick = NULL; + char *volname = NULL; + char key[256] = { + 0, + }; + const char *eventstrformat = "volume=%s;bricks=%s"; + int32_t command = 0; + int32_t i = 1; + int32_t count = 0; + int32_t eventstrlen = 1; + int bricklen = 0; + char *tmp_ptr = NULL; + + if (!options || !event_str || !event) + goto out; + + ret = dict_get_str(options, "volname", &volname); + if (ret || !volname) { + gf_log("cli", GF_LOG_ERROR, "Failed to fetch volname"); + ret = -1; + goto out; + } + /* Get the list of bricks for the event */ + ret = dict_get_int32(options, "command", &command); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to fetch command"); + ret = -1; + goto out; + } + + switch (command) { + case GF_OP_CMD_START: + *event = EVENT_VOLUME_REMOVE_BRICK_START; + break; + case GF_OP_CMD_COMMIT: + *event = EVENT_VOLUME_REMOVE_BRICK_COMMIT; + break; + case GF_OP_CMD_COMMIT_FORCE: + *event = EVENT_VOLUME_REMOVE_BRICK_FORCE; + break; + case GF_OP_CMD_STOP: + *event = EVENT_VOLUME_REMOVE_BRICK_STOP; + break; + default: + *event = EVENT_LAST; + break; + } + + ret = -1; + + if (*event == EVENT_LAST) { + goto out; + } + + /* I could just get this from words[] but this is cleaner in case the + * format changes */ + while (i) { + snprintf(key, sizeof(key), "brick%d", i); + ret = dict_get_str(options, key, &brick); + if (ret) { + break; } + eventstrlen += strlen(brick) + 1; + i++; + } + + count = --i; + eventstrlen += 1; + + bricklist = GF_CALLOC(eventstrlen, sizeof(char), gf_common_mt_char); + if (!bricklist) { + gf_log(THIS->name, GF_LOG_ERROR, + "memory allocation failed for" + "bricklist"); + ret = -1; + goto out; + } + + tmp_ptr = bricklist; + + i = 1; + while (i <= count) { + snprintf(key, sizeof(key), "brick%d", i); + ret = dict_get_str(options, key, &brick); + if (ret) { + break; + } + snprintf(tmp_ptr, eventstrlen, "%s ", brick); + bricklen = strlen(brick); + eventstrlen -= (bricklen + 1); + tmp_ptr += (bricklen + 1); + i++; + } + + if (!ret) { + gf_asprintf(event_str, eventstrformat, volname, bricklist); + } else { + gf_asprintf(event_str, eventstrformat, volname, "<unavailable>"); + } + + ret = 0; out: - if (!proc && ret) { - if (dict) - dict_destroy (dict); - if (wordcount > 2) - cli_out ("Renaming Volume %s failed", (char *)words[2]); + GF_FREE(bricklist); + return ret; +} + +int +cli_cmd_volume_add_brick_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + gf_answer_t answer = GF_ANSWER_NO; + cli_local_t *local = NULL; + +#if (USE_EVENTS) + char *event_str = NULL; + char *bricks = NULL; + const char *eventstrformat = "volume=%s;bricks=%s"; +#endif + + const char *question = + "Changing the 'stripe count' of the volume is " + "not a supported feature. In some cases it may result in data " + "loss on the volume. Also there may be issues with regular " + "filesystem operations on the volume after the change. Do you " + "really want to continue with 'stripe' count option ? "; + + ret = cli_cmd_volume_add_brick_parse(state, words, wordcount, &options, 0); + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + /* TODO: there are challenges in supporting changing of + stripe-count, until it is properly supported give warning to user */ + if (dict_get(options, "stripe-count")) { + answer = cli_cmd_get_confirmation(state, question); + + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; } + } + +#if (USE_EVENTS) + /* Get the list of bricks for the event */ + + ret = dict_get_str(options, "bricks", &bricks); + + if (!ret) { + gf_asprintf(&event_str, eventstrformat, (char *)words[2], + &bricks[1] /*Skip leading space*/); + } else { + gf_asprintf(&event_str, eventstrformat, (char *)words[2], + "<unavailable>"); + } +#endif + + if (state->mode & GLUSTER_MODE_WIGNORE) { + ret = dict_set_int32(options, "force", _gf_true); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Failed to set force " + "option"); + goto out; + } + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_ADD_BRICK]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } - return ret; +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume add-brick failed"); + } else { +#if (USE_EVENTS) + gf_event(EVENT_VOLUME_ADD_BRICK, "%s", event_str); +#endif + } +#if (USE_EVENTS) + GF_FREE(event_str); +#endif + + CLI_STACK_DESTROY(frame); + return ret; } -void -cli_cmd_volume_defrag_usage () +int +cli_get_soft_limit(dict_t *options, const char **words, dict_t *xdata) +{ + call_frame_t *frame = NULL; + cli_local_t *local = NULL; + rpc_clnt_procedure_t *proc = NULL; + char *default_sl = NULL; + char *default_sl_dup = NULL; + int ret = -1; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + // We need a ref on @options to prevent CLI_STACK_DESTROY + // from destroying it prematurely. + dict_ref(options); + CLI_LOCAL_INIT(local, words, frame, options); + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_QUOTA]; + ret = proc->fn(frame, THIS, options); + + ret = dict_get_str(options, "default-soft-limit", &default_sl); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to get default soft limit"); + goto out; + } + + default_sl_dup = gf_strdup(default_sl); + if (!default_sl_dup) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr(xdata, "default-soft-limit", default_sl_dup); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to set default soft limit"); + GF_FREE(default_sl_dup); + goto out; + } + +out: + CLI_STACK_DESTROY(frame); + return ret; +} + +/* Checks if at least one limit has been set on the volume + * + * Returns true if at least one limit is set. Returns false otherwise. + */ +gf_boolean_t +_limits_set_on_volume(char *volname, int type) { - cli_out ("Usage: volume rebalance <VOLNAME> <start|stop|status>"); + gf_boolean_t limits_set = _gf_false; + int ret = -1; + char quota_conf_file[PATH_MAX] = { + 0, + }; + int fd = -1; + char buf[16] = { + 0, + }; + float version = 0.0f; + char gfid_type_stored = 0; + char gfid_type = 0; + + /* TODO: fix hardcoding; Need to perform an RPC call to glusterd + * to fetch working directory + */ + snprintf(quota_conf_file, sizeof quota_conf_file, "%s/vols/%s/quota.conf", + GLUSTERD_DEFAULT_WORKDIR, volname); + fd = open(quota_conf_file, O_RDONLY); + if (fd == -1) + goto out; + + ret = quota_conf_read_version(fd, &version); + if (ret) + goto out; + + if (type == GF_QUOTA_OPTION_TYPE_LIST) + gfid_type = GF_QUOTA_CONF_TYPE_USAGE; + else + gfid_type = GF_QUOTA_CONF_TYPE_OBJECTS; + + /* Try to read at least one gfid of type 'gfid_type' */ + while (1) { + ret = quota_conf_read_gfid(fd, buf, &gfid_type_stored, version); + if (ret <= 0) + break; + + if (gfid_type_stored == gfid_type) { + limits_set = _gf_true; + break; + } + } +out: + if (fd != -1) + sys_close(fd); + + return limits_set; } int -cli_cmd_volume_defrag_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_quota_handle_list_all(const char **words, dict_t *options) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *dict = NULL; + int all_failed = 1; + int count = 0; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + cli_local_t *local = NULL; + call_frame_t *frame = NULL; + dict_t *xdata = NULL; + char gfid_str[UUID_CANONICAL_FORM_LEN + 1]; + char *volname = NULL; + char *volname_dup = NULL; + unsigned char buf[16] = {0}; + int fd = -1; + char quota_conf_file[PATH_MAX] = {0}; + gf_boolean_t xml_err_flag = _gf_false; + char err_str[NAME_MAX] = { + 0, + }; + int32_t type = 0; + char gfid_type = 0; + float version = 0.0f; + int32_t max_count = 0; + + xdata = dict_new(); + if (!xdata) { + ret = -1; + goto out; + } + + ret = dict_get_str(options, "volname", &volname); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to get volume name"); + goto out; + } + + ret = dict_get_int32(options, "type", &type); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to get quota option type"); + goto out; + } + + ret = dict_set_int32(xdata, "type", type); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to set type in xdata"); + goto out; + } + + ret = cli_get_soft_limit(options, words, xdata); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Failed to fetch default " + "soft-limit"); + goto out; + } + + /* Check if at least one limit is set on volume. No need to check for + * quota enabled as cli_get_soft_limit() handles that + */ + if (!_limits_set_on_volume(volname, type)) { + snprintf(err_str, sizeof(err_str), + "No%s quota configured on" + " volume %s", + (type == GF_QUOTA_OPTION_TYPE_LIST) ? "" : " inode", volname); + if (global_state->mode & GLUSTER_MODE_XML) { + xml_err_flag = _gf_true; + } else { + cli_out("quota: %s", err_str); + } + ret = 0; + goto out; + } + + volname_dup = gf_strdup(volname); + if (!volname_dup) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr(xdata, "volume-uuid", volname_dup); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to set volume-uuid"); + GF_FREE(volname_dup); + goto out; + } + + // TODO: fix hardcoding; Need to perform an RPC call to glusterd + // to fetch working directory + snprintf(quota_conf_file, sizeof quota_conf_file, "%s/vols/%s/quota.conf", + GLUSTERD_DEFAULT_WORKDIR, volname); + fd = open(quota_conf_file, O_RDONLY); + if (fd == -1) { + // This may because no limits were yet set on the volume + gf_log("cli", GF_LOG_TRACE, + "Unable to open " + "quota.conf"); + ret = 0; + goto out; + } + + ret = quota_conf_read_version(fd, &version); + if (ret) + goto out; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, xdata); + proc = &cli_quotad_clnt.proctable[GF_AGGREGATOR_GETLIMIT]; + + for (count = 0;; count++) { + ret = quota_conf_read_gfid(fd, buf, &gfid_type, version); + if (ret == 0) { + break; + } else if (ret < 0) { + gf_log(THIS->name, GF_LOG_CRITICAL, + "Quota " + "configuration store may be corrupt."); + goto out; + } - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + if ((type == GF_QUOTA_OPTION_TYPE_LIST && + gfid_type == GF_QUOTA_CONF_TYPE_OBJECTS) || + (type == GF_QUOTA_OPTION_TYPE_LIST_OBJECTS && + gfid_type == GF_QUOTA_CONF_TYPE_USAGE)) + continue; + + max_count++; + } + ret = dict_set_int32(xdata, "max_count", max_count); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to set max_count"); + goto out; + } + + ret = sys_lseek(fd, 0L, SEEK_SET); + if (ret < 0) { + gf_log(THIS->name, GF_LOG_ERROR, + "failed to move offset to " + "the beginning: %s", + strerror(errno)); + goto out; + } + ret = quota_conf_read_version(fd, &version); + if (ret) + goto out; + + for (count = 0;; count++) { + ret = quota_conf_read_gfid(fd, buf, &gfid_type, version); + if (ret == 0) { + break; + } else if (ret < 0) { + gf_log(THIS->name, GF_LOG_CRITICAL, + "Quota " + "configuration store may be corrupt."); + goto out; + } - dict = dict_new (); - if (!dict) - goto out; + if ((type == GF_QUOTA_OPTION_TYPE_LIST && + gfid_type == GF_QUOTA_CONF_TYPE_OBJECTS) || + (type == GF_QUOTA_OPTION_TYPE_LIST_OBJECTS && + gfid_type == GF_QUOTA_CONF_TYPE_USAGE)) + continue; - if (wordcount != 4) { - cli_cmd_volume_defrag_usage(); - goto out; + uuid_utoa_r(buf, gfid_str); + ret = dict_set_str(xdata, "gfid", gfid_str); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to set gfid"); + goto out; } - ret = dict_set_str (dict, "volname", (char *)words[2]); - if (ret) - goto out; + ret = proc->fn(frame, THIS, xdata); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Failed to get quota " + "limits for %s", + uuid_utoa((unsigned char *)buf)); + } - ret = dict_set_str (dict, "command", (char *)words[3]); - if (ret) - goto out; + dict_del(xdata, "gfid"); + all_failed = all_failed && ret; + } + + if (global_state->mode & GLUSTER_MODE_XML) { + ret = cli_xml_output_vol_quota_limit_list_end(local); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Error in printing " + "xml output"); + goto out; + } + } - proc = &cli_rpc_prog->proctable[GF1_CLI_DEFRAG_VOLUME]; + if (count > 0) { + ret = all_failed ? -1 : 0; + } else { + ret = 0; + } - if (proc->fn) { - ret = proc->fn (frame, THIS, dict); +out: + if (xml_err_flag) { + ret = cli_xml_output_str("volQuota", NULL, -1, 0, err_str); + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Error outputting in " + "xml format"); } + } + if (xdata) + dict_unref(xdata); + + if (fd != -1) { + sys_close(fd); + } + + if (ret) { + gf_log("cli", GF_LOG_ERROR, + "Could not fetch and display quota" + " limits"); + } + CLI_STACK_DESTROY(frame); + return ret; +} + +int +cli_cmd_bitrot_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + int parse_err = 0; + call_frame_t *frame = NULL; + dict_t *options = NULL; + cli_local_t *local = NULL; + rpc_clnt_procedure_t *proc = NULL; + int sent = 0; +#if (USE_EVENTS) + int cmd_type = -1; + int ret1 = -1; + int event_type = -1; + char *tmp = NULL; + char *events_str = NULL; + char *volname = NULL; +#endif + + ret = cli_cmd_bitrot_parse(words, wordcount, &options); + if (ret < 0) { + cli_usage_out(word->pattern); + parse_err = 1; + goto out; + } + + if (ret == 1) { + /* this is 'volume bitrot help' */ + cli_cmd_bitrot_help_cbk(state, word, words, wordcount); + ret = 0; + goto out2; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_BITROT]; + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (!proc && ret) { - if (dict) - dict_destroy (dict); + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_err == 0)) + cli_err( + "Bit rot command failed. Please check the cli " + "logs for more details"); + } + +#if (USE_EVENTS) + if (ret == 0) { + ret1 = dict_get_int32(options, "type", &cmd_type); + if (ret1) + cmd_type = -1; + else { + ret1 = dict_get_str(options, "volname", &volname); + if (ret1) + volname = ""; + } - if (wordcount > 2) - cli_out ("Rebalance of Volume %s failed", - (char *)words[2]); + switch (cmd_type) { + case GF_BITROT_OPTION_TYPE_ENABLE: + event_type = EVENT_BITROT_ENABLE; + break; + case GF_BITROT_OPTION_TYPE_DISABLE: + event_type = EVENT_BITROT_DISABLE; + break; + case GF_BITROT_CMD_SCRUB_ONDEMAND: + event_type = EVENT_BITROT_SCRUB_ONDEMAND; + break; + case GF_BITROT_OPTION_TYPE_SCRUB_THROTTLE: + event_type = EVENT_BITROT_SCRUB_THROTTLE; + ret1 = dict_get_str(options, "scrub-throttle-value", &tmp); + if (ret1) + tmp = ""; + gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp); + break; + case GF_BITROT_OPTION_TYPE_SCRUB_FREQ: + event_type = EVENT_BITROT_SCRUB_FREQ; + ret1 = dict_get_str(options, "scrub-frequency-value", &tmp); + if (ret1) + tmp = ""; + gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp); + break; + case GF_BITROT_OPTION_TYPE_SCRUB: + event_type = EVENT_BITROT_SCRUB_OPTION; + ret1 = dict_get_str(options, "scrub-value", &tmp); + if (ret1) + tmp = ""; + gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp); + break; + default: + break; } - return 0; -} + if (event_type > -1) + gf_event(event_type, "%s", events_str); + + if (events_str) + GF_FREE(events_str); + } +#endif + CLI_STACK_DESTROY(frame); +out2: + return ret; +} int -cli_cmd_volume_set_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_quota_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_cmd_broadcast_response (0); - return 0; + int ret = 0; + int parse_err = 0; + int32_t type = 0; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + gf_answer_t answer = GF_ANSWER_NO; + cli_local_t *local = NULL; + int sent = 0; + char *volname = NULL; + const char *question = + "Disabling quota will delete all the quota " + "configuration. Do you want to continue?"; + + // parse **words into options dictionary + if (strcmp(words[1], "inode-quota") == 0) { + ret = cli_cmd_inode_quota_parse(words, wordcount, &options); + if (ret < 0) { + cli_usage_out(word->pattern); + parse_err = 1; + goto out; + } + } else { + ret = cli_cmd_quota_parse(words, wordcount, &options); + + if (ret == 1) { + cli_cmd_quota_help_cbk(state, word, words, wordcount); + ret = 0; + goto out; + } + if (ret < 0) { + cli_usage_out(word->pattern); + parse_err = 1; + goto out; + } + } + + ret = dict_get_int32(options, "type", &type); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to get opcode"); + goto out; + } + + // handle quota-disable and quota-list-all different from others + switch (type) { + case GF_QUOTA_OPTION_TYPE_DISABLE: + answer = cli_cmd_get_confirmation(state, question); + if (answer == GF_ANSWER_NO) + goto out; + break; + case GF_QUOTA_OPTION_TYPE_LIST: + case GF_QUOTA_OPTION_TYPE_LIST_OBJECTS: + if (wordcount != 4) + break; + ret = cli_cmd_quota_handle_list_all(words, options); + goto out; + default: + break; + } + + ret = dict_get_str(options, "volname", &volname); + if (ret) { + gf_log("cli", GF_LOG_ERROR, "Failed to get volume name"); + goto out; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_QUOTA]; + + if (proc->fn) + ret = proc->fn(frame, THIS, options); + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if (sent == 0 && parse_err == 0) + cli_out( + "Quota command failed. Please check the cli " + "logs for more details"); + } + if (options) + dict_unref(options); + + /* Events for Quota */ + if (ret == 0) { + switch (type) { + case GF_QUOTA_OPTION_TYPE_ENABLE: + gf_event(EVENT_QUOTA_ENABLE, "volume=%s", volname); + break; + case GF_QUOTA_OPTION_TYPE_DISABLE: + gf_event(EVENT_QUOTA_DISABLE, "volume=%s", volname); + break; + case GF_QUOTA_OPTION_TYPE_LIMIT_USAGE: + gf_event(EVENT_QUOTA_SET_USAGE_LIMIT, + "volume=%s;" + "path=%s;limit=%s", + volname, words[4], words[5]); + break; + case GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS: + gf_event(EVENT_QUOTA_SET_OBJECTS_LIMIT, + "volume=%s;" + "path=%s;limit=%s", + volname, words[4], words[5]); + break; + case GF_QUOTA_OPTION_TYPE_REMOVE: + gf_event(EVENT_QUOTA_REMOVE_USAGE_LIMIT, + "volume=%s;" + "path=%s", + volname, words[4]); + break; + case GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS: + gf_event(EVENT_QUOTA_REMOVE_OBJECTS_LIMIT, + "volume=%s;" + "path=%s", + volname, words[4]); + break; + case GF_QUOTA_OPTION_TYPE_ALERT_TIME: + gf_event(EVENT_QUOTA_ALERT_TIME, "volume=%s;time=%s", volname, + words[4]); + break; + case GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT: + gf_event(EVENT_QUOTA_SOFT_TIMEOUT, + "volume=%s;" + "soft-timeout=%s", + volname, words[4]); + break; + case GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT: + gf_event(EVENT_QUOTA_HARD_TIMEOUT, + "volume=%s;" + "hard-timeout=%s", + volname, words[4]); + break; + case GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT: + gf_event(EVENT_QUOTA_DEFAULT_SOFT_LIMIT, + "volume=%s;" + "default-soft-limit=%s", + volname, words[4]); + break; + } + } + + CLI_STACK_DESTROY(frame); + return ret; } -void -cli_cmd_volume_add_brick_usage () +int +cli_cmd_volume_remove_brick_cbk(struct cli_state *state, + struct cli_cmd_word *word, const char **words, + int wordcount) { - cli_out ("Usage: volume add-brick <VOLNAME> " - "<NEW-BRICK> ..."); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + gf_answer_t answer = GF_ANSWER_NO; + int brick_count = 0; + int sent = 0; + int parse_error = 0; + int need_question = 0; + cli_local_t *local = NULL; + char *volname = NULL; +#if (USE_EVENTS) + eventtypes_t event = EVENT_LAST; + char *event_str = NULL; + int event_ret = -1; +#endif + int32_t command = GF_OP_CMD_NONE; + char *question = NULL; + + ret = cli_cmd_volume_remove_brick_parse(state, words, wordcount, &options, + &need_question, &brick_count, + &command); + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + if (command == GF_OP_CMD_COMMIT_FORCE) { + question = + "Remove-brick force will not migrate files from the " + "removed bricks, so they will no longer be available" + " on the volume.\nDo you want to continue?"; + } else if (command == GF_OP_CMD_START) { + question = + "It is recommended that remove-brick be run with" + " cluster.force-migration option disabled to prevent" + " possible data corruption. Doing so will ensure that" + " files that receive writes during migration will not" + " be migrated and will need to be manually copied" + " after the remove-brick commit operation. Please" + " check the value of the option and update accordingly." + " \nDo you want to continue with your current" + " cluster.force-migration settings?"; + } + + if (!brick_count) { + cli_err("No bricks specified"); + cli_usage_out(word->pattern); + parse_error = 1; + ret = -1; + goto out; + } + ret = dict_get_str(options, "volname", &volname); + if (ret || !volname) { + gf_log("cli", GF_LOG_ERROR, "Failed to fetch volname"); + ret = -1; + goto out; + } + +#if (USE_EVENTS) + event_ret = cli_event_remove_brick_str(options, &event_str, &event); +#endif + + if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) { + question = + "Removing brick from the shared storage volume" + "(gluster_shared_storage), will affect features " + "like snapshot scheduler, geo-replication " + "and NFS-Ganesha. Do you still want to " + "continue?"; + need_question = _gf_true; + } + + if (!(state->mode & GLUSTER_MODE_SCRIPT) && need_question) { + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + ret = 0; + goto out; + } + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_REMOVE_BRICK]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume remove-brick failed"); + } +#if (USE_EVENTS) + if (!ret && !event_ret) + gf_event(event, "%s", event_str); + if (event_str) + GF_FREE(event_str); + +#endif + + CLI_STACK_DESTROY(frame); + if (options) + dict_unref(options); + + return ret; } int -cli_cmd_volume_add_brick_cbk (struct cli_state *state, - struct cli_cmd_word *word, const char **words, - int wordcount) +cli_cmd_volume_reset_brick_cbk(struct cli_state *state, + struct cli_cmd_word *word, const char **words, + int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + +#ifdef GF_SOLARIS_HOST_OS + cli_out("Command not supported on Solaris"); + goto out; +#endif + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RESET_BRICK]; - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + ret = cli_cmd_volume_reset_brick_parse(words, wordcount, &options); - ret = cli_cmd_volume_add_brick_parse (words, wordcount, &options); + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + if (state->mode & GLUSTER_MODE_WIGNORE_PARTITION) { + ret = dict_set_int32(options, "ignore-partition", _gf_true); if (ret) { - cli_cmd_volume_add_brick_usage (); - goto out; + gf_log("cli", GF_LOG_ERROR, + "Failed to set ignore-" + "partition option"); + goto out; } + } - proc = &cli_rpc_prog->proctable[GF1_CLI_ADD_BRICK]; + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (!proc && ret) { - if (wordcount > 2) { - char *volname = (char *) words[2]; - cli_out ("Adding brick to Volume %s failed",volname ); - } + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume reset-brick failed"); + } else { + if (wordcount > 5) { + gf_event(EVENT_BRICK_RESET_COMMIT, + "Volume=%s;source-brick=%s;" + "destination-brick=%s", + (char *)words[2], (char *)words[3], (char *)words[4]); + } else { + gf_event(EVENT_BRICK_RESET_START, "Volume=%s;source-brick=%s", + (char *)words[2], (char *)words[3]); } - return ret; -} + } + CLI_STACK_DESTROY(frame); + return ret; +} -void -cli_cmd_volume_remove_brick_usage () +int +cli_cmd_volume_replace_brick_cbk(struct cli_state *state, + struct cli_cmd_word *word, const char **words, + int wordcount) { - cli_out ("Usage: volume remove-brick <VOLNAME> " - "<BRICK> ..."); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + +#ifdef GF_SOLARIS_HOST_OS + cli_out("Command not supported on Solaris"); + goto out; +#endif + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_REPLACE_BRICK]; + + ret = cli_cmd_volume_replace_brick_parse(words, wordcount, &options); + + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume replace-brick failed"); + } else { + gf_event(EVENT_BRICK_REPLACE, + "Volume=%s;source-brick=%s;destination-brick=%s", + (char *)words[2], (char *)words[3], (char *)words[4]); + } + CLI_STACK_DESTROY(frame); + + return ret; } int -cli_cmd_volume_remove_brick_cbk (struct cli_state *state, +cli_cmd_volume_set_transport_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; + cli_cmd_broadcast_response(0); + return 0; +} - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; +int +cli_cmd_volume_top_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; - ret = cli_cmd_volume_remove_brick_parse (words, wordcount, &options); + ret = cli_cmd_volume_top_parse(words, wordcount, &options); - if (ret) { - cli_cmd_volume_remove_brick_usage (); - goto out; - } + if (ret) { + parse_error = 1; + cli_usage_out(word->pattern); + goto out; + } - proc = &cli_rpc_prog->proctable[GF1_CLI_REMOVE_BRICK]; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_TOP_VOLUME]; - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (!proc && ret) { - if (wordcount > 2) { - char *volname = (char *) words[2]; - cli_out ("Removing brick from Volume %s failed",volname ); - } + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume top failed"); + } + + CLI_STACK_DESTROY(frame); + + return ret; +} + +int +cli_cmd_log_rotate_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + + if (!((wordcount == 4) || (wordcount == 5))) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + if (!(strcmp("rotate", words[3]) == 0)) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LOG_ROTATE]; + + ret = cli_cmd_log_rotate_parse(words, wordcount, &options); + if (ret) + goto out; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume log rotate failed"); + } + CLI_STACK_DESTROY(frame); + + return ret; +} + +#if (SYNCDAEMON_COMPILE) +static int +cli_check_gsync_present() +{ + char buff[PATH_MAX] = { + 0, + }; + runner_t runner = { + 0, + }; + char *ptr = NULL; + int ret = 0; + + ret = setenv("_GLUSTERD_CALLED_", "1", 1); + if (-1 == ret) { + gf_log("", GF_LOG_WARNING, + "setenv syscall failed, hence could" + "not assert if geo-replication is installed"); + goto out; + } + + runinit(&runner); + runner_add_args(&runner, GSYNCD_PREFIX "/gsyncd", "--version", NULL); + runner_redir(&runner, STDOUT_FILENO, RUN_PIPE); + ret = runner_start(&runner); + if (ret == -1) { + gf_log("", GF_LOG_INFO, "geo-replication not installed"); + goto out; + } + + ptr = fgets(buff, sizeof(buff), runner_chio(&runner, STDOUT_FILENO)); + if (ptr) { + if (!strstr(buff, "gsyncd")) { + ret = -1; + goto out; } - return ret; + } else { + ret = -1; + goto out; + } + + ret = runner_end(&runner); + if (ret) + gf_log("", GF_LOG_ERROR, "geo-replication not installed"); + +out: + gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret ? -1 : 0; } void -cli_cmd_volume_replace_brick_usage () +cli_cmd_check_gsync_exists_cbk(struct cli_cmd *this) { - cli_out("Usage: volume replace-brick <VOLNAME> " - "(<BRICK> <NEW-BRICK>) start|pause|abort|status"); -} + int ret = 0; + ret = cli_check_gsync_present(); + if (ret) + this->disable = _gf_true; +} +#endif int -cli_cmd_volume_replace_brick_cbk (struct cli_state *state, - struct cli_cmd_word *word, - const char **words, - int wordcount) +cli_cmd_volume_gsync_set_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; + 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; + char *errstr = NULL; +#if (USE_EVENTS) + int ret1 = -1; + int cmd_type = -1; + int tmpi = 0; + char *tmp = NULL; + char *events_str = NULL; + int event_type = -1; +#endif - proc = &cli_rpc_prog->proctable[GF1_CLI_REPLACE_BRICK]; + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GSYNC_SET]; - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + ret = cli_cmd_gsync_set_parse(state, words, wordcount, &options, &errstr); + if (ret) { + if (errstr) { + cli_err("%s", errstr); + GF_FREE(errstr); + } else { + cli_usage_out(word->pattern); + } + parse_err = 1; + goto out; + } - ret = cli_cmd_volume_replace_brick_parse (words, wordcount, &options); + frame = create_frame(THIS, THIS->ctx->pool); + if (frame == NULL) { + ret = -1; + goto out; + } - if (ret) { - cli_cmd_volume_replace_brick_usage (); - goto out; - } + CLI_LOCAL_INIT(local, words, frame, options); - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } + if (proc->fn) + ret = proc->fn(frame, THIS, options); out: - if (ret) { - if (wordcount > 2) { - char *volname = (char *) words[2]; - cli_out ("Replacing brick from Volume %s failed",volname ); + if (ret && parse_err == 0) + cli_out(GEOREP " command failed"); + +#if (USE_EVENTS) + if (ret == 0) { + events_str = gf_strdup(""); + + /* Type of Geo-rep Action - Create, Start etc */ + ret1 = dict_get_int32(options, "type", &cmd_type); + if (ret1) + cmd_type = -1; + + /* Only capture Events for modification commands */ + switch (cmd_type) { + case GF_GSYNC_OPTION_TYPE_CREATE: + event_type = EVENT_GEOREP_CREATE; + break; + case GF_GSYNC_OPTION_TYPE_START: + event_type = EVENT_GEOREP_START; + break; + case GF_GSYNC_OPTION_TYPE_STOP: + event_type = EVENT_GEOREP_STOP; + break; + case GF_GSYNC_OPTION_TYPE_PAUSE: + event_type = EVENT_GEOREP_PAUSE; + break; + case GF_GSYNC_OPTION_TYPE_RESUME: + event_type = EVENT_GEOREP_RESUME; + break; + case GF_GSYNC_OPTION_TYPE_DELETE: + event_type = EVENT_GEOREP_DELETE; + break; + case GF_GSYNC_OPTION_TYPE_CONFIG: + ret1 = dict_get_str(options, "subop", &tmp); + if (ret1) + tmp = ""; + + /* For Config Set additionally capture key and value */ + /* For Config Reset capture key */ + if (strcmp(tmp, "set") == 0) { + event_type = EVENT_GEOREP_CONFIG_SET; + + ret1 = dict_get_str(options, "op_name", &tmp); + if (ret1) + tmp = ""; + + gf_asprintf_append(&events_str, "%soption=%s;", events_str, + tmp); + + ret1 = dict_get_str(options, "op_value", &tmp); + if (ret1) + tmp = ""; + + gf_asprintf_append(&events_str, "%svalue=%s;", events_str, + tmp); + } else if (strcmp(tmp, "del") == 0) { + event_type = EVENT_GEOREP_CONFIG_RESET; + + ret1 = dict_get_str(options, "op_name", &tmp); + if (ret1) + tmp = ""; + + gf_asprintf_append(&events_str, "%soption=%s;", events_str, + tmp); } + break; + default: + break; + } + + if (event_type > -1) { + /* Capture all optional arguments used */ + ret1 = dict_get_int32(options, "force", &tmpi); + if (ret1 == 0) { + gf_asprintf_append(&events_str, "%sforce=%d;", events_str, + tmpi); + } + ret1 = dict_get_int32(options, "push_pem", &tmpi); + if (ret1 == 0) { + gf_asprintf_append(&events_str, "%spush_pem=%d;", events_str, + tmpi); + } + ret1 = dict_get_int32(options, "no_verify", &tmpi); + if (ret1 == 0) { + gf_asprintf_append(&events_str, "%sno_verify=%d;", events_str, + tmpi); + } + + ret1 = dict_get_int32(options, "ssh_port", &tmpi); + if (ret1 == 0) { + gf_asprintf_append(&events_str, "%sssh_port=%d;", events_str, + tmpi); + } + + ret1 = dict_get_int32(options, "reset-sync-time", &tmpi); + if (ret1 == 0) { + gf_asprintf_append(&events_str, "%sreset_sync_time=%d;", + events_str, tmpi); + } + /* Capture Master and Slave Info */ + ret1 = dict_get_str(options, "master", &tmp); + if (ret1) + tmp = ""; + gf_asprintf_append(&events_str, "%smaster=%s;", events_str, tmp); + + ret1 = dict_get_str(options, "slave", &tmp); + if (ret1) + tmp = ""; + gf_asprintf_append(&events_str, "%sslave=%s", events_str, tmp); + + gf_event(event_type, "%s", events_str); } - return ret; + /* Allocated by gf_strdup and gf_asprintf */ + if (events_str) + GF_FREE(events_str); + } +#endif + + CLI_STACK_DESTROY(frame); + + return ret; } +int +cli_cmd_volume_status_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *dict = NULL; + uint32_t cmd = 0; + cli_local_t *local = NULL; + + ret = cli_cmd_volume_status_parse(words, wordcount, &dict); + + if (ret) { + cli_usage_out(word->pattern); + goto out; + } + + ret = dict_get_uint32(dict, "cmd", &cmd); + if (ret) + goto out; + + if (!(cmd & GF_CLI_STATUS_ALL)) { + /* for one volume or brick */ + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATUS_VOLUME]; + } else { + /* volume status all or all detail */ + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATUS_ALL]; + } + + if (!proc->fn) { + ret = -1; + goto out; + } + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, dict); + + ret = proc->fn(frame, THIS, dict); + +out: + CLI_STACK_DESTROY(frame); + + return ret; +} int -cli_cmd_volume_set_transport_cbk (struct cli_state *state, - struct cli_cmd_word *word, - const char **words, int wordcount) +cli_get_detail_status(dict_t *dict, int i, cli_volume_status_t *status) { - cli_cmd_broadcast_response (0); - return 0; + uint64_t free = 0; + uint64_t total = 0; + char key[1024] = {0}; + int ret = 0; + + snprintf(key, sizeof(key), "brick%d.free", i); + ret = dict_get_uint64(dict, key, &free); + + status->free = gf_uint64_2human_readable(free); + if (!status->free) + goto out; + + snprintf(key, sizeof(key), "brick%d.total", i); + ret = dict_get_uint64(dict, key, &total); + + status->total = gf_uint64_2human_readable(total); + if (!status->total) + goto out; + + snprintf(key, sizeof(key), "brick%d.device", i); + ret = dict_get_str(dict, key, &(status->device)); + if (ret) + status->device = NULL; + + snprintf(key, sizeof(key), "brick%d.block_size", i); + ret = dict_get_uint64(dict, key, &(status->block_size)); + if (ret) { + ret = 0; + status->block_size = 0; + } + + snprintf(key, sizeof(key), "brick%d.mnt_options", i); + ret = dict_get_str(dict, key, &(status->mount_options)); + if (ret) + status->mount_options = NULL; + + snprintf(key, sizeof(key), "brick%d.fs_name", i); + ret = dict_get_str(dict, key, &(status->fs_name)); + if (ret) { + ret = 0; + status->fs_name = NULL; + } + + snprintf(key, sizeof(key), "brick%d.inode_size", i); + ret = dict_get_str(dict, key, &(status->inode_size)); + if (ret) + status->inode_size = NULL; + + snprintf(key, sizeof(key), "brick%d.total_inodes", i); + ret = dict_get_uint64(dict, key, &(status->total_inodes)); + if (ret) + status->total_inodes = 0; + + snprintf(key, sizeof(key), "brick%d.free_inodes", i); + ret = dict_get_uint64(dict, key, &(status->free_inodes)); + if (ret) { + ret = 0; + status->free_inodes = 0; + } + +out: + return ret; } void -cli_cmd_log_filename_usage () +cli_print_detailed_status(cli_volume_status_t *status) { - cli_out ("Usage: volume log filename <VOLNAME> [BRICK] <PATH>"); + cli_out("%-20s : %-20s", "Brick", status->brick); + + if (status->online) { + cli_out("%-20s : %-20d", "TCP Port", status->port); + cli_out("%-20s : %-20d", "RDMA Port", status->rdma_port); + } else { + cli_out("%-20s : %-20s", "TCP Port", "N/A"); + cli_out("%-20s : %-20s", "RDMA Port", "N/A"); + } + + cli_out("%-20s : %-20c", "Online", (status->online) ? 'Y' : 'N'); + cli_out("%-20s : %-20s", "Pid", status->pid_str); + + if (status->fs_name) + cli_out("%-20s : %-20s", "File System", status->fs_name); + else + cli_out("%-20s : %-20s", "File System", "N/A"); + + if (status->device) + cli_out("%-20s : %-20s", "Device", status->device); + else + cli_out("%-20s : %-20s", "Device", "N/A"); + + if (status->mount_options) { + cli_out("%-20s : %-20s", "Mount Options", status->mount_options); + } else { + cli_out("%-20s : %-20s", "Mount Options", "N/A"); + } + + if (status->inode_size) { + cli_out("%-20s : %-20s", "Inode Size", status->inode_size); + } else { + cli_out("%-20s : %-20s", "Inode Size", "N/A"); + } + if (status->free) + cli_out("%-20s : %-20s", "Disk Space Free", status->free); + else + cli_out("%-20s : %-20s", "Disk Space Free", "N/A"); + + if (status->total) + cli_out("%-20s : %-20s", "Total Disk Space", status->total); + else + cli_out("%-20s : %-20s", "Total Disk Space", "N/A"); + + if (status->total_inodes) { + cli_out("%-20s : %-20" GF_PRI_INODE, "Inode Count", + status->total_inodes); + } else { + cli_out("%-20s : %-20s", "Inode Count", "N/A"); + } + + if (status->free_inodes) { + cli_out("%-20s : %-20" GF_PRI_INODE, "Free Inodes", + status->free_inodes); + } else { + cli_out("%-20s : %-20s", "Free Inodes", "N/A"); + } } int -cli_cmd_log_filename_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_print_brick_status(cli_volume_status_t *status) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; - - if (!((wordcount == 5) || (wordcount == 6))) { - cli_cmd_log_filename_usage (); - goto out; + int fieldlen = CLI_VOL_STATUS_BRICK_LEN; + int bricklen = 0; + char *p = NULL; + int num_spaces = 0; + + p = status->brick; + bricklen = strlen(p); + while (bricklen > 0) { + if (bricklen > fieldlen) { + cli_out("%.*s", fieldlen, p); + p += fieldlen; + bricklen -= fieldlen; + } else { + num_spaces = (fieldlen - bricklen) + 1; + printf("%s", p); + while (num_spaces-- != 0) + printf(" "); + if (status->port || status->rdma_port) { + if (status->online) + cli_out("%-10d%-11d%-8c%-5s", status->port, + status->rdma_port, status->online ? 'Y' : 'N', + status->pid_str); + else + cli_out("%-10s%-11s%-8c%-5s", "N/A", "N/A", + status->online ? 'Y' : 'N', status->pid_str); + } else + cli_out("%-10s%-11s%-8c%-5s", "N/A", "N/A", + status->online ? 'Y' : 'N', status->pid_str); + bricklen = 0; } + } - proc = &cli_rpc_prog->proctable[GF1_CLI_LOG_FILENAME]; + return 0; +} - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; +#define NEEDS_GLFS_HEAL(op) \ + ((op == GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE) || \ + (op == GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME) || \ + (op == GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK) || \ + (op == GF_SHD_OP_INDEX_SUMMARY) || (op == GF_SHD_OP_SPLIT_BRAIN_FILES) || \ + (op == GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE) || \ + (op == GF_SHD_OP_HEAL_SUMMARY)) - ret = cli_cmd_log_filename_parse (words, wordcount, &options); - if (ret) - goto out; +int +cli_launch_glfs_heal(int heal_op, dict_t *options) +{ + char buff[PATH_MAX] = {0}; + runner_t runner = {0}; + char *filename = NULL; + char *hostname = NULL; + char *path = NULL; + char *volname = NULL; + char *out = NULL; + int ret = 0; + + runinit(&runner); + ret = dict_get_str(options, "volname", &volname); + runner_add_args(&runner, GLFSHEAL_PREFIX "/glfsheal", volname, NULL); + runner_redir(&runner, STDOUT_FILENO, RUN_PIPE); + + switch (heal_op) { + case GF_SHD_OP_INDEX_SUMMARY: + if (global_state->mode & GLUSTER_MODE_XML) { + runner_add_args(&runner, "--xml", NULL); + } + break; + case GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE: + ret = dict_get_str(options, "file", &filename); + runner_add_args(&runner, "bigger-file", filename, NULL); + break; + case GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME: + ret = dict_get_str(options, "file", &filename); + runner_add_args(&runner, "latest-mtime", filename, NULL); + break; + case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK: + ret = dict_get_str(options, "heal-source-hostname", &hostname); + ret = dict_get_str(options, "heal-source-brickpath", &path); + runner_add_args(&runner, "source-brick", NULL); + runner_argprintf(&runner, "%s:%s", hostname, path); + if (dict_get_str(options, "file", &filename) == 0) + runner_argprintf(&runner, "%s", filename); + break; + case GF_SHD_OP_SPLIT_BRAIN_FILES: + runner_add_args(&runner, "split-brain-info", NULL); + if (global_state->mode & GLUSTER_MODE_XML) { + runner_add_args(&runner, "--xml", NULL); + } + break; + case GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE: + case GF_SHD_OP_GRANULAR_ENTRY_HEAL_DISABLE: + runner_add_args(&runner, "granular-entry-heal-op", NULL); + break; + case GF_SHD_OP_HEAL_SUMMARY: + runner_add_args(&runner, "info-summary", NULL); + if (global_state->mode & GLUSTER_MODE_XML) { + runner_add_args(&runner, "--xml", NULL); + } + break; + default: + ret = -1; + goto out; + } + if (global_state->mode & GLUSTER_MODE_GLFSHEAL_NOLOG) + runner_add_args(&runner, "--nolog", NULL); + ret = runner_start(&runner); + if (ret == -1) + goto out; + while (( + out = fgets(buff, sizeof(buff), runner_chio(&runner, STDOUT_FILENO)))) { + printf("%s", out); + } + ret = runner_end(&runner); - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } +out: + return ret; +} +int +cli_cmd_volume_heal_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + int sent = 0; + int parse_error = 0; + dict_t *options = NULL; + xlator_t *this = NULL; + cli_local_t *local = NULL; + int heal_op = 0; + + this = THIS; + + if (wordcount < 3) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + ret = cli_cmd_volume_heal_options_parse(words, wordcount, &options); + if (ret) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + ret = dict_get_int32(options, "heal-op", &heal_op); + if (ret < 0) + goto out; + if (NEEDS_GLFS_HEAL(heal_op)) { + ret = cli_launch_glfs_heal(heal_op, options); + if (ret < 0) + goto out; + if (heal_op != GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE) + goto out; + } + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_HEAL_VOLUME]; + + frame = create_frame(this, this->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (ret) - cli_out ("setting log filename failed"); + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0) && + !(global_state->mode & GLUSTER_MODE_XML)) { + cli_out("Volume heal failed."); + } + } - if (options) - dict_destroy (options); + if (options) + dict_unref(options); - return ret; -} + CLI_STACK_DESTROY(frame); + return ret; +} -void -cli_cmd_log_locate_usage () +int +cli_cmd_volume_statedump_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume log locate <VOLNAME> [BRICK]"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + + if (wordcount < 3) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + if (wordcount >= 3) { + ret = cli_cmd_volume_statedump_options_parse(words, wordcount, + &options); + if (ret) { + parse_error = 1; + gf_log("cli", GF_LOG_ERROR, + "Error parsing " + "statedump options"); + cli_out("Error parsing options"); + cli_usage_out(word->pattern); + } + } + + ret = dict_set_str(options, "volname", (char *)words[2]); + if (ret) + goto out; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATEDUMP_VOLUME]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume statedump failed"); + } + + CLI_STACK_DESTROY(frame); + + return ret; } int -cli_cmd_log_locate_cbk (struct cli_state *state, struct cli_cmd_word *word, +cli_cmd_volume_list_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; - - if (!((wordcount == 4) || (wordcount == 5))) { - cli_cmd_log_locate_usage (); - goto out; - } + int ret = -1; + call_frame_t *frame = NULL; + rpc_clnt_procedure_t *proc = NULL; + int sent = 0; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LIST_VOLUME]; + if (proc->fn) { + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) + goto out; + ret = proc->fn(frame, THIS, NULL); + } - proc = &cli_rpc_prog->proctable[GF1_CLI_LOG_LOCATE]; +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if (sent == 0) + cli_out("Volume list failed"); + } - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + CLI_STACK_DESTROY(frame); - ret = cli_cmd_log_locate_parse (words, wordcount, &options); - if (ret) - goto out; + return ret; +} - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } +int +cli_cmd_volume_clearlocks_cbk(struct cli_state *state, + struct cli_cmd_word *word, const char **words, + int wordcount) +{ + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + + if (wordcount < 7 || wordcount > 8) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + ret = cli_cmd_volume_clrlks_opts_parse(words, wordcount, &options); + if (ret) { + parse_error = 1; + gf_log("cli", GF_LOG_ERROR, + "Error parsing " + "clear-locks options"); + cli_out("Error parsing options"); + cli_usage_out(word->pattern); + } + + ret = dict_set_str(options, "volname", (char *)words[2]); + if (ret) + goto out; + + ret = dict_set_str(options, "path", (char *)words[3]); + if (ret) + goto out; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_CLRLOCKS_VOLUME]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) { + ret = proc->fn(frame, THIS, options); + } out: - if (ret) - cli_out ("getting log file location information failed"); - - if (options) - dict_destroy (options); + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_out("Volume clear-locks failed"); + } + CLI_STACK_DESTROY(frame); - return ret; + return ret; } -void -cli_cmd_log_rotate_usage () +int +cli_cmd_volume_barrier_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - cli_out ("Usage: volume log rotate <VOLNAME> [BRICK]"); + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_error = 0; + cli_local_t *local = NULL; + + if (wordcount != 4) { + cli_usage_out(word->pattern); + parse_error = 1; + goto out; + } + + options = dict_new(); + if (!options) { + ret = -1; + goto out; + } + ret = dict_set_str(options, "volname", (char *)words[2]); + if (ret) + goto out; + + ret = dict_set_str(options, "barrier", (char *)words[3]); + if (ret) + goto out; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_BARRIER_VOLUME]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); + + if (proc->fn) + ret = proc->fn(frame, THIS, options); + +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_error == 0)) + cli_err("Volume barrier failed"); + } + CLI_STACK_DESTROY(frame); + + return ret; } int -cli_cmd_log_rotate_cbk (struct cli_state *state, struct cli_cmd_word *word, - const char **words, int wordcount) +cli_cmd_volume_getopt_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) { - int ret = -1; - rpc_clnt_procedure_t *proc = NULL; - call_frame_t *frame = NULL; - dict_t *options = NULL; + int ret = -1; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + dict_t *options = NULL; + int sent = 0; + int parse_err = 0; + cli_local_t *local = NULL; - if (!((wordcount == 4) || (wordcount == 5))) { - cli_cmd_log_rotate_usage (); - goto out; - } + if (wordcount != 4) { + cli_usage_out(word->pattern); + parse_err = 1; + goto out; + } - proc = &cli_rpc_prog->proctable[GF1_CLI_LOG_ROTATE]; + options = dict_new(); + if (!options) + goto out; - frame = create_frame (THIS, THIS->ctx->pool); - if (!frame) - goto out; + ret = dict_set_str(options, "volname", (char *)words[2]); + if (ret) + goto out; - ret = cli_cmd_log_rotate_parse (words, wordcount, &options); - if (ret) - goto out; + ret = dict_set_str(options, "key", (char *)words[3]); + if (ret) + goto out; - if (proc->fn) { - ret = proc->fn (frame, THIS, options); - } + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOL_OPT]; -out: - if (ret) - cli_out ("getting log file location information failed"); + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + CLI_LOCAL_INIT(local, words, frame, options); - if (options) - dict_destroy (options); + if (proc->fn) + ret = proc->fn(frame, THIS, options); - return ret; +out: + if (ret) { + cli_cmd_sent_status_get(&sent); + if ((sent == 0) && (parse_err == 0)) + cli_err("Volume get option failed"); + } + CLI_STACK_DESTROY(frame); + return ret; } +/* This is a bit of a hack to display the help. The current bitrot cmd + * format does not work well when registering the cmds. + * Ideally the should have been of the form + * gluster volume bitrot <subcommand> <volumename> ... + */ + +struct cli_cmd bitrot_cmds[] = { + + {"volume bitrot help", cli_cmd_bitrot_help_cbk, + "display help for volume bitrot commands"}, + + {"volume bitrot <VOLNAME> {enable|disable}", NULL, /*cli_cmd_bitrot_cbk,*/ + "Enable/disable bitrot for volume <VOLNAME>"}, + + {"volume bitrot <VOLNAME> signing-time <time-in-secs>", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Waiting time for an object after last fd is closed to start signing " + "process"}, + + {"volume bitrot <VOLNAME> signer-threads <count>", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Number of signing process threads. Usually set to number of available " + "cores"}, + + {"volume bitrot <VOLNAME> scrub-throttle {lazy|normal|aggressive}", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Set the speed of the scrubber for volume <VOLNAME>"}, + + {"volume bitrot <VOLNAME> scrub-frequency {hourly|daily|weekly|biweekly" + "|monthly}", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Set the frequency of the scrubber for volume <VOLNAME>"}, + + {"volume bitrot <VOLNAME> scrub {pause|resume|status|ondemand}", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Pause/resume the scrubber for <VOLNAME>. Status displays the status of " + "the scrubber. ondemand starts the scrubber immediately."}, + + {"volume bitrot <VOLNAME> {enable|disable}\n" + "volume bitrot <VOLNAME> signing-time <time-in-secs>\n" + "volume bitrot <VOLNAME> signer-threads <count>\n" + "volume bitrot <volname> scrub-throttle {lazy|normal|aggressive}\n" + "volume bitrot <volname> scrub-frequency {hourly|daily|weekly|biweekly" + "|monthly}\n" + "volume bitrot <volname> scrub {pause|resume|status|ondemand}", + cli_cmd_bitrot_cbk, NULL}, + + {NULL, NULL, NULL}}; + +struct cli_cmd quota_cmds[] = { + + /* Quota commands */ + {"volume quota help", cli_cmd_quota_help_cbk, + "display help for volume quota commands"}, + + {"volume quota <VOLNAME> {enable|disable|list [<path> ...]| " + "list-objects [<path> ...] | remove <path>| remove-objects <path> | " + "default-soft-limit <percent>}", + cli_cmd_quota_cbk, "Enable/disable and configure quota for <VOLNAME>"}, + + {"volume quota <VOLNAME> {limit-usage <path> <size> [<percent>]}", + cli_cmd_quota_cbk, "Set maximum size for <path> for <VOLNAME>"}, + + {"volume quota <VOLNAME> {limit-objects <path> <number> [<percent>]}", + cli_cmd_quota_cbk, + "Set the maximum number of entries allowed in <path> for <VOLNAME>"}, + + {"volume quota <VOLNAME> {alert-time|soft-timeout|hard-timeout} {<time>}", + cli_cmd_quota_cbk, "Set quota timeout for <VOLNAME>"}, + + {"volume inode-quota <VOLNAME> enable", cli_cmd_quota_cbk, + "Enable/disable inode-quota for <VOLNAME>"}, + + {"volume quota <VOLNAME> {enable|disable|list [<path> ...]| " + "list-objects [<path> ...] | remove <path>| remove-objects <path> | " + "default-soft-limit <percent>}\n" + "volume quota <VOLNAME> {limit-usage <path> <size> [<percent>]}\n" + "volume quota <VOLNAME> {limit-objects <path> <number> [<percent>]}\n" + "volume quota <VOLNAME> {alert-time|soft-timeout|hard-timeout} {<time>}", + cli_cmd_quota_cbk, NULL}, + + {NULL, NULL, NULL}}; struct cli_cmd volume_cmds[] = { - { "volume info [all|<VOLNAME>]", - cli_cmd_volume_info_cbk, - "list information of all volumes"}, + {"volume help", cli_cmd_volume_help_cbk, + "display help for volume commands"}, - { "volume create <NEW-VOLNAME> [stripe <COUNT>] [replica <COUNT>] <NEW-BRICK> ...", - cli_cmd_volume_create_cbk, - "create a new volume of specified type with mentioned bricks"}, + {"volume info [all|<VOLNAME>]", cli_cmd_volume_info_cbk, + "list information of all volumes"}, - { "volume delete <VOLNAME>", - cli_cmd_volume_delete_cbk, - "delete volume specified by <VOLNAME>"}, + {"volume create <NEW-VOLNAME> [stripe <COUNT>] " + "[[replica <COUNT> [arbiter <COUNT>]]|[replica 2 thin-arbiter 1]] " + "[disperse [<COUNT>]] [disperse-data <COUNT>] [redundancy <COUNT>] " + "[transport <tcp|rdma|tcp,rdma>] <NEW-BRICK> <TA-BRICK>" + "... [force]", - { "volume start <VOLNAME>", - cli_cmd_volume_start_cbk, - "start volume specified by <VOLNAME>"}, + cli_cmd_volume_create_cbk, + "create a new volume of specified type with mentioned bricks"}, - { "volume stop <VOLNAME> [force]", - cli_cmd_volume_stop_cbk, - "stop volume specified by <VOLNAME>"}, + {"volume delete <VOLNAME>", cli_cmd_volume_delete_cbk, + "delete volume specified by <VOLNAME>"}, - { "volume rename <VOLNAME> <NEW-VOLNAME>", - cli_cmd_volume_rename_cbk, - "rename volume <VOLNAME> to <NEW-VOLNAME>"}, + {"volume start <VOLNAME> [force]", cli_cmd_volume_start_cbk, + "start volume specified by <VOLNAME>"}, - { "volume add-brick <VOLNAME> [(replica <COUNT>)|(stripe <COUNT>)] <NEW-BRICK> ...", - cli_cmd_volume_add_brick_cbk, - "add brick to volume <VOLNAME>"}, + {"volume stop <VOLNAME> [force]", cli_cmd_volume_stop_cbk, + "stop volume specified by <VOLNAME>"}, - { "volume remove-brick <VOLNAME> [(replica <COUNT>)|(stripe <COUNT>)] <BRICK> ...", - cli_cmd_volume_remove_brick_cbk, - "remove brick from volume <VOLNAME>"}, + /*{ "volume rename <VOLNAME> <NEW-VOLNAME>", + cli_cmd_volume_rename_cbk, + "rename volume <VOLNAME> to <NEW-VOLNAME>"},*/ - { "volume rebalance <VOLNAME> start", - cli_cmd_volume_defrag_cbk, - "start rebalance of volume <VOLNAME>"}, + {"volume add-brick <VOLNAME> [<stripe|replica> <COUNT> " + "[arbiter <COUNT>]] <NEW-BRICK> ... [force]", + cli_cmd_volume_add_brick_cbk, "add brick to volume <VOLNAME>"}, - { "volume rebalance <VOLNAME> stop", - cli_cmd_volume_defrag_cbk, - "stop rebalance of volume <VOLNAME>"}, + {"volume remove-brick <VOLNAME> [replica <COUNT>] <BRICK> ..." + " <start|stop|status|commit|force>", + cli_cmd_volume_remove_brick_cbk, "remove brick from volume <VOLNAME>"}, - { "volume rebalance <VOLNAME> status", - cli_cmd_volume_defrag_cbk, - "rebalance status of volume <VOLNAME>"}, + {"volume rebalance <VOLNAME> {{fix-layout start} | {start " + "[force]|stop|status}}", + cli_cmd_volume_defrag_cbk, "rebalance operations"}, - { "volume replace-brick <VOLNAME> (<BRICK> <NEW-BRICK>) start|pause|abort|status", - cli_cmd_volume_replace_brick_cbk, - "replace-brick operations"}, + {"volume replace-brick <VOLNAME> <SOURCE-BRICK> <NEW-BRICK> " + "{commit force}", + cli_cmd_volume_replace_brick_cbk, "replace-brick operations"}, - { "volume set-transport <VOLNAME> <TRANSPORT-TYPE> [<TRANSPORT-TYPE>] ...", - cli_cmd_volume_set_transport_cbk, - "set transport type for volume <VOLNAME>"}, + /*{ "volume set-transport <VOLNAME> <TRANSPORT-TYPE> [<TRANSPORT-TYPE>] + ...", cli_cmd_volume_set_transport_cbk, "set transport type for volume + <VOLNAME>"},*/ - { "volume set <VOLNAME> <KEY> <VALUE>", - cli_cmd_volume_set_cbk, - "set options for volume <VOLNAME>"}, + {"volume set <VOLNAME> <KEY> <VALUE>", cli_cmd_volume_set_cbk, + "set options for volume <VOLNAME>"}, - { "volume --help", - cli_cmd_volume_help_cbk, - "display help for the volume command"}, + {"volume set <VOLNAME> group <GROUP>", cli_cmd_volume_set_cbk, + "This option can be used for setting multiple pre-defined volume options " + "where group_name is a file under /var/lib/glusterd/groups containing one " + "key value pair per line"}, - { "volume log filename <VOLNAME> [BRICK] <PATH>", - cli_cmd_log_filename_cbk, - "set the log file for corresponding volume/brick"}, + {"volume log <VOLNAME> rotate [BRICK]", cli_cmd_log_rotate_cbk, + "rotate the log file for corresponding volume/brick"}, - { "volume log locate <VOLNAME> [BRICK]", - cli_cmd_log_locate_cbk, - "locate the log file for corresponding volume/brick"}, + {"volume sync <HOSTNAME> [all|<VOLNAME>]", cli_cmd_sync_volume_cbk, + "sync the volume information from a peer"}, - { "volume log rotate <VOLNAME> [BRICK]", - cli_cmd_log_rotate_cbk, - "rotate the log file for corresponding volume/brick"}, + {"volume reset <VOLNAME> [option] [force]", cli_cmd_volume_reset_cbk, + "reset all the reconfigured options"}, - { NULL, NULL, NULL } -}; +#if (SYNCDAEMON_COMPILE) + {"volume " GEOREP " [<MASTER-VOLNAME>] [<SLAVE-IP>]::[<SLAVE-VOLNAME>] {" + "\\\n create [[ssh-port n] [[no-verify] \\\n | [push-pem]]] [force] \\\n" + " | start [force] \\\n | stop [force] \\\n | pause [force] \\\n | resume " + "[force] \\\n" + " | config [[[\\!]<option>] [<value>]] \\\n | status " + "[detail] \\\n | delete [reset-sync-time]} ", + cli_cmd_volume_gsync_set_cbk, "Geo-sync operations", + cli_cmd_check_gsync_exists_cbk}, +#endif + + {"volume profile <VOLNAME> {start|info [peek|incremental " + "[peek]|cumulative|clear]|stop} [nfs]", + cli_cmd_volume_profile_cbk, "volume profile operations"}, + + {"volume top <VOLNAME> {open|read|write|opendir|readdir|clear} [nfs|brick " + "<brick>] [list-cnt <value>] | " + "{read-perf|write-perf} [bs <size> count <count>] " + "[brick <brick>] [list-cnt <value>]", + cli_cmd_volume_top_cbk, "volume top operations"}, + + {"volume status [all | <VOLNAME> [nfs|shd|<BRICK>|quotad]]" + " [detail|clients|mem|inode|fd|callpool|tasks|client-list]", + cli_cmd_volume_status_cbk, + "display status of all or specified volume(s)/brick"}, + + {"volume heal <VOLNAME> [enable | disable | full |" + "statistics [heal-count [replica <HOSTNAME:BRICKNAME>]] |" + "info [summary | split-brain] |" + "split-brain {bigger-file <FILE> | latest-mtime <FILE> |" + "source-brick <HOSTNAME:BRICKNAME> [<FILE>]} |" + "granular-entry-heal {enable | disable}]", + cli_cmd_volume_heal_cbk, + "self-heal commands on volume specified by <VOLNAME>"}, + + {"volume statedump <VOLNAME> [[nfs|quotad] [all|mem|iobuf|callpool|" + "priv|fd|inode|history]... | [client <hostname:process-id>]]", + cli_cmd_volume_statedump_cbk, "perform statedump on bricks"}, + + {"volume list", cli_cmd_volume_list_cbk, "list all volumes in cluster"}, + + {"volume clear-locks <VOLNAME> <path> kind {blocked|granted|all}" + "{inode [range]|entry [basename]|posix [range]}", + cli_cmd_volume_clearlocks_cbk, "Clear locks held on path"}, + {"volume barrier <VOLNAME> {enable|disable}", cli_cmd_volume_barrier_cbk, + "Barrier/unbarrier file operations on a volume"}, + {"volume get <VOLNAME|all> <key|all>", cli_cmd_volume_getopt_cbk, + "Get the value of the all options or given option for volume <VOLNAME>" + " or all option. gluster volume get all all is to get all global " + "options"}, + + {"volume reset-brick <VOLNAME> <SOURCE-BRICK> {{start} |" + " {<NEW-BRICK> commit}}", + cli_cmd_volume_reset_brick_cbk, "reset-brick operations"}, + + {NULL, NULL, NULL}}; int -cli_cmd_volume_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, - const char **words, int wordcount) +cli_cmd_quota_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount) { - struct cli_cmd *cmd = NULL; + struct cli_cmd *cmd = NULL; + struct cli_cmd *quota_cmd = NULL; + int count = 0; - for (cmd = volume_cmds; cmd->pattern; cmd++) - cli_out ("%s - %s", cmd->pattern, cmd->desc); + cmd = GF_MALLOC(sizeof(quota_cmds), cli_mt_cli_cmd); + memcpy(cmd, quota_cmds, sizeof(quota_cmds)); + count = (sizeof(quota_cmds) / sizeof(struct cli_cmd)); + cli_cmd_sort(cmd, count); + cli_out("\ngluster quota commands"); + cli_out("=======================\n"); - if (!state->rl_enabled) - exit (0); + for (quota_cmd = cmd; quota_cmd->pattern; quota_cmd++) + if ((_gf_false == quota_cmd->disable) && (quota_cmd->desc)) + cli_out("%s - %s", quota_cmd->pattern, quota_cmd->desc); - return 0; + cli_out("\n"); + GF_FREE(cmd); + + return 0; } int -cli_cmd_volume_register (struct cli_state *state) +cli_cmd_bitrot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount) { - int ret = 0; - struct cli_cmd *cmd = NULL; + struct cli_cmd *cmd = NULL; + struct cli_cmd *bitrot_cmd = NULL; + int count = 0; + + cmd = GF_MALLOC(sizeof(bitrot_cmds), cli_mt_cli_cmd); + memcpy(cmd, bitrot_cmds, sizeof(bitrot_cmds)); + count = (sizeof(bitrot_cmds) / sizeof(struct cli_cmd)); + cli_cmd_sort(cmd, count); + + cli_out("\ngluster bitrot commands"); + cli_out("========================\n"); + + for (bitrot_cmd = cmd; bitrot_cmd->pattern; bitrot_cmd++) + if ((_gf_false == bitrot_cmd->disable) && (bitrot_cmd->desc)) + cli_out("%s - %s", bitrot_cmd->pattern, bitrot_cmd->desc); + + cli_out("\n"); + GF_FREE(cmd); + + return 0; +} + +int +cli_cmd_volume_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount) +{ + struct cli_cmd *cmd = NULL; + struct cli_cmd *vol_cmd = NULL; + int count = 0; + + cmd = GF_MALLOC(sizeof(volume_cmds), cli_mt_cli_cmd); + memcpy(cmd, volume_cmds, sizeof(volume_cmds)); + count = (sizeof(volume_cmds) / sizeof(struct cli_cmd)); + cli_cmd_sort(cmd, count); + + cli_out("\ngluster volume commands"); + cli_out("========================\n"); + + for (vol_cmd = cmd; vol_cmd->pattern; vol_cmd++) + if (_gf_false == vol_cmd->disable) + cli_out("%s - %s", vol_cmd->pattern, vol_cmd->desc); + + cli_out("\n"); + GF_FREE(cmd); + return 0; +} + +int +cli_cmd_volume_register(struct cli_state *state) +{ + int ret = 0; + struct cli_cmd *cmd = NULL; + + for (cmd = volume_cmds; cmd->pattern; cmd++) { + ret = cli_cmd_register(&state->tree, cmd); + if (ret) + goto out; + } + + for (cmd = bitrot_cmds; cmd->pattern; cmd++) { + ret = cli_cmd_register(&state->tree, cmd); + if (ret) + goto out; + } + + for (cmd = quota_cmds; cmd->pattern; cmd++) { + ret = cli_cmd_register(&state->tree, cmd); + if (ret) + goto out; + } - for (cmd = volume_cmds; cmd->pattern; cmd++) { - ret = cli_cmd_register (&state->tree, cmd->pattern, cmd->cbk, - cmd->desc); - if (ret) - goto out; - } out: - return ret; + return ret; +} + +static int +gf_asprintf_append(char **string_ptr, const char *format, ...) +{ + va_list arg; + int rv = 0; + char *tmp = *string_ptr; + + va_start(arg, format); + rv = gf_vasprintf(string_ptr, format, arg); + va_end(arg); + + if (tmp) + GF_FREE(tmp); + + return rv; } |
