/* Copyright (c) 2013 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "globals.h" #include "compat.h" #include "protocol-common.h" #include "xlator.h" #include "logging.h" #include "timer.h" #include "glusterd-mem-types.h" #include "glusterd.h" #include "glusterd-sm.h" #include "glusterd-op-sm.h" #include "glusterd-utils.h" #include "glusterd-store.h" #include "run.h" #include "glusterd-volgen.h" #include "glusterd-mgmt.h" #include "glusterd-syncop.h" #include "syscall.h" #include "cli1-xdr.h" #include "xdr-generic.h" #ifdef GF_LINUX_HOST_OS #include #endif char * generate_snapname (char *volname, char *name, gf_boolean_t volume_from_cg); /* This function will restore a snapshot for the entire * volume or the entire CG (Consistency Group) * * @param dict dictionary containing snapshot restore request * @param op_errstr In case of any failure error message will be returned * in this variable * @return Negative value on Failure and 0 in success */ int glusterd_snapshot_restore (dict_t *dict, char **op_errstr) { int ret = -1; int64_t i = 0; int64_t volcount = 0; char *volname = NULL; char *snapname = NULL; xlator_t *this = NULL; glusterd_volinfo_t *volinfo = NULL; glusterd_snap_t *snap = NULL; char key[PATH_MAX] = {0,}; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (op_errstr); ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume count"); goto out; } /* If we are performing snapshot restore of a CG then volcount will be * greater than 1 else volcount will be 1. */ for (i = 0; i < volcount; ++i) { /* TODO: Start the index from 0 when Jarvis code is fixed */ snprintf (key, sizeof (key), "volname%ld", i+1); ret = dict_get_str (dict, key, &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) { ret = gf_asprintf (op_errstr, "Volume (%s) not found", volname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snap name"); goto out; } snap = glusterd_find_snap_by_name (volinfo, snapname); if (NULL == snap) { ret = gf_asprintf (op_errstr, "Snap (%s) not found", snapname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } /* Restore the snap for the entire volume */ ret = gd_restore_snap_volume (volinfo, snap->snap_volume); if (ret) { /* No need to update op_errstr because it is assumed * that the called function will do that in case of * failure. */ gf_log (this->name, GF_LOG_ERROR, "Failed to restore " "snap for %s volume", volname); goto out; } } ret = 0; /* Success */ /* TODO: Need to check if we need to delete the snap after the * operation is successful or not. Also need to persist the state * of restore operation in the store. */ out: return ret; } /* This function is called before actual restore is taken place. This * function will validate whether the volume or CG is ready to be restored * or not. * * @param dict dictionary containing snapshot restore request * @param op_errstr In case of any failure error message will be returned * in this variable * @param rsp_dict response dictionary * @return Negative value on Failure and 0 in success */ int glusterd_snapshot_restore_prevalidate (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int ret = -1; int64_t i = 0; int64_t volcount = 0; gf_boolean_t snap_restored = _gf_false; char *volname = NULL; char *snapname = NULL; char *cgname = NULL; glusterd_volinfo_t *volinfo = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; char key[PATH_MAX] = {0, }; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (op_errstr); GF_ASSERT (rsp_dict); ret = dict_get_str (dict, "cgname", &cgname); if (ret) { ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "neither snapname nor cgname"); goto out; } ret = dict_set_str (rsp_dict, "snapname", snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap name"); goto out; } } else { ret = dict_set_str (rsp_dict, "cgname", cgname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "CG name"); goto out; } } ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume count"); goto out; } /* Snapshot restore will only work if the volume is stopped. * If volume is running then snapshot restore will fail. In * case of CG if any of the volume in the CG is running then * snapshot restore for the entire CG will fail */ for (i = 0; i < volcount; ++i) { /* TODO: Start the index from 0 when Jarvis code is fixed */ snprintf (key, sizeof (key), "volname%ld", i+1); ret = dict_get_str (dict, key, &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) { ret = gf_asprintf (op_errstr, "Volume (%s) not found", volname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } if (glusterd_is_volume_started (volinfo)) { ret = gf_asprintf (op_errstr, "Volume (%s) is running", volname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snap name"); goto out; } snap = glusterd_find_snap_by_name (volinfo, snapname); if (NULL == snap) { ret = gf_asprintf (op_errstr, "Snap (%s) not found", snapname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } snap_restored = snap->snap_restored; if (snap_restored) { ret = gf_asprintf (op_errstr, "Snap (%s) already " "restored", snapname); if (ret < 0) { goto out; } gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); ret = -1; goto out; } } ret = 0; out: return ret; } int snap_max_limits_validate (dict_t *dict, char *volname, char *key, uint64_t value, char **op_errstr) { char err_str[PATH_MAX] = ""; glusterd_conf_t *conf = NULL; glusterd_volinfo_t *volinfo = NULL; int ret = -1; uint64_t max_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (volname); GF_ASSERT (key); GF_ASSERT (op_errstr); conf = this->private; GF_ASSERT (conf); ret = glusterd_volinfo_find (volname, &volinfo); if (!ret) { if (volinfo->is_snap_volume) { ret = -1; snprintf (err_str, PATH_MAX,"%s is a snap volume. " "Configuring %s for a snap volume is " "prohibited.", volname, key); goto out; } } if (!strcmp (key, "snap-max-hard-limit")) { max_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; if ((max_limit > conf->snap_max_hard_limit) && (strncmp (volname, "all", strlen(volname)))) max_limit = conf->snap_max_hard_limit; } else { max_limit = GLUSTERD_SNAPS_MAX_SOFT_LIMIT_PERCENT; if (strncmp (volname, "all", strlen(volname))) { ret = -1; snprintf (err_str, PATH_MAX, "%s is not configurable " "for individual volumes. Configure %s for " "system.", key, key); goto out; } } if ((value < 0) || (value > max_limit)) { ret = -1; snprintf (err_str, PATH_MAX, "Invalid %s " "%"PRIu64 ". Expected range 0 - %"PRIu64, key, value, max_limit); goto out; } ret = 0; out: if (ret) { *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); } return ret; } int glusterd_snapshot_config_prevalidate (dict_t *dict, char **op_errstr) { char *volname = NULL; char *key = NULL; glusterd_volinfo_t *volinfo = NULL; xlator_t *this = NULL; int ret = -1; int config_command = 0; char err_str[PATH_MAX] = {0,}; glusterd_conf_t *conf = NULL; uint64_t value = 0; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (op_errstr); conf = this->private; GF_ASSERT (conf); ret = dict_get_int32 (dict, "config-command", &config_command); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get config-command type"); goto out; } ret = dict_get_str (dict, "config-key", &key); if (!ret) { ret = dict_get_uint64 (dict, key, &value); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " value for %s", key); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } } ret = dict_get_str (dict, "volname", &volname); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " volume name"); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } if (strncmp (volname, "all", strlen(volname))) { ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, PATH_MAX,"Volume %s does not exist.", volname); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } } switch (config_command) { case GF_SNAP_CONFIG_TYPE_SET: if ((!strncmp (key, "snap-max-hard-limit", strlen(key))) || (!strncmp (key, "snap-max-soft-limit", strlen(key)))) { /* Validations for snap-max-limits */ ret = snap_max_limits_validate (dict, volname, key, value, op_errstr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "%s validation failed.", key); goto out; } } break; default: break; } out: return ret; } int glusterd_snap_create_pre_val_use_rsp_dict (dict_t *dst, dict_t *src) { char *snap_mount = NULL; char *tmpstr = NULL; char snapmntname[PATH_MAX] = ""; char snapbrckcnt[PATH_MAX] = ""; char snapbrckord[PATH_MAX] = ""; int ret = -1; int64_t i = -1; int64_t j = -1; int64_t volume_count = 0; int64_t brick_count = 0; int64_t brick_order = 0; if (!dst || !src) { gf_log ("", GF_LOG_ERROR, "Source or Destination " "dict is empty."); goto out; } ret = dict_get_int64 (src, "volcount", &volume_count); if (ret) { gf_log ("", GF_LOG_ERROR, "failed to " "get the volume count"); goto out; } for (i = 0; i < volume_count; i++) { memset (snapbrckcnt, '\0', sizeof(snapbrckcnt)); ret = snprintf (snapbrckcnt, sizeof(snapbrckcnt) - 1, "vol%ld_brickcount", i+1); ret = dict_get_int64 (src, snapbrckcnt, &brick_count); if (ret) { gf_log ("", GF_LOG_TRACE, "No bricks for this volume in this dict"); continue; } for (j = 0; j < brick_count; j++) { /* Fetching data from source dict */ memset (snapmntname, '\0', sizeof(snapmntname)); ret = snprintf (snapmntname, sizeof(snapmntname) - 1, "vol%ld.brick%ld", i+1, j); ret = dict_get_ptr (src, snapmntname, (void **)&snap_mount); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "Unable to fetch " "snap mount path"); goto out; } memset (snapbrckord, '\0', sizeof(snapbrckord)); ret = snprintf (snapbrckord, sizeof(snapbrckord) - 1, "vol%ld.brick%ld.order", i+1, j); ret = dict_get_int64 (src, snapbrckord, &brick_order); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "Failed to get brick order"); goto out; } /* Adding the data in the dst dict */ memset (snapmntname, '\0', sizeof(snapmntname)); ret = snprintf (snapmntname, sizeof(snapmntname) - 1, "vol%ld.brick%ld", i+1, brick_order); tmpstr = gf_strdup (snap_mount); if (!tmpstr) { gf_log (THIS->name, GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } ret = dict_set_dynstr (dst, snapmntname, tmpstr); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, "Failed to set %s", snap_mount); GF_FREE (tmpstr); goto out; } } } ret = 0; out: gf_log (THIS->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } int glusterd_snap_pre_validate_use_rsp_dict (dict_t *dst, dict_t *src) { int ret = -1; int32_t snap_command = 0; if (!dst || !src) { gf_log ("", GF_LOG_ERROR, "Source or Destination " "dict is empty."); goto out; } ret = dict_get_int32 (dst, "type", &snap_command); if (ret) { gf_log ("", GF_LOG_ERROR, "unable to get the type of " "the snapshot command"); goto out; } switch (snap_command) { case GF_SNAP_OPTION_TYPE_CREATE: ret = glusterd_snap_create_pre_val_use_rsp_dict (dst, src); if (ret) { gf_log ("", GF_LOG_ERROR, "Unable to use rsp dict"); goto out; } break; default: break; } ret = 0; out: gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); return ret; } int glusterd_snapshot_create_prevalidate (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { char *volname = NULL; char *name = NULL; char *device = NULL; char *tmpstr = NULL; char *snap_brick_dir = NULL; char snap_brick_path[PATH_MAX] = ""; char *mnt_pt = NULL; char snapname[PATH_MAX] = ""; char tmp[2046] = ""; char key[PATH_MAX] = ""; char snap_mount[PATH_MAX] = ""; char snapmntname[PATH_MAX] = ""; char snapbrckcnt[PATH_MAX] = ""; char snapbrckord[PATH_MAX] = ""; int ret = -1; int64_t i = 0; int64_t volume_count = 0; int64_t brick_count = 0; int64_t brick_order = 0; glusterd_brickinfo_t *brickinfo = NULL; glusterd_volinfo_t *volinfo = NULL; gf_boolean_t is_cg = _gf_false; xlator_t *this = NULL; this = THIS; 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; } GF_ASSERT (volume_count); if (volume_count > 1) { is_cg = _gf_true; ret = dict_get_str (dict, "cg-name", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get cg-name"); goto out; } } else { ret = dict_get_str (dict, "snap-name", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get snap-name"); goto out; } } snprintf (snapname, sizeof (snapname), "%s", name); for (i = 0; i < volume_count; i++) { snprintf (key, sizeof (key), "volname%ld", i+1); ret = dict_get_str (dict, key, &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_volume_started (volinfo)) { gf_log (this->name, GF_LOG_WARNING, "volume %s is not started", volinfo->volname); ret = -1; goto out; } if (glusterd_is_defrag_on (volinfo)) { ret = -1; gf_log (this->name, GF_LOG_WARNING, "rebalance process is running " "for the volume %s", volname); goto out; } if (is_cg) { memset(snapname, '\0', sizeof (snapname)); snprintf (tmp, sizeof (tmp), "%s_snap", volinfo->volname); snprintf (snapname, sizeof (snapname), "%s_%s", name, tmp); } //Also check whether geo replication is running brick_count = 0; brick_order = 0; /* Adding snap bricks mount paths to the dict */ list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) { brick_order++; continue; } memset (snapmntname, '\0', sizeof(snapmntname)); memset (snapbrckord, '\0', sizeof(snapbrckord)); memset (snap_mount, '\0', sizeof(snap_mount)); 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); ret = -1; goto out; } device = glusterd_build_snap_device_path (device, snapname); if (!device) { gf_log (this->name, GF_LOG_WARNING, "cannot copy the " "snapshot device name (volname: %s, " "snapname: %s)", volinfo->volname, snapname); ret = -1; goto out; } glusterd_replace_slash_with_hyphen (device); if (device[0] == '-') device[0] = '/'; ret = snprintf (snap_mount, sizeof(snap_mount) - 1, "%s/%s%s-brick", GLUSTERD_DEFAULT_SNAPS_BRICK_DIR, snapname, device); snap_mount[ret] = '\0'; ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); if (ret) { gf_log (this->name, GF_LOG_WARNING, "could not get the root of" "the brick path %s", brickinfo->path); goto out; } if (strncmp (brickinfo->path, mnt_pt, strlen (mnt_pt))) { gf_log (this->name, GF_LOG_WARNING, "brick: %s brick mount: %s", brickinfo->path, mnt_pt); goto out; } snap_brick_dir = &brickinfo->path[strlen (mnt_pt)]; if (snap_brick_dir) snap_brick_dir++; snprintf (snap_brick_path, sizeof (snap_brick_path), "%s/%s", snap_mount, snap_brick_dir); ret = snprintf (snapmntname, sizeof(snapmntname) - 1, "vol%ld.brick%ld", i+1, brick_count); snapmntname[ret] = '\0'; tmpstr = gf_strdup (snap_brick_path); if (!tmpstr) { gf_log ("", GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } ret = dict_set_dynstr (rsp_dict, snapmntname, tmpstr); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set %s", snap_mount); GF_FREE (tmpstr); goto out; } tmpstr = NULL; ret = snprintf (snapbrckord, sizeof(snapbrckord) - 1, "vol%ld.brick%ld.order", i+1, brick_count); snapbrckord[ret] = '\0'; ret = dict_set_int64 (rsp_dict, snapbrckord, brick_order); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set brick order"); goto out; } brick_count++; brick_order++; } memset (snapbrckcnt, '\0', sizeof(snapbrckcnt)); ret = snprintf (snapbrckcnt, sizeof(snapbrckcnt) - 1, "vol%ld_brickcount", i+1); ret = dict_set_int64 (rsp_dict, snapbrckcnt, brick_count); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set %s", snapbrckcnt); goto out; } } ret = dict_set_int64 (rsp_dict, "volcount", volume_count); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set volcount"); goto out; } ret = 0; out: if (ret) GF_FREE (tmpstr); gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } glusterd_snap_t* glusterd_new_snap_object() { glusterd_snap_t *snap = NULL; snap = GF_CALLOC (1, sizeof (*snap), gf_gld_mt_snap_t); if (snap) { LOCK_INIT (&snap->lock); INIT_LIST_HEAD (&snap->snap_list); snap->snap_status = GD_SNAP_STATUS_INIT; } return snap; }; glusterd_snap_cg_t* glusterd_new_snap_cg_object(int64_t volume_count) { glusterd_snap_cg_t *cg = NULL; glusterd_volinfo_t *volinfo = NULL; if (volume_count < 0) { gf_log (THIS->name, GF_LOG_ERROR, "Volume count < 0"); return NULL; } cg = GF_CALLOC (1, (sizeof (*cg) + (volume_count * sizeof (volinfo))), gf_gld_mt_snap_cg_t); if (cg) { LOCK_INIT (&cg->lock); INIT_LIST_HEAD (&cg->cg_list); cg->cg_status = GD_SNAP_STATUS_INIT; cg->volume_count = volume_count; } return cg; } int32_t glusterd_add_snap (glusterd_volinfo_t *volinfo, glusterd_snap_t *snap) { int ret = -1; uint64_t count = -1; glusterd_snap_t *entry = NULL; glusterd_snap_t *last = NULL; glusterd_snap_t *tmp = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); GF_VALIDATE_OR_GOTO ("glusterd", snap, out); LOCK (&volinfo->lock); { list_for_each_entry_safe (entry, tmp, &volinfo->snaps, snap_list) { count++; if (!strcmp (entry->snap_name, snap->snap_name) || !uuid_compare (entry->snap_id, snap->snap_id)) { gf_log (THIS->name, GF_LOG_ERROR, "Found " "duplicate snap %s (%s)", entry->snap_name, uuid_utoa (entry->snap_id)); goto unlock; } last = entry; } list_add_tail (&snap->snap_list, &volinfo->snaps); volinfo->snap_count++; gf_log (THIS->name, GF_LOG_DEBUG, "Snap %s added @ %"PRIu64, snap->snap_name, count); ret = 0; } unlock: UNLOCK (&volinfo->lock); out: gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } glusterd_snap_t* glusterd_find_snap_by_index (glusterd_volinfo_t *volinfo, uint64_t index) { uint64_t count = 0; glusterd_snap_t *entry = NULL; glusterd_snap_t *tmp = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); LOCK (&volinfo->lock); { list_for_each_entry_safe (entry, tmp, &volinfo->snaps, snap_list) { if (index == count) { gf_log (THIS->name, GF_LOG_DEBUG, "Found " "snap %s (%s)", entry->snap_name, uuid_utoa (entry->snap_id)); break; } ++count; } } UNLOCK (&volinfo->lock); out: return entry; } glusterd_snap_t* glusterd_find_snap_by_name (glusterd_volinfo_t *volinfo, char *snap_name) { uint64_t count = -1; glusterd_snap_t *entry = NULL; glusterd_snap_t *dup = NULL; glusterd_snap_t *tmp = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); GF_VALIDATE_OR_GOTO ("glusterd", snap_name, out); LOCK (&volinfo->lock); { list_for_each_entry_safe (entry, tmp, &volinfo->snaps, snap_list) { count++; if (!strcmp (entry->snap_name, snap_name)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found " "snap %s (%s)", entry->snap_name, uuid_utoa (entry->snap_id)); dup = entry; break; } } } UNLOCK (&volinfo->lock); out: return dup; } glusterd_snap_t* glusterd_find_snap_by_id (glusterd_volinfo_t *volinfo, uuid_t snap_id) { uint64_t count = -1; glusterd_snap_t *entry = NULL; glusterd_snap_t *dup = NULL; glusterd_snap_t *tmp = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); if (uuid_is_null(snap_id)) goto out; LOCK (&volinfo->lock); { list_for_each_entry_safe (entry, tmp, &volinfo->snaps, snap_list) { count++; if (!uuid_compare (entry->snap_id, snap_id)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found " "snap %s (%s)", entry->snap_name, uuid_utoa (entry->snap_id)); dup = entry; break; } } } UNLOCK (&volinfo->lock); out: return dup; } glusterd_snap_t* glusterd_remove_snap_by_id (glusterd_volinfo_t *volinfo, uuid_t snap_id) { glusterd_snap_t *entry = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); if (uuid_is_null(snap_id)) goto out; entry = glusterd_find_snap_by_id (volinfo, snap_id); if (entry) { LOCK (&volinfo->lock); { entry->snap_status = GD_SNAP_STATUS_DECOMMISSION; list_del_init (&entry->snap_list); } UNLOCK (&volinfo->lock); } out: return entry; } glusterd_snap_t* glusterd_remove_snap_by_name (glusterd_volinfo_t *volinfo, char *snap_name) { glusterd_snap_t *entry = NULL; GF_VALIDATE_OR_GOTO ("glusterd", volinfo, out); GF_VALIDATE_OR_GOTO ("glusterd", snap_name, out); entry = glusterd_find_snap_by_name (volinfo, snap_name); if (entry) { LOCK (&volinfo->lock); { entry->snap_status = GD_SNAP_STATUS_DECOMMISSION; list_del_init (&entry->snap_list); volinfo->snap_count--; } UNLOCK (&volinfo->lock); } out: return entry; } // Big lock should already acquired before this is called int32_t glusterd_add_snap_cg (glusterd_conf_t *conf, glusterd_snap_cg_t *cg) { int ret = -1; uint64_t count = -1; glusterd_snap_cg_t *entry = NULL; glusterd_snap_cg_t *last = NULL; glusterd_snap_cg_t *tmp = NULL; GF_VALIDATE_OR_GOTO (THIS->name, conf, out); GF_VALIDATE_OR_GOTO (THIS->name, cg, out); list_for_each_entry_safe (entry, tmp, &conf->snap_cg, cg_list) { count++; if (!strcmp (entry->cg_name, cg->cg_name) || !uuid_compare (entry->cg_id, cg->cg_id)) { gf_log (THIS->name, GF_LOG_ERROR, "Found duplicate " "CG %s(%s)", entry->cg_name, uuid_utoa(entry->cg_id)); goto out; } last = entry; } list_add_tail (&cg->cg_list, &conf->snap_cg); gf_log (THIS->name, GF_LOG_DEBUG, "Added CG %s (%s) @ %"PRIu64, cg->cg_name, uuid_utoa(cg->cg_id), count); ret = 0; out: gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } glusterd_snap_cg_t* glusterd_find_snap_cg_by_name (glusterd_conf_t *conf, char *cg_name) { glusterd_snap_cg_t *entry = NULL; glusterd_snap_cg_t *dup = NULL; glusterd_snap_cg_t *tmp = NULL; GF_VALIDATE_OR_GOTO (THIS->name, conf, out); GF_VALIDATE_OR_GOTO (THIS->name, cg_name, out); list_for_each_entry_safe (entry, tmp, &conf->snap_cg, cg_list) { if (!strcmp (entry->cg_name, cg_name)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found CG %s(%s)", entry->cg_name, uuid_utoa(entry->cg_id)); dup = entry; break; } } out: return dup; } glusterd_snap_cg_t* glusterd_find_snap_cg_by_id (glusterd_conf_t *conf, uuid_t cg_id) { glusterd_snap_cg_t *entry = NULL; glusterd_snap_cg_t *dup = NULL; glusterd_snap_cg_t *tmp = NULL; GF_VALIDATE_OR_GOTO (THIS->name, conf, out); if (uuid_is_null (cg_id)) goto out; list_for_each_entry_safe (entry, tmp, &conf->snap_cg, cg_list) { if (!uuid_compare (entry->cg_id, cg_id)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found CG %s(%s)", entry->cg_name, uuid_utoa(entry->cg_id)); dup = entry; break; } } out: return dup; } glusterd_snap_cg_t* glusterd_remove_snap_cg_by_name (glusterd_conf_t *conf, char *cg_name) { glusterd_snap_cg_t *entry = NULL; GF_VALIDATE_OR_GOTO (THIS->name, conf, out); GF_VALIDATE_OR_GOTO (THIS->name, cg_name, out); entry = glusterd_find_snap_cg_by_name(conf, cg_name); if (entry) { entry->cg_status = GD_SNAP_STATUS_DECOMMISSION; list_del_init (&entry->cg_list); } out: return entry; } glusterd_snap_cg_t* glusterd_remove_snap_cg_by_id (glusterd_conf_t *conf, uuid_t cg_id) { glusterd_snap_cg_t *entry = NULL; GF_VALIDATE_OR_GOTO (THIS->name, conf, out); if (uuid_is_null (cg_id)) goto out; entry = glusterd_find_snap_cg_by_id (conf, cg_id); if (entry) { entry->cg_status = GD_SNAP_STATUS_DECOMMISSION; list_del_init (&entry->cg_list); } out: return entry; } int32_t 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); if (ret) goto out; ret = glusterd_volinfo_delete (snapinfo); out: gf_log (THIS->name, GF_LOG_DEBUG, "returning %d", ret); return ret; } /* This function will retrieve the details of a single snap * and then serialize them to dictionary (dict) * This function is called under snap lock * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param entry Snap object * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_get_snapdetail_lk (dict_t *dict, char *keyprefix, glusterd_snap_t *entry, int8_t detail) { int ret = -1; /* Failure */ const int maxstrlen = 256; char *value = NULL; char *timestr = NULL; struct tm *tmptr = NULL; xlator_t *this = NULL; char key[maxstrlen]; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (entry); /* Snap Name */ value = gf_strdup (entry->snap_name); if (NULL == value) { goto out; } snprintf (key, sizeof (key), "%s.snapname", keyprefix); ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap name in dictionary"); goto out; } /* Snap ID */ value = gf_strdup (uuid_utoa (entry->snap_id)); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.snap-id", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap id in dictionary"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; /* Snap Timestamp */ /* convert time_t to tm struct. */ tmptr = localtime (&(entry->time_stamp)); if (NULL == tmptr) { gf_log (this->name, GF_LOG_ERROR, "Failed to convert " "time_t to *tm"); ret = -1; goto out; } timestr = GF_CALLOC (1, maxstrlen, gf_gld_mt_char); if (NULL == timestr) { ret = -1; goto out; } /* Format time into string */ ret = strftime (timestr, maxstrlen, "%Y-%m-%d %H:%M:%S", tmptr); if (0 == ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to convert time_t " "to string"); ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.snap-time", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, timestr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap time stamp in dictionary"); goto out; } /* Ownership of timestr transferred to dict. Therefore we must initalize * it to NULL */ timestr = NULL; if (!detail) { /* If detail is not needed then return from here */ goto out; } /* Add detail */ /* If CG name is set the add the details in the dictionary */ if (0 != entry->cg_name[0] ) { /* CG name */ value = gf_strdup (entry->cg_name); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.cg-name", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap name in dictionary"); goto out; } /* CG ID */ value = gf_strdup (uuid_utoa (entry->cg_id)); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.cg-id", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "cg id in dictionary"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; } /* If snap description is provided then add that into dictionary */ if (NULL != entry->description) { /* Snap Description */ value = gf_strdup (entry->description); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.snap-desc", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap description in dictionary"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; } /* Snap status */ ret = snprintf (key, sizeof (key), "%s.snap-status", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } switch (entry->snap_status) { case GD_SNAP_STATUS_INIT: ret = dict_set_str (dict, key, "Init"); break; case GD_SNAP_STATUS_IN_USE: ret = dict_set_str (dict, key, "In-use"); break; case GD_SNAP_STATUS_DECOMMISSION: ret = dict_set_str (dict, key, "Decommisioned"); break; case GD_SNAP_STATUS_RESTORED: ret = dict_set_str (dict, key, "Restored"); break; case GD_SNAP_STATUS_NONE: ret = dict_set_str (dict, key, "None"); break; default: gf_log (this->name, GF_LOG_ERROR, "Invalid snap " "status"); ret = -1; goto out; } if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap status in dictionary"); goto out; } ret = 0; /* Success */ out: if (NULL != value) { GF_FREE (value); } if (NULL != timestr) { GF_FREE(timestr); } return ret; } /* This function will retrieve the details of a single snap * and then serialize them to dictionary (dict) * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param entry Snap object * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_get_snapdetail (dict_t *dict, char *keyprefix, glusterd_snap_t *entry, int8_t detail) { int ret = -1; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (entry); /* Acquire snap lock */ LOCK (&(entry->lock)); { ret = glusterd_snapshot_get_snapdetail_lk (dict, keyprefix, entry, detail); } UNLOCK (&(entry->lock)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap detail"); } return ret; } /* This function will retrieve snap list for the given volume * and then serialize them to dict. * This function is called under volinfo lock. * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param volinfo Volinfo object of the volume * @param snapname snap name. This field can be NULL * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_vol_get_snaplist_lk (dict_t *dict, char *keyprefix, glusterd_volinfo_t *volinfo, char *snapname, int8_t detail) { int ret = -1; uint64_t index = 0; glusterd_snap_t *entry = NULL; glusterd_snap_t *tmp = NULL; xlator_t *this = NULL; char *value = NULL; char key[256]; glusterd_conf_t *conf = NULL; uint64_t snap_limit = 0; char err_str[PATH_MAX]; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (volinfo); conf = this->private; GF_ASSERT (conf); value = gf_strdup (volinfo->volname); if (NULL == value) { goto out; } /* First set the volume name */ ret = snprintf (key, sizeof (key), "%s.volname", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set volume name"); goto out; } ret = snprintf (key, sizeof (key), "%s.snap-count-total", keyprefix); if (ret < 0) { goto out; } ret = dict_set_int64 (dict, key, volinfo->snap_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set total" " snap count"); goto out; } ret = snprintf (key, sizeof (key), "%s.snap-available", keyprefix); if (ret < 0) { goto out; } if (conf->snap_max_hard_limit < volinfo->snap_max_hard_limit) { snap_limit = conf->snap_max_hard_limit; gf_log(this->name, GF_LOG_DEBUG, "system snap-max-hard-limit is" " lesser than volume snap-max-hard-limit, " "snap-max-hard-limit value is set to %ld", snap_limit); } else { snap_limit = volinfo->snap_max_hard_limit ; gf_log(this->name, GF_LOG_DEBUG, "volume snap-max-hard-limit is" " lesser than system snap-max-hard-limit, " "snap-max-hard-limit value is set to %ld",snap_limit); } if (snap_limit > volinfo->snap_count) ret = dict_set_int64 (dict, key, snap_limit - volinfo->snap_count); else ret = dict_set_int64 (dict, key, 0); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set available snaps"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; /* snapshot taken first should be displayed first */ list_for_each_entry_safe (entry, tmp, &volinfo->snaps, snap_list) { ret = snprintf (key, sizeof (key), "%s.snap-%ld", keyprefix, index); if (ret < 0) { /* Only negative value is error */ goto out; } ++index; /* If snapname is NULL then get all the snaps * for the given volume */ if (NULL == snapname) { ret = glusterd_snapshot_get_snapdetail (dict, key, entry, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get snap detail for %s snap", snapname); goto out; /* something wrong */ } continue; /* Get the next entry */ } /* If snapname is provided then get snap detail * for only that snap */ if (strncmp (entry->snap_name, snapname, sizeof (entry->snap_name))) { /* Entry not yet found.*/ ret = -1; continue; /* Check the next entry */ } /* snap found */ ret = snprintf (key, sizeof (key), "%s.snap-0", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = glusterd_snapshot_get_snapdetail (dict, key, entry, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap " "detail for %s snap", snapname); goto out; } /* Index is used to identify how many snap objects are * added to the dictionary. If snapshot name is passed * as argument then we would send only one snap object. * Therefore index should be reset to 0. */ index = 1; break; /* Found the snap */ } /* If all the snap is written into the dictionary then write the * snap count into the dictionary */ if (0 == ret) { ret = snprintf (key, sizeof (key), "%s.snap-count", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_int64 (dict, key, index); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap count"); goto out; } } else if (NULL != snapname) { gf_log (this->name, GF_LOG_ERROR, "Snap (%s) not found", snapname); ret = snprintf (key, sizeof (key), "%s.err_str", keyprefix); if (ret < 0) { goto out; } ret = snprintf (err_str, sizeof (err_str), "Snapshot %s" " not found", snapname); if (ret < 0) { goto out; } ret = dict_set_str (dict, key, err_str); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to" "set error string"); goto out; } } out: if (NULL != value) { GF_FREE (value); } return ret; } /* This function will retrieve snap list for the given volume * and then serialize them to dict. * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param volinfo Volinfo object of the volume * @param snapname snap name. This field can be NULL * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_vol_get_snaplist (dict_t *dict, char *keyprefix, glusterd_volinfo_t *volinfo, char *snapname, int8_t detail) { int ret = -1; /* Failure */ xlator_t *this = NULL; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (volinfo); /* Acquire the volinfo lock before proceeding */ LOCK (&(volinfo->lock)); { ret = glusterd_snapshot_vol_get_snaplist_lk (dict, keyprefix, volinfo, snapname, detail); } UNLOCK (&(volinfo->lock)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap list for" " %s volume", volinfo->volname); } return ret; } /* This function will retrieve snap list for the given volume * and then serialize them to dict. * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param volname Volname whose snap list is requested * @param snapname snap name. This field can be NULL. * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_vol_get_snaplist_by_name (dict_t *dict, char *keyprefix, char *volname, char *snapname, int8_t detail) { int ret = -1; glusterd_volinfo_t *volinfo = NULL; xlator_t *this = NULL; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (volname); /* Find te volinfo from the volname */ ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volinfo for " "%s volume", volname); goto out; } /* Now using the volinfo object get the snap list */ ret = glusterd_snapshot_vol_get_snaplist (dict, keyprefix, volinfo, snapname, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snaplist for " "%s volume", volname); goto out; } out: return ret; } /* This function will retrieve snap list for all the volumes * present in a given CG and then serialize them to dict. * This function is called under CG lock. * * @param dict dictionary where response should be serialized * @param cg CG object which need to be written into dictionary * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param cgname CG name. * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_cg_get_snaplist_lk (dict_t *dict, glusterd_snap_cg_t *cg, char *keyprefix, char *cgname, int8_t detail) { int ret = -1; /* Failure */ glusterd_conf_t *conf = NULL; char *value = NULL; xlator_t *this = NULL; int64_t i = 0; char key[256]= {0,}; glusterd_volinfo_t *volinfo = NULL; this = THIS; /* General parameter validation */ GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (dict); GF_ASSERT (cg); GF_ASSERT (keyprefix); GF_ASSERT (cgname); /* CG Name */ value = gf_strdup (cg->cg_name); if (NULL == value) { goto out; } ret = snprintf (key, sizeof (key), "%s.cg-name", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "cg name in dictionary"); goto out; } /* CG ID */ value = gf_strdup (uuid_utoa (cg->cg_id)); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.cg-id", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "cg id in dictionary"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; /* Volume count */ ret = snprintf (key, sizeof (key), "%s.vol-count", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_int64 (dict, key, cg->volume_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "volume count in dictionary"); goto out; } /* Get snap list for all volumes present in the CG */ for (i = 0; i < cg->volume_count; ++i) { volinfo = cg->volumes[i]; ret = snprintf (key, sizeof (key), "%s.vol%ld", keyprefix, i); if (ret < 0) { /* Only negative value is error */ goto out; } ret = glusterd_snapshot_vol_get_snaplist (dict, key, volinfo, NULL, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snaplist for %s volume", volinfo->volname); goto out; } } if (!detail) { /* If detail is not needed then return from here */ goto out; } /* If CG description is provided then add that into dictionary */ if (NULL != cg->description) { /* CG Description */ value = gf_strdup (cg->description); if (NULL == value) { ret = -1; goto out; } ret = snprintf (key, sizeof (key), "%s.cg-desc", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "cg description in dictionary"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; } /* CG status */ ret = snprintf (key, sizeof (key), "%s.cg-status", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } switch (cg->cg_status) { case GD_SNAP_STATUS_INIT: ret = dict_set_str (dict, key, "Init"); break; case GD_SNAP_STATUS_IN_USE: ret = dict_set_str (dict, key, "In-use"); break; case GD_SNAP_STATUS_DECOMMISSION: ret = dict_set_str (dict, key, "Decommisioned"); break; case GD_SNAP_STATUS_RESTORED: ret = dict_set_str (dict, key, "Restored"); break; case GD_SNAP_STATUS_NONE: ret = dict_set_str (dict, key, "None"); break; default: gf_log (this->name, GF_LOG_ERROR, "Invalid snap " "status"); ret = -1; /* Failure */ goto out; } if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap status in dictionary"); goto out; } ret = 0; out: if (NULL != value) { GF_FREE (value); } return ret; } /* This function will retrieve snap list for all the volumes * present in a given CG and then serialize them to dict. * * @param dict dictionary where response should be serialized * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param cgname CG name. * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_cg_get_snaplist (dict_t *dict, char *keyprefix, char *cgname, int8_t detail, char *err_str, size_t len) { int ret = -1; /* Failure */ glusterd_conf_t *conf = NULL; glusterd_snap_cg_t *cg = NULL; xlator_t *this = NULL; this = THIS; /* General parameter validation */ GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (cgname); /* Find the CG object from CG name */ cg = glusterd_find_snap_cg_by_name (conf, cgname); if (NULL == cg) { snprintf (err_str, len, "CG %s not found", cgname); gf_log (this->name, GF_LOG_WARNING, "%s", err_str); ret = -1; goto out; } /* Got CG. Now serialize the CG content to dictionary */ LOCK (&(cg->lock)); { ret = glusterd_snapshot_cg_get_snaplist_lk (dict, cg, keyprefix, cgname, detail); } UNLOCK (&(cg->lock)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get CG details"); } ret = 0; /* Success */ out: return ret; } /* This function will retrieve snap list for all the volumes * present in voldict dictionary. And then serialize them to * rspdict. * * @param voldict dictionary containing volumes * @param rspdict dictionary where response should be serialized * @param volcount Total volume count * @param keyprefix Prefix used for all the keys for rspdict dictionary * @param snapname snap name. This field can be NULL. * @param detail if 1 then more details will be added for snap list * * @return -1 on failure and 0 on success. */ static int glusterd_snapshot_get_snaplist (dict_t *voldict, dict_t *rspdict, int64_t volcount, char* keyprefix, char *snapname, int8_t detail) { int ret = -1; /* Failure */ int64_t i = 0; char *volname = NULL; xlator_t *this = NULL; char key[256] = {0,}; gf_boolean_t exist = _gf_false; char *err_str = NULL; char err_prefix[PATH_MAX] = ""; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (voldict); GF_ASSERT (rspdict); GF_ASSERT (keyprefix); /* Write the total volume count into the rspdict */ ret = snprintf (key, sizeof (key), "%s.vol-count", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_set_int64 (rspdict, key, volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "volume count in dictionary"); goto out; } /* For each volume add all the snap list to rspdict dictionary */ for (i = 0; i < volcount; ++i) { /* This key is used to get the volume name from voldict * dictionary. Therefore do not use keyprefix here */ ret = snprintf (key, sizeof (key), "vol%ld", i); if (ret < 0) { /* Only negative value is error */ goto out; } ret = dict_get_str (voldict, key, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get" "volname for %s", key); goto out; } ret = snprintf (key, sizeof (key), "%s.vol%ld", keyprefix, i); if (ret < 0) { /* Only negative value is error */ goto out; } exist = glusterd_check_volume_exists (volname); if (!exist) { err_str = (char *) GF_CALLOC(1, PATH_MAX, sizeof(char)); gf_log ("", GF_LOG_ERROR, "Volume %s Does not exist", volname); ret = snprintf (err_str, PATH_MAX, "Volume %s Does not exist",volname); if (ret < 0) { goto out; } ret = snprintf (err_prefix, sizeof (err_prefix), "%s.err_str", key); if (ret < 0) { goto out; } ret = dict_set_str (rspdict, err_prefix, err_str); if (ret < 0 ) { gf_log ("", GF_LOG_ERROR, "Could not" "save the err_str"); goto out; } continue; } /* Now for each volume get the snap list */ ret = glusterd_snapshot_vol_get_snaplist_by_name (rspdict, key, volname, snapname, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapshot list for %s volume", volname); goto out; } } ret = 0; /* Success */ out: return ret; } /* This function will be called from RPC handler routine. * This function is responsible for getting the requested * snapshot list into the dictionary. * * @param req RPC request object. Required for sending a response back. * @param op glusterd operation. Required for sending a response back. * @param dict pointer to dictionary which will contain both * request and response key-pair values. * @return -1 on error and 0 on success */ int glusterd_handle_snapshot_list (rpcsvc_request_t *req, glusterd_op_t op, dict_t *dict, char *err_str, size_t len) { int ret = -1; int64_t volcount = 0; int vol_count = 0; int8_t detail = 0; char *keyprefix = "snaplist"; char *cgname = NULL; char *snapname = NULL; dict_t *voldict = NULL; xlator_t *this = NULL; char key[256] = {0,}; this = THIS; GF_ASSERT (this); GF_VALIDATE_OR_GOTO (this->name, req, out); GF_VALIDATE_OR_GOTO (this->name, dict, out); /* Get the request key-pair from the dictionary */ /* All these options are optonal. Therefore ignore * error returned by following dictionary operations */ ret = dict_get_str (dict, "snapname", &snapname); /* Ignore error */ ret = dict_get_int8 (dict, "snap-details", &detail); ret = dict_get_int64 (dict, "vol-count", &volcount); if (ret) { /* Ignore error */ ret = dict_get_str (dict, "cgname", &cgname); } /* If volume names are passed as argument then we should * get all the snapshots for the said volumes. */ if (volcount > 0) { ret = glusterd_snapshot_get_snaplist (dict, dict, volcount, keyprefix, snapname, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapshot list"); goto out; } } else if (NULL != cgname) { /* If CG (Consistency Group) name is passed as argument then * we should get snapshots of all the volumes present in the * said CG */ /* TODO: Handle multiple CG if needed */ ret = snprintf (key, sizeof (key), "%s.cg-0", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = glusterd_snapshot_cg_get_snaplist (dict, key, cgname, detail, err_str, len); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapshot list for %s CG", cgname); goto out; } } else { /* If no argument is provided then we should get snapshots of * all the volumes managed by this server */ /* Create a dictionary to hold all the volumes retrieved from * glusterd */ voldict = dict_new (); if (NULL == voldict) { ret = -1; goto out; } /* Get all the volumes from glusterd */ ret = glusterd_get_all_volnames (voldict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get all " "volume names"); goto out; } /* Get the volume count */ ret = dict_get_int32 (voldict, "vol_count", &vol_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume" " count"); goto out; } volcount = vol_count; /* Get snap list for all the volumes*/ ret = glusterd_snapshot_get_snaplist (voldict, dict, volcount, keyprefix, NULL, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapshot list"); goto out; } } /* If everything is successful then send the response back to cli. * In case of failure the caller of this function will take of response.*/ ret = glusterd_op_send_cli_response (op, 0, 0, req, dict, err_str); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to send cli " "response"); goto out; } ret = 0; /* Success */ out: if (voldict) { dict_unref (voldict); } return ret; } /* TODO: This function needs a revisit. * * As of now only one snap is supported per CG. This function will * retrieve the snap name which bleongs to the CG and put it in the * dictionary. * * @param dict dictionary containing snapshot restore request * @param cg CG object. * in this variable * @return Negative value on Failure and 0 in success */ int glusterd_get_cg_snap_name_lk (dict_t *dict, glusterd_snap_cg_t *cg) { int ret = -1; uint64_t snap_count = 0; char *snapname = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; glusterd_volinfo_t *volinfo = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (cg); /* CG should have at least one volume*/ GF_ASSERT (cg->volume_count > 0); /* TODO: As of now only one snap is supported per CG When CG * management module comes in then this restriction can be removed. */ /* TODO: As of now, consistency group can be created out of some volumes, even though the volumes already have snapshots taken. But the future plan is to have consistency group where past or future individual snapshots are not allowed. So this below check of snap count of the first volume of the CG to 1, is in accordance with that. */ volinfo = cg->volumes[0]; snap_count = volinfo->snap_count; if (1 != snap_count) { gf_log (this->name, GF_LOG_ERROR, "More than one snap is " "associated with the cg (%s)", cg->cg_name); ret = -1; goto out; } snap = glusterd_find_snap_by_index (volinfo, 0); if (NULL == snap) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap for " "%s CG", cg->cg_name); ret = -1; goto out; } snapname = gf_strdup (snap->snap_name); if (NULL == snapname) { ret = -1; goto out; } ret = dict_set_dynstr (dict, "snapname", snapname); if (ret) { GF_FREE (snapname); gf_log (this->name, GF_LOG_ERROR, "Failed to " "set snap name"); goto out; } out: return ret; } /* This is a helper function will get all the volume names present in CG * and write into dictionary. * * @param dict dictionary where volume names should be written * @param cg CG object. * in this variable * @return Negative value on Failure and 0 in success */ int glusterd_get_cg_volume_names_lk (dict_t *dict, glusterd_snap_cg_t *cg) { int ret = -1; int64_t i = 0; char *volname = NULL; xlator_t *this = NULL; char key[PATH_MAX] = {0,}; glusterd_volinfo_t *volinfo = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (cg); ret = dict_set_int64 (dict, "volcount", cg->volume_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "set volume count"); goto out; } /* Set volume name of all the volumes present in CG in dict so that * Jarvis can use this to acquire volume locks on all the volume * present in the CG. */ for (i = 0; i < cg->volume_count; ++i) { volinfo = cg->volumes[i]; /* TODO: When Jarvis framework is fixed change the index * to start from 0 instead of 1 */ snprintf (key, sizeof (key), "volname%ld", i+1); volname = gf_strdup (volinfo->volname); if (NULL == volname) { ret = -1; goto out; } ret = dict_set_dynstr (dict, key, volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set volname"); GF_FREE (volname); volname = NULL; goto out; } } ret = 0; out: return ret; } /* This is a snapshot create 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 creation 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_create (rpcsvc_request_t *req, glusterd_op_t op, dict_t *dict, char *err_str, size_t len) { int ret = -1; char *volname = NULL; char *name = NULL; int64_t volcount = 0; xlator_t *this = NULL; char key[PATH_MAX] = ""; char *username = NULL; char *password = NULL; gf_boolean_t is_cg = _gf_false; uuid_t *cg_id = NULL; uuid_t *snap_volid = NULL; uuid_t tmp_uuid = {0}; int i = 0; this = THIS; GF_ASSERT (this); GF_ASSERT (req); GF_ASSERT (dict); GF_ASSERT (err_str); ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "get the volume count"); goto out; } GF_ASSERT (volcount); if (volcount > 1) { is_cg = _gf_true; ret = dict_get_str (dict, "cg-name", &name); } else { ret = dict_get_str (dict, "snap-name", &name); } if (!name) { name = generate_snapname (NULL, NULL, is_cg); if (!name) { gf_log (this->name, GF_LOG_ERROR, "Failed to generate snapname"); goto out; } if (!is_cg) ret = dict_set_dynstr (dict, "snap-name", name); else ret = dict_set_dynstr (dict, "cg-name", name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set %s", is_cg?"cg name":"snap name"); ret = -1; GF_FREE (name); goto out; } } if (volcount > 1) { /* Generate a cg-id for this operation and * save it in the dict */ cg_id = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); if (!cg_id) { gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } uuid_generate (*cg_id); ret = dict_set_bin (dict, "cg-id", cg_id, sizeof(uuid_t)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set cg-id"); GF_FREE (cg_id); goto out; } } for (i = 1; i <= volcount; i++) { snprintf (key, sizeof (key), "volname%d", i); ret = dict_get_str (dict, key, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume name"); goto out; } /* generate internal username and password for the snap*/ uuid_generate (tmp_uuid); username = gf_strdup (uuid_utoa (tmp_uuid)); snprintf (key, sizeof(key), "volume%d_username", i); ret = dict_set_dynstr (dict, key, username); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snap " "username for volume %s", volname); GF_FREE (username); goto out; } uuid_generate (tmp_uuid); password = gf_strdup (uuid_utoa (tmp_uuid)); snprintf (key, sizeof(key), "volume%d_password", i); ret = dict_set_dynstr (dict, key, password); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snap " "password for volume %s", volname); GF_FREE (password); goto out; } snap_volid = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); if (!snap_volid) { gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } snprintf (key, sizeof(key) - 1, "vol%d_volid", i); uuid_generate (*snap_volid); ret = dict_set_bin (dict, key, snap_volid, sizeof(uuid_t)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snap_volid"); GF_FREE (snap_volid); 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 * calling mgmt_v3 framework to do the actual restore 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_restore (rpcsvc_request_t *req, glusterd_op_t op, dict_t *dict, char *err_str, size_t len) { int ret = -1; int64_t vol_count = 0; char *volname = NULL; char *snapname = NULL; char *cgname = NULL; glusterd_conf_t *conf = NULL; glusterd_snap_cg_t *cg = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (req); GF_ASSERT (dict); GF_ASSERT (err_str); /* If volume name is provided then volcount will be set */ ret = dict_get_int64 (dict, "volcount", &vol_count); if (ret) { /* If volcount is not provided then cgname must be there */ ret = dict_get_str (dict, "cgname", &cgname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get neither volcount nor cgname"); goto out; } } else { /* TODO: Change the index to 0 when Jarvis code is fixed */ ret = dict_get_str (dict, "volname1", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get volname"); goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get snapname"); goto out; } } if (NULL != cgname) { /* Snapshot restore of CG */ cg = glusterd_find_snap_cg_by_name (conf, cgname); if (NULL == cg) { snprintf (err_str, len, "CG %s not found", cgname); gf_log (this->name, GF_LOG_WARNING, "%s", err_str); ret = -1; goto out; } LOCK (&cg->lock); { /* First get the snap name of the CG */ ret = glusterd_get_cg_snap_name_lk (dict, cg); if (ret) { goto unlock; } /* Then get the volumes belong to CG */ ret = glusterd_get_cg_volume_names_lk (dict, cg); } unlock: UNLOCK (&cg->lock); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "volume names or snap name of %s CG", cgname); 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"); goto out; } ret = 0; /* Success */ out: return ret; } int32_t glusterd_snap_delete (glusterd_snap_t *snap) { int32_t ret = -1; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); if (!snap) { gf_log (this->name, GF_LOG_WARNING, "snap object is NULL"); goto out; } GF_FREE (snap->description); ret = glusterd_volinfo_delete (snap->snap_volume); if (ret) gf_log (this->name, GF_LOG_WARNING, "deleting the snap volume" "failed for the snap %s", snap->snap_name); GF_FREE (snap); ret = 0; out: return ret; } int32_t glusterd_cg_delete (glusterd_snap_cg_t *cg) { int32_t ret = -1; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); if (!cg) { gf_log (this->name, GF_LOG_WARNING, "consistency group is " "NULL"); goto out; } GF_FREE (cg->description); GF_FREE (cg); ret = 0; 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_cg_t *cg, char *cg_name) { glusterd_snap_t *snap = NULL; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; int ret = -1; this = THIS; priv = this->private; GF_ASSERT (snap_volinfo); if (cg_id) GF_ASSERT (cg_name); 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) { if (cg_id) { cg->description = gf_strdup (description); if (cg->description == NULL) { gf_log ("", GF_LOG_ERROR, "Saving the CG Description Failed"); ret = -1; goto out; } } else { snap->description = gf_strdup (description); if (snap->description == NULL) { gf_log ("", GF_LOG_ERROR, "Saving the Snap Description Failed"); ret = -1; goto out; } } } snap->time_stamp = time (NULL); uuid_copy (snap->snap_id, snap_volinfo->volume_id); if (cg_id){ uuid_copy (snap->cg_id, *cg_id); strncpy (snap->cg_name, cg_name, sizeof (snap->cg_name) - 1); } snap->snap_volume = snap_volinfo; 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; } snap->snap_status = GD_SNAP_STATUS_IN_USE; 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); } } gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } /* This function is called to get the device path of the snap lvm. Usually if /dev//- is the device for the lvm, then the snap device will be /dev//-. 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 " "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, "--setactivationskip", "n", "--name", snapname, NULL); 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; } } out: gf_log ("", GF_LOG_TRACE, "Returning %d", ret); 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; char snap_brick_mount_path[PATH_MAX] = {0, }; char *tmp = NULL; char *mnt_pt = NULL; struct mntent *entry = NULL; FILE *mtab = NULL; char *snap_brick_dir = NULL; char snap_brick_path[PATH_MAX] = {0, }; struct stat statbuf = {0, }; runner_t runner = {0, }; char msg[1024] = {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] = '/'; ret = glusterd_get_brick_root (original_brickinfo->path, &mnt_pt); if (ret) { gf_log (this->name, GF_LOG_WARNING, "could not get the root of" "the brick path %s", original_brickinfo->path); goto out; } if (strncmp (original_brickinfo->path, mnt_pt, strlen (mnt_pt))) { gf_log (this->name, GF_LOG_WARNING, "brick: %s brick mount: %s", original_brickinfo->path, mnt_pt); goto out; } snap_brick_dir = &original_brickinfo->path[strlen (mnt_pt)]; if (snap_brick_dir) snap_brick_dir++; snprintf (snap_brick_mount_path, sizeof (snap_brick_mount_path), "%s/%s%s-brick", GLUSTERD_DEFAULT_SNAPS_BRICK_DIR, snap_volinfo->volname, tmp); snprintf (snap_brick_path, sizeof (snap_brick_path), "%s/%s", snap_brick_mount_path, snap_brick_dir); 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 /run/gluster/snaps//@snap_brick_mount_path Way to mount the snap brick via mount api is this. ret = mount (device, snap_brick_mount_path, entry->mnt_type, MS_MGC_VAL, "nouuid"); But for now, mounting using runner apis. */ runinit (&runner); snprintf (msg, sizeof (msg), "mounting snapshot of the brick %s:%s", original_brickinfo->hostname, original_brickinfo->path); runner_add_args (&runner, "mount", "-t", entry->mnt_type, "-o", "nouuid", device, snap_brick_mount_path, NULL); 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, "mounting the snapshot " "logical device %s failed (error: %s)", device, strerror (errno)); goto out; } else gf_log (this->name, GF_LOG_DEBUG, "mounting the snapshot " "logical device %s successful", device); ret = stat (snap_brick_path, &statbuf); if (ret) { gf_log (this->name, GF_LOG_WARNING, "stat of the brick %s" "(brick mount: %s) failed (%s)", snap_brick_path, snap_brick_mount_path, strerror (errno)); goto out; } ret = sys_lsetxattr (snap_brick_path, GF_XATTR_VOL_ID_KEY, snap_volinfo->volume_id, 16, XATTR_REPLACE); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "extended attribute %s on %s. Reason: " "%s, snap: %s", GF_XATTR_VOL_ID_KEY, snap_brick_path, strerror (errno), snap_volinfo->volname); goto out; } out: GF_FREE (tmp); if (ret) { gf_log (this->name, GF_LOG_WARNING, "unmounting the snap brick" " mount %s", snap_brick_mount_path); umount (snap_brick_mount_path); } if (mtab) endmntent (mtab); gf_log ("", GF_LOG_TRACE, "Returning %d", ret); GF_FREE (mnt_pt); 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 *snapname, dict_t *dict, glusterd_snap_cg_t *cg, uuid_t *cg_id, int volcount, uuid_t snap_volid, char *cg_name) { char *snap_brick_mount_path = ""; char snapmntname[PATH_MAX] = ""; char tmpname[PATH_MAX] = ""; char *device = NULL; char *description = NULL; char *username = NULL; char *password = NULL; glusterd_brickinfo_t *snap_brickinfo = NULL; glusterd_brickinfo_t *brickinfo = NULL; glusterd_conf_t *priv = NULL; glusterd_volinfo_t *snap_volume = NULL; int32_t ret = -1; int32_t brick_count = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); GF_ASSERT (volinfo); GF_ASSERT (snapname); GF_ASSERT (dict); if (cg_id) GF_ASSERT (cg_name); ret = glusterd_volinfo_dup (volinfo, &snap_volume); strncpy (snap_volume->volname, snapname, sizeof(snap_volume->volname) - 1); snap_volume->is_snap_volume = _gf_true; strncpy (snap_volume->parent_volname, volinfo->volname, sizeof(snap_volume->parent_volname) - 1); uuid_copy (snap_volume->volume_id, snap_volid); /* fetch internal username and password for the snap*/ snprintf (tmpname, sizeof(tmpname), "volume%d_username", volcount); ret = dict_get_str (dict, tmpname, &username); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap username for " "volume %s", volinfo->volname); goto out; } snprintf (tmpname, sizeof(tmpname), "volume%d_password", volcount); ret = dict_get_str (dict, tmpname, &password); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap password for " "volume %s", volinfo->volname); goto out; } glusterd_auth_set_username (snap_volume, username); glusterd_auth_set_password (snap_volume, password); /* Adding snap brickinfos to the snap volinfo */ brick_count = 0; list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { snap_brickinfo = NULL; device = NULL; 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_volume->volname); goto out; } memset (snapmntname, '\0', sizeof(snapmntname)); ret = snprintf (snapmntname, sizeof(snapmntname) - 1, "vol%d.brick%d", volcount, brick_count); snapmntname[ret] = '\0'; ret = dict_get_ptr (dict, snapmntname, (void **)&snap_brick_mount_path); if (ret) { gf_log ("", GF_LOG_ERROR, "Unable to fetch " "snap mount path (%s)", snapmntname); GF_FREE (snap_brickinfo); goto out; } strcpy (snap_brickinfo->hostname, brickinfo->hostname); strcpy (snap_brickinfo->path, snap_brick_mount_path); uuid_copy (snap_brickinfo->uuid, brickinfo->uuid); LOCK (&snap_volume->lock); { list_add_tail (&snap_brickinfo->brick_list, &snap_volume->bricks); } UNLOCK (&snap_volume->lock); brick_count++; } list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) { continue; } if (!glusterd_is_brick_started (brickinfo)) { gf_log (this->name, GF_LOG_WARNING, "brick %s:%s is not" " started (volume: %s snap: %s)", brickinfo->hostname, brickinfo->path, volinfo->volname, snapname); continue; } ret = glusterd_take_snapshot (brickinfo, volinfo->volname, snapname, dict, &device); /* Fail the snapshot even though snapshot on one of the bricks fails. At the end when we check whether the snapshot volume meets quorum or not, then the the snapshot can either be treated as success, or in case of failure we can undo the changes and return failure to cli. */ if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to take snapshot of %s:%s", brickinfo->hostname, brickinfo->path); goto out; } /* create the complete brick here */ 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, cg, cg_name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "creating the" "snap object failed for the volume %s", volinfo->volname); goto out; } ret = glusterd_store_perform_snap_store (volinfo, snapname); if (ret) { gf_log (this->name, GF_LOG_WARNING, "could not do volume store" " after taking the snapshot (volume: %s)", volinfo->volname); goto out; } 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; } ret = generate_snap_client_volfiles (volinfo, snap_volume, GF_CLIENT_TRUSTED, _gf_false); 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, _gf_false); if (ret) { gf_log (this->name, GF_LOG_ERROR, "generating the client " "volfiles for the snap %s (volume: %s) failed", snapname, volinfo->volname); goto out; } //check whether this is needed or not list_add_tail (&snap_volume->vol_list, &priv->snap_list); list_for_each_entry (brickinfo, &snap_volume->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) continue; 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; } } snap_volume->status = GLUSTERD_STATUS_STARTED; ret = glusterd_store_perform_snap_volume_store (volinfo, snap_volume); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to store snapshot volinfo (%s) for volume " "%s", snap_volume->volname, volinfo->volname); goto out; } ret = glusterd_volume_compute_cksum (volinfo, snap_volume); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to compute cksum for %s", snap_volume->volname); goto out; } out: if (ret) glusterd_volinfo_delete (snap_volume); gf_log ("", GF_LOG_TRACE, "Returning %d", ret); 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_snap", name, volname); } 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; } /* This is a snapshot remove handler function. This function will be * executed in the originator node. This function is responsible for * calling mgmt v3 framework to do the actual remove on all the bricks * * @param req RPC request object * @param op gluster operation * @param dict dictionary containing snapshot remove 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_remove (rpcsvc_request_t *req, glusterd_op_t op, dict_t *dict, char *err_str, size_t len) { int ret = -1; int64_t vol_count = 0; char *volname = NULL; char *snapname = NULL; char *cgname = NULL; glusterd_conf_t *conf = NULL; glusterd_snap_cg_t *cg = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (req); GF_ASSERT (dict); GF_ASSERT (err_str); /* If volume name is provided then volcount will be set */ ret = dict_get_int64 (dict, "volcount", &vol_count); if (ret) { /* If volcount is not provided then cgname must be there */ ret = dict_get_str (dict, "cgname", &cgname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get neither volcount nor cgname"); goto out; } } else { /* TODO: Change the index to 0 when mgmt v3 code is fixed */ ret = dict_get_str (dict, "volname1", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get volname"); goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get snapname"); goto out; } } if (NULL != cgname) { cg = glusterd_find_snap_cg_by_name (conf, cgname); if (NULL == cg) { snprintf (err_str, len, "CG %s does not exist", cgname); gf_log (this->name, GF_LOG_WARNING, "%s", err_str); ret = -1; goto out; } LOCK (&cg->lock); { /* Get the volumes belong to CG */ ret = glusterd_get_cg_volume_names_lk (dict, cg); } UNLOCK (&cg->lock); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "volume names of %s CG", cgname); 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"); goto out; } ret = 0; /* Success */ out: return ret; } int glusterd_snapshot_remove_prevalidate (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int32_t ret = -1; char *volname = NULL; char *name = NULL; xlator_t *this = NULL; glusterd_conf_t *conf = NULL; glusterd_snap_t *snap = NULL; glusterd_snap_cg_t *cg = NULL; char volname_buf[PATH_MAX] = {0, }; glusterd_volinfo_t *volinfo = NULL; char err_str[PATH_MAX] = {0, }; int64_t volcount = 0; int64_t i = 0; int64_t j = 0; gf_boolean_t volume_found = _gf_false; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (op_errstr); if (!dict) { gf_log (this->name, GF_LOG_ERROR, "input dict is NULL"); goto out; } ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting volcount failed"); goto out; } /* TODO: The policy for the snapshot create is not decided on whether to start the snap volume upon the execution of the snapshot create command. Once the policy is decided, check here whether the volume is in use before delete command is executed. (May be a force option to delete forcefully even when the snap volume is in use.) */ if (volcount > 1) { ret = dict_get_str (dict, "cgname", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting the cg name" " failed"); goto out; } cg = glusterd_find_snap_cg_by_name (conf, name); if (!cg) { snprintf (err_str, sizeof (err_str), "consistency group" "%s does not exist", name); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); *op_errstr = gf_strdup (err_str); goto out; } GF_ASSERT (volcount == cg->volume_count); for (i = 0; i < volcount; i++) { volume_found = _gf_false; 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, "volume name " "is notgiven"); goto out; } for (j = 0; j < volcount; j++) { volinfo = cg->volumes[j]; if (!strcmp (volinfo->volname, volname)) { volume_found = _gf_true; break; } } if (!volume_found) { gf_log (this->name, GF_LOG_ERROR, "volume %s" " is not found in the consistency " "group %s", volname, name); goto out; } } } else { snprintf (volname_buf, sizeof (volname_buf), "volname1"); ret = dict_get_str (dict, volname_buf, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "volume name is not " "given"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, sizeof (err_str), "Volume %s does " "not exist", volname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); *op_errstr = gf_strdup (err_str); goto out; } ret = dict_get_str (dict, "snapname", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting snap name " "failed (volume: %s)", volname); goto out; } snap = glusterd_find_snap_by_name (volinfo, name); if (!snap) { ret = -1; snprintf (err_str, sizeof (err_str), "snap %s does " "not exist", name); gf_log (this->name, GF_LOG_ERROR, "%s, (volume: %s)", err_str, volinfo->volname); *op_errstr = gf_strdup (err_str); goto out; } } ret = 0; out: return ret; } int glusterd_remove_snap (glusterd_brickinfo_t *brickinfo, const char *mount_pt, const char *volname, const char *snapname, const char *snap_device) { int ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; runner_t runner = {0,}; char msg[1024] = {0, }; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); if (!brickinfo) { gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); goto out; } GF_ASSERT (mount_pt); GF_ASSERT (volname); GF_ASSERT (snapname); GF_ASSERT (snap_device); /* sleep for some time so that the glusterfsd process associated with the snap lvm does not have any reference to the snap and has been stopped completely TODO: find the issue because of which umount fails and fix it. */ //usleep (24007); //ret = umount2 (mount_pt, MNT_FORCE); runinit (&runner); snprintf (msg, sizeof (msg), "umount the snapshot mounted path %s", mount_pt); runner_add_args (&runner, "umount", mount_pt, NULL); runner_log (&runner, "", GF_LOG_DEBUG, msg); /* We need not do synclock_unlock => runner_run => synclock_lock here. Because it is needed if we are running a glusterfs process in runner_run, so that when the glusterfs process started wants to communicate to glusterd, glusterd wont be able to respond if it has held the big lock. So we do unlock, run glusterfs process (thus communicate to glusterd), lock. But since this is not a glusterfs command that is being run, unlocking and then relocking is not needed. */ ret = runner_run (&runner); if (ret) { gf_log (this->name, GF_LOG_WARNING, "unmounting the " "path %s (brick: %s) failed (%s)", mount_pt, brickinfo->path, strerror (errno)); goto out; } runinit (&runner); snprintf (msg, sizeof(msg), "remove snapshot of the brick %s:%s, " "device: %s", brickinfo->hostname, brickinfo->path, snap_device); runner_add_args (&runner, "/sbin/lvremove", "-f", snap_device, NULL); runner_log (&runner, "", GF_LOG_DEBUG, msg); ret = runner_run (&runner); 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); if (!snap_volinfo) { gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); goto out; } 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) { if (uuid_compare (brickinfo->uuid, MY_UUID)) continue; ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); if (ret) { gf_log (this->name, GF_LOG_WARNING, "getting the root " "of the brick for snap volume %s faile", name); goto out; } entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); if (!entry) { gf_log (this->name, GF_LOG_WARNING, "getting the mount" " entry for the brick %s:%s of the snap %s " "(volume: %s) failed", brickinfo->hostname, brickinfo->path, name, actual_volinfo->volname); ret = -1; goto out; } ret = glusterd_remove_snap (brickinfo, mnt_pt, actual_volinfo->volname, name, entry->mnt_fsname); if (mtab) endmntent (mtab); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "remove the snapshot %s (%s)", brickinfo->path, entry->mnt_fsname); goto out; } } ret = 0; out: return ret; } int32_t glusterd_do_snap_remove (glusterd_volinfo_t *volinfo, char *name) { int32_t ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; glusterd_snap_t *snap = NULL; glusterd_volinfo_t *snap_volinfo = NULL; glusterd_brickinfo_t *brickinfo = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); 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) { gf_log (this->name, GF_LOG_ERROR, "could not find " "the snap object by the name %s", name); goto out; } else { gf_log (this->name, GF_LOG_DEBUG, "found the snap %s " "(volume: %s)", name, volinfo->volname); } snap_volinfo = snap->snap_volume; GF_ASSERT (snap_volinfo); list_for_each_entry (brickinfo, &snap_volinfo->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) continue; /* TODO: The policy for the snapshot create is not decided on whether to start the snap volume upon the execution of the snapshot create command. Once the policy is decided, check here whether the volume is in use before delete command is executed. (May be a force option to delete forcefully even when the snap volume is in use.) */ ret = glusterd_snap_brick_stop (volinfo, snap_volinfo, brickinfo, _gf_false); if (ret) { gf_log (this->name, GF_LOG_WARNING, "stopping " "the brick %s:%s for the snap %s " "(volume: %s) failed", brickinfo->hostname, brickinfo->path, snap_volinfo->volname, volinfo->volname); //goto out; } } ret = glusterd_brick_snapshot_remove (snap_volinfo, 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 = glusterd_store_delete_volume (volinfo, snap->snap_volume); if (ret) { gf_log (this->name, GF_LOG_ERROR, "removing the snap " "store for the snap %s (volume %s) failed", snap->snap_name, volinfo->volname); goto out; } snap = glusterd_remove_snap_by_name (volinfo, snap->snap_name); GF_ASSERT (snap); //snap cannot be NULL; glusterd_snap_delete (snap); //deletes in memory snap object ret = glusterd_store_perform_snap_list_store (volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "doing the snap " "store for the volume %s failed", volinfo->volname); goto out; } ret = 0; out: return ret; } char * glusterd_get_snap_from_cg (glusterd_volinfo_t *volinfo, glusterd_snap_cg_t *cg) { char *snap_name = NULL; glusterd_snap_t *tmp_snap = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); if (!volinfo) { gf_log (this->name, GF_LOG_WARNING, "volinfo NULL"); goto out; } if (!cg) { gf_log (this->name, GF_LOG_WARNING, "consistency group NULL"); goto out; } list_for_each_entry (tmp_snap, &volinfo->snaps, snap_list) { if ((!uuid_is_null (tmp_snap->cg_id)) && (uuid_compare (tmp_snap->cg_id, cg->cg_id) == 0)) { snap = tmp_snap; break; } } if (snap) snap_name = gf_strdup (snap->snap_name); out: return snap_name; } int32_t glusterd_snapshot_remove_commit (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int32_t ret = -1; char *cg_name = NULL; char *name = NULL; glusterd_snap_cg_t *cg = NULL; xlator_t *this = NULL; glusterd_conf_t *conf = NULL; char *volname = NULL; glusterd_volinfo_t *volinfo = NULL; int64_t volcount = -1; char volname_buf[PATH_MAX] = {0, }; int i = 0; gf_boolean_t free_name = _gf_false; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); if (!dict || !op_errstr) { gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); goto out; } ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "volume count not given"); goto out; } if (volcount > 1) { ret = dict_get_str (dict, "cgname", &cg_name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting the cg name" " failed"); goto out; } cg = glusterd_find_snap_cg_by_name (conf, cg_name); if (!cg) { gf_log (this->name, GF_LOG_ERROR, "consistency group " "%s not found", cg_name); goto out; } GF_ASSERT (volcount == cg->volume_count); } for (i = 0; i < volcount; i++) { snprintf (volname_buf, sizeof (volname_buf), "volname%d", i+1); ret = dict_get_str (dict, volname_buf, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting volume name" "failed"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting the volinfo" " failed for the volume %s", volname); goto out; } ret = dict_get_str (dict, "snapname", &name); if (ret && volcount == 1) { gf_log (this->name, GF_LOG_ERROR, "getting the snap " "name failed (volume: %s)", volinfo->volname); goto out; } if (!name) { name = glusterd_get_snap_from_cg (volinfo, cg); if (!name) { gf_log (this->name, GF_LOG_ERROR, "failed to " "get the snap name " "(volname: %s, cg name: %s)", volinfo->volname, cg->cg_name); goto out; } free_name = _gf_true; } ret = glusterd_do_snap_remove (volinfo, name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "removing the %s %s failed", (volinfo)?"snap":"cg", name); goto out; } if (free_name) GF_FREE (name); name = NULL; } if (volcount > 1) { ret = glusterd_store_delete_snap_cg (cg); /* TODO: Handle the errors of deleting store handle of cg. For now, the error is ignored and we proceed with the deletion of the in memory cg object. */ if (ret) gf_log (this->name, GF_LOG_WARNING, "removing the " "store handle for the consistency group %s " "failed", cg->cg_name); cg = glusterd_remove_snap_cg_by_name (conf, cg_name); ret = glusterd_cg_delete (cg); if (ret) { gf_log (this->name, GF_LOG_WARNING, "removing the cg " "object failed"); goto out; } } ret = 0; out: if (free_name) GF_FREE (name); return ret; } /* 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_create_commit (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int ret = -1; int i = 0; int64_t volume_count = 0; gf_boolean_t is_cg = _gf_false; char *name = NULL; char *volname = NULL; char *tmp_name = NULL; char volname_buf[PATH_MAX] = ""; char key[PATH_MAX] = ""; xlator_t *this = NULL; glusterd_volinfo_t *volinfo = NULL; glusterd_snap_cg_t *cg = NULL; glusterd_conf_t *priv = NULL; uuid_t *cg_id = NULL; uuid_t *snap_volid = NULL; glusterd_snap_t *snap = NULL; char err_str[PATH_MAX] = ""; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); 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; } if (volume_count == 1) { ret = dict_get_str (dict, "snap-name", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snap-name"); goto out; } tmp_name = gf_strdup (name); if (!tmp_name) { gf_log (this->name, GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } ret = dict_set_dynstr (rsp_dict, "snap-name", tmp_name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snap-name in rsp_dict"); GF_FREE (tmp_name); tmp_name = NULL; goto out; } } else { ret = dict_get_str (dict, "cg-name", &name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch cg-name"); goto out; } tmp_name = gf_strdup (name); if (!tmp_name) { gf_log (this->name, GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } ret = dict_set_dynstr (rsp_dict, "cg-name", tmp_name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set cg-name in rsp_dict"); GF_FREE (tmp_name); tmp_name = NULL; goto out; } } tmp_name = NULL; ret = dict_get_bin (dict, "cg-id", (void **)&cg_id); if (ret) gf_log (this->name, GF_LOG_DEBUG, "Not a cg."); else { is_cg = _gf_true; gf_log (this->name, GF_LOG_DEBUG, "cg-id = %s", uuid_utoa(*cg_id)); } 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); ret = -1; 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_name = generate_snapname (volname, name, is_cg); if (!tmp_name) { gf_log (this->name, GF_LOG_ERROR, "strdup failed (%s)", name); ret = -1; goto out; } list_for_each_entry (snap, &volinfo->snaps, snap_list) { if (!strcmp (snap->snap_name, tmp_name)) { snprintf (err_str, sizeof (err_str), "snap with" " name %s already exists", tmp_name); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); ret = -1; *op_errstr = gf_strdup (err_str); goto out; } } snprintf (key, sizeof(key) - 1, "vol%d_volid", i); 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; } /* TODO: Create a stub where the bricks are added parallely by worker threads so that the snap creating happens parallely. */ if (is_cg) { ret = glusterd_do_snap (volinfo, tmp_name, dict, cg, cg_id, i, *snap_volid, name); } else { ret = glusterd_do_snap (volinfo, tmp_name, dict, cg, cg_id, i, *snap_volid, NULL); } if (ret) { gf_log (this->name, GF_LOG_WARNING, "taking the " "snapshot of the volume %s failed", volname); goto out; } if (cg) cg->volumes[i-1] = volinfo; GF_FREE (tmp_name); tmp_name = NULL; } if (volume_count > 1) { uuid_copy (cg->cg_id, *cg_id); strncpy (cg->cg_name, name, sizeof (cg->cg_name)); 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 = glusterd_store_snap_cg (cg); if (ret) { gf_log (this->name, GF_LOG_ERROR, "storing the cg " "failed (cg: %s)", cg->cg_name); goto out; } } ret = 0; out: if (ret) GF_FREE (tmp_name); gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } int snap_max_hard_limit_set_commit (dict_t *dict, char *key, uint64_t value, char *volname, char **op_errstr) { char err_str[PATH_MAX] = ""; glusterd_conf_t *conf = NULL; glusterd_volinfo_t *volinfo = NULL; int ret = -1; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (key); GF_ASSERT (volname); GF_ASSERT (op_errstr); conf = this->private; GF_ASSERT (conf); /* TODO: Initiate auto deletion when there is a limit change */ if (!strncmp (volname, "all", strlen(volname))) { /* For system limit */ conf->snap_max_hard_limit = value; ret = glusterd_store_global_info (this); if (ret) { snprintf (err_str, PATH_MAX,"Failed to store %s " "for system", key); goto out; } } else { /* For one volume */ ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " volinfo for volume %s", volname); goto out; } volinfo->snap_max_hard_limit = value; ret = glusterd_store_volinfo (volinfo, GLUSTERD_VOLINFO_VER_AC_INCREMENT); if (ret) { snprintf (err_str, PATH_MAX,"Failed to store %s " "for volume %s", key, volname); goto out; } } ret = 0; out: if (ret) { *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); } return ret; } int snap_max_limits_display_commit (dict_t *rsp_dict, char *volname, char **op_errstr) { char err_str[PATH_MAX] = ""; char buf[PATH_MAX] = ""; glusterd_conf_t *conf = NULL; glusterd_volinfo_t *volinfo = NULL; int ret = -1; uint64_t active_hard_limit = 0; uint64_t snap_max_limit = 0; uint64_t soft_limit_value = -1; uint64_t count = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (rsp_dict); GF_ASSERT (volname); GF_ASSERT (op_errstr); conf = this->private; GF_ASSERT (conf); if (!strncmp (volname, "all", strlen(volname))) { /* For system limit */ list_for_each_entry (volinfo, &conf->volumes, vol_list) { if (volinfo->is_snap_volume == _gf_true) continue; snap_max_limit = volinfo->snap_max_hard_limit; if (snap_max_limit > conf->snap_max_hard_limit) active_hard_limit = conf->snap_max_hard_limit; else active_hard_limit = snap_max_limit; soft_limit_value = (active_hard_limit * conf->snap_max_soft_limit) / 100; snprintf (buf, sizeof(buf), "volume%ld-volname", count); ret = dict_set_str (rsp_dict, buf, volinfo->volname); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } snprintf (buf, sizeof(buf), "volume%ld-snap-max-hard-limit", count); ret = dict_set_uint64 (rsp_dict, buf, snap_max_limit); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } snprintf (buf, sizeof(buf), "volume%ld-snap-max-soft-limit-value", count); ret = dict_set_uint64 (rsp_dict, buf, soft_limit_value); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } count++; } ret = dict_set_uint64 (rsp_dict, "voldisplaycount", count); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set voldisplaycount"); goto out; } } else { /* For one volume */ ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " volinfo for volume %s", volname); goto out; } snap_max_limit = volinfo->snap_max_hard_limit; soft_limit_value = (volinfo->snap_max_hard_limit * conf->snap_max_soft_limit) / 100; snprintf (buf, sizeof(buf), "volume%ld-volname", count); ret = dict_set_str (rsp_dict, buf, volinfo->volname); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } snprintf (buf, sizeof(buf), "volume%ld-snap-max-hard-limit", count); ret = dict_set_uint64 (rsp_dict, buf, snap_max_limit); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } snprintf (buf, sizeof(buf), "volume%ld-snap-max-soft-limit-value", count); ret = dict_set_uint64 (rsp_dict, buf, soft_limit_value); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set %s", buf); goto out; } count++; ret = dict_set_uint64 (rsp_dict, "voldisplaycount", count); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set voldisplaycount"); goto out; } } ret = dict_set_uint64 (rsp_dict, "snap-max-hard-limit", conf->snap_max_hard_limit); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set sys-snap-max-hard-limit "); goto out; } ret = dict_set_uint64 (rsp_dict, "snap-max-soft-limit", conf->snap_max_soft_limit); if (ret) { snprintf (err_str, PATH_MAX, "Failed to set sys-snap-max-hard-limit "); goto out; } ret = 0; out: if (ret) { *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); } return ret; } int glusterd_snapshot_config_commit (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { char *volname = NULL; char *key = NULL; xlator_t *this = NULL; int ret = -1; char err_str[PATH_MAX] = {0,}; glusterd_conf_t *conf = NULL; int config_command = 0; uint64_t value = 0; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (op_errstr); conf = this->private; GF_ASSERT (conf); ret = dict_get_int32 (dict, "config-command", &config_command); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get config-command type"); goto out; } ret = dict_get_str (dict, "volname", &volname); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " volume name"); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } ret = dict_get_str (dict, "config-key", &key); if (!ret) { ret = dict_get_uint64 (dict, key, &value); if (ret) { snprintf (err_str, PATH_MAX,"Failed to get the" " value for %s", key); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } } switch (config_command) { case GF_SNAP_CONFIG_TYPE_SET: if (!strncmp (key, "snap-max-hard-limit", strlen(key))) { /* Commit ops for snap-max-hard-limit */ ret = snap_max_hard_limit_set_commit (dict, key, value, volname, op_errstr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "%s set commit failed.", key); goto out; } } else if (!strncmp (key, "snap-max-soft-limit", strlen(key))) { /* For system limit */ conf->snap_max_soft_limit = value; ret = glusterd_store_global_info (this); if (ret) { snprintf (err_str, PATH_MAX,"Failed to store %s " "for system", key); *op_errstr = gf_strdup (err_str); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } } break; case GF_SNAP_CONFIG_DISPLAY: /* Reading data from local node only */ if (!is_origin_glusterd (dict)) { ret = 0; break; } ret = snap_max_limits_display_commit (rsp_dict, volname, op_errstr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "snap-max-limit " "display commit failed."); goto out; } break; default: break; } out: gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } int32_t glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { xlator_t *this = NULL; glusterd_conf_t *priv = NULL; int32_t snap_command = 0; int ret = -1; 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 = glusterd_snapshot_create_commit (dict, op_errstr, rsp_dict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "create snapshot"); goto out; } break; case GF_SNAP_OPTION_TYPE_CONFIG: ret = glusterd_snapshot_config_commit (dict, op_errstr, rsp_dict); break; case GF_SNAP_OPTION_TYPE_DELETE: ret = glusterd_snapshot_remove_commit (dict, op_errstr, rsp_dict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "delete snapshot"); goto out; } break; case GF_SNAP_OPTION_TYPE_RESTORE: ret = glusterd_snapshot_restore (dict, op_errstr); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Failed to " "restore snapshot"); goto out; } break; default: gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); goto out; break; } ret = 0; out: return ret; } int glusterd_snapshot_brickop (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int ret = -1; int64_t vol_count = 0; int64_t count = 1; char key[1024] = {0,}; char *volname = NULL; int32_t snap_command = 0; xlator_t *this = NULL; 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", &vol_count); if (ret) goto out; while (count <= vol_count) { snprintf (key, 1024, "volname%"PRId64, count); ret = dict_get_str (dict, key, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to get volname"); goto out; } ret = dict_set_str (dict, "volname", volname); if (ret) goto out; ret = gd_brick_op_phase (GD_OP_SNAP, NULL, dict, op_errstr); if (ret) goto out; volname = NULL; count++; } dict_del (dict, "volname"); ret = 0; break; } case GF_SNAP_OPTION_TYPE_DELETE: break; default: break; } out: return ret; } int glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { 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 = glusterd_snapshot_create_prevalidate (dict, op_errstr, rsp_dict); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot create " "pre-validation failed"); goto out; } break; case (GF_SNAP_OPTION_TYPE_CONFIG): ret = glusterd_snapshot_config_prevalidate (dict, op_errstr); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot config " "pre-validation failed"); goto out; } break; case GF_SNAP_OPTION_TYPE_RESTORE: ret = glusterd_snapshot_restore_prevalidate (dict, op_errstr, rsp_dict); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot restore " "validation failed"); goto out; } break; case GF_SNAP_OPTION_TYPE_DELETE: ret = glusterd_snapshot_remove_prevalidate (dict, op_errstr, rsp_dict); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot remove " "validation failed"); goto out; } break; default: gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); goto out; } ret = 0; out: return ret; } 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) { GF_FREE (host_uuid); 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_handle_snapshot_create (req, cli_op, dict, err_str, sizeof (err_str)); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot create " "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)); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot restore " "failed: %s", err_str); } break; case GF_SNAP_OPTION_TYPE_LIST: ret = glusterd_handle_snapshot_list (req, cli_op, dict, err_str, sizeof (err_str)); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot list " "failed"); } break; case GF_SNAP_OPTION_TYPE_CONFIG: ret = glusterd_mgmt_v3_initiate_all_phases (req, cli_op, dict); break; case GF_SNAP_OPTION_TYPE_DELETE: ret = glusterd_handle_snapshot_remove (req, cli_op, dict, err_str, sizeof (err_str)); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot delete " "failed: %s", err_str); } break; case GF_SNAP_OPTION_TYPE_START: case GF_SNAP_OPTION_TYPE_STOP: case GF_SNAP_OPTION_TYPE_STATUS: gf_log (this->name, GF_LOG_ERROR, "Operation (%d) not " "supported ", type); ret = -1; /* Failure */ break; default: gf_log (this->name, GF_LOG_ERROR, "Unkown snapshot request " "type (%d)", type); ret = -1; /* Failure */ } out: 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); } return ret; } int glusterd_handle_snapshot (rpcsvc_request_t *req) { return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); }