mirror of
https://github.com/gluster/glusterfs.git
synced 2026-02-06 18:48:16 +01:00
GCC and Clang communities are hinting that in versions 14 and 16 respectively they will deprecate or disable legacy C89 features, e.g. K&R1 style function definitions, among others. In parallel, Fedora is going to start enforcing C99 as the minimum, expected to land in F40. (I.e. around Spring 2024 IIRC.) Currently Fedora is recommending that use of -Werror=implicit-int, -Werror=implicit-function-declaration, -Werror=int-conversion, -Werror=strict-prototypes, and -Werror=old-style-definition a set of options to build with in the mean time to clean up code. This change fixes a subset of the errors found when compiling with this set of options. Change-Id: I2025e39566aeb1f68836226d6d18c43418ca7f95 Signed-off-by: Kaleb S. KEITHLEY <kkeithle@redhat.com>
3234 lines
86 KiB
C
3234 lines
86 KiB
C
/*
|
|
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 <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 <glusterfs/run.h>
|
|
#include <glusterfs/syscall.h>
|
|
#include <glusterfs/events.h>
|
|
|
|
extern rpc_clnt_prog_t cli_quotad_clnt;
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
int
|
|
cli_cmd_sync_volume_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 *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);
|
|
if (dict)
|
|
dict_unref(dict);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_create_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;
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_delete_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;
|
|
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 (dict)
|
|
dict_unref(dict);
|
|
|
|
if (ret == 0 && GF_ANSWER_YES == answer) {
|
|
gf_event(EVENT_VOLUME_DELETE, "name=%s", (char *)words[2]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
dict = dict_new();
|
|
if (!dict) {
|
|
goto out;
|
|
}
|
|
|
|
ret = dict_set_str(dict, "volname", (char *)words[2]);
|
|
if (ret) {
|
|
gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
|
|
goto out;
|
|
}
|
|
|
|
ret = dict_set_int32(dict, "flags", flags);
|
|
if (ret) {
|
|
gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
|
|
goto out;
|
|
}
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_START_VOLUME];
|
|
|
|
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 start failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (dict)
|
|
dict_unref(dict);
|
|
|
|
if (ret == 0) {
|
|
gf_event(EVENT_VOLUME_START, "name=%s;force=%d", (char *)words[2],
|
|
(flags & GF_CLI_FLAG_OP_FORCE));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gf_answer_t
|
|
cli_cmd_get_confirmation(struct cli_state *state, const char *question)
|
|
{
|
|
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_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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
ret = dict_set_int32(dict, "flags", flags);
|
|
if (ret) {
|
|
gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
|
|
goto out;
|
|
}
|
|
|
|
answer = cli_cmd_get_confirmation(state, question);
|
|
|
|
if (GF_ANSWER_NO == answer) {
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STOP_VOLUME];
|
|
|
|
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 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_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;
|
|
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
|
|
|
|
ret = cli_cmd_volume_defrag_parse(words, wordcount, &dict);
|
|
|
|
if (ret) {
|
|
cli_usage_out(word->pattern);
|
|
parse_error = 1;
|
|
}
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DEFRAG_VOLUME];
|
|
|
|
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 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;
|
|
}
|
|
|
|
if (event != EVENT_LAST)
|
|
gf_event(event, "volume=%s", (char *)words[2]);
|
|
#endif
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (dict)
|
|
dict_unref(dict);
|
|
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_profile_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;
|
|
|
|
ret = cli_cmd_volume_profile_parse(words, wordcount, &options);
|
|
|
|
if (ret) {
|
|
cli_usage_out(word->pattern);
|
|
parse_error = 1;
|
|
goto out;
|
|
}
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_PROFILE_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, 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 profile failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_set_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;
|
|
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];
|
|
|
|
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);
|
|
|
|
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 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gf_event(EVENT_VOLUME_SET, "name=%s;options=%s", (char *)words[2],
|
|
opts_str);
|
|
|
|
/* 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
|
|
|
|
end:
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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:
|
|
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;
|
|
cli_local_t *local = NULL;
|
|
|
|
#if (USE_EVENTS)
|
|
char *event_str = NULL;
|
|
char *bricks = NULL;
|
|
const char *eventstrformat = "volume=%s;bricks=%s";
|
|
#endif
|
|
|
|
ret = cli_cmd_volume_add_brick_parse(state, words, wordcount, &options, 0);
|
|
if (ret) {
|
|
cli_usage_out(word->pattern);
|
|
parse_error = 1;
|
|
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);
|
|
}
|
|
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int
|
|
cli_cmd_quota_handle_list_all(cli_state_t *state, const char **words,
|
|
dict_t *options)
|
|
{
|
|
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 (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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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 = 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));
|
|
}
|
|
|
|
dict_del(xdata, "gfid");
|
|
all_failed = all_failed && ret;
|
|
}
|
|
|
|
if (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;
|
|
}
|
|
}
|
|
|
|
if (count > 0) {
|
|
ret = all_failed ? -1 : 0;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
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 (fd != -1) {
|
|
sys_close(fd);
|
|
}
|
|
|
|
if (ret) {
|
|
gf_log("cli", GF_LOG_ERROR,
|
|
"Could not fetch and display quota"
|
|
" limits");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (xdata)
|
|
dict_unref(xdata);
|
|
|
|
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 (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 = "";
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (event_type > -1)
|
|
gf_event(event_type, "%s", events_str);
|
|
|
|
if (events_str)
|
|
GF_FREE(events_str);
|
|
}
|
|
#endif
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
out2:
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_quota_cbk(struct cli_state *state, struct cli_cmd_word *word,
|
|
const char **words, int wordcount)
|
|
{
|
|
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(state, 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");
|
|
}
|
|
|
|
/* 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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_remove_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;
|
|
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_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 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];
|
|
|
|
ret = cli_cmd_volume_reset_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) {
|
|
gf_log("cli", GF_LOG_ERROR,
|
|
"Failed to set ignore-"
|
|
"partition 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 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]);
|
|
}
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_replace_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;
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_top_parse(words, wordcount, &options);
|
|
|
|
if (ret) {
|
|
parse_error = 1;
|
|
cli_usage_out(word->pattern);
|
|
goto out;
|
|
}
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_TOP_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, 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 top failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if (SYNCDAEMON_COMPILE)
|
|
static int
|
|
cli_check_gsync_present(void)
|
|
{
|
|
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;
|
|
}
|
|
} 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_check_gsync_exists_cbk(struct cli_cmd *this)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = cli_check_gsync_present();
|
|
if (ret)
|
|
this->disable = _gf_true;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
cli_cmd_volume_gsync_set_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;
|
|
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[GLUSTER_CLI_GSYNC_SET];
|
|
|
|
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;
|
|
}
|
|
|
|
frame = create_frame(THIS, THIS->ctx->pool);
|
|
if (frame == NULL) {
|
|
ret = -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(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 Primary and Secondary Info */
|
|
ret1 = dict_get_str(options, "primary", &tmp);
|
|
if (ret1)
|
|
tmp = "";
|
|
gf_asprintf_append(&events_str, "%sprimary=%s;", events_str, tmp);
|
|
|
|
ret1 = dict_get_str(options, "secondary", &tmp);
|
|
if (ret1)
|
|
tmp = "";
|
|
gf_asprintf_append(&events_str, "%ssecondary=%s", events_str, tmp);
|
|
|
|
gf_event(event_type, "%s", events_str);
|
|
}
|
|
|
|
/* Allocated by gf_strdup and gf_asprintf */
|
|
if (events_str)
|
|
GF_FREE(events_str);
|
|
}
|
|
#endif
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
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);
|
|
if (dict)
|
|
dict_unref(dict);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_get_detail_status(dict_t *dict, int i, cli_volume_status_t *status)
|
|
{
|
|
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_print_detailed_status(cli_volume_status_t *status)
|
|
{
|
|
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_print_brick_status(cli_volume_status_t *status)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#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))
|
|
|
|
int
|
|
cli_launch_glfs_heal(cli_state_t *state, 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 (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 (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 (state->mode & GLUSTER_MODE_XML) {
|
|
runner_add_args(&runner, "--xml", NULL);
|
|
}
|
|
break;
|
|
default:
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (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);
|
|
|
|
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(state, 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_cmd_sent_status_get(&sent);
|
|
if ((sent == 0) && (parse_error == 0) &&
|
|
!(state->mode & GLUSTER_MODE_XML)) {
|
|
cli_out("Volume heal failed.");
|
|
}
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_statedump_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 < 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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_list_cbk(struct cli_state *state, struct cli_cmd_word *word,
|
|
const char **words, int wordcount)
|
|
{
|
|
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);
|
|
}
|
|
|
|
out:
|
|
if (ret) {
|
|
cli_cmd_sent_status_get(&sent);
|
|
if (sent == 0)
|
|
cli_out("Volume list failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_cmd_sent_status_get(&sent);
|
|
if ((sent == 0) && (parse_error == 0))
|
|
cli_out("Volume clear-locks failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
cli_cmd_volume_barrier_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) {
|
|
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);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
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 sent = 0;
|
|
int parse_err = 0;
|
|
cli_local_t *local = NULL;
|
|
|
|
if (wordcount != 4) {
|
|
cli_usage_out(word->pattern);
|
|
parse_err = 1;
|
|
goto out;
|
|
}
|
|
|
|
options = dict_new();
|
|
if (!options)
|
|
goto out;
|
|
|
|
ret = dict_set_str(options, "volname", (char *)words[2]);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = dict_set_str(options, "key", (char *)words[3]);
|
|
if (ret)
|
|
goto out;
|
|
|
|
proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOL_OPT];
|
|
|
|
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_err == 0))
|
|
cli_err("Volume get option failed");
|
|
}
|
|
|
|
CLI_STACK_DESTROY(frame);
|
|
if (options)
|
|
dict_unref(options);
|
|
|
|
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 help", cli_cmd_volume_help_cbk,
|
|
"display help for volume commands"},
|
|
|
|
{"volume info [all|<VOLNAME>]", cli_cmd_volume_info_cbk,
|
|
"list information of all volumes"},
|
|
|
|
{"volume create <NEW-VOLNAME> "
|
|
"[[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]",
|
|
|
|
cli_cmd_volume_create_cbk,
|
|
"create a new volume of specified type with mentioned bricks"},
|
|
|
|
{"volume delete <VOLNAME>", cli_cmd_volume_delete_cbk,
|
|
"delete volume specified by <VOLNAME>"},
|
|
|
|
{"volume start <VOLNAME> [force]", cli_cmd_volume_start_cbk,
|
|
"start volume specified by <VOLNAME>"},
|
|
|
|
{"volume stop <VOLNAME> [force]", cli_cmd_volume_stop_cbk,
|
|
"stop volume specified by <VOLNAME>"},
|
|
|
|
{"volume add-brick <VOLNAME> [<replica> <COUNT> "
|
|
"[arbiter <COUNT>]] <NEW-BRICK> ... [force]",
|
|
cli_cmd_volume_add_brick_cbk, "add brick to 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> {{fix-layout start} | {start "
|
|
"[force]|stop|status}}",
|
|
cli_cmd_volume_defrag_cbk, "rebalance operations"},
|
|
|
|
{"volume replace-brick <VOLNAME> <SOURCE-BRICK> <NEW-BRICK> "
|
|
"{commit force}",
|
|
cli_cmd_volume_replace_brick_cbk, "replace-brick operations"},
|
|
|
|
{"volume set <VOLNAME> <KEY> <VALUE>", cli_cmd_volume_set_cbk,
|
|
"set options for volume <VOLNAME>"},
|
|
|
|
{"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 <VOLNAME> rotate [BRICK]", cli_cmd_log_rotate_cbk,
|
|
"rotate 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 reset <VOLNAME> [option] [force]", cli_cmd_volume_reset_cbk,
|
|
"reset all the reconfigured options"},
|
|
|
|
#if (SYNCDAEMON_COMPILE)
|
|
{"volume " GEOREP
|
|
" [<PRIMARY-VOLNAME>] [<SECONDARY-IP>]::[<SECONDARY-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_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 *quota_cmd = NULL;
|
|
int count = 0;
|
|
|
|
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");
|
|
|
|
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);
|
|
|
|
cli_out("\n");
|
|
GF_FREE(cmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cli_cmd_bitrot_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 *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;
|
|
}
|
|
|
|
out:
|
|
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 (rv >= 0)
|
|
GF_FREE(tmp);
|
|
|
|
return rv;
|
|
}
|