e3c68b
From 1029c27982d2f91cb2d3c4fcc19aa5171111dfb9 Mon Sep 17 00:00:00 2001
e3c68b
From: Jiffin Tony Thottan <jthottan@redhat.com>
e3c68b
Date: Mon, 16 Oct 2017 14:24:29 +0530
e3c68b
Subject: [PATCH 054/124] Revert "glusterd: (storhaug) remove ganesha"
e3c68b
e3c68b
This reverts commit 843e1b04b554ab887ec656ae7b468bb93ee4e2f7.
e3c68b
e3c68b
Label: DOWNSTREAM ONLY
e3c68b
e3c68b
Change-Id: I06b5450344c33f26da3d94b6f67051d41dfbba17
e3c68b
Signed-off-by: Jiffin Tony Thottan <jthottan@redhat.com>
e3c68b
Reviewed-on: https://code.engineering.redhat.com/gerrit/167103
e3c68b
Reviewed-by: Soumya Koduri <skoduri@redhat.com>
e3c68b
Tested-by: RHGS Build Bot <nigelb@redhat.com>
e3c68b
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
e3c68b
---
e3c68b
 cli/src/cli-cmd-global.c                           |  57 ++
e3c68b
 cli/src/cli-cmd-parser.c                           | 122 ++-
e3c68b
 cli/src/cli-cmd.c                                  |   3 +-
e3c68b
 cli/src/cli-rpc-ops.c                              |  82 ++
e3c68b
 cli/src/cli.h                                      |   4 +
e3c68b
 xlators/mgmt/glusterd/src/Makefile.am              |   4 +-
e3c68b
 xlators/mgmt/glusterd/src/glusterd-errno.h         |   2 +-
e3c68b
 xlators/mgmt/glusterd/src/glusterd-ganesha.c       | 915 +++++++++++++++++++++
e3c68b
 xlators/mgmt/glusterd/src/glusterd-handler.c       |  79 ++
e3c68b
 xlators/mgmt/glusterd/src/glusterd-messages.h      |   2 +-
e3c68b
 xlators/mgmt/glusterd/src/glusterd-op-sm.c         |  45 +-
e3c68b
 .../mgmt/glusterd/src/glusterd-snapshot-utils.c    | 196 +++++
e3c68b
 xlators/mgmt/glusterd/src/glusterd-store.h         |   2 +
e3c68b
 xlators/mgmt/glusterd/src/glusterd-volume-ops.c    |  37 +
e3c68b
 xlators/mgmt/glusterd/src/glusterd-volume-set.c    |   7 +
e3c68b
 xlators/mgmt/glusterd/src/glusterd.h               |  22 +
e3c68b
 16 files changed, 1568 insertions(+), 11 deletions(-)
e3c68b
 create mode 100644 xlators/mgmt/glusterd/src/glusterd-ganesha.c
e3c68b
e3c68b
diff --git a/cli/src/cli-cmd-global.c b/cli/src/cli-cmd-global.c
e3c68b
index d0729ac..270b76f 100644
e3c68b
--- a/cli/src/cli-cmd-global.c
e3c68b
+++ b/cli/src/cli-cmd-global.c
e3c68b
@@ -36,6 +36,10 @@ int
e3c68b
 cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word,
e3c68b
                       const char **words, int wordcount);
e3c68b
 
e3c68b
+int
e3c68b
+cli_cmd_ganesha_cbk(struct cli_state *state, struct cli_cmd_word *word,
e3c68b
+                    const char **words, int wordcount);
e3c68b
+
e3c68b
 struct cli_cmd global_cmds[] = {
e3c68b
     {
e3c68b
         "global help",
e3c68b
@@ -48,6 +52,11 @@ struct cli_cmd global_cmds[] = {
e3c68b
         cli_cmd_get_state_cbk,
e3c68b
         "Get local state representation of mentioned daemon",
e3c68b
     },
e3c68b
+    {
e3c68b
+        "nfs-ganesha {enable| disable} ",
e3c68b
+        cli_cmd_ganesha_cbk,
e3c68b
+        "Enable/disable NFS-Ganesha support",
e3c68b
+    },
e3c68b
     {NULL, NULL, NULL}};
e3c68b
 
e3c68b
 int
e3c68b
@@ -89,6 +98,54 @@ out:
e3c68b
 }
e3c68b
 
e3c68b
 int
