diff options
| author | Mohammed Rafi KC <rkavunga@redhat.com> | 2015-02-23 17:28:47 +0530 | 
|---|---|---|
| committer | Vijay Bellur <vbellur@redhat.com> | 2015-03-18 00:07:55 -0700 | 
| commit | 260a6943849f99227248a8fc852a8c8fc3d1e289 (patch) | |
| tree | cf8d81b9e89a855975434638ac89abac7cde4696 | |
| parent | c99c72b35fac16e08c4d170b6a46a786caaeef58 (diff) | |
Snapshot/clone: clone of a snapshot that will act as a regular volume
snapshot clone will allow us to take a snpahot of a snapshot.
Newly created clone volume will be a regular volume with read/write
permissions.
CLI command
snapshot clone <clonename> <snapname>
Change-Id: Icadb993fa42fff787a330f8f49452da54e9db7de
BUG: 1199894
Signed-off-by: Mohammed Rafi KC <rkavunga@redhat.com>
Reviewed-on: http://review.gluster.org/9750
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Rajesh Joseph <rjoseph@redhat.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
| -rw-r--r-- | cli/src/cli-cmd-parser.c | 131 | ||||
| -rw-r--r-- | cli/src/cli-cmd-snapshot.c | 4 | ||||
| -rw-r--r-- | cli/src/cli-rpc-ops.c | 30 | ||||
| -rw-r--r-- | rpc/xdr/src/cli1-xdr.x | 1 | ||||
| -rwxr-xr-x | tests/basic/volume-snapshot-clone.t | 114 | ||||
| -rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c | 16 | ||||
| -rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-snapshot.c | 967 | 
7 files changed, 1074 insertions, 189 deletions
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index b2ef1d77104..aa512738784 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -3358,6 +3358,83 @@ out:          return ret;  } +/* snapshot clone <clonename> <snapname> + * @arg-0, dict     : Request Dictionary to be sent to server side. + * @arg-1, words    : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + *                 0 on success + */ +int +cli_snap_clone_parse (dict_t *dict, const char **words, int wordcount) { +        uint64_t        i               =       0; +        int             ret             =       -1; +        char            key[PATH_MAX]   =       ""; +        char            *clonename      =       NULL; +        unsigned int    cmdi            =       2; +        int             flags           =       0; +        /* cmdi is command index, here cmdi is "2" (gluster snapshot clone)*/ + +        GF_ASSERT (words); +        GF_ASSERT (dict); + +        if (wordcount == cmdi + 1) { +                cli_err ("Invalid Syntax."); +                gf_log ("cli", GF_LOG_ERROR, +                        "Invalid number of  words for snap clone command"); +                goto out; +        } + +        if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { +                cli_err ("snapshot clone: failed: clonename cannot exceed " +                         "255 characters."); +                gf_log ("cli", GF_LOG_ERROR, "Clone name too long"); + +                goto out; +        } + +        clonename = (char *) words[cmdi]; +        for (i = 0 ; i < strlen (clonename); i++) { +                /* Following volume name convention */ +                if (!isalnum (clonename[i]) && (clonename[i] != '_' +                                           && (clonename[i] != '-'))) { +                        /* TODO : Is this message enough?? */ +                        cli_err ("Clonename can contain only alphanumeric, " +                                 "\"-\" and \"_\" characters"); +                        goto out; +                } +        } + +        ret = dict_set_int32 (dict, "volcount", 1); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not save volcount"); +                goto out; +        } + +        ret = dict_set_str (dict, "clonename", (char *)words[cmdi]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not save clone " +                        "name(%s)", (char *)words[cmdi]); +                goto out; +        } + +        /* Filling snap name in the dictionary */ +        ret = dict_set_str (dict, "snapname", (char *)words[cmdi+1]); +        if (ret) { +                gf_log ("cli", GF_LOG_ERROR, "Could not " +                        "save snap name(%s)", (char *)words[cmdi+1]); +                goto out; +        } + + +        ret = 0; + +out: +        return ret; +} + +  /* snapshot create <snapname> <vol-name(s)> [description <description>]   *                                           [force]   * @arg-0, dict     : Request Dictionary to be sent to server side. @@ -4223,16 +4300,16 @@ out:  }  int -validate_snapname (const char *snapname, char **opwords) { +validate_op_name (const char *op, const char *opname, char **opwords) {          int     ret     =       -1;          int     i       =       0; -        GF_ASSERT (snapname); +        GF_ASSERT (opname);          GF_ASSERT (opwords);          for (i = 0 ; opwords[i] != NULL; i++) { -                if (strcmp (opwords[i], snapname) == 0) { -                        cli_out ("\"%s\" cannot be a snapname", snapname); +                if (strcmp (opwords[i], opname) == 0) { +                        cli_out ("\"%s\" cannot be a %s", opname, op);                          goto out;                  }          } @@ -4251,9 +4328,19 @@ cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options,          char               *w         = NULL;          char               *opwords[] = {"create", "delete", "restore",                                          "activate", "deactivate", "list", -                                        "status", "config", "info", NULL}; +                                        "status", "config", "info", "clone", +                                        NULL};          char               *invalid_snapnames[] = {"description", "force",                                                    "volume", "all", NULL}; +        char               *invalid_volnames[]  = {"volume", "type", +                                                   "subvolumes", "option", +                                                   "end-volume", "all", +                                                   "volume_not_in_ring", +                                                   "description", "force", +                                                   "snap-max-hard-limit", +                                                   "snap-max-soft-limit", +                                                   "auto-delete", +                                                   "activate-on-create", NULL};          GF_ASSERT (words);          GF_ASSERT (options); @@ -4295,6 +4382,8 @@ cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options,                  type = GF_SNAP_OPTION_TYPE_ACTIVATE;          } else if (!strcmp (w, "deactivate")) {                  type = GF_SNAP_OPTION_TYPE_DEACTIVATE; +        } else if (!strcmp(w, "clone")) { +                type = GF_SNAP_OPTION_TYPE_CLONE;          }          if (type != GF_SNAP_OPTION_TYPE_CONFIG && @@ -4339,7 +4428,8 @@ cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options,                          goto out;                  } -                ret = validate_snapname (words[2], invalid_snapnames); +                ret = validate_op_name ("snapname", words[2], +                                        invalid_snapnames);                  if (ret) {                          goto out;                  } @@ -4352,6 +4442,35 @@ cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options,                  }                  break; +        case GF_SNAP_OPTION_TYPE_CLONE: +                /* Syntax : +                 * gluster snapshot clone <clonename> <snapname> +                 */ +                /* In cases where the clonename is not given then +                 * parsing fails & snapname cannot be "description", +                 * "force" and "volume", that check is made here +                 */ +                if (wordcount == 2) { +                        ret = -1; +                        gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); +                        goto out; +                } + +                ret = validate_op_name ("clonename", words[2], +                                        invalid_volnames); +                if (ret) { +                        goto out; +                } + +                ret = cli_snap_clone_parse (dict, words, wordcount); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "clone command parsing failed."); +                        goto out; +                } +                break; + +          case GF_SNAP_OPTION_TYPE_INFO:                  /* Syntax :                   * gluster snapshot info [(snapname] | [vol <volname>)] diff --git a/cli/src/cli-cmd-snapshot.c b/cli/src/cli-cmd-snapshot.c index 7c6a52e0a88..e885a641de2 100644 --- a/cli/src/cli-cmd-snapshot.c +++ b/cli/src/cli-cmd-snapshot.c @@ -87,6 +87,10 @@ struct cli_cmd snapshot_cmds[] = {            cli_cmd_snapshot_cbk,            "Snapshot Create."          }, +        { "snapshot clone <clonename> <snapname>", +          cli_cmd_snapshot_cbk, +          "Snapshot Clone." +        },          { "snapshot restore <snapname>",            cli_cmd_snapshot_cbk,            "Snapshot Restore." diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 8ea43f824bc..a02761d5e6e 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -8870,6 +8870,7 @@ gf_cli_snapshot_cbk (struct rpc_req *req, struct iovec *iov,          gf_cli_rsp            rsp                      = {0, };          dict_t               *dict                     = NULL;          char                 *snap_name                = NULL; +        char                 *clone_name               = NULL;          int32_t               type                     =  0;          call_frame_t         *frame                    = NULL;          gf_boolean_t         snap_driven               = _gf_false; @@ -8961,6 +8962,35 @@ gf_cli_snapshot_cbk (struct rpc_req *req, struct iovec *iov,                  ret = 0;                  break; +        case GF_SNAP_OPTION_TYPE_CLONE: +                if (rsp.op_ret) { +                        cli_err("snapshot clone: failed: %s", +                                 rsp.op_errstr ? rsp.op_errstr : +                                 "Please check log file for details"); +                                 ret = rsp.op_ret; +                                 goto out; +                } + +                ret = dict_get_str (dict, "clonename", &clone_name); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Failed to get clone name"); +                        goto out; +                } + +                ret = dict_get_str (dict, "snapname", &snap_name); +                if (ret) { +                        gf_log ("cli", GF_LOG_ERROR, +                                "Failed to get snapname name"); +                        goto out; +                } + +                cli_out ("snapshot clone: success: Clone %s created " +                                        "successfully", clone_name); + +                ret = 0; +                break; +          case GF_SNAP_OPTION_TYPE_RESTORE:                  /* TODO: Check if rsp.op_ret needs to be checked here. Or is                   * it ok to check this in the start of the function where we diff --git a/rpc/xdr/src/cli1-xdr.x b/rpc/xdr/src/cli1-xdr.x index f7bfcf469d5..565f2182f6e 100644 --- a/rpc/xdr/src/cli1-xdr.x +++ b/rpc/xdr/src/cli1-xdr.x @@ -151,6 +151,7 @@ enum gf1_cli_snapshot {          GF_SNAP_OPTION_TYPE_LIST,          GF_SNAP_OPTION_TYPE_STATUS,          GF_SNAP_OPTION_TYPE_CONFIG, +        GF_SNAP_OPTION_TYPE_CLONE,          GF_SNAP_OPTION_TYPE_INFO  }; diff --git a/tests/basic/volume-snapshot-clone.t b/tests/basic/volume-snapshot-clone.t new file mode 100755 index 00000000000..b142804e51b --- /dev/null +++ b/tests/basic/volume-snapshot-clone.t @@ -0,0 +1,114 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc +. $(dirname $0)/../cluster.rc +. $(dirname $0)/../snapshot.rc + + +V1="patchy2" + +function create_volumes() { +        $CLI_1 volume create $V0 $H1:$L1 & +        PID_1=$! + +        $CLI_2 volume create $V1 $H2:$L2 $H3:$L3 & +        PID_2=$! + +        wait $PID_1 $PID_2 +} + +function create_snapshots() { + +        $CLI_1 snapshot create $1 $2 no-timestamp& +        PID_1=$! + +        wait $PID_1 +} + + + +function delete_snapshot() { +        $CLI_1 snapshot delete $1 & +        PID_1=$! + +        wait $PID_1 +} + +cleanup; + +TEST verify_lvm_version; +#Create cluster with 3 nodes +TEST launch_cluster 3; +TEST setup_lvm 3 + +TEST $CLI_1 peer probe $H2; +TEST $CLI_1 peer probe $H3; +EXPECT_WITHIN $PROBE_TIMEOUT 2 peer_count; + +create_volumes +EXPECT 'Created' volinfo_field $V0 'Status'; +EXPECT 'Created' volinfo_field $V1 'Status'; + +start_volumes 2 +EXPECT 'Started' volinfo_field $V0 'Status'; +EXPECT 'Started' volinfo_field $V1 'Status'; + +TEST $CLI_1 snapshot config activate-on-create enable + +#Snapshot Operations +create_snapshots ${V0}_snap ${V0}; +create_snapshots ${V1}_snap ${V1}; + +EXPECT 'Started' snapshot_status ${V0}_snap; +EXPECT 'Started' snapshot_status ${V1}_snap; + +sleep 5 +TEST $CLI_1 snapshot clone ${V0}_clone ${V0}_snap +TEST $CLI_1 snapshot clone ${V1}_clone ${V1}_snap + +EXPECT 'Created' volinfo_field ${V0}_clone 'Status'; +EXPECT 'Created' volinfo_field ${V1}_clone 'Status'; + +TEST $CLI_1 volume start ${V0}_clone force; +TEST $CLI_1 volume start ${V1}_clone force; + +EXPECT 'Started' volinfo_field ${V0}_clone 'Status'; +EXPECT 'Started' volinfo_field ${V1}_clone 'Status'; + + +TEST glusterfs -s $H1 --volfile-id=/${V0}_clone $M0 +TEST glusterfs -s $H2 --volfile-id=/${V1}_clone $M1 + +TEST touch $M0/file1 +TEST touch $M1/file1 + +TEST echo "Hello world" $M0/file1 +TEST echo "Hello world" $M1/file1 + +EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0 +EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M1 + +TEST kill_glusterd 2; +TEST $glusterd_2; + +sleep 5 + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'Started' volinfo_field ${V0}_clone 'Status'; +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'Started' volinfo_field ${V1}_clone 'Status'; +#Clean up +stop_force_volumes 2 +EXPECT 'Stopped' volinfo_field $V0 'Status'; +EXPECT 'Stopped' volinfo_field $V1 'Status'; + +TEST delete_snapshot ${V0}_snap +TEST delete_snapshot ${V1}_snap + +TEST ! snapshot_exists 1 ${V0}_snap +TEST ! snapshot_exists 1 ${V1}_snap + +delete_volumes 2 +TEST ! volume_exists $V0 +TEST ! volume_exists $V1 + +cleanup; diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c b/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c index 97935b6a975..fb22fc1c23e 100644 --- a/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot-utils.c @@ -2172,6 +2172,7 @@ glusterd_snap_use_rsp_dict (dict_t *dst, dict_t *src)          switch (snap_command) {          case GF_SNAP_OPTION_TYPE_CREATE:          case GF_SNAP_OPTION_TYPE_DELETE: +        case GF_SNAP_OPTION_TYPE_CLONE:                  ret = glusterd_snap_create_use_rsp_dict (dst, src);                  if (ret) {                          gf_log ("", GF_LOG_ERROR, "Unable to use rsp dict"); @@ -2781,6 +2782,21 @@ glusterd_snap_quorum_check (dict_t *dict, gf_boolean_t snap_volume,                          goto out;                  }                  break; +        case GF_SNAP_OPTION_TYPE_CLONE: + +                if (!does_gd_meet_server_quorum (this, peers_list, _gf_true)) { +                        ret = -1; +                        snprintf (err_str, sizeof (err_str), +                                  "glusterds are not in quorum"); +                        gf_log (this->name, GF_LOG_WARNING, "%s", +                                err_str); +                        *op_errstr = gf_strdup (err_str); +                        goto out; +                } + +                gf_log (this->name, GF_LOG_DEBUG, "glusterds are in " +                        "quorum"); +                break;          case GF_SNAP_OPTION_TYPE_DELETE:          case GF_SNAP_OPTION_TYPE_RESTORE:                  if (!does_gd_meet_server_quorum (this, peers_list, _gf_true)) { diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot.c b/xlators/mgmt/glusterd/src/glusterd-snapshot.c index 93e695c85f5..761115d22ed 100644 --- a/xlators/mgmt/glusterd/src/glusterd-snapshot.c +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot.c @@ -1388,7 +1388,7 @@ out:          return ret;  }  int -glusterd_snap_create_pre_val_use_rsp_dict (dict_t *dst, dict_t *src) +glusterd_snap_create_clone_pre_val_use_rsp_dict (dict_t *dst, dict_t *src)  {          char                  *snap_brick_dir        = NULL;          char                  *snap_device           = NULL; @@ -1696,7 +1696,9 @@ glusterd_snap_pre_validate_use_rsp_dict (dict_t *dst, dict_t *src)          switch (snap_command) {          case GF_SNAP_OPTION_TYPE_CREATE: -                ret = glusterd_snap_create_pre_val_use_rsp_dict (dst, src); +        case GF_SNAP_OPTION_TYPE_CLONE: +                ret = glusterd_snap_create_clone_pre_val_use_rsp_dict (dst, +                                                                       src);                  if (ret) {                          gf_log (this->name, GF_LOG_ERROR, "Unable to use "                                  "rsp dict"); @@ -1833,21 +1835,302 @@ out:  }  int +glusterd_snap_create_clone_common_prevalidate (dict_t *rsp_dict, int flags, +            char *snapname, char *err_str, char *snap_volname, int64_t volcount, +            glusterd_volinfo_t *volinfo, gf_loglevel_t *loglevel, int clone)  { + +        char                  *device            = NULL; +        char                   key[PATH_MAX]     = ""; +        int                    ret               = -1; +        int64_t                i                 = 1; +        int64_t                brick_order       = 0; +        int64_t                brick_count       = 0; +        xlator_t              *this              = NULL; +        glusterd_conf_t       *conf              = NULL; +        glusterd_brickinfo_t  *brickinfo         = NULL; + +        this = THIS; +        conf = this->private; +        GF_ASSERT (conf); + +        if (!snapname || !volinfo) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to validate " +                        "snapname or volume information"); +                ret = -1; +                goto out; +        } + +        cds_list_for_each_entry (brickinfo, &volinfo->bricks, +                                 brick_list) { +                if (uuid_compare (brickinfo->uuid, MY_UUID)) { +                        brick_order++; +                        continue; +                } + +                if (!glusterd_is_brick_started (brickinfo)) { +                        if (!clone && (flags & GF_CLI_FLAG_OP_FORCE)) { +                                gf_log (this->name, GF_LOG_WARNING, +                                        "brick %s:%s is not started", +                                        brickinfo->hostname, +                                        brickinfo->path); +                                brick_order++; +                                brick_count++; +                                continue; +                        } +                        if (!clone) { +                        snprintf (err_str, PATH_MAX, +                                  "brick %s:%s is not started. " +                                  "Please start the stopped brick " +                                  "and then issue snapshot create " +                                  "command or use [force] option in " +                                  "snapshot create to override this " +                                  "behavior.", brickinfo->hostname, +                                  brickinfo->path); +                        } else { +                        snprintf (err_str, PATH_MAX, +                                  "brick %s:%s is not started. " +                                  "Please start the stopped brick " +                                  "and then issue snapshot clone " +                                  "command ", brickinfo->hostname, +                                  brickinfo->path); +                        } +                        ret = -1; +                        goto out; +                } + + +                device = glusterd_get_brick_mount_device +                                          (brickinfo->path); +                if (!device) { +                        snprintf (err_str, PATH_MAX, +                                  "getting device name for the brick " +                                 "%s:%s failed", brickinfo->hostname, +                                  brickinfo->path); +                        ret = -1; +                        goto out; +                } +                if (!clone) { +                     if (!glusterd_is_thinp_brick (device)) { +                                snprintf (err_str, PATH_MAX, +                                          "Snapshot is supported only for " +                                          "thin provisioned LV. Ensure that " +                                          "all bricks of %s are thinly " +                                          "provisioned LV.", volinfo->volname); +                                ret = -1; +                                goto out; +                     } +                } + +                device = glusterd_build_snap_device_path (device, +                                                  snap_volname, +                                                  brick_count); +                if (!device) { +                        snprintf (err_str, PATH_MAX, +                                  "cannot copy the snapshot device " +                                "name (volname: %s, snapname: %s)", +                                volinfo->volname, snapname); +                        *loglevel = GF_LOG_WARNING; +                        ret = -1; +                        goto out; +                } + +                snprintf (key, sizeof(key), +                        "vol%"PRId64".brick_snapdevice%"PRId64, +                        i, brick_count); +                ret = dict_set_dynstr (rsp_dict, key, device); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to set %s", key); +                        GF_FREE (device); +                        goto out; +                } +                device = NULL; + +                ret = glusterd_update_mntopts (brickinfo->path, +                                               brickinfo); +                if (ret) { +                         gf_log (this->name, GF_LOG_ERROR, "Failed to " +                                "update mount options for %s brick", +                                brickinfo->path); +                } + +                snprintf (key, sizeof(key), "vol%"PRId64".fstype%" +                          PRId64, i, brick_count); +                ret = dict_set_dynstr_with_alloc (rsp_dict, key, +                                                  brickinfo->fstype); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to set %s", key); +                        goto out; +                } + +                snprintf (key, sizeof(key), "vol%"PRId64".mnt_opts%" +                          PRId64, i, brick_count); +                ret = dict_set_dynstr_with_alloc (rsp_dict, key, +                                                  brickinfo->mnt_opts); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to set %s", key); +                        goto out; +                } + +                snprintf (key, sizeof(key), "vol%"PRId64".brickdir%"PRId64, i, +                          brick_count); +                ret = dict_set_dynstr_with_alloc (rsp_dict, key, +                                                  brickinfo->mount_dir); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to set %s", key); +                        goto out; +                } + +                snprintf (key, sizeof(key) - 1, +                          "vol%"PRId64".brick%"PRId64".order", i, brick_count); +                ret = dict_set_int64 (rsp_dict, key, brick_order); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to set %s", key); +                        goto out; +                } + +                snprintf (key, sizeof (key), +                          "vol%"PRId64".brick%"PRId64".status", i, brick_order); + +                ret = glusterd_add_brick_status_to_dict (rsp_dict, +                                                         volinfo, +                                                         brickinfo, +                                                         key); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to " +                                "add brick status to dict"); +                        goto out; +                } +                brick_count++; +                brick_order++; +        } +        snprintf (key, sizeof(key) - 1, "vol%"PRId64"_brickcount", volcount); +        ret = dict_set_int64 (rsp_dict, key, brick_count); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", +                        key); +                goto out; +        } +        ret = 0; +out: +        if (device) +               GF_FREE (device); + +       return ret; + +} + + +int +glusterd_snapshot_clone_prevalidate (dict_t *dict, char **op_errstr, +                                      dict_t *rsp_dict) +{ +        char                  *clonename         = NULL; +        char                  *snapname          = NULL; +        char                   key[PATH_MAX]     = ""; +        glusterd_snap_t       *snap              = NULL; +        char                   err_str[PATH_MAX] = ""; +        int                    ret               = -1; +        int64_t                volcount          = 1; +        glusterd_volinfo_t    *snap_vol          = NULL; +        xlator_t              *this              = NULL; +        uuid_t                *snap_volid        = NULL; +        gf_loglevel_t          loglevel          = GF_LOG_ERROR; + +        this = THIS; +        GF_ASSERT (op_errstr); +        GF_ASSERT (dict); + +        ret = dict_get_str (dict, "clonename", &clonename); +        if (ret) { +                snprintf (err_str, sizeof (err_str), "Failed to " +                          "get the clone name"); +                goto out; +        } + +        ret = dict_get_str (dict, "snapname", &snapname); +        if (ret) { +                snprintf (err_str, sizeof (err_str), "Failed to get snapname"); +                goto out; +        } + +        if (glusterd_check_volume_exists(clonename)) { +                ret = -1; +                snprintf (err_str, sizeof (err_str), "Volume with name:%s " +                          "already exists", clonename); +                goto out; +        } +        /* need to find snap volinfo*/ +        snap = glusterd_find_snap_by_name (snapname); +        if (!snap) { +                ret = -1; +                snprintf (err_str, sizeof (err_str), "Failed to find :%s " +                          "snap", snapname); +                goto out; +        } + +        /* TODO : As of now there is only one volume in snapshot. +         * Change this when multiple volume snapshot is introduced +         */ +        snap_vol = list_entry (snap->volumes.next, +                                   glusterd_volinfo_t, vol_list); +        if (!snap_vol) { +                gf_log ("glusterd", GF_LOG_ERROR, "Failed to get snap " +                "volinfo %s", snap->snapname); +                goto out; +        } + +        snprintf (key, sizeof(key) - 1, "vol1_volid"); +        ret = dict_get_bin (dict, key, (void **)&snap_volid); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, +                        "Unable to fetch snap_volid"); +                goto out; +        } + + +        /* Adding snap bricks mount paths to the dict */ +        ret = glusterd_snap_create_clone_common_prevalidate (rsp_dict, 0, +                        snapname, err_str, clonename, 1, snap_vol, +                        &loglevel, 1); +        if (ret) { +                goto out; +        } +        ret = dict_set_int64 (rsp_dict, "volcount", volcount); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to set volcount"); +                goto out; +        } + +        ret = 0; +out: + +        if (ret && err_str[0] != '\0') { +                gf_log (this->name, loglevel, "%s", err_str); +                *op_errstr = gf_strdup (err_str); +        } + +        gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); +        return ret; +} + + +int  glusterd_snapshot_create_prevalidate (dict_t *dict, char **op_errstr,                                        dict_t *rsp_dict)  {          char                  *volname           = NULL;          char                  *snapname          = NULL; -        char                  *device            = NULL;          char                   key[PATH_MAX]     = "";          char                   snap_volname[64]  = "";          char                   err_str[PATH_MAX] = "";          int                    ret               = -1;          int64_t                i                 = 0;          int64_t                volcount          = 0; -        int64_t                brick_count       = 0; -        int64_t                brick_order       = 0; -        glusterd_brickinfo_t  *brickinfo         = NULL;          glusterd_volinfo_t    *volinfo           = NULL;          xlator_t              *this              = NULL;          uuid_t                *snap_volid        = NULL; @@ -1986,156 +2269,15 @@ glusterd_snapshot_create_prevalidate (dict_t *dict, char **op_errstr,                     provided by user */                  GLUSTERD_GET_UUID_NOHYPHEN (snap_volname, *snap_volid); -                brick_count = 0; -                brick_order = 0; -                /* Adding snap bricks mount paths to the dict */ -                cds_list_for_each_entry (brickinfo, &volinfo->bricks, -                                         brick_list) { -                        if (uuid_compare (brickinfo->uuid, MY_UUID)) { -                                brick_order++; -                                continue; -                        } - -                        if (!glusterd_is_brick_started (brickinfo)) { -                                if(flags & GF_CLI_FLAG_OP_FORCE) { -                                        gf_log (this->name, GF_LOG_WARNING, -                                                "brick %s:%s is not started", -                                                brickinfo->hostname, -                                                brickinfo->path); -                                        brick_order++; -                                        brick_count++; -                                        continue; -                                } - -                                snprintf (err_str, sizeof (err_str), -                                          "brick %s:%s is not started. " -                                          "Please start the stopped brick " -                                          "and then issue snapshot create " -                                          "command or use [force] option in " -                                          "snapshot create to override this " -                                          "behavior.", brickinfo->hostname, -                                          brickinfo->path); -                                ret = -1; -                                goto out; -                        } - -                        device = glusterd_get_brick_mount_device -                                                          (brickinfo->path); -                        if (!device) { -                                snprintf (err_str, sizeof (err_str), -                                          "getting device name for the brick " -                                          "%s:%s failed", brickinfo->hostname, -                                          brickinfo->path); -                                ret = -1; -                                goto out; -                        } - -                        if (!glusterd_is_thinp_brick (device)) { -                                snprintf (err_str, sizeof (err_str), -                                          "Snapshot is supported only for " -                                          "thin provisioned LV. Ensure that " -                                          "all bricks of %s are thinly " -                                          "provisioned LV.", volinfo->volname); -                                ret = -1; -                                goto out; -                        } - -                        device = glusterd_build_snap_device_path (device, -                                                                  snap_volname, -                                                                  brick_count); -                        if (!device) { -                                snprintf (err_str, sizeof (err_str), -                                          "cannot copy the snapshot device " -                                          "name (volname: %s, snapname: %s)", -                                          volinfo->volname, snapname); -                                loglevel = GF_LOG_WARNING; -                                ret = -1; -                                goto out; -                        } - -                        snprintf (key, sizeof(key), -                                  "vol%"PRId64".brick_snapdevice%"PRId64, i, -                                  brick_count); -                        ret = dict_set_dynstr (rsp_dict, key, device); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, -                                        "Failed to set %s", key); -                                GF_FREE (device); -                                goto out; -                        } -                        device = NULL; - -                        ret = glusterd_update_mntopts (brickinfo->path, -                                                       brickinfo); -                        if (ret) { -                                 gf_log (this->name, GF_LOG_ERROR, "Failed to " -                                         "update mount options for %s brick", -                                         brickinfo->path); -                        } - -                        snprintf (key, sizeof(key), "vol%"PRId64".fstype%" -                                  PRId64, i, brick_count); -                        ret = dict_set_dynstr_with_alloc (rsp_dict, key, -                                                          brickinfo->fstype); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, -                                        "Failed to set %s", key); -                                goto out; -                        } - -                        snprintf (key, sizeof(key), "vol%"PRId64".mnt_opts%" -                                  PRId64, i, brick_count); -                        ret = dict_set_dynstr_with_alloc (rsp_dict, key, -                                                          brickinfo->mnt_opts); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, -                                        "Failed to set %s", key); -                                goto out; -                        } - -                        snprintf (key, sizeof(key), "vol%"PRId64".brickdir%"PRId64, i, -                                  brick_count); -                        ret = dict_set_dynstr_with_alloc (rsp_dict, key, -                                                          brickinfo->mount_dir); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, -                                        "Failed to set %s", key); -                                goto out; -                        } - -                        snprintf (key, sizeof(key) - 1, "vol%"PRId64".brick%"PRId64".order", -                                  i, brick_count); -                        ret = dict_set_int64 (rsp_dict, key, brick_order); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, -                                        "Failed to set %s", key); -                                goto out; -                        } - -                        snprintf (key, sizeof (key), "vol%"PRId64".brick%"PRId64".status", -                                  i, brick_order); - -                        ret = glusterd_add_brick_status_to_dict (rsp_dict, -                                                                 volinfo, -                                                                 brickinfo, -                                                                 key); -                        if (ret) { -                                gf_log (this->name, GF_LOG_ERROR, "failed to " -                                        "add brick status to dict"); -                                goto out; -                        } - -                        brick_count++; -                        brick_order++; -                } - -                snprintf (key, sizeof(key) - 1, "vol%"PRId64"_brickcount", i); -                ret = dict_set_int64 (rsp_dict, key, brick_count); +                ret = glusterd_snap_create_clone_common_prevalidate (rsp_dict, +                                flags, snapname, err_str, snap_volname, i, +                                volinfo, &loglevel, 0);                  if (ret) { -                        gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", -                                key); +                        gf_log (this->name, GF_LOG_ERROR, +                                "Failed to pre validate");                          goto out;                  } +          }          ret = dict_set_int64 (rsp_dict, "volcount", volcount); @@ -2146,9 +2288,6 @@ glusterd_snapshot_create_prevalidate (dict_t *dict, char **op_errstr,          ret = 0;  out: -        if (device) -                GF_FREE (device); -          if (ret && err_str[0] != '\0') {                  gf_log (this->name, loglevel, "%s", err_str);                  *op_errstr = gf_strdup (err_str); @@ -3678,6 +3817,145 @@ out:          return ret;  } +/* This is a snapshot clone handler function. This function will be + * executed in the originator node. This function is responsible for + * calling mgmt_v3 framework to do the actual snap clone on all the bricks + * + * @param req           RPC request object + * @param op            gluster operation + * @param dict          dictionary containing snapshot restore request + * @param err_str       In case of an err this string should be populated + * @param len           length of err_str buffer + * + * @return              Negative value on Failure and 0 in success + */ +int +glusterd_handle_snapshot_clone (rpcsvc_request_t *req, glusterd_op_t op, +                               dict_t *dict, char *err_str, size_t len) +{ +        int           ret                              = -1; +        char         *clonename                        = NULL; +        char         *snapname                         = NULL; +        int64_t       volcount                         = 0; +        xlator_t     *this                             = NULL; +        char          key[PATH_MAX]                    = ""; +        char         *username                         = NULL; +        char         *password                         = NULL; +        char         *volname                          = NULL; +        uuid_t       *uuid_ptr                         = NULL; +        uuid_t        tmp_uuid                         = {0}; +        int           i                                = 0; +        char          snap_volname[GD_VOLUME_NAME_MAX] = {0, }; + +        this = THIS; +        GF_ASSERT (this); +        GF_ASSERT (req); +        GF_ASSERT (dict); +        GF_ASSERT (err_str); + +        ret = dict_get_str (dict, "clonename", &clonename); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to " +                        "get the clone name"); +                goto out; +        } +        /*We need to take a volume lock on clone name*/ +        volname = gf_strdup (clonename); +        snprintf (key, sizeof(key), "volname1"); +        ret = dict_set_dynstr (dict, key, volname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to set clone " +                        "name for volume locking"); +                GF_FREE (volname); +                goto out; +        } + +        ret = dict_get_str (dict, "snapname", &snapname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the snapname"); +                goto out; +        } + +        uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); +        if (!uuid_ptr) { +                gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); +                ret = -1; +                goto out; +        } + +        uuid_generate (*uuid_ptr); +        ret = dict_set_bin (dict, "clone-id", uuid_ptr, sizeof(uuid_t)); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Unable to set clone-id"); +                GF_FREE (uuid_ptr); +                goto out; +        } +        uuid_ptr = NULL; + +        ret = dict_get_str (dict, "snapname", &snapname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, +                        "Failed to get snapname name"); +                goto out; +        } + +        uuid_generate (tmp_uuid); +        username = gf_strdup (uuid_utoa (tmp_uuid)); +        snprintf (key, sizeof(key), "volume1_username"); +        ret = dict_set_dynstr (dict, key, username); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to set clone " +                        "username for volume %s", clonename); +                GF_FREE (username); +                goto out; +        } + +        uuid_generate (tmp_uuid); +        password = gf_strdup (uuid_utoa (tmp_uuid)); +        snprintf (key, sizeof(key), "volume1_password"); +        ret = dict_set_dynstr (dict, key, password); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to set clone " +                        "password for volume %s", clonename); +                GF_FREE (password); +                goto out; +        } + +        uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); +        if (!uuid_ptr) { +                gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); +                ret = -1; +                goto out; +        } + +        snprintf (key, sizeof(key) - 1, "vol1_volid"); +        uuid_generate (*uuid_ptr); +        ret = dict_set_bin (dict, key, uuid_ptr, sizeof(uuid_t)); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, +                        "Unable to set clone_volid"); +                GF_FREE (uuid_ptr); +                goto out; +        } +        snprintf (key, sizeof (key), "clone-volname%d", i); +        ret = dict_set_dynstr_with_alloc (dict, key, snap_volname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, +                        "Unable to set snap volname"); +                GF_FREE (uuid_ptr); +                goto out; +        } + +        ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to initiate snap " +                                "phases"); +        } + +out: +        return ret; +} +  /* This is a snapshot restore handler function. This function will be   * executed in the originator node. This function is responsible for @@ -3730,7 +4008,7 @@ glusterd_handle_snapshot_restore (rpcsvc_request_t *req, glusterd_op_t op,                  goto out;          } -        cds_list_for_each_entry (snap_volinfo, &snap->volumes, vol_list) { +        list_for_each_entry (snap_volinfo, &snap->volumes, vol_list) {                  i++;                  snprintf (key, sizeof (key), "volname%d", i);                  buf = gf_strdup (snap_volinfo->parent_volname); @@ -4112,7 +4390,8 @@ static int32_t  glusterd_add_brick_to_snap_volume (dict_t *dict, dict_t *rsp_dict,                                      glusterd_volinfo_t  *snap_vol,                                      glusterd_brickinfo_t *original_brickinfo, -                                    int64_t volcount, int32_t brick_count) +                                    int64_t volcount, int32_t brick_count, +                                    int clone)  {          char                    key[PATH_MAX]                   = "";          char                   *value                           = NULL; @@ -4255,9 +4534,14 @@ glusterd_add_brick_to_snap_volume (dict_t *dict, dict_t *rsp_dict,          uuid_copy (snap_brickinfo->uuid, original_brickinfo->uuid);          /* AFR changelog names are based on brick_id and hence the snap           * volume's bricks must retain the same ID */ -        strcpy (snap_brickinfo->brick_id, original_brickinfo->brick_id);          cds_list_add_tail (&snap_brickinfo->brick_list, &snap_vol->bricks); +        if (clone) { +                GLUSTERD_ASSIGN_BRICKID_TO_BRICKINFO (snap_brickinfo, snap_vol, +                                                      brick_count); +        } else +                strcpy (snap_brickinfo->brick_id, original_brickinfo->brick_id); +  out:          if (ret && snap_brickinfo)                  GF_FREE (snap_brickinfo); @@ -4414,7 +4698,8 @@ out:  glusterd_volinfo_t *  glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap, -                      dict_t *dict, dict_t *rsp_dict, int64_t volcount) +                      dict_t *dict, dict_t *rsp_dict, int64_t volcount, +                      int clone)  {          char                    key[PATH_MAX]                   = "";          char                   *username                        = NULL; @@ -4427,6 +4712,7 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap,          int32_t                 brick_count                     = 0;          xlator_t               *this                            = NULL;          int64_t                 brick_order                     = 0; +        char *clonename         = NULL;          this = THIS;          GF_ASSERT (this); @@ -4445,7 +4731,6 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap,                          "snap %s", key, snap->snapname);                  goto out;          } -          snprintf (key, sizeof(key), "volume%"PRId64"_password", volcount);          ret = dict_get_str (dict, key, &password);          if (ret) { @@ -4475,34 +4760,54 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap,          /* uuid is used as lvm snapshot name.             This will avoid restrictions on snapshot names provided by user */ -        GLUSTERD_GET_UUID_NOHYPHEN (snap_vol->volname, *snap_volid);          uuid_copy (snap_vol->volume_id, *snap_volid);          snap_vol->is_snap_volume = _gf_true; -        strcpy (snap_vol->parent_volname, origin_vol->volname);          snap_vol->snapshot = snap; -        glusterd_auth_set_username (snap_vol, username); -        glusterd_auth_set_password (snap_vol, password); - +        if (clone) { +                snap_vol->is_snap_volume = _gf_false; +                ret = dict_get_str (dict, "clonename", &clonename); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "Failed to get %s " +                                "for snap %s", key, snap->snapname); +                        goto out; +                } +                cds_list_add_tail (&snap_vol->vol_list, &snap->volumes); +                strcpy(snap_vol->volname, clonename); +        } else { +                GLUSTERD_GET_UUID_NOHYPHEN (snap_vol->volname, *snap_volid); +                strcpy (snap_vol->parent_volname, origin_vol->volname); +                ret = glusterd_list_add_snapvol (origin_vol, snap_vol); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "could not add the " +                                "snap volume %s to the list", +                                snap_vol->volname); +                        goto out; +                }          /* TODO : Sync before taking a snapshot */          /* Copy the status and config files of geo-replication before           * taking a snapshot. During restore operation these files needs           * to be copied back in /var/lib/glusterd/georeplication/           */ -        ret = glusterd_copy_geo_rep_files (origin_vol, snap_vol, rsp_dict); -        if (ret) { -                gf_log (this->name, GF_LOG_ERROR, "Failed to copy geo-rep " -                        "config and status files for volume %s", -                        origin_vol->volname); -                goto out; +                ret = glusterd_copy_geo_rep_files (origin_vol, snap_vol, +                                                   rsp_dict); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "Failed to copy " +                                "geo-rep config and status files for volume %s", +                                origin_vol->volname); +                        goto out; +                }          } +        glusterd_auth_set_username (snap_vol, username); +        glusterd_auth_set_password (snap_vol, password);          /* Adding snap brickinfos to the snap volinfo */          brick_count = 0;          cds_list_for_each_entry (brickinfo, &origin_vol->bricks, brick_list) {                  ret = glusterd_add_brick_to_snap_volume (dict, rsp_dict,                                                           snap_vol, brickinfo, -                                                         volcount, brick_count); +                                                         volcount, brick_count, +                                                         clone);                  if (ret) {                          gf_log (this->name, GF_LOG_ERROR,                                  "Failed to add the snap brick for " @@ -4510,7 +4815,6 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap,                                  brickinfo->hostname, brickinfo->path);                          goto out;                  } -                  brick_count++;          } @@ -4566,13 +4870,6 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap,                  goto out;          } -        ret = glusterd_list_add_snapvol (origin_vol, snap_vol); -        if (ret) { -                gf_log (this->name, GF_LOG_ERROR, "could not add the snap " -                        "volume %s to the list", snap_vol->volname); -                goto out; -        } -  out:          if (ret) {                  if (snap_vol) @@ -5523,7 +5820,7 @@ glusterd_schedule_brick_snapshot (dict_t *dict, dict_t *rsp_dict,                  brickcount = 0;                  brickorder = 0;                  cds_list_for_each_entry (brickinfo, &snap_vol->bricks, -                                         brick_list) { +                                                            brick_list) {                          snprintf (key, sizeof(key) - 1,                                    "snap-vol%d.brick%d.order", volcount,                                    brickcount); @@ -5609,6 +5906,187 @@ out:          return ret;  } +glusterd_snap_t* +glusterd_create_snap_object_for_clone (dict_t *dict, dict_t *rsp_dict) +{ +        char              *snapname    = NULL; +        uuid_t            *snap_id     = NULL; +        glusterd_snap_t   *snap        = NULL; +        xlator_t          *this        = NULL; +        glusterd_conf_t   *priv        = NULL; +        int                ret         = -1; +        int64_t            time_stamp  = 0; + +        this = THIS; +        priv = this->private; + +        GF_ASSERT (dict); +        GF_ASSERT (rsp_dict); + +        /* Fetch snapname, description, id and time from dict */ +        ret = dict_get_str (dict, "clonename", &snapname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Unable to fetch clonename"); +                goto out; +        } + +        ret = dict_get_bin (dict, "clone-id", (void **)&snap_id); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Unable to fetch clone_id"); +                goto out; +        } + +        snap = glusterd_new_snap_object (); +        if (!snap) { +                gf_log (this->name, GF_LOG_ERROR, "Could not create " +                        "the snap object for snap %s", snapname); +                goto out; +        } + +        strcpy (snap->snapname, snapname); +        uuid_copy (snap->snap_id, *snap_id); + +        ret = 0; + +out: +        if (ret) { +                if (snap) +                        glusterd_snap_remove (rsp_dict, snap, +                                              _gf_true, _gf_true); +                snap = NULL; +        } + +        return snap; +} + + +int32_t +glusterd_snapshot_clone_commit (dict_t *dict, char **op_errstr, +                                 dict_t *rsp_dict) +{ +        int                     ret                     = -1; +        int64_t                 i                       = 0; +        int64_t                 volcount                = 0; +        int32_t                 snap_activate           = 0; +        char                    *snapname               = NULL; +        char                    *volname                = NULL; +        char                    *tmp_name               = NULL; +        char                    key[PATH_MAX]           = ""; +        xlator_t                *this                   = NULL; +        glusterd_snap_t         *snap_parent            = NULL; +        glusterd_snap_t         *snap                   = NULL; +        glusterd_volinfo_t      *origin_vol             = NULL; +        glusterd_volinfo_t      *snap_vol               = NULL; +        glusterd_brickinfo_t    *brickinfo              = NULL; +        glusterd_conf_t         *priv                   = NULL; + +        this = THIS; +        GF_ASSERT(this); +        GF_ASSERT(dict); +        GF_ASSERT(op_errstr); +        GF_ASSERT(rsp_dict); +        priv = this->private; +        GF_ASSERT(priv); + + +        ret = dict_get_str (dict, "clonename", &snapname); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Unable to fetch clonename"); +                goto out; +        } +        tmp_name = gf_strdup (snapname); +        if (!tmp_name) { +                gf_log (this->name, GF_LOG_ERROR, "Out of memory"); +                ret = -1; +                goto out; +        } + +        ret = dict_set_dynstr (rsp_dict, "clonename", tmp_name); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, +                        "Unable to set clonename in rsp_dict"); +                GF_FREE (tmp_name); +                goto out; +        } +        tmp_name = NULL; + + +       ret = dict_get_str (dict, "snapname", &volname); +       if (ret) { +               gf_log (this->name, GF_LOG_ERROR, +                       "failed to get snap name"); +               goto out; +       } +       snap_parent = glusterd_find_snap_by_name (volname); +       /* TODO : As of now there is only one volume in snapshot. +        * Change this when multiple volume snapshot is introduced +        */ +       origin_vol = cds_list_entry (snap_parent->volumes.next, +                                  glusterd_volinfo_t, vol_list); +       if (!origin_vol) { +               gf_log ("glusterd", GF_LOG_ERROR, "Failed to get snap " +               "volinfo %s", snap_parent->snapname); +               goto out; +       } +       snap = glusterd_create_snap_object_for_clone (dict, rsp_dict); +       if (!snap) { +               gf_log (this->name, GF_LOG_ERROR, "creating the" +                       "snap object %s failed", snapname); +               ret = -1; +               goto out; +       } + +       snap_vol = glusterd_do_snap_vol (origin_vol, snap, dict, +                                       rsp_dict, 1, 1); +       if (!snap_vol) { +              ret = -1; +              gf_log (this->name, GF_LOG_WARNING, "taking the " +                      "snapshot of the volume %s failed", volname); +              goto out; +       } +       volcount = 1; +       ret = dict_set_int64 (rsp_dict, "volcount", volcount); +       if (ret) { +               gf_log (this->name, GF_LOG_ERROR, "Failed to set volcount"); +               goto out; +       } + +       ret = glusterd_schedule_brick_snapshot (dict, rsp_dict, snap); +       if (ret) { +               gf_log (this->name, GF_LOG_ERROR, "Failed to take backend " +                       "snapshot %s", snap->snapname); +               goto out; +       } + +       cds_list_del_init (&snap_vol->vol_list); +       ret = dict_set_dynstr_with_alloc (rsp_dict, "snapuuid", +                                         uuid_utoa (snap->snap_id)); +       if (ret) { +               gf_log (this->name, GF_LOG_ERROR, "Failed to set snap " +                       "uuid in response dictionary for %s snapshot", +                       snap->snapname); +               goto out; +       } +       ret = glusterd_snapdsvc_init (snap_vol); +       glusterd_list_add_order (&snap_vol->vol_list, &priv->volumes, +                       glusterd_compare_volume_name); + +       ret = 0; + + +out: +       if (ret) { +               if (snap) +                       glusterd_snap_remove (rsp_dict, snap, +                                             _gf_true, _gf_true); +               snap = NULL; +       } + +       gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); +       return ret; +} + +  int32_t  glusterd_snapshot_create_commit (dict_t *dict, char **op_errstr,                                   dict_t *rsp_dict) @@ -5701,7 +6179,7 @@ glusterd_snapshot_create_commit (dict_t *dict, char **op_errstr,                  }                  snap_vol = glusterd_do_snap_vol (origin_vol, snap, dict, -                                                 rsp_dict, i); +                                                 rsp_dict, i, 0);                  if (!snap_vol) {                          ret = -1;                          gf_log (this->name, GF_LOG_WARNING, "taking the " @@ -6786,6 +7264,86 @@ out:  }  int32_t +glusterd_snapshot_clone_postvalidate (dict_t *dict, int32_t op_ret, +                                       char **op_errstr, dict_t *rsp_dict) +{ +        xlator_t              *this           = NULL; +        glusterd_conf_t       *priv           = NULL; +        int                    ret            = -1; +        int32_t                cleanup        = 0; +        glusterd_snap_t       *snap           = NULL; +        glusterd_volinfo_t    *snap_vol       = NULL; +        char                  *clonename      = NULL; +        char                  *auto_delete    = NULL; + +        this = THIS; + +        GF_ASSERT (this); +        GF_ASSERT (dict); +        GF_ASSERT (rsp_dict); + +        priv = this->private; +        GF_ASSERT (priv); + +        if (op_ret) { +                ret = dict_get_int32 (dict, "cleanup", &cleanup); +                if (!ret && cleanup) { +                        ret = glusterd_do_snap_cleanup (dict, op_errstr, +                                                        rsp_dict); +                        if (ret) { +                                gf_log (this->name, GF_LOG_WARNING, "cleanup " +                                        "operation failed"); +                                goto out; +                        } +                } +                /* Irrespective of status of cleanup its better +                 * to return from this function. As the functions +                 * following this block is not required to be +                 * executed in case of failure scenario. +                 */ +                ret = 0; +                goto out; +        } + +        ret = dict_get_str (dict, "clonename", &clonename); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Unable to fetch " +                        "clonename"); +                goto out; +        } + +        ret = glusterd_volinfo_find (clonename, &snap_vol); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "unable to find clone " +                        "%s volinfo", clonename); +                goto out; +        } + +        ret = glusterd_snapshot_update_snaps_post_validate (dict, +                                                            op_errstr, +                                                            rsp_dict); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "Failed to " +                        "create snapshot"); +                goto out; +        } +        snap = snap_vol->snapshot; +        ret = glusterd_snapobject_delete (snap); +        if (ret) { +                gf_log (this->name, GF_LOG_WARNING, "Failed to delete " +                        "snap object %s", snap->snapname); +                goto out; +        } +        snap_vol->snapshot = NULL; + +        ret = 0; + +out: +        return ret; +} + + +int32_t  glusterd_snapshot_create_postvalidate (dict_t *dict, int32_t op_ret,                                         char **op_errstr, dict_t *rsp_dict)  { @@ -6902,6 +7460,7 @@ glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict)          }          switch (snap_command) { +          case (GF_SNAP_OPTION_TYPE_CREATE):                  ret = glusterd_snapshot_create_commit (dict, op_errstr,                                                         rsp_dict); @@ -6912,6 +7471,16 @@ glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict)                  }                  break; +       case (GF_SNAP_OPTION_TYPE_CLONE): +                ret = glusterd_snapshot_clone_commit (dict, op_errstr, +                                                       rsp_dict); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "Failed to " +                                "clone snapshot"); +                        goto out; +                } +                break; +          case GF_SNAP_OPTION_TYPE_CONFIG:                  ret = glusterd_snapshot_config_commit (dict, op_errstr,                                                         rsp_dict); @@ -7138,6 +7707,17 @@ glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr,                  }                  break; +        case (GF_SNAP_OPTION_TYPE_CLONE): +                ret = glusterd_snapshot_clone_prevalidate (dict, op_errstr, +                                                            rsp_dict); +                if (ret) { +                        gf_log (this->name, GF_LOG_WARNING, "Snapshot clone " +                                "pre-validation failed"); +                        goto out; +                } +                break; + +          case (GF_SNAP_OPTION_TYPE_CONFIG):                  ret = glusterd_snapshot_config_prevalidate (dict, op_errstr);                  if (ret) { @@ -7531,6 +8111,17 @@ glusterd_snapshot_postvalidate (dict_t *dict, int32_t op_ret, char **op_errstr,                  }                  glusterd_fetchsnap_notify (this);                  break; +         case GF_SNAP_OPTION_TYPE_CLONE: +                ret = glusterd_snapshot_clone_postvalidate (dict, op_ret, +                                                             op_errstr, +                                                             rsp_dict); +                if (ret) { +                        gf_log (this->name, GF_LOG_WARNING, "Snapshot create " +                                "post-validation failed"); +                        goto out; +                } +                glusterd_fetchsnap_notify (this); +                break;          case GF_SNAP_OPTION_TYPE_DELETE:                  if (op_ret) {                          gf_log (this->name, GF_LOG_DEBUG, @@ -7727,6 +8318,16 @@ glusterd_handle_snapshot_fn (rpcsvc_request_t *req)                                          "failed: %s", err_str);                  }                  break; + +        case GF_SNAP_OPTION_TYPE_CLONE: +                ret = glusterd_handle_snapshot_clone (req, cli_op, dict, +                                                err_str, sizeof (err_str)); +                if (ret) { +                        gf_log (this->name, GF_LOG_WARNING, "Snapshot clone " +                                        "failed: %s", err_str); +                } +                break; +          case GF_SNAP_OPTION_TYPE_RESTORE:                  ret = glusterd_handle_snapshot_restore (req, cli_op, dict,                                                  err_str, sizeof (err_str));  | 
