diff options
Diffstat (limited to 'xlators/mgmt/glusterd/src/glusterd-snapshot.c')
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-snapshot.c | 1015 |
1 files changed, 955 insertions, 60 deletions
diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot.c b/xlators/mgmt/glusterd/src/glusterd-snapshot.c index 1c1e28a86..f3428ff03 100644 --- a/xlators/mgmt/glusterd/src/glusterd-snapshot.c +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot.c @@ -17,6 +17,7 @@ #include <unistd.h> #include <sys/resource.h> #include <sys/statvfs.h> +#include <sys/mount.h> #include "globals.h" #include "compat.h" @@ -38,6 +39,184 @@ #include "cli1-xdr.h" #include "xdr-generic.h" +#ifdef GF_LINUX_HOST_OS +#include <mntent.h> +#endif + +int +glusterd_handle_snapshot_fn (rpcsvc_request_t *req) +{ + int32_t ret = 0; + dict_t *dict = NULL; + gf_cli_req cli_req = {{0},}; + glusterd_op_t cli_op = GD_OP_SNAP; + int type = 0; + glusterd_conf_t *conf = NULL; + char *host_uuid = NULL; + char err_str[2048] = {0,}; + xlator_t *this = NULL; + + GF_ASSERT (req); + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + + ret = xdr_to_generic (req->msg[0], &cli_req, + (xdrproc_t)xdr_gf_cli_req); + if (ret < 0) { + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (cli_req.dict.dict_len > 0) { + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (cli_req.dict.dict_val, + cli_req.dict.dict_len, + &dict); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "unserialize req-buffer to dictionary"); + snprintf (err_str, sizeof (err_str), "Unable to decode " + "the command"); + goto out; + } + + dict->extra_stdfree = cli_req.dict.dict_val; + + host_uuid = gf_strdup (uuid_utoa(MY_UUID)); + if (host_uuid == NULL) { + snprintf (err_str, sizeof (err_str), "Failed to get " + "the uuid of local glusterd"); + ret = -1; + goto out; + } + ret = dict_set_dynstr (dict, "host-uuid", host_uuid); + if (ret) + goto out; + + } else { + gf_log (this->name, GF_LOG_ERROR, "request dict length is %d", + cli_req.dict.dict_len); + goto out; + } + + ret = dict_get_int32 (dict, "type", &type); + if (ret < 0) { + snprintf (err_str, sizeof (err_str), "Command type not found"); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + goto out; + } + + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + ret = glusterd_mgmt_v3_initiate_all_phases (req, cli_op, dict); + break; + case GF_SNAP_OPTION_TYPE_LIST: + ret = glusterd_handle_snapshot_list (req, cli_op, dict); + break; + default: + gf_log (this->name, GF_LOG_ERROR, "Unkown snapshot request " + "type (%d)", type); + ret = -1; /* Failure */ + } + +out: + if (ret) { + GF_FREE (host_uuid); + if (err_str[0] == '\0') + snprintf (err_str, sizeof (err_str), + "Operation failed"); + ret = glusterd_op_send_cli_response (cli_op, ret, 0, req, + dict, err_str); + } + + return ret; +} + +int +glusterd_handle_snapshot (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); +} + +int +glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + char *volname = NULL; + glusterd_volinfo_t *volinfo = NULL; + int64_t volume_count = 0; + char volname_buf[PATH_MAX] = {0, }; + int64_t i = 0; + int snap_command = 0; + xlator_t *this = NULL; + int ret = -1; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); //not sure if this is needed, verify. + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case (GF_SNAP_OPTION_TYPE_CREATE): + { + ret = dict_get_int64 (dict, "volcount", &volume_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "get the volume count"); + goto out; + } + for (i = 0; i < volume_count; i++) { + snprintf (volname_buf, sizeof (volname_buf), + "volname%ld", i+1); + ret = dict_get_str (dict, volname_buf, + &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get volume name"); + goto out; + } + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get the volinfo for " + "the volume %s", volname); + goto out; + } + if (glusterd_is_defrag_on (volinfo)) { + gf_log (this->name, GF_LOG_WARNING, + "rebalance process is running " + "for the volume %s", volname); + goto out; + } + //Also check whether geo replication is running + } + break; + } + default: + gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); + goto out; + break; + } + + ret = 0; + +out: + return ret; +} + glusterd_snap_t* glusterd_new_snap_object() { @@ -107,7 +286,7 @@ glusterd_add_snap (glusterd_volinfo_t *volinfo, glusterd_snap_t *snap) } last = entry; } - list_add (&snap->snap_list, &last->snap_list); + list_add_tail (&snap->snap_list, &volinfo->snaps); gf_log (THIS->name, GF_LOG_DEBUG, "Snap %s added @ %"PRIu64, snap->snap_name, count); @@ -343,7 +522,6 @@ glusterd_delete_snap_volume (glusterd_volinfo_t *volinfo, glusterd_volinfo_t *snapinfo) { int ret = -1; - GF_ASSERT (volinfo); ret = glusterd_store_delete_volume (volinfo, snapinfo); @@ -1334,99 +1512,816 @@ out: return ret; } +/* this should be the last thing to be done. + 1. Do op stage related checks such as whether volume is there or not etc + 2. Do quorum checks. + 3. Then do this and take the snapshot OR take the snapshot and build the + snap object (Can be decided later) +*/ +int32_t +glusterd_snap_create (glusterd_volinfo_t *volinfo, + glusterd_volinfo_t *snap_volinfo, + char *description, uuid_t cg_id) +{ + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + int ret = -1; + uuid_t snap_uuid; + + this = THIS; + priv = this->private; + + if (!volinfo) { + gf_log (this->name, GF_LOG_ERROR, "volinfo is NULL"); + goto out; + } + + snap = glusterd_new_snap_object (); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "could not create " + "the snap object fot the volume %s (snap: %s)", + volinfo->volname, snap_volinfo->volname); + goto out; + } + + + // for now ignore if description is not strduped + if (description) + snap->description = gf_strdup (description); + snap->time_stamp = time (NULL); + uuid_generate (snap_uuid); + uuid_copy (snap->snap_id, snap_uuid); + if (!uuid_is_null (cg_id)) + uuid_copy (snap->cg_id, cg_id); + snap->snap_volume = snap_volinfo; + uuid_copy (snap_volinfo->volume_id, snap_uuid); + strcpy (snap->snap_name, snap_volinfo->volname); + //TODO: replace strcpy with strncpy + + ret = glusterd_add_snap (volinfo, snap); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "could not add the snap " + "object %s to the snap list of the volume %s", + snap_volinfo->volname, volinfo->volname); + goto out; + } + +out: + if (ret) { + if (snap) { + list_del_init (&snap->snap_list); + LOCK_DESTROY (&snap->lock); + GF_FREE (snap->description); + GF_FREE (snap->snap_volume); + GF_FREE (snap); + } + } + return ret; +} + int -glusterd_handle_snapshot_fn (rpcsvc_request_t *req) +glusterd_remove_snapshot (glusterd_brickinfo_t *brickinfo, char *volname, + char *snapname, const char *snap_device) { - int32_t ret = 0; - dict_t *dict = NULL; - gf_cli_req cli_req = {{0},}; - glusterd_op_t cli_op = GD_OP_SNAP; - int type = 0; - glusterd_conf_t *priv = NULL; - char *host_uuid = NULL; - char err_str[2048] = {0,}; - xlator_t *this = NULL; + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + runner_t runner = {0,}; + char msg[1024] = {0, }; - GF_ASSERT (req); + this = THIS; + priv = this->private; + + if (!brickinfo) { + gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); + goto out; + } + + snprintf (msg, sizeof(msg), "remove snapshot of the brick %s:%s, " + "device: %s", brickinfo->hostname, brickinfo->path, + snap_device); + runner_add_args (&runner, "/sbin/lvmremove", snap_device); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + + //let glusterd get blocked till snapshot is over + synclock_unlock (&priv->big_lock); + ret = runner_run (&runner); + synclock_lock (&priv->big_lock); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "removing snapshot of the " + "brick (%s:%s) of device %s failed", + brickinfo->hostname, brickinfo->path, snap_device); + goto out; + } + +out: + return ret; +} + +int32_t +glusterd_brick_snapshot_remove (glusterd_volinfo_t *snap_volinfo, + glusterd_volinfo_t *actual_volinfo, char *name) +{ + char *mnt_pt = NULL; + struct mntent *entry = NULL; + int32_t ret = -1; + glusterd_brickinfo_t *brickinfo = NULL; + xlator_t *this = NULL; + FILE *mtab = NULL; this = THIS; GF_ASSERT (this); - priv = this->private; - GF_ASSERT (priv); - ret = xdr_to_generic (req->msg[0], &cli_req, - (xdrproc_t)xdr_gf_cli_req); - if (ret < 0) { - req->rpc_err = GARBAGE_ARGS; + if (!snap_volinfo) { + gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); goto out; } - if (cli_req.dict.dict_len) { - dict = dict_new (); - if (!dict) + if (!actual_volinfo) { + gf_log (this->name, GF_LOG_ERROR, "volinfo for the volume " + "is NULL"); + goto out; + } + + if (!name) { + gf_log (this->name, GF_LOG_ERROR, "snapname is NULL " + "(volume: %s)", actual_volinfo->volname); + goto out; + } + + list_for_each_entry (brickinfo, &snap_volinfo->bricks, brick_list) { + ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); + if (ret) goto out; - ret = dict_unserialize (cli_req.dict.dict_val, - cli_req.dict.dict_len, - &dict); - if (ret < 0) { + entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); + if (!entry) { + ret = -1; + goto out; + } + ret = glusterd_remove_snapshot (brickinfo, + actual_volinfo->volname, + name, entry->mnt_fsname); + if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " - "unserialize req-buffer to dictionary"); - snprintf (err_str, sizeof (err_str), "Unable to decode " - "the command"); + "remove the snapshot %s (%s)", + brickinfo->path, entry->mnt_fsname); goto out; - } else { - dict->extra_stdfree = cli_req.dict.dict_val; } + } - host_uuid = gf_strdup (uuid_utoa(MY_UUID)); - if (host_uuid == NULL) { - snprintf (err_str, sizeof (err_str), "Failed to get " - "the uuid of local glusterd"); + ret = 0; + +out: + if (mtab) + endmntent (mtab); + return ret; +} + +/* This function is called to get the device path of the snap lvm. Usually + if /dev/<group-name>/<group-name>-<lvm-name> is the device for the lvm, + then the snap device will be /dev/<group-name>/<group-name>-<snap-name>. + This function takes care of building the path for the snap device. +*/ +char * +glusterd_build_snap_device_path (char *device, char *snapname) +{ + char *tmp = NULL; + char snap[PATH_MAX] = {0, }; + char msg[1024] = {0, }; + char *str = NULL; + int device_len = 0; + int tmp_len = 0; + int var = 0; + char *snap_device = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "device is NULL"); + goto out; + } + + device_len = strlen (device); + + tmp = strrchr (device, '/'); + if (tmp) + tmp++; + str = gf_strdup (tmp); + if (!str) { + goto out; + } + + tmp_len = strlen (str); + var = device_len - tmp_len; + device[var] = '\0'; + tmp = strchr (str, '-'); + if (tmp) + tmp++; + device_len = tmp_len; + tmp_len = strlen (tmp); + var = device_len - tmp_len; + str[var] = '\0'; + msg[0] = '\0'; + strcpy (msg, str); + strcat (msg, snapname); + strcpy (snap, device); + strcat (snap, msg); + snap_device = gf_strdup (snap); + if (!snap_device) { + gf_log (this->name, GF_LOG_WARNING, "cannot copy the " + "snapshot device name (volname: %s, " + "snapname: %s)", snapname); + goto out; + } + +out: + GF_FREE (str); + return snap_device; +} + +/* This function actually calls the command (or the API) for taking the + snapshot of the backend brick filesystem. If this is successful, + then call the glusterd_snap_create function to create the snap object + for glusterd +*/ +int +glusterd_take_snapshot (glusterd_brickinfo_t *brickinfo, char *volname, + char *snapname, dict_t *dict, char **snap_device) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + runner_t runner = {0,}; + char *device = NULL; + char msg[1024] = {0, }; + char *tmp = NULL; + + this = THIS; + priv = this->private; + + if (!brickinfo) { + gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); + goto out; + } + + device = glusterd_get_brick_mount_details (brickinfo); + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "getting device name for " + "the brick %s:%s failed", brickinfo->hostname, + brickinfo->path); + goto out; + } + + runinit (&runner); + snprintf (msg, sizeof (msg), "taking snapshot of the brick %s:%s", + brickinfo->hostname, brickinfo->path); + runner_add_args (&runner, "/sbin/lvcreate", "-s", device, "--name", + snapname); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + + //let glusterd get blocked till snapshot is over + synclock_unlock (&priv->big_lock); + ret = runner_run (&runner); + synclock_lock (&priv->big_lock); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "taking snapshot of the " + "brick (%s:%s) of device %s failed", + brickinfo->hostname, brickinfo->path, device); + goto out; + } + + gf_log (this->name, GF_LOG_INFO, "device: %s", device); + if (device) { + tmp = glusterd_build_snap_device_path (device, snapname); + *snap_device = tmp; + if (!*snap_device) { + gf_log (this->name, GF_LOG_WARNING, "cannot copy the " + "snapshot device name (volname: %s, " + "snapname: %s)", volname, snapname); ret = -1; goto out; } - ret = dict_set_dynstr (dict, "host-uuid", host_uuid); + } + +out: + return ret; +} + +int32_t +glusterd_snap_brick_create (char *device, glusterd_volinfo_t *snap_volinfo, + glusterd_brickinfo_t *original_brickinfo) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + glusterd_brickinfo_t *snap_brickinfo = NULL; + char snap_brick_mount_path[PATH_MAX] = {0, }; + char *tmp = NULL; + char *mnt_pt = NULL; + struct mntent *entry = NULL; + FILE *mtab = NULL; + char msg[PATH_MAX] = {0, }; + + this = THIS; + priv = this->private; + + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "device is NULL"); + goto out; + } + + if (!snap_volinfo) { + gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); + goto out; + } + + if (!original_brickinfo) { + gf_log (this->name, GF_LOG_ERROR, "original brickinfo is NULL" + "(snap: %s)", snap_volinfo->volname); + goto out; + } + + tmp = gf_strdup (device); + if (!tmp) { + gf_log (this->name, GF_LOG_INFO, "out of memory"); + goto out; + } + + glusterd_replace_slash_with_hyphen (tmp); + if (tmp[0] == '-') + tmp[0] = '/'; + + snprintf (snap_brick_mount_path, sizeof (snap_brick_mount_path), "%s" + "/%s%s-brick", GLUSTERD_DEFAULT_SNAPS_BRICK_DIR, + snap_volinfo->volname, tmp); + + ret = glusterd_get_brick_root (original_brickinfo->path, &mnt_pt); + if (ret) + goto out; + + entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); + if (!entry) { + ret = -1; + goto out; + } + + ret = mkdir_p (snap_brick_mount_path, 0777, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "creating the brick directory" + " %s for the snapshot %s(device: %s) failed", + snap_brick_mount_path, snap_volinfo->volname, device); + goto out; + } + /* mount the snap logical device on the directory inside + /var/run/gluster/snaps/<snapname>/@snap_brick_mount_path + */ + ret = mount (device, snap_brick_mount_path, entry->mnt_type, MS_MGC_VAL, + "nouuid"); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "mounting the snapshot " + "logical device %s failed (error: %s)", device, + strerror (errno)); + goto out; + } + + ret = glusterd_brickinfo_new (&snap_brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "initializing the brick for the snap " + "volume failed (snapname: %s)", snap_volinfo->volname); + goto out; + } + + strcpy (snap_brickinfo->hostname, original_brickinfo->hostname); + strcpy (snap_brickinfo->path, snap_brick_mount_path); + LOCK (&snap_volinfo->lock); + { + list_add_tail (&snap_brickinfo->brick_list, + &snap_volinfo->bricks); + } + UNLOCK (&snap_volinfo->lock); +out: + GF_FREE (tmp); + if (ret) { + umount (snap_brick_mount_path); + if (snap_brickinfo) + glusterd_brickinfo_delete (snap_brickinfo); + } + if (mtab) + endmntent (mtab); + return ret; +} + +/* TODO: lvm uses '-' as the delimter for differentiating the logical volume + name and the volume group name. So as of now, if the snapname given from + cli contains '-', it confuses lvm. Handle it. +*/ +int32_t +glusterd_do_snap (glusterd_volinfo_t *volinfo, char *name, dict_t *dict, + gf_boolean_t cg, uuid_t cg_id) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + char volfile[PATH_MAX] = {0,}; + char exp_path[PATH_MAX] = {0,}; + char logfile[PATH_MAX] = {0,}; + int port = 0; + int rdma_port = 0; + char socketpath[PATH_MAX] = {0}; + glusterd_brickinfo_t *brickinfo = NULL; + char *device = NULL; + char snapname[PATH_MAX] = {0, }; + char tmp[2046] = {0, }; + glusterd_volinfo_t *snap_volume = NULL; + char *description = NULL; + + this = THIS; + GF_ASSERT (this); + + priv = this->private; + GF_ASSERT (priv); + + strcpy (snapname, name); + if (cg) { + snprintf (tmp, sizeof (tmp), "%s-snap", volinfo->volname); + strcat (snapname, tmp); + } + + ret = glusterd_volinfo_dup (volinfo, &snap_volume); + strcpy (snap_volume->volname, snapname); + + list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { + if (!glusterd_is_brick_started (brickinfo)) + continue; + + ret = glusterd_take_snapshot (brickinfo, + volinfo->volname, + snapname, dict, &device); + /* continue with the snapshot even though snapshot + on one of the bricks fails. At the end check + whether the snapshot volume meets quorum or not. + If so, then the snapshot can be treated as success. + If not, undo the changes and return failure to cli. + */ if (ret) + continue; + + /*create the complete brick here and add it to the + volinfo + */ + ret = glusterd_snap_brick_create (device, snap_volume, + brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "not able to" + " create the brickinfo for the snap %s" + ", volume %s", snapname, + volinfo->volname); goto out; + } + } + + //TODO: the quorum check of the snap volume here + + ret = dict_get_str (dict, "snap-description", &description); + // for now continue the snap, if getting description fails. + ret = glusterd_snap_create (volinfo, snap_volume, description, cg_id); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "creating the" + "snap object failed for the volume %s", + volinfo->volname); + goto out; } - ret = dict_get_int32 (dict, "type", &type); - if (ret < 0) { - snprintf (err_str, sizeof (err_str), "Command type not found"); - gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + ret = glusterd_store_perform_snap_store (volinfo); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "could not do volume store" + " after taking the snapshot (volume: %s)", + volinfo->volname); goto out; } - switch (type) { - case GF_SNAP_OPTION_TYPE_CREATE: - ret = glusterd_mgmt_v3_initiate_all_phases (req, cli_op, dict); - break; - case GF_SNAP_OPTION_TYPE_LIST: - ret = glusterd_handle_snapshot_list (req, cli_op, dict); - break; - default: - gf_log (this->name, GF_LOG_ERROR, "Unkown snapshot request " - "type (%d)", type); - ret = -1; /* Failure */ + ret = generate_snap_brick_volfiles (volinfo, snap_volume); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "generating the brick " + "volfiles for the snap %s (volume: %s) failed", + snapname, volinfo->volname); + goto out; } -out: + ret = generate_snap_client_volfiles (volinfo, snap_volume, + GF_CLIENT_TRUSTED); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "generating the trusted " + "client volfiles for the snap %s (volume: %s) failed", + snapname, volinfo->volname); + goto out; + } + ret = generate_snap_client_volfiles (volinfo, snap_volume, + GF_CLIENT_OTHER); if (ret) { - if (err_str[0] == '\0') - snprintf (err_str, sizeof (err_str), - "Operation failed"); - ret = glusterd_op_send_cli_response (cli_op, ret, 0, req, - dict, err_str); + gf_log (this->name, GF_LOG_ERROR, "generating the client " + "volfiles for the snap %s (volume: %s) failed", + snapname, volinfo->volname); + goto out; } + list_for_each_entry (brickinfo, &snap_volume->bricks, brick_list) { + ret = glusterd_snap_brick_start (volinfo, snap_volume, brickinfo, + _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "starting the " + "brick %s:%s for the snap %s (volume: %s) " + "failed", brickinfo->hostname, + brickinfo->path, snapname, volinfo->volname); + goto out; + } + } + +out: return ret; } -int -glusterd_handle_snapshot (rpcsvc_request_t *req) +int32_t +glusterd_do_snap_remove (glusterd_volinfo_t *volinfo, char *name, dict_t *dict) { - return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + glusterd_snap_t *snap = NULL; + glusterd_snap_cg_t *cg = NULL; + int i = 0; + + this = THIS; + priv = this->private; + + if (!volinfo) { + gf_log (this->name, GF_LOG_ERROR, "volinfo NULL"); + goto out; + } + + if (!name) { + gf_log (this->name, GF_LOG_ERROR, "name is NULL (volume: %s)", + volinfo->volname); + goto out; + } + + snap = glusterd_find_snap_by_name (volinfo, name); + if (!snap) { + cg = glusterd_find_snap_cg_by_name (priv, volinfo->volname); + if (!cg) { + gf_log (this->name, GF_LOG_ERROR, "could not find " + "the snap or the cg object by the name %s", + name); + goto out; + } + } + + if (snap) { + ret = glusterd_brick_snapshot_remove (snap->snap_volume, + volinfo, name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "removing the bricks" + " snapshots for the snap %s (volume: %s) " + "failed", name, volinfo->volname); + goto out; + } + } + + if (cg) { + for (i = 0; i < cg->volume_count ; i++) { + ret = glusterd_brick_snapshot_remove (&cg->volumes[i], + volinfo, name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "removing the" + " bricks snapshots for the snap %s " + "(volume: %s) failed", name, + volinfo->volname); + goto out; + } + } + } + + ret = 0; +out: + return ret; +} + + +/* This function helps in generating the names for either the snapshot + (if only one volume name is given in the snap create command) or + the consistency group (if multiple volume names are given in the snaap + create command). Apart from that, it also helps in generating the names + for the snaps of the individual volumes in a consistency group. +*/ +char * +generate_snapname (char *volname, char *name, gf_boolean_t volume_from_cg) +{ + char internal_snapname[PATH_MAX] = {0, }; + char timestr[256] = {0, }; + int ret = -1; + char *snapname = NULL; + struct timeval tv = {0, }; + xlator_t *this = NULL; + int i = 0; + + this = THIS; + GF_ASSERT (this); + + if (name) { + GF_ASSERT (volname); + if (volume_from_cg) { + snprintf (internal_snapname, sizeof (internal_snapname), + "%s_%s", volname, name); + } else { + snprintf (internal_snapname, sizeof (internal_snapname), + "%s", name); + } + } else { + ret = gettimeofday (&tv, NULL); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "getting time failed. snapname is not given via" + "cli. "); + goto out; + } + gf_time_fmt (timestr, sizeof (timestr), + tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), + sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, + tv.tv_usec); + + for (i = 0; i < strlen (timestr); i++) { + if (timestr[i] == ' ' || timestr[i] == ':' || + timestr[i] == '.' || timestr[i] == '-') + timestr[i] = '_'; + } + + snprintf (internal_snapname, + sizeof (internal_snapname), "%s%s", + (volume_from_cg)?"cg_":"", + timestr); + } + + snapname = gf_strdup (internal_snapname); +out: + return snapname; +} + +/* name can be either the snapname if @volnames contains only one volume or + cg name if there are multiple volume names in volnames string +*/ + +int32_t +glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict) +{ + char *name = NULL; + char *volname = NULL; + glusterd_volinfo_t *volinfo = NULL; + int64_t volume_count = 0; + char volname_buf[PATH_MAX] = {0, }; + int i = 0; + int snap_command = 0; + xlator_t *this = NULL; + int ret = -1; + glusterd_snap_cg_t *cg = NULL; + gf_boolean_t is_cg = _gf_false; + uuid_t cg_id; + glusterd_conf_t *priv = NULL; + char *tmp = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); //not sure if this is needed, verify. + + priv = this->private; + GF_ASSERT (priv); + + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case (GF_SNAP_OPTION_TYPE_CREATE): + { + ret = dict_get_int64 (dict, "volcount", &volume_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "get the volume count"); + goto out; + } + + //snap-name should not be set if volume_count > 1 + ret = dict_get_str (dict, "snap-name", &name); + if (volume_count > 1 && !ret) + GF_ASSERT (0); + + if (volume_count > 1) { + is_cg = _gf_true; + ret = dict_get_str (dict, "cg-name", &name); + uuid_generate (cg_id); + } else if (volume_count == 1) { + ret = dict_get_str (dict, "snap-name", &name); + } + + if (!name) { + name = generate_snapname (volname, NULL, is_cg); + if (!name) { + gf_log (this->name, GF_LOG_ERROR, + "strdup of internal snapname" + " ((%s) failed for the " + "volume %s", name, + volname); + goto out; + } + } + + for (i = 1; i < volume_count + 1; i++) { + snprintf (volname_buf, sizeof (volname_buf), + "volname%d", i); + ret = dict_get_str (dict, volname_buf, + &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get volume name"); + goto out; + } + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get the volinfo for " + "the volume %s", volname); + goto out; + } + + tmp = generate_snapname (volname, name, is_cg); + if (!tmp) { + gf_log (this->name, + GF_LOG_ERROR, "strdup " + "failed (%s)", name); + goto out; + } + + /* TODO: Create a stub where the bricks are + added parallely by worker threads so that + the snap creating happens parallely. + */ + ret = glusterd_do_snap (volinfo, tmp, dict, + is_cg, cg_id); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "taking the " + "snapshot of the volume %s failed", + volname); + goto out; + } + } + break; + } + default: + gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); + goto out; + break; + } + + if (volume_count > 1) { + cg = glusterd_new_snap_cg_object (volume_count); + if (!cg) { + gf_log (this->name, GF_LOG_ERROR, "cannot create the " + "consistency group %s", name); + goto out; + } + + uuid_copy (cg->cg_id, cg_id); + ret = glusterd_add_snap_cg (priv, cg); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "could not add the" + " consistency group %s to the glusterd list ", + name); + cg = glusterd_remove_snap_cg_by_name (priv, + name); + if (!cg) + gf_log (this->name, GF_LOG_WARNING, "cannot " + "find the consistency group %s", name); + goto out; + } + } + + ret = 0; + +out: + return ret; } |