e3c68b
+cli_cmd_ganesha_cbk(struct cli_state *state, struct cli_cmd_word *word,
e3c68b
+                    const char **words, int wordcount)
e3c68b
+
e3c68b
+{
e3c68b
+    int sent = 0;
e3c68b
+    int parse_error = 0;
e3c68b
+    int ret = -1;
e3c68b
+    rpc_clnt_procedure_t *proc = NULL;
e3c68b
+    call_frame_t *frame = NULL;
e3c68b
+    dict_t *options = NULL;
e3c68b
+    cli_local_t *local = NULL;
e3c68b
+    char *op_errstr = NULL;
e3c68b
+
e3c68b
+    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GANESHA];
e3c68b
+
e3c68b
+    frame = create_frame(THIS, THIS->ctx->pool);
e3c68b
+    if (!frame)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    ret = cli_cmd_ganesha_parse(state, words, wordcount, &options, &op_errstr);
e3c68b
+    if (ret) {
e3c68b
+        if (op_errstr) {
e3c68b
+            cli_err("%s", op_errstr);
e3c68b
+            GF_FREE(op_errstr);
e3c68b
+        } else
e3c68b
+            cli_usage_out(word->pattern);
e3c68b
+        parse_error = 1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    CLI_LOCAL_INIT(local, words, frame, options);
e3c68b
+
e3c68b
+    if (proc->fn) {
e3c68b
+        ret = proc->fn(frame, THIS, options);
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+    if (ret) {
e3c68b
+        cli_cmd_sent_status_get(&sent);
e3c68b
+        if ((sent == 0) && (parse_error == 0))
e3c68b
+            cli_out("Setting global option failed");
e3c68b
+    }
e3c68b
+
e3c68b
+    CLI_STACK_DESTROY(frame);
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
 cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word,
e3c68b
                       const char **words, int wordcount)
e3c68b
 {
e3c68b
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c
e3c68b
index d9ccba1..cd9c445 100644
e3c68b
--- a/cli/src/cli-cmd-parser.c
e3c68b
+++ b/cli/src/cli-cmd-parser.c
e3c68b
@@ -1694,7 +1694,7 @@ cli_cmd_volume_set_parse(struct cli_state *state, const char **words,
e3c68b
             }
e3c68b
         }
e3c68b
 
e3c68b
-        if ((strcmp (key, "cluster.brick-multiplex") == 0)) {
e3c68b
+        if ((strcmp(key, "cluster.brick-multiplex") == 0)) {
e3c68b
             question =
e3c68b
                 "Brick-multiplexing is supported only for "
e3c68b
                 "OCS converged or independent mode. Also it is "
e3c68b
@@ -1703,11 +1703,12 @@ cli_cmd_volume_set_parse(struct cli_state *state, const char **words,
e3c68b
                 "are running before this option is modified."
e3c68b
                 "Do you still want to continue?";
e3c68b
 
e3c68b
-            answer = cli_cmd_get_confirmation (state, question);
e3c68b
+            answer = cli_cmd_get_confirmation(state, question);
e3c68b
             if (GF_ANSWER_NO == answer) {
e3c68b
-                gf_log ("cli", GF_LOG_ERROR, "Operation "
e3c68b
-                        "cancelled, exiting");
e3c68b
-                *op_errstr = gf_strdup ("Aborted by user.");
e3c68b
+                gf_log("cli", GF_LOG_ERROR,
e3c68b
+                       "Operation "
e3c68b
+                       "cancelled, exiting");
e3c68b
+                *op_errstr = gf_strdup("Aborted by user.");
e3c68b
                 ret = -1;
e3c68b
                 goto out;
e3c68b
             }
e3c68b
@@ -5848,3 +5849,114 @@ out:
e3c68b
 
e3c68b
     return ret;
e3c68b
 }
e3c68b
+
e3c68b
+/* Parsing global option for NFS-Ganesha config
e3c68b
+ *  gluster nfs-ganesha enable/disable */
e3c68b
+
e3c68b
+int32_t
e3c68b
+cli_cmd_ganesha_parse(struct cli_state *state, const char **words,
e3c68b
+                      int wordcount, dict_t **options, char **op_errstr)
e3c68b
+{
e3c68b
+    dict_t *dict = NULL;
e3c68b
+    int ret = -1;
e3c68b
+    char *key = NULL;
e3c68b
+    char *value = NULL;
e3c68b
+    char *w = NULL;
e3c68b
+    char *opwords[] = {"enable", "disable", NULL};
e3c68b
+    const char *question = NULL;
e3c68b
+    gf_answer_t answer = GF_ANSWER_NO;
e3c68b
+
e3c68b
+    GF_ASSERT(words);
e3c68b
+    GF_ASSERT(options);
e3c68b
+
e3c68b
+    dict = dict_new();
e3c68b
+
e3c68b
+    if (!dict)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    if (wordcount != 2)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    key = (char *)words[0];
e3c68b
+    value = (char *)words[1];
e3c68b
+
e3c68b
+    if (!key || !value) {
e3c68b
+        cli_out("Usage : nfs-ganesha <enable/disable>");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = gf_strip_whitespace(value, strlen(value));
e3c68b
+    if (ret == -1)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    if (strcmp(key, "nfs-ganesha")) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "Global option: error: ' %s '"
e3c68b
+                    "is not a valid global option.",
e3c68b
+                    key);
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    w = str_getunamb(value, opwords);
e3c68b
+    if (!w) {
e3c68b
+        cli_out(
e3c68b
+            "Invalid global option \n"
e3c68b
+            "Usage : nfs-ganesha <enable/disable>");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    question =
e3c68b
+        "Enabling NFS-Ganesha requires Gluster-NFS to be"
e3c68b
+        " disabled across the trusted pool. Do you "
e3c68b
+        "still want to continue?\n";
e3c68b
+
e3c68b
+    if (strcmp(value, "enable") == 0) {
e3c68b
+        answer = cli_cmd_get_confirmation(state, question);
e3c68b
+        if (GF_ANSWER_NO == answer) {
e3c68b
+            gf_log("cli", GF_LOG_ERROR,
e3c68b
+                   "Global operation "
e3c68b
+                   "cancelled, exiting");
e3c68b
+            ret = -1;
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+    cli_out("This will take a few minutes to complete. Please wait ..");
e3c68b
+
e3c68b
+    ret = dict_set_str(dict, "key", key);
e3c68b
+    if (ret) {
e3c68b
+        gf_log(THIS->name, GF_LOG_ERROR, "dict set on key failed");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = dict_set_str(dict, "value", value);
e3c68b
+    if (ret) {
e3c68b
+        gf_log(THIS->name, GF_LOG_ERROR, "dict set on value failed");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = dict_set_str(dict, "globalname", "All");
e3c68b
+    if (ret) {
e3c68b
+        gf_log(THIS->name, GF_LOG_ERROR,
e3c68b
+               "dict set on global"
e3c68b
+               " key failed.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
e3c68b
+    if (ret) {
e3c68b
+        gf_log(THIS->name, GF_LOG_ERROR,
e3c68b
+               "dict set on global key "
e3c68b
+               "failed.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    *options = dict;
e3c68b
+out:
e3c68b
+    if (ret)
e3c68b
+        dict_unref(dict);
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
diff --git a/cli/src/cli-cmd.c b/cli/src/cli-cmd.c
e3c68b
index 2ee8b1b..8c06905 100644
e3c68b
--- a/cli/src/cli-cmd.c
e3c68b
+++ b/cli/src/cli-cmd.c
e3c68b
@@ -366,7 +366,8 @@ cli_cmd_submit(struct rpc_clnt *rpc, void *req, call_frame_t *frame,
e3c68b
     unsigned timeout = 0;
e3c68b
 
e3c68b
     if ((GLUSTER_CLI_PROFILE_VOLUME == procnum) ||
e3c68b
-        (GLUSTER_CLI_HEAL_VOLUME == procnum))
e3c68b
+        (GLUSTER_CLI_HEAL_VOLUME == procnum) ||
e3c68b
+        (GLUSTER_CLI_GANESHA == procnum))
e3c68b
         timeout = cli_ten_minutes_timeout;
e3c68b
     else
e3c68b
         timeout = cli_default_conn_timeout;
e3c68b
diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c
e3c68b
index 12e7fcc..736cd18 100644
e3c68b
--- a/cli/src/cli-rpc-ops.c
e3c68b
+++ b/cli/src/cli-rpc-ops.c
e3c68b
@@ -2207,6 +2207,62 @@ out:
e3c68b
     return ret;
e3c68b
 }
e3c68b
 
e3c68b
+int
e3c68b
+gf_cli_ganesha_cbk(struct rpc_req *req, struct iovec *iov, int count,
e3c68b
+                   void *myframe)
e3c68b
+{
e3c68b
+    gf_cli_rsp rsp = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    int ret = -1;
e3c68b
+    dict_t *dict = NULL;
e3c68b
+
e3c68b
+    GF_ASSERT(myframe);
e3c68b
+
e3c68b
+    if (-1 == req->rpc_status) {
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp);
e3c68b
+    if (ret < 0) {
e3c68b
+        gf_log(((call_frame_t *)myframe)->this->name, GF_LOG_ERROR,
e3c68b
+               "Failed to decode xdr response");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    gf_log("cli", GF_LOG_DEBUG, "Received resp to ganesha");
e3c68b
+
e3c68b
+    dict = dict_new();
e3c68b
+
e3c68b
+    if (!dict) {
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = dict_unserialize(rsp.dict.dict_val, rsp.dict.dict_len, &dict);
e3c68b
+    if (ret)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    if (rsp.op_ret) {
e3c68b
+        if (strcmp(rsp.op_errstr, ""))
e3c68b
+            cli_err("nfs-ganesha: failed: %s", rsp.op_errstr);
e3c68b
+        else
e3c68b
+            cli_err("nfs-ganesha: failed");
e3c68b
+    }
e3c68b
+
e3c68b
+    else {
e3c68b
+        cli_out("nfs-ganesha : success ");
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = rsp.op_ret;
e3c68b
+
e3c68b
+out:
e3c68b
+    if (dict)
e3c68b
+        dict_unref(dict);
e3c68b
+    cli_cmd_broadcast_response(ret);
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
 char *
e3c68b
 is_server_debug_xlator(void *myframe)
e3c68b
 {
e3c68b
@@ -4880,6 +4936,31 @@ out:
e3c68b
 }
e3c68b
 
e3c68b
 int32_t
e3c68b
+gf_cli_ganesha(call_frame_t *frame, xlator_t *this, void *data)
e3c68b
+{
e3c68b
+    gf_cli_req req = {{
e3c68b
+        0,
e3c68b
+    }};
e3c68b
+    int ret = 0;
e3c68b
+    dict_t *dict = NULL;
e3c68b
+
e3c68b
+    if (!frame || !this || !data) {
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    dict = data;
e3c68b
+
e3c68b
+    ret = cli_to_glusterd(&req, frame, gf_cli_ganesha_cbk,
e3c68b
+                          (xdrproc_t)xdr_gf_cli_req, dict, GLUSTER_CLI_GANESHA,
e3c68b
+                          this, cli_rpc_prog, NULL);
e3c68b
+out:
e3c68b
+    gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int32_t
e3c68b
 gf_cli_set_volume(call_frame_t *frame, xlator_t *this, void *data)
e3c68b
 {
e3c68b
     gf_cli_req req = {{
e3c68b
@@ -12214,6 +12295,7 @@ struct rpc_clnt_procedure gluster_cli_actors[GLUSTER_CLI_MAXVALUE] = {
e3c68b
     [GLUSTER_CLI_SYS_EXEC] = {"SYS_EXEC", gf_cli_sys_exec},
e3c68b
     [GLUSTER_CLI_SNAP] = {"SNAP", gf_cli_snapshot},
e3c68b
     [GLUSTER_CLI_BARRIER_VOLUME] = {"BARRIER VOLUME", gf_cli_barrier_volume},
e3c68b
+    [GLUSTER_CLI_GANESHA] = {"GANESHA", gf_cli_ganesha},
e3c68b
     [GLUSTER_CLI_GET_VOL_OPT] = {"GET_VOL_OPT", gf_cli_get_vol_opt},
e3c68b
     [GLUSTER_CLI_BITROT] = {"BITROT", gf_cli_bitrot},
e3c68b
     [GLUSTER_CLI_ATTACH_TIER] = {"ATTACH_TIER", gf_cli_attach_tier},
e3c68b
diff --git a/cli/src/cli.h b/cli/src/cli.h
e3c68b
index b79a0a2..37e4d9d 100644
e3c68b
--- a/cli/src/cli.h
e3c68b
+++ b/cli/src/cli.h
e3c68b
@@ -282,6 +282,10 @@ cli_cmd_volume_set_parse(struct cli_state *state, const char **words,
e3c68b
                          int wordcount, dict_t **options, char **op_errstr);
e3c68b
 
e3c68b
 int32_t
e3c68b
+cli_cmd_ganesha_parse(struct cli_state *state, const char **words,
e3c68b
+                      int wordcount, dict_t **options, char **op_errstr);
e3c68b
+
e3c68b
+int32_t
e3c68b
 cli_cmd_get_state_parse(struct cli_state *state, const char **words,
e3c68b
                         int wordcount, dict_t **options, char **op_errstr);
e3c68b
 
e3c68b
diff --git a/xlators/mgmt/glusterd/src/Makefile.am b/xlators/mgmt/glusterd/src/Makefile.am
e3c68b
index c8dd8e3..5fe5156 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/Makefile.am
e3c68b
+++ b/xlators/mgmt/glusterd/src/Makefile.am
e3c68b
@@ -10,7 +10,7 @@ glusterd_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) $(LIB_DL)
e3c68b
 glusterd_la_SOURCES = glusterd.c glusterd-handler.c glusterd-sm.c \
e3c68b
 	glusterd-op-sm.c glusterd-utils.c glusterd-rpc-ops.c \
e3c68b
 	glusterd-store.c glusterd-handshake.c glusterd-pmap.c \
e3c68b
-	glusterd-volgen.c glusterd-rebalance.c \
e3c68b
+	glusterd-volgen.c glusterd-rebalance.c glusterd-ganesha.c \
e3c68b
 	glusterd-quota.c glusterd-bitrot.c glusterd-geo-rep.c \
e3c68b
 	glusterd-replace-brick.c glusterd-log-ops.c glusterd-tier.c \
e3c68b
 	glusterd-volume-ops.c glusterd-brick-ops.c glusterd-mountbroker.c \
e3c68b
@@ -52,6 +52,8 @@ AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \
e3c68b
 	-I$(CONTRIBDIR)/mount -I$(CONTRIBDIR)/userspace-rcu \
e3c68b
 	-DSBIN_DIR=\"$(sbindir)\" -DDATADIR=\"$(localstatedir)\" \
e3c68b
 	-DGSYNCD_PREFIX=\"$(GLUSTERFS_LIBEXECDIR)\" \
e3c68b
+	-DCONFDIR=\"$(localstatedir)/run/gluster/shared_storage/nfs-ganesha\" \
e3c68b
+	-DGANESHA_PREFIX=\"$(libexecdir)/ganesha\" \
e3c68b
 	-DSYNCDAEMON_COMPILE=$(SYNCDAEMON_COMPILE)
e3c68b
 
e3c68b
 
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-errno.h b/xlators/mgmt/glusterd/src/glusterd-errno.h
e3c68b
index 7e1575b..c74070e 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-errno.h
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-errno.h
e3c68b
@@ -27,7 +27,7 @@ enum glusterd_op_errno {
e3c68b
     EG_ISSNAP = 30813,    /* Volume is a snap volume           */
e3c68b
     EG_GEOREPRUN = 30814, /* Geo-Replication is running        */
e3c68b
     EG_NOTTHINP = 30815,  /* Bricks are not thinly provisioned */
e3c68b
-    EG_NOGANESHA = 30816, /* obsolete ganesha is not enabled   */
e3c68b
+    EG_NOGANESHA = 30816, /* Global ganesha is not enabled   */
e3c68b
 };
e3c68b
 
e3c68b
 #endif
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-ganesha.c b/xlators/mgmt/glusterd/src/glusterd-ganesha.c
e3c68b
new file mode 100644
e3c68b
index 0000000..fac16e6
e3c68b
--- /dev/null
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-ganesha.c
e3c68b
@@ -0,0 +1,915 @@
e3c68b
+/*
e3c68b
+   Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
e3c68b
+   This file is part of GlusterFS.
e3c68b
+
e3c68b
+   This file is licensed to you under your choice of the GNU Lesser
e3c68b
+   General Public License, version 3 or any later version (LGPLv3 or
e3c68b
+   later), or the GNU General Public License, version 2 (GPLv2), in all
e3c68b
+   cases as published by the Free Software Foundation.
e3c68b
+*/
e3c68b
+
e3c68b
+#include <glusterfs/common-utils.h>
e3c68b
+#include "glusterd.h"
e3c68b
+#include "glusterd-op-sm.h"
e3c68b
+#include "glusterd-store.h"
e3c68b
+#include "glusterd-utils.h"
e3c68b
+#include "glusterd-nfs-svc.h"
e3c68b
+#include "glusterd-volgen.h"
e3c68b
+#include "glusterd-messages.h"
e3c68b
+#include <glusterfs/syscall.h>
e3c68b
+
e3c68b
+#include <ctype.h>
e3c68b
+
e3c68b
+int
e3c68b
+start_ganesha(char **op_errstr);
e3c68b
+
e3c68b
+typedef struct service_command {
e3c68b
+    char *binary;
e3c68b
+    char *service;
e3c68b
+    int (*action)(struct service_command *, char *);
e3c68b
+} service_command;
e3c68b
+
e3c68b
+/* parsing_ganesha_ha_conf will allocate the returned string
e3c68b
+ * to be freed (GF_FREE) by the caller
e3c68b
+ * return NULL if error or not found */
e3c68b
+static char *
e3c68b
+parsing_ganesha_ha_conf(const char *key)
e3c68b
+{
e3c68b
+#define MAX_LINE 1024
e3c68b
+    char scratch[MAX_LINE * 2] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    char *value = NULL, *pointer = NULL, *end_pointer = NULL;
e3c68b
+    FILE *fp;
e3c68b
+
e3c68b
+    fp = fopen(GANESHA_HA_CONF, "r");
e3c68b
+    if (fp == NULL) {
e3c68b
+        gf_msg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
e3c68b
+               "couldn't open the file %s", GANESHA_HA_CONF);
e3c68b
+        goto end_ret;
e3c68b
+    }
e3c68b
+    while ((pointer = fgets(scratch, MAX_LINE, fp)) != NULL) {
e3c68b
+        /* Read config file until we get matching "^[[:space:]]*key" */
e3c68b
+        if (*pointer == '#') {
e3c68b
+            continue;
e3c68b
+        }
e3c68b
+        while (isblank(*pointer)) {
e3c68b
+            pointer++;
e3c68b
+        }
e3c68b
+        if (strncmp(pointer, key, strlen(key))) {
e3c68b
+            continue;
e3c68b
+        }
e3c68b
+        pointer += strlen(key);
e3c68b
+        /* key found : if we fail to parse, we'll return an error
e3c68b
+         * rather than trying next one
e3c68b
+         * - supposition : conf file is bash compatible : no space
e3c68b
+         *   around the '=' */
e3c68b
+        if (*pointer != '=') {
e3c68b
+            gf_msg(THIS->name, GF_LOG_ERROR, errno,
e3c68b
+                   GD_MSG_GET_CONFIG_INFO_FAILED, "Parsing %s failed at key %s",
e3c68b
+                   GANESHA_HA_CONF, key);
e3c68b
+            goto end_close;
e3c68b
+        }
e3c68b
+        pointer++; /* jump the '=' */
e3c68b
+
e3c68b
+        if (*pointer == '"' || *pointer == '\'') {
e3c68b
+            /* dont get the quote */
e3c68b
+            pointer++;
e3c68b
+        }
e3c68b
+        end_pointer = pointer;
e3c68b
+        /* stop at the next closing quote or  blank/newline */
e3c68b
+        do {
e3c68b
+            end_pointer++;
e3c68b
+        } while (!(*end_pointer == '\'' || *end_pointer == '"' ||
e3c68b
+                   isspace(*end_pointer) || *end_pointer == '\0'));
e3c68b
+        *end_pointer = '\0';
e3c68b
+
e3c68b
+        /* got it. copy it and return */
e3c68b
+        value = gf_strdup(pointer);
e3c68b
+        break;
e3c68b
+    }
e3c68b
+
e3c68b
+end_close:
e3c68b
+    fclose(fp);
e3c68b
+end_ret:
e3c68b
+    return value;
e3c68b
+}
e3c68b
+
e3c68b
+static int
e3c68b
+sc_systemctl_action(struct service_command *sc, char *command)
e3c68b
+{
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    runinit(&runner);
e3c68b
+    runner_add_args(&runner, sc->binary, command, sc->service, NULL);
e3c68b
+    return runner_run(&runner);
e3c68b
+}
e3c68b
+
e3c68b
+static int
e3c68b
+sc_service_action(struct service_command *sc, char *command)
e3c68b
+{
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    runinit(&runner);
e3c68b
+    runner_add_args(&runner, sc->binary, sc->service, command, NULL);
e3c68b
+    return runner_run(&runner);
e3c68b
+}
e3c68b
+
e3c68b
+static int
e3c68b
+manage_service(char *action)
e3c68b
+{
e3c68b
+    struct stat stbuf = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    int i = 0;
e3c68b
+    int ret = 0;
e3c68b
+    struct service_command sc_list[] = {{.binary = "/usr/bin/systemctl",
e3c68b
+                                         .service = "nfs-ganesha",
e3c68b
+                                         .action = sc_systemctl_action},
e3c68b
+                                        {.binary = "/sbin/invoke-rc.d",
e3c68b
+                                         .service = "nfs-ganesha",
e3c68b
+                                         .action = sc_service_action},
e3c68b
+                                        {.binary = "/sbin/service",
e3c68b
+                                         .service = "nfs-ganesha",
e3c68b
+                                         .action = sc_service_action},
e3c68b
+                                        {.binary = NULL}};
e3c68b
+
e3c68b
+    while (sc_list[i].binary != NULL) {
e3c68b
+        ret = sys_stat(sc_list[i].binary, &stbuf);
e3c68b
+        if (ret == 0) {
e3c68b
+            gf_msg_debug(THIS->name, 0, "%s found.", sc_list[i].binary);
e3c68b
+            if (strcmp(sc_list[i].binary, "/usr/bin/systemctl") == 0)
e3c68b
+                ret = sc_systemctl_action(&sc_list[i], action);
e3c68b
+            else
e3c68b
+                ret = sc_service_action(&sc_list[i], action);
e3c68b
+
e3c68b
+            return ret;
e3c68b
+        }
e3c68b
+        i++;
e3c68b
+    }
e3c68b
+    gf_msg(THIS->name, GF_LOG_ERROR, 0, GD_MSG_UNRECOGNIZED_SVC_MNGR,
e3c68b
+           "Could not %s NFS-Ganesha.Service manager for distro"
e3c68b
+           " not recognized.",
e3c68b
+           action);
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+/*
e3c68b
+ * Check if the cluster is a ganesha cluster or not *
e3c68b
+ */
e3c68b
+gf_boolean_t
e3c68b
+glusterd_is_ganesha_cluster()
e3c68b
+{
e3c68b
+    int ret = -1;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+    gf_boolean_t ret_bool = _gf_false;
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    GF_VALIDATE_OR_GOTO("ganesha", this, out);
e3c68b
+    priv = this->private;
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, priv, out);
e3c68b
+
e3c68b
+    ret = dict_get_str_boolean(priv->opts, GLUSTERD_STORE_KEY_GANESHA_GLOBAL,
e3c68b
+                               _gf_false);
e3c68b
+    if (ret == _gf_true) {
e3c68b
+        ret_bool = _gf_true;
e3c68b
+        gf_msg_debug(this->name, 0, "nfs-ganesha is enabled for the cluster");
e3c68b
+    } else
e3c68b
+        gf_msg_debug(this->name, 0, "nfs-ganesha is disabled for the cluster");
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret_bool;
e3c68b
+}
e3c68b
+
e3c68b
+/* Check if ganesha.enable is set to 'on', that checks if
e3c68b
+ * a  particular volume is exported via NFS-Ganesha */
e3c68b
+gf_boolean_t
e3c68b
+glusterd_check_ganesha_export(glusterd_volinfo_t *volinfo)
e3c68b
+{
e3c68b
+    char *value = NULL;
e3c68b
+    gf_boolean_t is_exported = _gf_false;
e3c68b
+    int ret = 0;
e3c68b
+
e3c68b
+    ret = glusterd_volinfo_get(volinfo, "ganesha.enable", &value);
e3c68b
+    if ((ret == 0) && value) {
e3c68b
+        if (strcmp(value, "on") == 0) {
e3c68b
+            gf_msg_debug(THIS->name, 0,
e3c68b
+                         "ganesha.enable set"
e3c68b
+                         " to %s",
e3c68b
+                         value);
e3c68b
+            is_exported = _gf_true;
e3c68b
+        }
e3c68b
+    }
e3c68b
+    return is_exported;
e3c68b
+}
e3c68b
+
e3c68b
+/* *
e3c68b
+ * The below function is called as part of commit phase for volume set option
e3c68b
+ * "ganesha.enable". If the value is "on", it creates export configuration file
e3c68b
+ * and then export the volume via dbus command. Incase of "off", the volume
e3c68b
+ * will be already unexported during stage phase, so it will remove the conf
e3c68b
+ * file from shared storage
e3c68b
+ */
e3c68b
+int
e3c68b
+glusterd_check_ganesha_cmd(char *key, char *value, char **errstr, dict_t *dict)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+    char *volname = NULL;
e3c68b
+
e3c68b
+    GF_ASSERT(key);
e3c68b
+    GF_ASSERT(value);
e3c68b
+    GF_ASSERT(dict);
e3c68b
+
e3c68b
+    if ((strcmp(key, "ganesha.enable") == 0)) {
e3c68b
+        if ((strcmp(value, "on")) && (strcmp(value, "off"))) {
e3c68b
+            gf_asprintf(errstr,
e3c68b
+                        "Invalid value"
e3c68b
+                        " for volume set command. Use on/off only.");
e3c68b
+            ret = -1;
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+        if (strcmp(value, "on") == 0) {
e3c68b
+            ret = glusterd_handle_ganesha_op(dict, errstr, key, value);
e3c68b
+
e3c68b
+        } else if (is_origin_glusterd(dict)) {
e3c68b
+            ret = dict_get_str(dict, "volname", &volname);
e3c68b
+            if (ret) {
e3c68b
+                gf_msg("glusterd-ganesha", GF_LOG_ERROR, errno,
e3c68b
+                       GD_MSG_DICT_GET_FAILED, "Unable to get volume name");
e3c68b
+                goto out;
e3c68b
+            }
e3c68b
+            ret = manage_export_config(volname, "off", errstr);
e3c68b
+        }
e3c68b
+    }
e3c68b
+out:
e3c68b
+    if (ret) {
e3c68b
+        gf_msg("glusterd-ganesha", GF_LOG_ERROR, 0,
e3c68b
+               GD_MSG_NFS_GNS_OP_HANDLE_FAIL,
e3c68b
+               "Handling NFS-Ganesha"
e3c68b
+               " op failed.");
e3c68b
+    }
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+glusterd_op_stage_set_ganesha(dict_t *dict, char **op_errstr)
e3c68b
+{
e3c68b
+    int ret = -1;
e3c68b
+    int value = -1;
e3c68b
+    gf_boolean_t option = _gf_false;
e3c68b
+    char *str = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+
e3c68b
+    GF_ASSERT(dict);
e3c68b
+    this = THIS;
e3c68b
+    GF_ASSERT(this);
e3c68b
+    priv = this->private;
e3c68b
+    GF_ASSERT(priv);
e3c68b
+
e3c68b
+    value = dict_get_str_boolean(dict, "value", _gf_false);
e3c68b
+    if (value == -1) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "value not present.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    /* This dict_get will fail if the user had never set the key before */
e3c68b
+    /*Ignoring the ret value and proceeding */
e3c68b
+    ret = dict_get_str(priv->opts, GLUSTERD_STORE_KEY_GANESHA_GLOBAL, &str);
e3c68b
+    if (ret == -1) {
e3c68b
+        gf_msg(this->name, GF_LOG_WARNING, errno, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "Global dict not present.");
e3c68b
+        ret = 0;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    /* Validity of the value is already checked */
e3c68b
+    ret = gf_string2boolean(str, &option);
e3c68b
+    /* Check if the feature is already enabled, fail in that case */
e3c68b
+    if (value == option) {
e3c68b
+        gf_asprintf(op_errstr, "nfs-ganesha is already %sd.", str);
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    if (value) {
e3c68b
+        ret = start_ganesha(op_errstr);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg(THIS->name, GF_LOG_ERROR, 0, GD_MSG_NFS_GNS_START_FAIL,
e3c68b
+                   "Could not start NFS-Ganesha");
e3c68b
+        }
e3c68b
+    } else {
e3c68b
+        ret = stop_ganesha(op_errstr);
e3c68b
+        if (ret)
e3c68b
+            gf_msg_debug(THIS->name, 0,
e3c68b
+                         "Could not stop "
e3c68b
+                         "NFS-Ganesha.");
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+
e3c68b
+    if (ret) {
e3c68b
+        if (!(*op_errstr)) {
e3c68b
+            *op_errstr = gf_strdup("Error, Validation Failed");
e3c68b
+            gf_msg_debug(this->name, 0, "Error, Cannot Validate option :%s",
e3c68b
+                         GLUSTERD_STORE_KEY_GANESHA_GLOBAL);
e3c68b
+        } else {
e3c68b
+            gf_msg_debug(this->name, 0, "Error, Cannot Validate option");
e3c68b
+        }
e3c68b
+    }
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+glusterd_op_set_ganesha(dict_t *dict, char **errstr)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    char *key = NULL;
e3c68b
+    char *value = NULL;
e3c68b
+    char *next_version = NULL;
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    GF_ASSERT(this);
e3c68b
+    GF_ASSERT(dict);
e3c68b
+
e3c68b
+    priv = this->private;
e3c68b
+    GF_ASSERT(priv);
e3c68b
+
e3c68b
+    ret = dict_get_str(dict, "key", &key);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "Couldn't get key in global option set");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = dict_get_str(dict, "value", &value);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "Couldn't get value in global option set");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = glusterd_handle_ganesha_op(dict, errstr, key, value);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_NFS_GNS_SETUP_FAIL,
e3c68b
+               "Initial NFS-Ganesha set up failed");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    ret = dict_set_dynstr_with_alloc(priv->opts,
e3c68b
+                                     GLUSTERD_STORE_KEY_GANESHA_GLOBAL, value);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_WARNING, errno, GD_MSG_DICT_SET_FAILED,
e3c68b
+               "Failed to set"
e3c68b
+               " nfs-ganesha in dict.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    ret = glusterd_get_next_global_opt_version_str(priv->opts, &next_version);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg_debug(THIS->name, 0,
e3c68b
+                     "Could not fetch "
e3c68b
+                     " global op version");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    ret = dict_set_str(priv->opts, GLUSTERD_GLOBAL_OPT_VERSION, next_version);
e3c68b
+    if (ret)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    ret = glusterd_store_options(this, priv->opts);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_STORE_FAIL,
e3c68b
+               "Failed to store options");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+    gf_msg_debug(this->name, 0, "returning %d", ret);
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+/* Following function parse GANESHA_HA_CONF
e3c68b
+ * The sample file looks like below,
e3c68b
+ * HA_NAME="ganesha-ha-360"
e3c68b
+ * HA_VOL_NAME="ha-state"
e3c68b
+ * HA_CLUSTER_NODES="server1,server2"
e3c68b
+ * VIP_rhs_1="10.x.x.x"
e3c68b
+ * VIP_rhs_2="10.x.x.x." */
e3c68b
+
e3c68b
+/* Check if the localhost is listed as one of nfs-ganesha nodes */
e3c68b
+gf_boolean_t
e3c68b
+check_host_list(void)
e3c68b
+{
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    char *hostname, *hostlist;
e3c68b
+    gf_boolean_t ret = _gf_false;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    priv = THIS->private;
e3c68b
+    GF_ASSERT(priv);
e3c68b
+
e3c68b
+    hostlist = parsing_ganesha_ha_conf("HA_CLUSTER_NODES");
e3c68b
+    if (hostlist == NULL) {
e3c68b
+        gf_msg(this->name, GF_LOG_INFO, errno, GD_MSG_GET_CONFIG_INFO_FAILED,
e3c68b
+               "couldn't get HA_CLUSTER_NODES from file %s", GANESHA_HA_CONF);
e3c68b
+        return _gf_false;
e3c68b
+    }
e3c68b
+
e3c68b
+    /* Hostlist is a comma separated list now */
e3c68b
+    hostname = strtok(hostlist, ",");
e3c68b
+    while (hostname != NULL) {
e3c68b
+        ret = gf_is_local_addr(hostname);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_NFS_GNS_HOST_FOUND,
e3c68b
+                   "ganesha host found "
e3c68b
+                   "Hostname is %s",
e3c68b
+                   hostname);
e3c68b
+            break;
e3c68b
+        }
e3c68b
+        hostname = strtok(NULL, ",");
e3c68b
+    }
e3c68b
+
e3c68b
+    GF_FREE(hostlist);
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+manage_export_config(char *volname, char *value, char **op_errstr)
e3c68b
+{
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    int ret = -1;
e3c68b
+
e3c68b
+    GF_ASSERT(volname);
e3c68b
+    runinit(&runner);
e3c68b
+    runner_add_args(&runner, "sh", GANESHA_PREFIX "/create-export-ganesha.sh",
e3c68b
+                    CONFDIR, value, volname, NULL);
e3c68b
+    ret = runner_run(&runner);
e3c68b
+
e3c68b
+    if (ret)
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "Failed to create"
e3c68b
+                    " NFS-Ganesha export config file.");
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+/* Exports and unexports a particular volume via NFS-Ganesha */
e3c68b
+int
e3c68b
+ganesha_manage_export(dict_t *dict, char *value, char **op_errstr)
e3c68b
+{
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    int ret = -1;
e3c68b
+    glusterd_volinfo_t *volinfo = NULL;
e3c68b
+    dict_t *vol_opts = NULL;
e3c68b
+    char *volname = NULL;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    gf_boolean_t option = _gf_false;
e3c68b
+
e3c68b
+    runinit(&runner);
e3c68b
+    this = THIS;
e3c68b
+    GF_ASSERT(this);
e3c68b
+    priv = this->private;
e3c68b
+
e3c68b
+    GF_ASSERT(value);
e3c68b
+    GF_ASSERT(dict);
e3c68b
+    GF_ASSERT(priv);
e3c68b
+
e3c68b
+    ret = dict_get_str(dict, "volname", &volname);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "Unable to get volume name");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    ret = gf_string2boolean(value, &option);
e3c68b
+    if (ret == -1) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY,
e3c68b
+               "invalid value.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = glusterd_volinfo_find(volname, &volinfo);
e3c68b
+    if (ret) {
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_VOL_NOT_FOUND,
e3c68b
+               FMTSTR_CHECK_VOL_EXISTS, volname);
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = glusterd_check_ganesha_export(volinfo);
e3c68b
+    if (ret && option) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "ganesha.enable "
e3c68b
+                    "is already 'on'.");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    } else if (!option && !ret) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "ganesha.enable "
e3c68b
+                    "is already 'off'.");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    /* Check if global option is enabled, proceed only then */
e3c68b
+    ret = dict_get_str_boolean(priv->opts, GLUSTERD_STORE_KEY_GANESHA_GLOBAL,
e3c68b
+                               _gf_false);
e3c68b
+    if (ret == -1) {
e3c68b
+        gf_msg_debug(this->name, 0,
e3c68b
+                     "Failed to get "
e3c68b
+                     "global option dict.");
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "The option "
e3c68b
+                    "nfs-ganesha should be "
e3c68b
+                    "enabled before setting ganesha.enable.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+    if (!ret) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "The option "
e3c68b
+                    "nfs-ganesha should be "
e3c68b
+                    "enabled before setting ganesha.enable.");
e3c68b
+        ret = -1;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    /* *
e3c68b
+     * Create the export file from the node where ganesha.enable "on"
e3c68b
+     * is executed
e3c68b
+     * */
e3c68b
+    if (option) {
e3c68b
+        ret = manage_export_config(volname, "on", op_errstr);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_EXPORT_FILE_CREATE_FAIL,
e3c68b
+                   "Failed to create"
e3c68b
+                   "export file for NFS-Ganesha\n");
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+    if (check_host_list()) {
e3c68b
+        runner_add_args(&runner, "sh", GANESHA_PREFIX "/dbus-send.sh", CONFDIR,
e3c68b
+                        value, volname, NULL);
e3c68b
+        ret = runner_run(&runner);
e3c68b
+        if (ret) {
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "Dynamic export"
e3c68b
+                        " addition/deletion failed."
e3c68b
+                        " Please see log file for details");
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+    vol_opts = volinfo->dict;
e3c68b
+    ret = dict_set_dynstr_with_alloc(vol_opts, "features.cache-invalidation",
e3c68b
+                                     value);
e3c68b
+    if (ret)
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "Cache-invalidation could not"
e3c68b
+                    " be set to %s.",
e3c68b
+                    value);
e3c68b
+    ret = glusterd_store_volinfo(volinfo, GLUSTERD_VOLINFO_VER_AC_INCREMENT);
e3c68b
+    if (ret)
e3c68b
+        gf_asprintf(op_errstr, "failed to store volinfo for %s",
e3c68b
+                    volinfo->volname);
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+tear_down_cluster(gf_boolean_t run_teardown)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    struct stat st = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    DIR *dir = NULL;
e3c68b
+    struct dirent *entry = NULL;
e3c68b
+    struct dirent scratch[2] = {
e3c68b
+        {
e3c68b
+            0,
e3c68b
+        },
e3c68b
+    };
e3c68b
+    char path[PATH_MAX] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    if (run_teardown) {
e3c68b
+        runinit(&runner);
e3c68b
+        runner_add_args(&runner, "sh", GANESHA_PREFIX "/ganesha-ha.sh",
e3c68b
+                        "teardown", CONFDIR, NULL);
e3c68b
+        ret = runner_run(&runner);
e3c68b
+        /* *
e3c68b
+         * Remove all the entries in CONFDIR expect ganesha.conf and
e3c68b
+         * ganesha-ha.conf
e3c68b
+         */
e3c68b
+        dir = sys_opendir(CONFDIR);
e3c68b
+        if (!dir) {
e3c68b
+            gf_msg_debug(THIS->name, 0,
e3c68b
+                         "Failed to open directory %s. "
e3c68b
+                         "Reason : %s",
e3c68b
+                         CONFDIR, strerror(errno));
e3c68b
+            ret = 0;
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+
e3c68b
+        GF_SKIP_IRRELEVANT_ENTRIES(entry, dir, scratch);
e3c68b
+        while (entry) {
e3c68b
+            snprintf(path, PATH_MAX, "%s/%s", CONFDIR, entry->d_name);
e3c68b
+            ret = sys_lstat(path, &st);
e3c68b
+            if (ret == -1) {
e3c68b
+                gf_msg_debug(THIS->name, 0,
e3c68b
+                             "Failed to stat entry %s :"
e3c68b
+                             " %s",
e3c68b
+                             path, strerror(errno));
e3c68b
+                goto out;
e3c68b
+            }
e3c68b
+
e3c68b
+            if (strcmp(entry->d_name, "ganesha.conf") == 0 ||
e3c68b
+                strcmp(entry->d_name, "ganesha-ha.conf") == 0)
e3c68b
+                gf_msg_debug(THIS->name, 0,
e3c68b
+                             " %s is not required"
e3c68b
+                             " to remove",
e3c68b
+                             path);
e3c68b
+            else if (S_ISDIR(st.st_mode))
e3c68b
+                ret = recursive_rmdir(path);
e3c68b
+            else
e3c68b
+                ret = sys_unlink(path);
e3c68b
+
e3c68b
+            if (ret) {
e3c68b
+                gf_msg_debug(THIS->name, 0,
e3c68b
+                             " Failed to remove %s. "
e3c68b
+                             "Reason : %s",
e3c68b
+                             path, strerror(errno));
e3c68b
+            }
e3c68b
+
e3c68b
+            gf_msg_debug(THIS->name, 0, "%s %s",
e3c68b
+                         ret ? "Failed to remove" : "Removed", entry->d_name);
e3c68b
+            GF_SKIP_IRRELEVANT_ENTRIES(entry, dir, scratch);
e3c68b
+        }
e3c68b
+
e3c68b
+        ret = sys_closedir(dir);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg_debug(THIS->name, 0,
e3c68b
+                         "Failed to close dir %s. Reason :"
e3c68b
+                         " %s",
e3c68b
+                         CONFDIR, strerror(errno));
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+setup_cluster(gf_boolean_t run_setup)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    if (run_setup) {
e3c68b
+        runinit(&runner);
e3c68b
+        runner_add_args(&runner, "sh", GANESHA_PREFIX "/ganesha-ha.sh", "setup",
e3c68b
+                        CONFDIR, NULL);
e3c68b
+        ret = runner_run(&runner);
e3c68b
+    }
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+static int
e3c68b
+teardown(gf_boolean_t run_teardown, char **op_errstr)
e3c68b
+{
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    int ret = 1;
e3c68b
+    glusterd_volinfo_t *volinfo = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    dict_t *vol_opts = NULL;
e3c68b
+
e3c68b
+    priv = THIS->private;
e3c68b
+
e3c68b
+    ret = tear_down_cluster(run_teardown);
e3c68b
+    if (ret == -1) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "Cleanup of NFS-Ganesha"
e3c68b
+                    " HA config failed.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    runinit(&runner);
e3c68b
+    runner_add_args(&runner, "sh", GANESHA_PREFIX "/ganesha-ha.sh", "cleanup",
e3c68b
+                    CONFDIR, NULL);
e3c68b
+    ret = runner_run(&runner);
e3c68b
+    if (ret)
e3c68b
+        gf_msg_debug(THIS->name, 0,
e3c68b
+                     "Could not clean up"
e3c68b
+                     " NFS-Ganesha related config");
e3c68b
+
e3c68b
+    cds_list_for_each_entry(volinfo, &priv->volumes, vol_list)
e3c68b
+    {
e3c68b
+        vol_opts = volinfo->dict;
e3c68b
+        /* All the volumes exported via NFS-Ganesha will be
e3c68b
+        unexported, hence setting the appropriate keys */
e3c68b
+        ret = dict_set_str(vol_opts, "features.cache-invalidation", "off");
e3c68b
+        if (ret)
e3c68b
+            gf_msg(THIS->name, GF_LOG_WARNING, errno, GD_MSG_DICT_SET_FAILED,
e3c68b
+                   "Could not set features.cache-invalidation "
e3c68b
+                   "to off for %s",
e3c68b
+                   volinfo->volname);
e3c68b
+
e3c68b
+        ret = dict_set_str(vol_opts, "ganesha.enable", "off");
e3c68b
+        if (ret)
e3c68b
+            gf_msg(THIS->name, GF_LOG_WARNING, errno, GD_MSG_DICT_SET_FAILED,
e3c68b
+                   "Could not set ganesha.enable to off for %s",
e3c68b
+                   volinfo->volname);
e3c68b
+
e3c68b
+        ret = glusterd_store_volinfo(volinfo,
e3c68b
+                                     GLUSTERD_VOLINFO_VER_AC_INCREMENT);
e3c68b
+        if (ret)
e3c68b
+            gf_msg(THIS->name, GF_LOG_WARNING, 0, GD_MSG_VOLINFO_SET_FAIL,
e3c68b
+                   "failed to store volinfo for %s", volinfo->volname);
e3c68b
+    }
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+stop_ganesha(char **op_errstr)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    runinit(&runner);
e3c68b
+    runner_add_args(&runner, "sh", GANESHA_PREFIX "/ganesha-ha.sh",
e3c68b
+                    "--setup-ganesha-conf-files", CONFDIR, "no", NULL);
e3c68b
+    ret = runner_run(&runner);
e3c68b
+    if (ret) {
e3c68b
+        gf_asprintf(op_errstr,
e3c68b
+                    "removal of symlink ganesha.conf "
e3c68b
+                    "in /etc/ganesha failed");
e3c68b
+    }
e3c68b
+
e3c68b
+    if (check_host_list()) {
e3c68b
+        ret = manage_service("stop");
e3c68b
+        if (ret)
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "NFS-Ganesha service could not"
e3c68b
+                        "be stopped.");
e3c68b
+    }
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+start_ganesha(char **op_errstr)
e3c68b
+{
e3c68b
+    int ret = -1;
e3c68b
+    dict_t *vol_opts = NULL;
e3c68b
+    glusterd_volinfo_t *volinfo = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    runner_t runner = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    priv = THIS->private;
e3c68b
+    GF_ASSERT(priv);
e3c68b
+
e3c68b
+    cds_list_for_each_entry(volinfo, &priv->volumes, vol_list)
e3c68b
+    {
e3c68b
+        vol_opts = volinfo->dict;
e3c68b
+        /* Gluster-nfs has to be disabled across the trusted pool */
e3c68b
+        /* before attempting to start nfs-ganesha */
e3c68b
+        ret = dict_set_str(vol_opts, NFS_DISABLE_MAP_KEY, "on");
e3c68b
+        if (ret)
e3c68b
+            goto out;
e3c68b
+
e3c68b
+        ret = glusterd_store_volinfo(volinfo,
e3c68b
+                                     GLUSTERD_VOLINFO_VER_AC_INCREMENT);
e3c68b
+        if (ret) {
e3c68b
+            *op_errstr = gf_strdup(
e3c68b
+                "Failed to store the "
e3c68b
+                "Volume information");
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+    /* If the nfs svc is not initialized it means that the service is not
e3c68b
+     * running, hence we can skip the process of stopping gluster-nfs
e3c68b
+     * service
e3c68b
+     */
e3c68b
+    if (priv->nfs_svc.inited) {
e3c68b
+        ret = priv->nfs_svc.stop(&(priv->nfs_svc), SIGKILL);
e3c68b
+        if (ret) {
e3c68b
+            ret = -1;
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "Gluster-NFS service could"
e3c68b
+                        "not be stopped, exiting.");
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+    if (check_host_list()) {
e3c68b
+        runinit(&runner);
e3c68b
+        runner_add_args(&runner, "sh", GANESHA_PREFIX "/ganesha-ha.sh",
e3c68b
+                        "--setup-ganesha-conf-files", CONFDIR, "yes", NULL);
e3c68b
+        ret = runner_run(&runner);
e3c68b
+        if (ret) {
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "creation of symlink ganesha.conf "
e3c68b
+                        "in /etc/ganesha failed");
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+        ret = manage_service("start");
e3c68b
+        if (ret)
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "NFS-Ganesha failed to start."
e3c68b
+                        "Please see log file for details");
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+static int
e3c68b
+pre_setup(gf_boolean_t run_setup, char **op_errstr)
e3c68b
+{
e3c68b
+    int ret = 0;
e3c68b
+
e3c68b
+    ret = check_host_list();
e3c68b
+
e3c68b
+    if (ret) {
e3c68b
+        ret = setup_cluster(run_setup);
e3c68b
+        if (ret == -1)
e3c68b
+            gf_asprintf(op_errstr,
e3c68b
+                        "Failed to set up HA "
e3c68b
+                        "config for NFS-Ganesha. "
e3c68b
+                        "Please check the log file for details");
e3c68b
+    }
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+glusterd_handle_ganesha_op(dict_t *dict, char **op_errstr, char *key,
e3c68b
+                           char *value)
e3c68b
+{
e3c68b
+    int32_t ret = -1;
e3c68b
+    gf_boolean_t option = _gf_false;
e3c68b
+
e3c68b
+    GF_ASSERT(dict);
e3c68b
+    GF_ASSERT(op_errstr);
e3c68b
+    GF_ASSERT(key);
e3c68b
+    GF_ASSERT(value);
e3c68b
+
e3c68b
+    if (strcmp(key, "ganesha.enable") == 0) {
e3c68b
+        ret = ganesha_manage_export(dict, value, op_errstr);
e3c68b
+        if (ret < 0)
e3c68b
+            goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    /* It is possible that the key might not be set */
e3c68b
+    ret = gf_string2boolean(value, &option);
e3c68b
+    if (ret == -1) {
e3c68b
+        gf_asprintf(op_errstr, "Invalid value in key-value pair.");
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    if (strcmp(key, GLUSTERD_STORE_KEY_GANESHA_GLOBAL) == 0) {
e3c68b
+        /* *
e3c68b
+         * The set up/teardown of pcs cluster should be performed only
e3c68b
+         * once. This will done on the node in which the cli command
e3c68b
+         * 'gluster nfs-ganesha <enable/disable>' got executed. So that
e3c68b
+         * node should part of ganesha HA cluster
e3c68b
+         */
e3c68b
+        if (option) {
e3c68b
+            ret = pre_setup(is_origin_glusterd(dict), op_errstr);
e3c68b
+            if (ret < 0)
e3c68b
+                goto out;
e3c68b
+        } else {
e3c68b
+            ret = teardown(is_origin_glusterd(dict), op_errstr);
e3c68b
+            if (ret < 0)
e3c68b
+                goto out;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-handler.c b/xlators/mgmt/glusterd/src/glusterd-handler.c
e3c68b
index de44af7..528993c 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-handler.c
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-handler.c
e3c68b
@@ -1911,6 +1911,83 @@ glusterd_op_begin(rpcsvc_request_t *req, glusterd_op_t op, void *ctx,
e3c68b
     return ret;
e3c68b
 }
e3c68b
 
e3c68b
+int
e3c68b
+__glusterd_handle_ganesha_cmd(rpcsvc_request_t *req)
e3c68b
+{
e3c68b
+    int32_t ret = -1;
e3c68b
+    gf_cli_req cli_req = {{
e3c68b
+        0,
e3c68b
+    }};
e3c68b
+    dict_t *dict = NULL;
e3c68b
+    glusterd_op_t cli_op = GD_OP_GANESHA;
e3c68b
+    char *op_errstr = NULL;
e3c68b
+    char err_str[2048] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    xlator_t *this = NULL;
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    GF_ASSERT(this);
e3c68b
+
e3c68b
+    GF_ASSERT(req);
e3c68b
+
e3c68b
+    ret = xdr_to_generic(req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req);
e3c68b
+    if (ret < 0) {
e3c68b
+        snprintf(err_str, sizeof(err_str),
e3c68b
+                 "Failed to decode "
e3c68b
+                 "request received from cli");
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_REQ_DECODE_FAIL, "%s",
e3c68b
+               err_str);
e3c68b
+        req->rpc_err = GARBAGE_ARGS;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    if (cli_req.dict.dict_len) {
e3c68b
+        /* Unserialize the dictionary */
e3c68b
+        dict = dict_new();
e3c68b
+        if (!dict) {
e3c68b
+            ret = -1;
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+
e3c68b
+        ret = dict_unserialize(cli_req.dict.dict_val, cli_req.dict.dict_len,
e3c68b
+                               &dict);
e3c68b
+        if (ret < 0) {
e3c68b
+            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_UNSERIALIZE_FAIL,
e3c68b
+                   "failed to "
e3c68b
+                   "unserialize req-buffer to dictionary");
e3c68b
+            snprintf(err_str, sizeof(err_str),
e3c68b
+                     "Unable to decode "
e3c68b
+                     "the command");
e3c68b
+            goto out;
e3c68b
+        } else {
e3c68b
+            dict->extra_stdfree = cli_req.dict.dict_val;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
+    gf_msg_trace(this->name, 0, "Received global option request");
e3c68b
+
e3c68b
+    ret = glusterd_op_begin_synctask(req, GD_OP_GANESHA, dict);
e3c68b
+out:
e3c68b
+    if (ret) {
e3c68b
+        if (err_str[0] == '\0')
e3c68b
+            snprintf(err_str, sizeof(err_str), "Operation failed");
e3c68b
+        ret = glusterd_op_send_cli_response(cli_op, ret, 0, req, dict, err_str);
e3c68b
+    }
e3c68b
+    if (op_errstr)
e3c68b
+        GF_FREE(op_errstr);
e3c68b
+    if (dict)
e3c68b
+        dict_unref(dict);
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
+int
e3c68b
+glusterd_handle_ganesha_cmd(rpcsvc_request_t *req)
e3c68b
+{
e3c68b
+    return glusterd_big_locked_handler(req, __glusterd_handle_ganesha_cmd);
e3c68b
+}
e3c68b
+
e3c68b
 static int
e3c68b
 __glusterd_handle_reset_volume(rpcsvc_request_t *req)
e3c68b
 {
e3c68b
@@ -6644,6 +6721,8 @@ rpcsvc_actor_t gd_svc_cli_actors[GLUSTER_CLI_MAXVALUE] = {
e3c68b
     [GLUSTER_CLI_BARRIER_VOLUME] = {"BARRIER_VOLUME",
e3c68b
                                     GLUSTER_CLI_BARRIER_VOLUME,
e3c68b
                                     glusterd_handle_barrier, NULL, 0, DRC_NA},
e3c68b
+    [GLUSTER_CLI_GANESHA] = {"GANESHA", GLUSTER_CLI_GANESHA,
e3c68b
+                             glusterd_handle_ganesha_cmd, NULL, 0, DRC_NA},
e3c68b
     [GLUSTER_CLI_GET_VOL_OPT] = {"GET_VOL_OPT", GLUSTER_CLI_GET_VOL_OPT,
e3c68b
                                  glusterd_handle_get_vol_opt, NULL, 0, DRC_NA},
e3c68b
     [GLUSTER_CLI_BITROT] = {"BITROT", GLUSTER_CLI_BITROT,
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-messages.h b/xlators/mgmt/glusterd/src/glusterd-messages.h
e3c68b
index 1a4bd54..9558480 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-messages.h
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-messages.h
e3c68b
@@ -297,7 +297,7 @@ GLFS_MSGID(
e3c68b
     GD_MSG_LOCALTIME_LOGGING_VOL_OPT_VALIDATE_FAIL,
e3c68b
     GD_MSG_LOCALTIME_LOGGING_ENABLE, GD_MSG_LOCALTIME_LOGGING_DISABLE,
e3c68b
     GD_MSG_PORTS_EXHAUSTED, GD_MSG_CHANGELOG_GET_FAIL,
e3c68b
-    GD_MSG_MANAGER_FUNCTION_FAILED,
e3c68b
+    GD_MSG_MANAGER_FUNCTION_FAILED, GD_MSG_NFS_GANESHA_DISABLED,
e3c68b
     GD_MSG_DAEMON_LOG_LEVEL_VOL_OPT_VALIDATE_FAIL);
e3c68b
 
e3c68b
 #endif /* !_GLUSTERD_MESSAGES_H_ */
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-op-sm.c b/xlators/mgmt/glusterd/src/glusterd-op-sm.c
e3c68b
index 12d857a..a630c48 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-op-sm.c
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-op-sm.c
e3c68b
@@ -1176,6 +1176,13 @@ glusterd_op_stage_set_volume(dict_t *dict, char **op_errstr)
e3c68b
         if (ret)
e3c68b
             goto out;
e3c68b
 
e3c68b
+        if ((strcmp(key, "ganesha.enable") == 0) &&
e3c68b
+            (strcmp(value, "off") == 0)) {
e3c68b
+            ret = ganesha_manage_export(dict, "off", op_errstr);
e3c68b
+            if (ret)
e3c68b
+                goto out;
e3c68b
+        }
e3c68b
+
e3c68b
         ret = glusterd_check_quota_cmd(key, value, errstr, sizeof(errstr));
e3c68b
         if (ret)
e3c68b
             goto out;
e3c68b
@@ -1677,6 +1684,20 @@ glusterd_op_stage_reset_volume(dict_t *dict, char **op_errstr)
e3c68b
         goto out;
e3c68b
     }
e3c68b
 
e3c68b
+    /* *
e3c68b
+     * If key ganesha.enable is set, then volume should be unexported from
e3c68b
+     * ganesha server. Also it is a volume-level option, perform only when
e3c68b
+     * volume name not equal to "all"(in other words if volinfo != NULL)
e3c68b
+     */
e3c68b
+    if (volinfo && (!strcmp(key, "all") || !strcmp(key, "ganesha.enable"))) {
e3c68b
+        if (glusterd_check_ganesha_export(volinfo)) {
e3c68b
+            ret = ganesha_manage_export(dict, "off", op_errstr);
e3c68b
+            if (ret)
e3c68b
+                gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_NFS_GNS_RESET_FAIL,
e3c68b
+                       "Could not reset ganesha.enable key");
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
     if (strcmp(key, "all")) {
e3c68b
         exists = glusterd_check_option_exists(key, &key_fixed);
e3c68b
         if (exists == -1) {
e3c68b
@@ -2393,6 +2414,15 @@ glusterd_op_reset_volume(dict_t *dict, char **op_rspstr)
e3c68b
         }
e3c68b
     }
e3c68b
 
e3c68b
+    if (!strcmp(key, "ganesha.enable") || !strcmp(key, "all")) {
e3c68b
+        if (glusterd_check_ganesha_export(volinfo)) {
e3c68b
+            ret = manage_export_config(volname, "off", op_rspstr);
e3c68b
+            if (ret)
e3c68b
+                gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_NFS_GNS_RESET_FAIL,
e3c68b
+                       "Could not reset ganesha.enable key");
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
 out:
e3c68b
     GF_FREE(key_fixed);
e3c68b
     if (quorum_action)
e3c68b
@@ -2964,6 +2994,10 @@ glusterd_op_set_volume(dict_t *dict, char **errstr)
e3c68b
             }
e3c68b
         }
e3c68b
 
e3c68b
+        ret = glusterd_check_ganesha_cmd(key, value, errstr, dict);
e3c68b
+        if (ret == -1)
e3c68b
+            goto out;
e3c68b
+
e3c68b
         if (!is_key_glusterd_hooks_friendly(key)) {
e3c68b
             ret = glusterd_check_option_exists(key, &key_fixed);
e3c68b
             GF_ASSERT(ret);
e3c68b
@@ -4494,7 +4528,8 @@ glusterd_op_build_payload(dict_t **req, char **op_errstr, dict_t *op_ctx)
e3c68b
 
e3c68b
         case GD_OP_SYNC_VOLUME:
e3c68b
         case GD_OP_COPY_FILE:
e3c68b
-        case GD_OP_SYS_EXEC: {
e3c68b
+        case GD_OP_SYS_EXEC:
e3c68b
+        case GD_OP_GANESHA: {
e3c68b
             dict_copy(dict, req_dict);
e3c68b
         } break;
e3c68b
 
e3c68b
@@ -5944,6 +5979,10 @@ glusterd_op_stage_validate(glusterd_op_t op, dict_t *dict, char **op_errstr,
e3c68b
             ret = glusterd_op_stage_set_volume(dict, op_errstr);
e3c68b
             break;
e3c68b
 
e3c68b
+        case GD_OP_GANESHA:
e3c68b
+            ret = glusterd_op_stage_set_ganesha(dict, op_errstr);
e3c68b
+            break;
e3c68b
+
e3c68b
         case GD_OP_RESET_VOLUME:
e3c68b
             ret = glusterd_op_stage_reset_volume(dict, op_errstr);
e3c68b
             break;
e3c68b
@@ -6074,7 +6113,9 @@ glusterd_op_commit_perform(glusterd_op_t op, dict_t *dict, char **op_errstr,
e3c68b
         case GD_OP_SET_VOLUME:
e3c68b
             ret = glusterd_op_set_volume(dict, op_errstr);
e3c68b
             break;
e3c68b
-
e3c68b
+        case GD_OP_GANESHA:
e3c68b
+            ret = glusterd_op_set_ganesha(dict, op_errstr);
e3c68b
+            break;
e3c68b
         case GD_OP_RESET_VOLUME:
e3c68b
             ret = glusterd_op_reset_volume(dict, op_errstr);
e3c68b
             break;
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c b/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c
e3c68b
index 2958443..041946d 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c
e3c68b
@@ -3788,6 +3788,148 @@ out:
e3c68b
     return ret;
e3c68b
 }
e3c68b
 
e3c68b
+/* *
e3c68b
+ * Here there are two possibilities, either destination is snaphot or
e3c68b
+ * clone. In the case of snapshot nfs_ganesha export file will be copied
e3c68b
+ * to snapdir. If it is clone , then new export file will be created for
e3c68b
+ * the clone in the GANESHA_EXPORT_DIRECTORY, replacing occurences of
e3c68b
+ * volname with clonename
e3c68b
+ */
e3c68b
+int
e3c68b
+glusterd_copy_nfs_ganesha_file(glusterd_volinfo_t *src_vol,
e3c68b
+                               glusterd_volinfo_t *dest_vol)
e3c68b
+{
e3c68b
+    int32_t ret = -1;
e3c68b
+    char snap_dir[PATH_MAX] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    char src_path[PATH_MAX] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    char dest_path[PATH_MAX] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    char buffer[BUFSIZ] = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    char *find_ptr = NULL;
e3c68b
+    char *buff_ptr = NULL;
e3c68b
+    char *tmp_ptr = NULL;
e3c68b
+    xlator_t *this = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    struct stat stbuf = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+    FILE *src = NULL;
e3c68b
+    FILE *dest = NULL;
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    GF_VALIDATE_OR_GOTO("snapshot", this, out);
e3c68b
+    priv = this->private;
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, priv, out);
e3c68b
+
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, src_vol, out);
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, dest_vol, out);
e3c68b
+
e3c68b
+    if (glusterd_check_ganesha_export(src_vol) == _gf_false) {
e3c68b
+        gf_msg_debug(this->name, 0,
e3c68b
+                     "%s is not exported via "
e3c68b
+                     "NFS-Ganesha. Skipping copy of export conf.",
e3c68b
+                     src_vol->volname);
e3c68b
+        ret = 0;
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    if (src_vol->is_snap_volume) {
e3c68b
+        GLUSTERD_GET_SNAP_DIR(snap_dir, src_vol->snapshot, priv);
e3c68b
+        ret = snprintf(src_path, PATH_MAX, "%s/export.%s.conf", snap_dir,
e3c68b
+                       src_vol->snapshot->snapname);
e3c68b
+    } else {
e3c68b
+        ret = snprintf(src_path, PATH_MAX, "%s/export.%s.conf",
e3c68b
+                       GANESHA_EXPORT_DIRECTORY, src_vol->volname);
e3c68b
+    }
e3c68b
+    if (ret < 0 || ret >= PATH_MAX)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    ret = sys_lstat(src_path, &stbuf);
e3c68b
+    if (ret) {
e3c68b
+        /*
e3c68b
+         * This code path is hit, only when the src_vol is being *
e3c68b
+         * exported via NFS-Ganesha. So if the conf file is not  *
e3c68b
+         * available, we fail the snapshot operation.            *
e3c68b
+         */
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
e3c68b
+               "Stat on %s failed with %s", src_path, strerror(errno));
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    if (dest_vol->is_snap_volume) {
e3c68b
+        memset(snap_dir, 0, PATH_MAX);
e3c68b
+        GLUSTERD_GET_SNAP_DIR(snap_dir, dest_vol->snapshot, priv);
e3c68b
+        ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
e3c68b
+                       snap_dir, dest_vol->snapshot->snapname);
e3c68b
+        if (ret < 0)
e3c68b
+            goto out;
e3c68b
+
e3c68b
+        ret = glusterd_copy_file(src_path, dest_path);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
e3c68b
+                   "Failed to copy %s in %s", src_path, dest_path);
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+
e3c68b
+    } else {
e3c68b
+        ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
e3c68b
+                       GANESHA_EXPORT_DIRECTORY, dest_vol->volname);
e3c68b
+        if (ret < 0)
e3c68b
+            goto out;
e3c68b
+
e3c68b
+        src = fopen(src_path, "r");
e3c68b
+        dest = fopen(dest_path, "w");
e3c68b
+
e3c68b
+        if (!src || !dest) {
e3c68b
+            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_FILE_OP_FAILED,
e3c68b
+                   "Failed to open %s", dest ? src_path : dest_path);
e3c68b
+            ret = -1;
e3c68b
+            goto out;
e3c68b
+        }
e3c68b
+
e3c68b
+        /* *
e3c68b
+         * if the source volume is snapshot, the export conf file
e3c68b
+         * consists of orginal volname
e3c68b
+         */
e3c68b
+        if (src_vol->is_snap_volume)
e3c68b
+            find_ptr = gf_strdup(src_vol->parent_volname);
e3c68b
+        else
e3c68b
+            find_ptr = gf_strdup(src_vol->volname);
e3c68b
+
e3c68b
+        if (!find_ptr)
e3c68b
+            goto out;
e3c68b
+
e3c68b
+        /* Replacing volname with clonename */
e3c68b
+        while (fgets(buffer, BUFSIZ, src)) {
e3c68b
+            buff_ptr = buffer;
e3c68b
+            while ((tmp_ptr = strstr(buff_ptr, find_ptr))) {
e3c68b
+                while (buff_ptr < tmp_ptr)
e3c68b
+                    fputc((int)*buff_ptr++, dest);
e3c68b
+                fputs(dest_vol->volname, dest);
e3c68b
+                buff_ptr += strlen(find_ptr);
e3c68b
+            }
e3c68b
+            fputs(buff_ptr, dest);
e3c68b
+            memset(buffer, 0, BUFSIZ);
e3c68b
+        }
e3c68b
+    }
e3c68b
+out:
e3c68b
+    if (src)
e3c68b
+        fclose(src);
e3c68b
+    if (dest)
e3c68b
+        fclose(dest);
e3c68b
+    if (find_ptr)
e3c68b
+        GF_FREE(find_ptr);
e3c68b
+
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
 int32_t
e3c68b
 glusterd_restore_geo_rep_files(glusterd_volinfo_t *snap_vol)
e3c68b
 {
e3c68b
@@ -3876,6 +4018,60 @@ out:
e3c68b
     return ret;
e3c68b
 }
e3c68b
 
e3c68b
+int
e3c68b
+glusterd_restore_nfs_ganesha_file(glusterd_volinfo_t *src_vol,
e3c68b
+                                  glusterd_snap_t *snap)
e3c68b
+{
e3c68b
+    int32_t ret = -1;
e3c68b
+    char snap_dir[PATH_MAX] = "";
e3c68b
+    char src_path[PATH_MAX] = "";
e3c68b
+    char dest_path[PATH_MAX] = "";
e3c68b
+    xlator_t *this = NULL;
e3c68b
+    glusterd_conf_t *priv = NULL;
e3c68b
+    struct stat stbuf = {
e3c68b
+        0,
e3c68b
+    };
e3c68b
+
e3c68b
+    this = THIS;
e3c68b
+    GF_VALIDATE_OR_GOTO("snapshot", this, out);
e3c68b
+    priv = this->private;
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, priv, out);
e3c68b
+
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, src_vol, out);
e3c68b
+    GF_VALIDATE_OR_GOTO(this->name, snap, out);
e3c68b
+
e3c68b
+    GLUSTERD_GET_SNAP_DIR(snap_dir, snap, priv);
e3c68b
+
e3c68b
+    ret = snprintf(src_path, sizeof(src_path), "%s/export.%s.conf", snap_dir,
e3c68b
+                   snap->snapname);
e3c68b
+    if (ret < 0)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    ret = sys_lstat(src_path, &stbuf);
e3c68b
+    if (ret) {
e3c68b
+        if (errno == ENOENT) {
e3c68b
+            ret = 0;
e3c68b
+            gf_msg_debug(this->name, 0, "%s not found", src_path);
e3c68b
+        } else
e3c68b
+            gf_msg(this->name, GF_LOG_WARNING, errno, GD_MSG_FILE_OP_FAILED,
e3c68b
+                   "Stat on %s failed with %s", src_path, strerror(errno));
e3c68b
+        goto out;
e3c68b
+    }
e3c68b
+
e3c68b
+    ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
e3c68b
+                   GANESHA_EXPORT_DIRECTORY, src_vol->volname);
e3c68b
+    if (ret < 0)
e3c68b
+        goto out;
e3c68b
+
e3c68b
+    ret = glusterd_copy_file(src_path, dest_path);
e3c68b
+    if (ret)
e3c68b
+        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
e3c68b
+               "Failed to copy %s in %s", src_path, dest_path);
e3c68b
+
e3c68b
+out:
e3c68b
+    return ret;
e3c68b
+}
e3c68b
+
e3c68b
 /* Snapd functions */
e3c68b
 int
e3c68b
 glusterd_is_snapd_enabled(glusterd_volinfo_t *volinfo)
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-store.h b/xlators/mgmt/glusterd/src/glusterd-store.h
e3c68b
index e60be6e..41d0001 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-store.h
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-store.h
e3c68b
@@ -118,6 +118,8 @@ typedef enum glusterd_store_ver_ac_ {
e3c68b
 #define GLUSTERD_STORE_KEY_VOL_MIGRATIONS_SKIPPED "migration-skipped"
e3c68b
 #define GLUSTERD_STORE_KEY_VOL_MIGRATION_RUN_TIME "migration-run-time"
e3c68b
 
e3c68b
+#define GLUSTERD_STORE_KEY_GANESHA_GLOBAL "nfs-ganesha"
e3c68b
+
e3c68b
 int32_t
e3c68b
 glusterd_store_volinfo(glusterd_volinfo_t *volinfo,
e3c68b
                        glusterd_volinfo_ver_ac_t ac);
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-ops.c b/xlators/mgmt/glusterd/src/glusterd-volume-ops.c
e3c68b
index 86ef470..a0417ca 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-volume-ops.c
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-volume-ops.c
e3c68b
@@ -1823,6 +1823,18 @@ glusterd_op_stage_stop_volume(dict_t *dict, char **op_errstr)
e3c68b
         goto out;
e3c68b
     }
e3c68b
 
e3c68b
+    ret = glusterd_check_ganesha_export(volinfo);
e3c68b
+    if (ret) {
e3c68b
+        ret = ganesha_manage_export(dict, "off", op_errstr);
e3c68b
+        if (ret) {
e3c68b
+            gf_msg(THIS->name, GF_LOG_WARNING, 0,
e3c68b
+                   GD_MSG_NFS_GNS_UNEXPRT_VOL_FAIL,
e3c68b
+                   "Could not "
e3c68b
+                   "unexport volume via NFS-Ganesha");
e3c68b
+            ret = 0;
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
     if (glusterd_is_defrag_on(volinfo)) {
e3c68b
         snprintf(msg, sizeof(msg),
e3c68b
                  "rebalance session is "
e3c68b
@@ -2674,6 +2686,8 @@ glusterd_op_start_volume(dict_t *dict, char **op_errstr)
e3c68b
     xlator_t *this = NULL;
e3c68b
     glusterd_conf_t *conf = NULL;
e3c68b
     glusterd_svc_t *svc = NULL;
e3c68b
+    char *str = NULL;
e3c68b
+    gf_boolean_t option = _gf_false;
e3c68b
 
e3c68b
     this = THIS;
e3c68b
     GF_ASSERT(this);
e3c68b
@@ -2731,6 +2745,29 @@ glusterd_op_start_volume(dict_t *dict, char **op_errstr)
e3c68b
         }
e3c68b
     }
e3c68b
 
e3c68b
+    ret = dict_get_str(conf->opts, GLUSTERD_STORE_KEY_GANESHA_GLOBAL, &str);
e3c68b
+    if (ret != 0) {
e3c68b
+        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_DICT_GET_FAILED,
e3c68b
+               "Global dict not present.");
e3c68b
+        ret = 0;
e3c68b
+
e3c68b
+    } else {
e3c68b
+        ret = gf_string2boolean(str, &option);
e3c68b
+        /* Check if the feature is enabled and set nfs-disable to true */
e3c68b
+        if (option) {
e3c68b
+            gf_msg_debug(this->name, 0, "NFS-Ganesha is enabled");
e3c68b
+            /* Gluster-nfs should not start when NFS-Ganesha is enabled*/
e3c68b
+            ret = dict_set_str(volinfo->dict, NFS_DISABLE_MAP_KEY, "on");
e3c68b
+            if (ret) {
e3c68b
+                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
e3c68b
+                       "Failed to set nfs.disable for"
e3c68b
+                       "volume %s",
e3c68b
+                       volname);
e3c68b
+                goto out;
e3c68b
+            }
e3c68b
+        }
e3c68b
+    }
e3c68b
+
e3c68b
     ret = glusterd_start_volume(volinfo, flags, _gf_true);
e3c68b
     if (ret)
e3c68b
         goto out;
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
e3c68b
index d1244e4..13f423a 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
e3c68b
@@ -2597,6 +2597,13 @@ struct volopt_map_entry glusterd_volopt_map[] = {
e3c68b
         .voltype = "features/upcall",
e3c68b
         .op_version = GD_OP_VERSION_3_7_0,
e3c68b
     },
e3c68b
+    {
e3c68b
+        .key = "ganesha.enable",
e3c68b
+        .voltype = "features/ganesha",
e3c68b
+        .value = "off",
e3c68b
+        .option = "ganesha.enable",
e3c68b
+        .op_version = GD_OP_VERSION_3_7_0,
e3c68b
+    },
e3c68b
     /* Lease translator options */
e3c68b
     {
e3c68b
         .key = "features.leases",
e3c68b
diff --git a/xlators/mgmt/glusterd/src/glusterd.h b/xlators/mgmt/glusterd/src/glusterd.h
e3c68b
index 67867f8..5135181 100644
e3c68b
--- a/xlators/mgmt/glusterd/src/glusterd.h
e3c68b
+++ b/xlators/mgmt/glusterd/src/glusterd.h
e3c68b
@@ -61,6 +61,9 @@
e3c68b
 #define GLUSTERD_LOCALTIME_LOGGING_KEY "cluster.localtime-logging"
e3c68b
 #define GLUSTERD_DAEMON_LOG_LEVEL_KEY "cluster.daemon-log-level"
e3c68b
 
e3c68b
+#define GANESHA_HA_CONF CONFDIR "/ganesha-ha.conf"
e3c68b
+#define GANESHA_EXPORT_DIRECTORY CONFDIR "/exports"
e3c68b
+
e3c68b
 #define GLUSTERD_SNAPS_MAX_HARD_LIMIT 256
e3c68b
 #define GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT 90
e3c68b
 #define GLUSTERD_SNAPS_MAX_SOFT_LIMIT_PERCENT 100
e3c68b
@@ -1356,6 +1359,25 @@ glusterd_op_stop_volume(dict_t *dict);
e3c68b
 int
e3c68b
 glusterd_op_delete_volume(dict_t *dict);
e3c68b
 int
e3c68b
+glusterd_handle_ganesha_op(dict_t *dict, char **op_errstr, char *key,
e3c68b
+                           char *value);
e3c68b
+int
e3c68b
+glusterd_check_ganesha_cmd(char *key, char *value, char **errstr, dict_t *dict);
e3c68b
+int
e3c68b
+glusterd_op_stage_set_ganesha(dict_t *dict, char **op_errstr);
e3c68b
+int
e3c68b
+glusterd_op_set_ganesha(dict_t *dict, char **errstr);
e3c68b
+int
e3c68b
+ganesha_manage_export(dict_t *dict, char *value, char **op_errstr);
e3c68b
+gf_boolean_t
e3c68b
+glusterd_is_ganesha_cluster();
e3c68b
+gf_boolean_t
e3c68b
+glusterd_check_ganesha_export(glusterd_volinfo_t *volinfo);
e3c68b
+int
e3c68b
+stop_ganesha(char **op_errstr);
e3c68b
+int
e3c68b
+tear_down_cluster(gf_boolean_t run_teardown);
e3c68b
+int
e3c68b
 manage_export_config(char *volname, char *value, char **op_errstr);
e3c68b
 
e3c68b
 int
e3c68b
-- 
e3c68b
1.8.3.1
e3c68b