diff --git a/Makefile.am b/Makefile.am index e0c795f..613382f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ SOURCES = site.h EXTRA_DIST = autogen.sh \ COPYING-GPLV2 COPYING-LGPLV3 COMMITMENT \ INSTALL README.md AUTHORS THANKS NEWS \ - glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in \ + glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in libgfdb.pc.in \ run-tests.sh \ build-aux/pkg-version \ contrib/umountd \ @@ -15,8 +15,12 @@ SUBDIRS = $(ARGP_STANDALONE_DIR) rpc/xdr/gen libglusterfs rpc api xlators \ pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = glusterfs-api.pc libgfchangelog.pc +if USE_GFDB +pkgconfig_DATA += libgfdb.pc +endif -CLEANFILES = glusterfs-api.pc libgfchangelog.pc contrib/umountd/Makefile +CLEANFILES = glusterfs-api.pc libgfchangelog.pc libgfdb.pc \ + contrib/umountd/Makefile gitclean: distclean find . -name Makefile.in -exec rm -f {} \; diff --git a/README b/README new file mode 100644 index 0000000..44a118b --- /dev/null +++ b/README @@ -0,0 +1,9 @@ + +'master' branch is just dummy branch in downstream. Any reference to 'upstream' +will point to http://git.gluster.org. + +You can checkout the release specific branch by running below command + bash$ git checkout -t -b rhs-x.y origin/rhs-x.y + +Happy Hacking!! + diff --git a/api/examples/glfsxmp.c b/api/examples/glfsxmp.c index 9d96eea..33f44df 100644 --- a/api/examples/glfsxmp.c +++ b/api/examples/glfsxmp.c @@ -1573,6 +1573,11 @@ main(int argc, char *argv[]) ret = glfs_set_logging(fs2, "/dev/stderr", 7); + ret = glfs_set_statedump_path(fs2, "/tmp"); + if (ret) { + fprintf(stderr, "glfs_set_statedump_path: %s\n", strerror(errno)); + } + ret = glfs_init(fs2); fprintf(stderr, "glfs_init: returned %d\n", ret); diff --git a/api/src/gfapi.aliases b/api/src/gfapi.aliases index 25e2d74..692ae13 100644 --- a/api/src/gfapi.aliases +++ b/api/src/gfapi.aliases @@ -172,6 +172,7 @@ _pub_glfs_upcall_lease_get_lease_type _glfs_upcall_lease_get_lease_type$GFAPI_4. _priv_glfs_statx _glfs_statx$GFAPI_6.0 _priv_glfs_iatt_from_statx _glfs_iatt_from_statx$GFAPI_6.0 +_priv_glfs_setfspid _glfs_setfspid$GFAPI_6.1 _pub_glfs_read_async _glfs_read_async$GFAPI_6.0 _pub_glfs_write_async _glfs_write_async$GFAPI_6.0 @@ -194,3 +195,5 @@ _pub_glfs_zerofill_async _glfs_zerofill_async$GFAPI_6.0 _pub_glfs_copy_file_range _glfs_copy_file_range$GFAPI_6.0 _pub_glfs_fsetattr _glfs_fsetattr$GFAPI_6.0 _pub_glfs_setattr _glfs_setattr$GFAPI_6.0 + +_pub_glfs_set_statedump_path _glfs_set_statedump_path@GFAPI_6.4 diff --git a/api/src/gfapi.map b/api/src/gfapi.map index bb201c7..df65837 100644 --- a/api/src/gfapi.map +++ b/api/src/gfapi.map @@ -267,3 +267,12 @@ GFAPI_6.0 { glfs_fsetattr; } GFAPI_PRIVATE_6.0; +GFAPI_PRIVATE_6.1 { + global: + glfs_setfspid; +} GFAPI_6.0; + +GFAPI_6.4 { + global: + glfs_set_statedump_path; +} GFAPI_PRIVATE_6.1; diff --git a/api/src/glfs-fops.c b/api/src/glfs-fops.c index 88cd32b..e6adea5 100644 --- a/api/src/glfs-fops.c +++ b/api/src/glfs-fops.c @@ -34,7 +34,7 @@ struct upcall_syncop_args { struct glfs *fs; - struct gf_upcall *upcall_data; + struct gf_upcall upcall_data; }; #define READDIRBUF_SIZE (sizeof(struct dirent) + GF_NAME_MAX + 1) @@ -5714,16 +5714,59 @@ out: } static int +upcall_syncop_args_free(struct upcall_syncop_args *args) +{ + dict_t *dict = NULL; + struct gf_upcall *upcall_data = NULL; + + if (args) { + upcall_data = &args->upcall_data; + switch (upcall_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + dict = ((struct gf_upcall_cache_invalidation *)(upcall_data + ->data)) + ->dict; + break; + case GF_UPCALL_RECALL_LEASE: + dict = ((struct gf_upcall_recall_lease *)(upcall_data->data)) + ->dict; + break; + } + if (dict) + dict_unref(dict); + + GF_FREE(upcall_data->client_uid); + GF_FREE(upcall_data->data); + } + GF_FREE(args); + return 0; +} + +static int +glfs_upcall_syncop_cbk(int ret, call_frame_t *frame, void *opaque) +{ + struct upcall_syncop_args *args = opaque; + + (void)upcall_syncop_args_free(args); + + return 0; +} + +static int glfs_cbk_upcall_syncop(void *opaque) { struct upcall_syncop_args *args = opaque; - int ret = -1; + struct gf_upcall *upcall_data = NULL; struct glfs_upcall *up_arg = NULL; struct glfs *fs; - struct gf_upcall *upcall_data; + int ret = -1; fs = args->fs; - upcall_data = args->upcall_data; + upcall_data = &args->upcall_data; + + if (!upcall_data) { + goto out; + } up_arg = GLFS_CALLOC(1, sizeof(struct gf_upcall), glfs_release_upcall, glfs_mt_upcall_entry_t); @@ -5744,41 +5787,154 @@ glfs_cbk_upcall_syncop(void *opaque) errno = EINVAL; } - if (!ret && (up_arg->reason != GLFS_UPCALL_EVENT_NULL)) { - /* It could so happen that the file which got - * upcall notification may have got deleted by - * the same client. In such cases up_arg->reason - * is set to GLFS_UPCALL_EVENT_NULL. No need to - * send upcall then */ - (fs->up_cbk)(up_arg, fs->up_data); - } else if (up_arg->reason == GLFS_UPCALL_EVENT_NULL) { + /* It could so happen that the file which got + * upcall notification may have got deleted by + * the same client. In such cases up_arg->reason + * is set to GLFS_UPCALL_EVENT_NULL. No need to + * send upcall then + */ + if (up_arg->reason == GLFS_UPCALL_EVENT_NULL) { gf_msg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_INVALID_ENTRY, "Upcall_EVENT_NULL received. Skipping it."); + ret = 0; + GLFS_FREE(up_arg); goto out; - } else { + } else if (ret) { gf_msg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ENTRY, "Upcall entry validation failed."); goto out; } + if (fs->up_cbk && up_arg) + (fs->up_cbk)(up_arg, fs->up_data); + /* application takes care of calling glfs_free on up_arg post * their processing */ - ret = 0; out: - if (ret && up_arg) { - GLFS_FREE(up_arg); + return ret; +} + +static struct gf_upcall_cache_invalidation * +gf_copy_cache_invalidation(struct gf_upcall_cache_invalidation *src) +{ + struct gf_upcall_cache_invalidation *dst = NULL; + + if (!src) + goto out; + + dst = GF_CALLOC(1, sizeof(struct gf_upcall_cache_invalidation), + glfs_mt_upcall_entry_t); + + if (!dst) { + gf_msg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, + "Upcall entry allocation failed."); + goto out; } - return ret; + dst->flags = src->flags; + dst->expire_time_attr = src->expire_time_attr; + dst->stat = src->stat; + dst->p_stat = src->p_stat; + dst->oldp_stat = src->oldp_stat; + + if (src->dict) + dst->dict = dict_copy_with_ref(src->dict, NULL); + + return dst; +out: + return NULL; +} + +static struct gf_upcall_recall_lease * +gf_copy_recall_lease(struct gf_upcall_recall_lease *src) +{ + struct gf_upcall_recall_lease *dst = NULL; + + if (!src) + goto out; + + dst = GF_CALLOC(1, sizeof(struct gf_upcall_recall_lease), + glfs_mt_upcall_entry_t); + + if (!dst) { + gf_msg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, + "Upcall entry allocation failed."); + goto out; + } + + dst->lease_type = src->lease_type; + memcpy(dst->tid, src->tid, 16); + + if (src->dict) + dst->dict = dict_copy_with_ref(src->dict, NULL); + + return dst; +out: + return NULL; +} + +static struct upcall_syncop_args * +upcall_syncop_args_init(struct glfs *fs, struct gf_upcall *upcall_data) +{ + struct upcall_syncop_args *args = NULL; + int ret = -1; + struct gf_upcall *t_data = NULL; + + if (!fs || !upcall_data) + goto out; + + args = GF_CALLOC(1, sizeof(struct upcall_syncop_args), + glfs_mt_upcall_entry_t); + if (!args) { + gf_msg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, + "Upcall syncop args allocation failed."); + goto out; + } + + /* Note: we are not taking any ref on fs here. + * Ideally applications have to unregister for upcall events + * or stop polling for upcall events before performing + * glfs_fini. And as for outstanding synctasks created, we wait + * for all syncenv threads to finish tasks before cleaning up the + * fs->ctx. Hence it seems safe to process these callback + * notification without taking any lock/ref. + */ + args->fs = fs; + t_data = &(args->upcall_data); + t_data->client_uid = gf_strdup(upcall_data->client_uid); + + gf_uuid_copy(t_data->gfid, upcall_data->gfid); + t_data->event_type = upcall_data->event_type; + + switch (t_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + t_data->data = gf_copy_cache_invalidation( + (struct gf_upcall_cache_invalidation *)upcall_data->data); + break; + case GF_UPCALL_RECALL_LEASE: + t_data->data = gf_copy_recall_lease( + (struct gf_upcall_recall_lease *)upcall_data->data); + break; + } + + if (!t_data->data) + goto out; + + return args; +out: + if (ret) { + GF_FREE(args->upcall_data.client_uid); + GF_FREE(args); + } + + return NULL; } static void glfs_cbk_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) { - struct upcall_syncop_args args = { - 0, - }; + struct upcall_syncop_args *args = NULL; int ret = -1; if (!fs || !upcall_data) @@ -5789,16 +5945,19 @@ glfs_cbk_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) goto out; } - args.fs = fs; - args.upcall_data = upcall_data; + args = upcall_syncop_args_init(fs, upcall_data); + + if (!args) + goto out; - ret = synctask_new(THIS->ctx->env, glfs_cbk_upcall_syncop, NULL, NULL, - &args); + ret = synctask_new(THIS->ctx->env, glfs_cbk_upcall_syncop, + glfs_upcall_syncop_cbk, NULL, args); /* should we retry incase of failure? */ if (ret) { gf_msg(THIS->name, GF_LOG_ERROR, errno, API_MSG_UPCALL_SYNCOP_FAILED, "Synctak for Upcall event_type(%d) and gfid(%s) failed", upcall_data->event_type, (char *)(upcall_data->gfid)); + upcall_syncop_args_free(args); } out: diff --git a/api/src/glfs-internal.h b/api/src/glfs-internal.h index 40bbb8a..55401b2 100644 --- a/api/src/glfs-internal.h +++ b/api/src/glfs-internal.h @@ -702,4 +702,10 @@ void glfs_iatt_from_statx(struct iatt *, const struct glfs_stat *) GFAPI_PRIVATE(glfs_iatt_from_statx, 6.0); +/* + * This API is a per thread setting, similar to glfs_setfs{u/g}id, because of + * the call to syncopctx_setfspid. + */ +int +glfs_setfspid(struct glfs *, pid_t) GFAPI_PRIVATE(glfs_setfspid, 6.1); #endif /* !_GLFS_INTERNAL_H */ diff --git a/api/src/glfs-mgmt.c b/api/src/glfs-mgmt.c index d502b4f..7476d5b 100644 --- a/api/src/glfs-mgmt.c +++ b/api/src/glfs-mgmt.c @@ -1015,6 +1015,10 @@ glfs_mgmt_init(struct glfs *fs) if (ctx->mgmt) return 0; + options = dict_new(); + if (!options) + goto out; + if (cmd_args->volfile_server_port) port = cmd_args->volfile_server_port; @@ -1029,11 +1033,11 @@ glfs_mgmt_init(struct glfs *fs) if (cmd_args->volfile_server_transport && !strcmp(cmd_args->volfile_server_transport, "unix")) { - ret = rpc_transport_unix_options_build(&options, host, 0); + ret = rpc_transport_unix_options_build(options, host, 0); } else { xlator_cmdline_option_t *opt = find_xlator_option_in_cmd_args_t( "address-family", cmd_args); - ret = rpc_transport_inet_options_build(&options, host, port, + ret = rpc_transport_inet_options_build(options, host, port, (opt ? opt->value : NULL)); } @@ -1075,5 +1079,7 @@ glfs_mgmt_init(struct glfs *fs) ret = rpc_clnt_start(rpc); out: + if (options) + dict_unref(options); return ret; } diff --git a/api/src/glfs-resolve.c b/api/src/glfs-resolve.c index a79f490..062b7dc 100644 --- a/api/src/glfs-resolve.c +++ b/api/src/glfs-resolve.c @@ -722,6 +722,7 @@ glfs_migrate_fd_safe(struct glfs *fs, xlator_t *newsubvol, fd_t *oldfd) 0, }; char uuid1[64]; + dict_t *xdata = NULL; oldinode = oldfd->inode; oldsubvol = oldinode->table->xl; @@ -730,7 +731,15 @@ glfs_migrate_fd_safe(struct glfs *fs, xlator_t *newsubvol, fd_t *oldfd) return fd_ref(oldfd); if (!oldsubvol->switched) { - ret = syncop_fsync(oldsubvol, oldfd, 0, NULL, NULL, NULL, NULL); + xdata = dict_new(); + if (!xdata || dict_set_int8(xdata, "last-fsync", 1)) { + gf_msg(fs->volname, GF_LOG_WARNING, ENOMEM, API_MSG_FSYNC_FAILED, + "last-fsync set failed on %s graph %s (%d)", + uuid_utoa_r(oldfd->inode->gfid, uuid1), + graphid_str(oldsubvol), oldsubvol->graph->id); + } + + ret = syncop_fsync(oldsubvol, oldfd, 0, NULL, NULL, xdata, NULL); DECODE_SYNCOP_ERR(ret); if (ret) { gf_msg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FSYNC_FAILED, @@ -809,6 +818,9 @@ out: newfd = NULL; } + if (xdata) + dict_unref(xdata); + return newfd; } diff --git a/api/src/glfs.c b/api/src/glfs.c index b741f6e..f36616d 100644 --- a/api/src/glfs.c +++ b/api/src/glfs.c @@ -829,8 +829,7 @@ pub_glfs_new(const char *volname) * Do this as soon as possible in case something else depends on * pool allocations. */ - mem_pools_init_early(); - mem_pools_init_late(); + mem_pools_init(); fs = glfs_new_fs(volname); if (!fs) @@ -1212,6 +1211,7 @@ glusterfs_ctx_destroy(glusterfs_ctx_t *ctx) glusterfs_graph_destroy_residual(trav_graph); } + GF_FREE(ctx->statedump_path); FREE(ctx); return ret; @@ -1461,6 +1461,21 @@ invalid_fs: GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_ipc, 3.12.0); +int +priv_glfs_setfspid(struct glfs *fs, pid_t pid) +{ + cmd_args_t *cmd_args = NULL; + int ret = 0; + + cmd_args = &fs->ctx->cmd_args; + cmd_args->client_pid = pid; + cmd_args->client_pid_set = 1; + ret = syncopctx_setfspid(&pid); + + return ret; +} +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_setfspid, 6.1); + void pub_glfs_free(void *ptr) { @@ -1723,3 +1738,65 @@ invalid_fs: } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_unregister, 3.13.0); + +int +pub_glfs_set_statedump_path(struct glfs *fs, const char *path) +{ + struct stat st; + int ret; + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (!path) { + gf_log("glfs", GF_LOG_ERROR, "path is NULL"); + errno = EINVAL; + goto err; + } + + /* If path is not present OR, if it is directory AND has enough permission + * to create files, then proceed */ + ret = sys_stat(path, &st); + if (ret && errno != ENOENT) { + gf_log("glfs", GF_LOG_ERROR, "%s: not a valid path (%s)", path, + strerror(errno)); + errno = EINVAL; + goto err; + } + + if (!ret) { + /* file is present, now check other things */ + if (!S_ISDIR(st.st_mode)) { + gf_log("glfs", GF_LOG_ERROR, "%s: path is not directory", path); + errno = EINVAL; + goto err; + } + if (sys_access(path, W_OK | X_OK) < 0) { + gf_log("glfs", GF_LOG_ERROR, + "%s: path doesn't have write permission", path); + errno = EPERM; + goto err; + } + } + + /* If set, it needs to be freed, so we don't have leak */ + GF_FREE(fs->ctx->statedump_path); + + fs->ctx->statedump_path = gf_strdup(path); + if (!fs->ctx->statedump_path) { + gf_log("glfs", GF_LOG_ERROR, + "%s: failed to set statedump path, no memory", path); + errno = ENOMEM; + goto err; + } + + __GLFS_EXIT_FS; + + return 0; +err: + __GLFS_EXIT_FS; + +invalid_fs: + return -1; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_statedump_path, 6.4); diff --git a/api/src/glfs.h b/api/src/glfs.h index 6714782..08b6ca0 100644 --- a/api/src/glfs.h +++ b/api/src/glfs.h @@ -1453,5 +1453,33 @@ int glfs_setattr(struct glfs *fs, const char *path, struct glfs_stat *stat, int follow) __THROW GFAPI_PUBLIC(glfs_setattr, 6.0); +/* + SYNOPSIS + + glfs_set_statedump_path: Function to set statedump path. + + DESCRIPTION + + This function is used to set statedump directory + + PARAMETERS + + @fs: The 'virtual mount' object to be configured with the volume + specification file. + + @path: statedump path. Should be a directory. But the API won't fail if the + directory doesn't exist yet, as one may create it later. + + RETURN VALUES + + 0 : Success. + -1 : Failure. @errno will be set with the type of failure. + + */ + +int +glfs_set_statedump_path(struct glfs *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_set_statedump_path, 6.4); + __END_DECLS #endif /* !_GLFS_H */ diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index 6be070f..3e7511f 100644 --- a/cli/src/Makefile.am +++ b/cli/src/Makefile.am @@ -1,6 +1,4 @@ -if WITH_SERVER sbin_PROGRAMS = gluster -endif gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c cli-cmd-global.c \ cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\ diff --git a/cli/src/cli-cmd-global.c b/cli/src/cli-cmd-global.c index d0729ac..270b76f 100644 --- a/cli/src/cli-cmd-global.c +++ b/cli/src/cli-cmd-global.c @@ -36,6 +36,10 @@ int cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount); +int +cli_cmd_ganesha_cbk(struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount); + struct cli_cmd global_cmds[] = { { "global help", @@ -48,6 +52,11 @@ struct cli_cmd global_cmds[] = { cli_cmd_get_state_cbk, "Get local state representation of mentioned daemon", }, + { + "nfs-ganesha {enable| disable} ", + cli_cmd_ganesha_cbk, + "Enable/disable NFS-Ganesha support", + }, {NULL, NULL, NULL}}; int @@ -89,6 +98,54 @@ out: } int +cli_cmd_ganesha_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; + + proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GANESHA]; + + frame = create_frame(THIS, THIS->ctx->pool); + if (!frame) + goto out; + + ret = cli_cmd_ganesha_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; + } + + 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("Setting global option failed"); + } + + CLI_STACK_DESTROY(frame); + return ret; +} + +int cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index d9913f6..5fd05f4 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -603,14 +603,11 @@ cli_cmd_volume_create_parse(struct cli_state *state, const char **words, if (replica_count == 2) { if (strcmp(words[wordcount - 1], "force")) { question = - "Replica 2 volumes are prone" - " to split-brain. Use " + "Support for replica 2 volumes stands deprecated as " + "they are prone to split-brain. Use " "Arbiter or Replica 3 to " - "avoid this. See: " - "http://docs.gluster.org/en/latest/" - "Administrator%20Guide/" - "Split%20brain%20and%20ways%20to%20deal%20with%20it/." - "\nDo you still want to " + "avoid this.\n" + "Do you still want to " "continue?\n"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { @@ -622,6 +619,23 @@ cli_cmd_volume_create_parse(struct cli_state *state, const char **words, } } } + + if (replica_count > 3) { + if (strcmp(words[wordcount - 1], "force")) { + question = + "Volumes with replica count greater than 3 are" + "not supported. \nDo you still want to continue?\n"; + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + gf_log("cli", GF_LOG_ERROR, + "Volume create " + "cancelled, exiting"); + ret = -1; + goto out; + } + } + } + ret = dict_set_int32(dict, "replica-count", replica_count); if (ret) goto out; @@ -1681,11 +1695,15 @@ cli_cmd_volume_set_parse(struct cli_state *state, const char **words, goto out; } } - if ((!strcmp(key, "nfs.disable")) && (!strcmp(value, "off"))) { + if ((strcmp(key, "cluster.brick-multiplex") == 0)) { question = - "Gluster NFS is being deprecated in favor " - "of NFS-Ganesha Enter \"yes\" to continue " - "using Gluster NFS"; + "Brick-multiplexing is supported only for " + "OCS converged or independent mode. Also it is " + "advised to make sure that either all " + "volumes are in stopped state or no bricks " + "are running before this option is modified." + "Do you still want to continue?"; + answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, @@ -1799,9 +1817,9 @@ cli_cmd_volume_add_brick_parse(struct cli_state *state, const char **words, if (count == 2) { if (strcmp(words[wordcount - 1], "force")) { question = - "Replica 2 volumes are prone to " - "split-brain. Use Arbiter or " - "Replica 3 to avaoid this. See: " + "Support for replica 2 volumes stands deprecated as they " + "are prone to split-brain. Use Arbiter or " + "Replica 3 to avoid this. See: " "http://docs.gluster.org/en/latest/Administrator%20Guide/" "Split%20brain%20and%20ways%20to%20deal%20with%20it/." "\nDo you still want to continue?\n"; @@ -1814,6 +1832,20 @@ cli_cmd_volume_add_brick_parse(struct cli_state *state, const char **words, goto out; } } + } else if (count > 3) { + if (strcmp(words[wordcount - 1], "force")) { + question = + "Volumes with replica count greater than 3 are" + "not supported. \nDo you still want to continue?\n"; + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + gf_log("cli", GF_LOG_ERROR, + "add-brick " + "cancelled, exiting"); + ret = -1; + goto out; + } + } } } else if ((strcmp(w, "stripe")) == 0) { cli_err("stripe option not supported"); @@ -2066,9 +2098,9 @@ cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words, if (count == 2) { if (strcmp(words[wordcount - 1], "force")) { ques = - "Replica 2 volumes are prone to " - "split-brain. Use Arbiter or Replica 3 " - "to avaoid this. See: " + "Support for replica 2 volumes stands deprecated as they " + "are prone to split-brain. Use Arbiter or Replica 3 " + "to avoid this. See: " "http://docs.gluster.org/en/latest/Administrator%20Guide/" "Split%20brain%20and%20ways%20to%20deal%20with%20it/." "\nDo you still want to continue?\n"; @@ -2081,6 +2113,20 @@ cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words, goto out; } } + } else if (count > 3) { + if (strcmp(words[wordcount - 1], "force")) { + ques = + "Volumes with replica count greater than 3 are" + "not supported. \nDo you still want to continue?\n"; + answer = cli_cmd_get_confirmation(state, ques); + if (GF_ANSWER_NO == answer) { + gf_log("cli", GF_LOG_ERROR, + "Remove-brick " + "cancelled, exiting"); + ret = -1; + goto out; + } + } } ret = dict_set_int32(dict, "replica-count", count); @@ -2100,8 +2146,6 @@ cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words, wordcount--; if (!strcmp("start", w)) { command = GF_OP_CMD_START; - if (question) - *question = 1; } else if (!strcmp("commit", w)) { command = GF_OP_CMD_COMMIT; } else if (!strcmp("stop", w)) { @@ -2593,8 +2637,6 @@ cli_cmd_log_rotate_parse(const char **words, int wordcount, dict_t **options) if (strcmp("rotate", words[3]) == 0) volname = (char *)words[2]; - else if (strcmp("rotate", words[2]) == 0) - volname = (char *)words[3]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); @@ -5619,7 +5661,7 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options) char *volname = NULL; char *opwords[] = { "enable", "disable", "scrub-throttle", "scrub-frequency", "scrub", - "signing-time", NULL}; + "signing-time", "signer-threads", NULL}; char *scrub_throt_values[] = {"lazy", "normal", "aggressive", NULL}; char *scrub_freq_values[] = {"hourly", "daily", "weekly", "biweekly", "monthly", "minute", NULL}; @@ -5627,6 +5669,7 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options) dict_t *dict = NULL; gf_bitrot_type type = GF_BITROT_OPTION_TYPE_NONE; int32_t expiry_time = 0; + int32_t signer_th_count = 0; GF_ASSERT(words); GF_ASSERT(options); @@ -5807,6 +5850,31 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options) } goto set_type; } + } else if (!strcmp(words[3], "signer-threads")) { + if (!words[4]) { + cli_err( + "Missing signer-thread value for bitrot " + "option"); + ret = -1; + goto out; + } else { + type = GF_BITROT_OPTION_TYPE_SIGNER_THREADS; + + signer_th_count = strtol(words[4], NULL, 0); + if (signer_th_count < 1) { + cli_err("signer-thread count should not be less than 1"); + ret = -1; + goto out; + } + + ret = dict_set_uint32(dict, "signer-threads", + (unsigned int)signer_th_count); + if (ret) { + cli_out("Failed to set dict for bitrot"); + goto out; + } + goto set_type; + } } else { cli_err( "Invalid option %s for bitrot. Please enter valid " @@ -5815,7 +5883,6 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options) ret = -1; goto out; } - set_type: ret = dict_set_int32(dict, "type", type); if (ret < 0) @@ -5832,3 +5899,121 @@ out: return ret; } + +/* Parsing global option for NFS-Ganesha config + * gluster nfs-ganesha enable/disable */ + +int32_t +cli_cmd_ganesha_parse(struct cli_state *state, const char **words, + int wordcount, dict_t **options, char **op_errstr) +{ + dict_t *dict = NULL; + int ret = -1; + char *key = NULL; + char *value = NULL; + char *w = NULL; + char *opwords[] = {"enable", "disable", NULL}; + const char *question = NULL; + gf_answer_t answer = GF_ANSWER_NO; + + GF_ASSERT(words); + GF_ASSERT(options); + + dict = dict_new(); + + if (!dict) + goto out; + + if (wordcount != 2) + goto out; + + key = (char *)words[0]; + value = (char *)words[1]; + + if (!key || !value) { + cli_out("Usage : nfs-ganesha "); + ret = -1; + goto out; + } + + ret = gf_strip_whitespace(value, strlen(value)); + if (ret == -1) + goto out; + + if (strcmp(key, "nfs-ganesha")) { + gf_asprintf(op_errstr, + "Global option: error: ' %s '" + "is not a valid global option.", + key); + ret = -1; + goto out; + } + + w = str_getunamb(value, opwords); + if (!w) { + cli_out( + "Invalid global option \n" + "Usage : nfs-ganesha "); + ret = -1; + goto out; + } + + if (strcmp(value, "enable") == 0) { + question = + "Enabling NFS-Ganesha requires Gluster-NFS to be " + "disabled across the trusted pool. Do you " + "still want to continue?\n"; + } else if (strcmp(value, "disable") == 0) { + question = + "Disabling NFS-Ganesha will tear down the entire " + "ganesha cluster across the trusted pool. Do you " + "still want to continue?\n"; + } else { + ret = -1; + goto out; + } + answer = cli_cmd_get_confirmation(state, question); + if (GF_ANSWER_NO == answer) { + gf_log("cli", GF_LOG_ERROR, + "Global operation " + "cancelled, exiting"); + ret = -1; + goto out; + } + cli_out("This will take a few minutes to complete. Please wait .."); + + ret = dict_set_str(dict, "key", key); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set on key failed"); + goto out; + } + + ret = dict_set_str(dict, "value", value); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, "dict set on value failed"); + goto out; + } + + ret = dict_set_str(dict, "globalname", "All"); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, + "dict set on global" + " key failed."); + goto out; + } + + ret = dict_set_int32(dict, "hold_global_locks", _gf_true); + if (ret) { + gf_log(THIS->name, GF_LOG_ERROR, + "dict set on global key " + "failed."); + goto out; + } + + *options = dict; +out: + if (ret) + dict_unref(dict); + + return ret; +} diff --git a/cli/src/cli-cmd-system.c b/cli/src/cli-cmd-system.c index 8cd1542..cb3a9ea 100644 --- a/cli/src/cli-cmd-system.c +++ b/cli/src/cli-cmd-system.c @@ -446,7 +446,7 @@ cli_cmd_sys_exec_cbk(struct cli_state *state, struct cli_cmd_word *word, dict_t *dict = NULL; cli_local_t *local = NULL; - if (wordcount < 3) { + if ((wordcount < 3) || (words[2] == NULL)) { cli_usage_out(word->pattern); goto out; } diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index 3432dbe..72504ca 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -2088,16 +2088,6 @@ cli_cmd_volume_remove_brick_cbk(struct cli_state *state, "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 = - "Running remove-brick with cluster.force-migration" - " enabled can result in data corruption. It is safer" - " to disable this option so that files that receive " - "writes during migration are not migrated.\nFiles " - "that are not migrated can then be manually copied " - "after the remove-brick commit operation.\nDo you " - "want to continue with your current " - "cluster.force-migration settings?"; } if (!brick_count) { @@ -2359,8 +2349,7 @@ cli_cmd_log_rotate_cbk(struct cli_state *state, struct cli_cmd_word *word, goto out; } - if (!((strcmp("rotate", words[2]) == 0) || - (strcmp("rotate", words[3]) == 0))) { + if (!(strcmp("rotate", words[3]) == 0)) { cli_usage_out(word->pattern); parse_error = 1; goto out; @@ -3247,6 +3236,16 @@ struct cli_cmd bitrot_cmds[] = { {"volume bitrot {enable|disable}", NULL, /*cli_cmd_bitrot_cbk,*/ "Enable/disable bitrot for volume "}, + {"volume bitrot signing-time ", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Waiting time for an object after last fd is closed to start signing " + "process"}, + + {"volume bitrot signer-threads ", + NULL, /*cli_cmd_bitrot_cbk,*/ + "Number of signing process threads. Usually set to number of available " + "cores"}, + {"volume bitrot scrub-throttle {lazy|normal|aggressive}", NULL, /*cli_cmd_bitrot_cbk,*/ "Set the speed of the scrubber for volume "}, @@ -3262,6 +3261,8 @@ struct cli_cmd bitrot_cmds[] = { "the scrubber. ondemand starts the scrubber immediately."}, {"volume bitrot {enable|disable}\n" + "volume bitrot signing-time \n" + "volume bitrot signer-threads \n" "volume bitrot scrub-throttle {lazy|normal|aggressive}\n" "volume bitrot scrub-frequency {hourly|daily|weekly|biweekly" "|monthly}\n" @@ -3403,18 +3404,14 @@ struct cli_cmd volume_cmds[] = { {"volume set ", cli_cmd_volume_set_cbk, "set options for volume "}, - {"volume set 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 set 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 rotate [BRICK]", cli_cmd_log_rotate_cbk, "rotate the log file for corresponding volume/brick"}, - {"volume log rotate [BRICK]", cli_cmd_log_rotate_cbk, - "rotate the log file for corresponding volume/brick" - " NOTE: This is an old syntax, will be deprecated from next release."}, - {"volume sync [all|]", cli_cmd_sync_volume_cbk, "sync the volume information from a peer"}, @@ -3422,8 +3419,8 @@ struct cli_cmd volume_cmds[] = { "reset all the reconfigured options"}, #if (SYNCDAEMON_COMPILE) - {"volume " GEOREP " [] [] {\\\n create [[ssh-port n] " - "[[no-verify] | [push-pem]]] [force] \\\n" + {"volume " GEOREP " [] []::[] {" + "\\\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 [[[\\!]