/* 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 snap_mount_folder[PATH_MAX]; /* 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; char *volname = NULL; char *snapname = NULL; xlator_t *this = NULL; glusterd_volinfo_t *snap_volinfo = NULL; glusterd_volinfo_t *volinfo = NULL; glusterd_snap_t *snap = NULL; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (op_errstr); 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 (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; } /* TODO : As of now there is only volume in snapshot. * Change this when multiple volume snapshot is introduced */ snap_volinfo = list_entry (snap->volumes.next, glusterd_volinfo_t, vol_list); ret = glusterd_volinfo_find (snap_volinfo->parent_volname, &volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Could not get volinfo of " "%s", snap_volinfo->parent_volname); goto out; } ret = gd_restore_snap_volume (volinfo, snap_volinfo); 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; int32_t i = 0; int32_t volcount = 0; gf_boolean_t snap_restored = _gf_false; char *volname = NULL; char *snapname = 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, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snap name"); goto out; } snap = glusterd_find_snap_by_name (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 = dict_set_str (rsp_dict, "snapname", snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "snap name"); goto out; } ret = dict_get_int32 (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%d", 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 = 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; gf_loglevel_t loglevel = GF_LOG_ERROR; 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) { snprintf (err_str, sizeof (err_str), "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, sizeof (err_str), "Failed to get the" " value for %s", key); goto out; } } ret = dict_get_str (dict, "volname", &volname); if (ret) { snprintf (err_str, sizeof (err_str), "Failed to get the" " volume name"); goto out; } if (strncmp (volname, "all", strlen(volname))) { ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, sizeof (err_str), "Volume %s does not exist.", volname); 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: if (ret && err_str[0] != '\0') { gf_log (this->name, loglevel, "%s", err_str); *op_errstr = gf_strdup (err_str); } 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 *snapname = NULL; char *device = NULL; char *tmpstr = NULL; char *snap_brick_dir = NULL; char snap_brick_path[PATH_MAX] = ""; char *mnt_pt = NULL; char key[PATH_MAX] = ""; char snap_mount[PATH_MAX] = ""; char snap_volname[64] = ""; char err_str[PATH_MAX] = ""; int ret = -1; int64_t i = 0; int64_t volcount = 0; int64_t brick_count = 0; int64_t brick_order = 0; glusterd_brickinfo_t *brickinfo = NULL; glusterd_volinfo_t *volinfo = NULL; xlator_t *this = NULL; uuid_t *snap_volid = NULL; gf_loglevel_t loglevel = GF_LOG_ERROR; this = THIS; GF_ASSERT (op_errstr); ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { snprintf (err_str, sizeof (err_str), "Failed to " "get the volume count"); goto out; } if (volcount <= 0) { snprintf (err_str, sizeof (err_str), "Invalid volume count %ld " "supplied", volcount); ret = -1; goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { snprintf (err_str, sizeof (err_str), "Failed to get snapname"); goto out; } if (glusterd_find_snap_by_name (snapname)) { ret = -1; snprintf (err_str, sizeof (err_str), "Snap %s already exist", snapname); goto out; } for (i = 1; i <= volcount; i++) { snprintf (key, sizeof (key), "volname%ld", i); ret = dict_get_str (dict, key, &volname); if (ret) { snprintf (err_str, sizeof (err_str), "failed to get volume name"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, sizeof (err_str), "Volume (%s) does not exist ", volname); goto out; } ret = -1; if (!glusterd_is_volume_started (volinfo)) { snprintf (err_str, sizeof (err_str), "volume %s is not started",volinfo->volname); loglevel = GF_LOG_WARNING; goto out; } if (glusterd_is_defrag_on (volinfo)) { snprintf (err_str, sizeof (err_str), "rebalance process is running for the " "volume %s", volname); loglevel = GF_LOG_WARNING; goto out; } /* TODO: Also check whether geo replication is running */ if (volinfo->is_snap_volume == _gf_true) { snprintf (err_str, sizeof (err_str), "Volume %s is a snap volume", volname); loglevel = GF_LOG_WARNING; goto out; } if (volinfo->snap_count >= volinfo->snap_max_hard_limit) { snprintf (err_str, sizeof (err_str), "The number of existing snaps has reached the " "maximum limit of %"PRIu64" ,for the volume %s", volinfo->snap_max_hard_limit, volname); loglevel = GF_LOG_WARNING; goto out; } snprintf (key, sizeof(key) - 1, "vol%ld_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; } /* snap volume uuid is used as lvm snapshot name. This will avoid restrictions on snapshot names provided by user */ GLUSTERD_GET_UUID_NOHYPHEN (snap_volname, *snap_volid); brick_count = 0; brick_order = 0; /* Adding snap bricks mount paths to the dict */ list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) { brick_order++; continue; } device = glusterd_get_brick_mount_details (brickinfo); if (!device) { snprintf (err_str, sizeof (err_str), "getting device name for the brick " "%s:%s failed", brickinfo->hostname, brickinfo->path); ret = -1; goto out; } device = glusterd_build_snap_device_path (device, snap_volname); if (!device) { snprintf (err_str, sizeof (err_str), "cannot copy the snapshot device " "name (volname: %s, snapname: %s)", volinfo->volname, snapname); loglevel = GF_LOG_WARNING; ret = -1; goto out; } glusterd_replace_slash_with_hyphen (device); if (device[0] == '-') device[0] = '/'; ret = snprintf (snap_mount, sizeof(snap_mount) - 1, "%s/%s%s-brick", snap_mount_folder, snap_volname, device); snap_mount[ret] = '\0'; ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); if (ret) { snprintf (err_str, sizeof (err_str), "could not get the root of the brick path %s", brickinfo->path); loglevel = GF_LOG_WARNING; goto out; } if (strncmp (brickinfo->path, mnt_pt, strlen (mnt_pt))) { snprintf (err_str, sizeof (err_str), "brick: %s brick mount: %s", brickinfo->path, mnt_pt); loglevel = GF_LOG_WARNING; 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); tmpstr = gf_strdup (snap_brick_path); if (!tmpstr) { ret = -1; goto out; } snprintf (key, sizeof(key), "vol%ld.brick%ld", i, brick_count); ret = dict_set_dynstr (rsp_dict, key, tmpstr); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", snap_mount); GF_FREE (tmpstr); goto out; } tmpstr = NULL; snprintf (key, sizeof(key) - 1, "vol%ld.brick%ld.order", i, brick_count); ret = dict_set_int64 (rsp_dict, key, brick_order); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", key); goto out; } brick_count++; brick_order++; } snprintf (key, sizeof(key) - 1, "vol%ld_brickcount", i); ret = dict_set_int64 (rsp_dict, key, brick_count); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set %s", key); goto out; } } ret = dict_set_int64 (rsp_dict, "volcount", volcount); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set volcount"); goto out; } ret = 0; out: if (ret) GF_FREE (tmpstr); if (ret && err_str[0] != '\0') { gf_log (this->name, loglevel, "%s", err_str); *op_errstr = gf_strdup (err_str); } gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } 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) { if (LOCK_INIT (&snap->lock)) { gf_log (THIS->name, GF_LOG_ERROR, "Failed initiating" " snap lock"); GF_FREE (snap); return NULL; } INIT_LIST_HEAD (&snap->snap_list); INIT_LIST_HEAD (&snap->volumes); snap->snapname[0] = 0; 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; } /* Function glusterd_list_add_snapvol adds the volinfo object (snapshot volume) to the snapshot object list and to the parent volume list */ int32_t glusterd_list_add_snapvol (glusterd_volinfo_t *origin_vol, glusterd_volinfo_t *snap_vol) { int ret = -1; glusterd_snap_t *snap = NULL; GF_VALIDATE_OR_GOTO ("glusterd", origin_vol, out); GF_VALIDATE_OR_GOTO ("glusterd", snap_vol, out); snap = snap_vol->snapshot; GF_ASSERT (snap); list_add_tail (&snap_vol->vol_list, &snap->volumes); /* TODO: As of now there is only one volume per snapshot. Below line should be removed once multiple volumes for snap driven structucture is implemented */ snap->snap_volume = snap_vol; LOCK (&origin_vol->lock); { list_add_tail (&snap_vol->snapvol_list, &origin_vol->snap_volumes); origin_vol->snap_count++; } UNLOCK (&origin_vol->lock); gf_log (THIS->name, GF_LOG_DEBUG, "Snap %s added to the list", snap->snapname); ret = 0; out: return ret; } glusterd_snap_t* glusterd_find_snap_by_name (char *snapname) { glusterd_snap_t *snap = NULL; glusterd_conf_t *priv = NULL; priv = THIS->private; GF_ASSERT (priv); GF_ASSERT (snapname); list_for_each_entry (snap, &priv->snapshots, snap_list) { if (!strcmp (snap->snapname, snapname)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found " "snap %s (%s)", snap->snapname, uuid_utoa (snap->snap_id)); goto out; } } snap = NULL; out: return snap; } glusterd_snap_t* glusterd_find_snap_by_id (uuid_t snap_id) { glusterd_snap_t *snap = NULL; glusterd_conf_t *priv = NULL; priv = THIS->private; GF_ASSERT (priv); if (uuid_is_null(snap_id)) goto out; list_for_each_entry (snap, &priv->snapshots, snap_list) { if (!uuid_compare (snap->snap_id, snap_id)) { gf_log (THIS->name, GF_LOG_DEBUG, "Found " "snap %s (%s)", snap->snapname, uuid_utoa (snap->snap_id)); goto out; } } snap = NULL; out: return snap; } // 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; } int glusterd_do_lvm_snapshot_remove (glusterd_volinfo_t *snap_vol, glusterd_brickinfo_t *brickinfo, const char *mount_pt, const char *snap_device) { int ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; runner_t runner = {0,}; char msg[1024] = {0, }; char pidfile[PATH_MAX] = {0, }; pid_t pid = -1; 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 (snap_vol); GF_ASSERT (mount_pt); GF_ASSERT (snap_device); GLUSTERD_GET_BRICK_PIDFILE (pidfile, snap_vol, brickinfo, priv); if (glusterd_is_service_running (pidfile, &pid)) { ret = kill (pid, SIGKILL); if (ret && errno != ESRCH) { gf_log (this->name, GF_LOG_ERROR, "Unable to kill pid " "%d reason : %s", pid, strerror(errno)); goto out; } } 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_lvm_snapshot_remove (glusterd_volinfo_t *snap_vol) { 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_vol) { gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); goto out; } list_for_each_entry (brickinfo, &snap_vol->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 volume %s (snap %s) failed ", snap_vol->volname, snap_vol->snapshot->snapname); 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, snap_vol->snapshot->snapname, snap_vol->volname); ret = -1; goto out; } ret = glusterd_do_lvm_snapshot_remove (snap_vol, brickinfo, mnt_pt, 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_snap_volume_remove (glusterd_volinfo_t *snap_vol, gf_boolean_t remove_lvm, gf_boolean_t force) { int ret = -1; int ret_1 = 0; glusterd_brickinfo_t *brickinfo = NULL; glusterd_volinfo_t *origin_vol = NULL; xlator_t *this = NULL; this = THIS; if (!snap_vol) { gf_log(this->name, GF_LOG_WARNING, "snap_vol in NULL"); ret_1 = -1; goto out; } list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) continue; ret = glusterd_brick_stop (snap_vol, brickinfo, _gf_false); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to stop " "brick for volume %s", snap_vol->volname); ret_1 = ret; /* Continue to cleaning up the snap in case of error if force flag is enabled */ if (!force) goto out; } } /* Only remove the backend lvm when required */ if (remove_lvm) { ret = glusterd_lvm_snapshot_remove (snap_vol); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to remove " "lvm snapshot volume %s", snap_vol->volname); ret_1 = ret; if (!force) goto out; } } ret = glusterd_store_delete_volume (snap_vol); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to remove volume %s " "from store", snap_vol->volname); ret_1 = ret; if (!force) goto out; } if (!list_empty(&snap_vol->snapvol_list) ) { ret = glusterd_volinfo_find (snap_vol->parent_volname, &origin_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "parent volinfo %s for volume %s", snap_vol->parent_volname, snap_vol->volname); ret_1 = ret; if (!force) goto out; } origin_vol->snap_count--; } ret = glusterd_volinfo_delete (snap_vol); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to remove volinfo " "%s ", snap_vol->volname); ret_1 = ret; if (!force) goto out; } out: gf_log (this->name, GF_LOG_TRACE, "returning %d", ret_1); return ret_1; } int32_t glusterd_snapobject_delete (glusterd_snap_t *snap) { if (snap == NULL) { gf_log(THIS->name, GF_LOG_WARNING, "snap in NULL"); return -1; } list_del_init (&snap->snap_list); list_del_init (&snap->volumes); if (LOCK_DESTROY(&snap->lock)) gf_log (THIS->name, GF_LOG_WARNING, "Failed destroying lock" "of snap %s", snap->snapname); GF_FREE (snap->description); GF_FREE (snap); return 0; } int32_t glusterd_snap_remove (glusterd_snap_t *snap, gf_boolean_t remove_lvm, gf_boolean_t force) { int ret = -1; int ret_1 = 0; glusterd_volinfo_t *snap_vol = NULL; glusterd_volinfo_t *tmp = NULL; xlator_t *this = NULL; this = THIS; if (!snap) { gf_log(this->name, GF_LOG_WARNING, "snap in NULL"); ret_1 = -1; goto out; } list_for_each_entry_safe (snap_vol, tmp, &snap->volumes, vol_list) { ret = glusterd_snap_volume_remove (snap_vol, remove_lvm, force); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to remove " "volinfo %s for snap %s", snap_vol->volname, snap->snapname); ret_1 = ret; /* Continue to cleaning up the snap in case of error if force flag is enabled */ if (!force) goto out; } } ret = glusterd_store_delete_snap (snap); if (ret) { gf_log(this->name, GF_LOG_WARNING, "Failed to remove snap %s " "from store", snap->snapname); ret_1 = ret; if (!force) goto out; } ret = glusterd_snapobject_delete (snap); if (ret) gf_log (this->name, GF_LOG_WARNING, "Failed to delete " "snap object %s", snap->snapname); out: gf_log (THIS->name, GF_LOG_TRACE, "returning %d", ret_1); return ret_1; } static int glusterd_snapshot_get_snapvol_detail (dict_t *dict, glusterd_volinfo_t *snap_vol, char *keyprefix, int detail) { int ret = -1; int snap_limit = 0; char key[PATH_MAX] = {0,}; char *value = NULL; glusterd_volinfo_t *origin_vol = NULL; glusterd_conf_t *conf = NULL; xlator_t *this = NULL; this = THIS; conf = this->private; GF_ASSERT (conf); GF_ASSERT (dict); GF_ASSERT (snap_vol); GF_ASSERT (keyprefix); /* Volume Name */ value = gf_strdup (snap_vol->volname); if (!value) goto out; snprintf (key, sizeof (key), "%s.volname", keyprefix); ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "volume name in dictionary: %s", key); goto out; } /* Volume ID */ value = gf_strdup (uuid_utoa (snap_vol->volume_id)); if (NULL == value) { ret = -1; goto out; } snprintf (key, sizeof (key), "%s.vol-id", keyprefix); ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "volume id in dictionary: %s", key); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; /* volume status */ snprintf (key, sizeof (key), "%s.vol-status", keyprefix); switch (snap_vol->status) { case GLUSTERD_STATUS_STARTED: ret = dict_set_str (dict, key, "Started"); break; case GLUSTERD_STATUS_STOPPED: ret = dict_set_str (dict, key, "Stopped"); break; case GD_SNAP_STATUS_NONE: ret = dict_set_str (dict, key, "None"); break; default: gf_log (this->name, GF_LOG_ERROR, "Invalid volume " "status"); ret = -1; goto out; } if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set volume status " "in dictionary: %s", key); goto out; } if (!detail) goto out; ret = glusterd_volinfo_find (snap_vol->parent_volname, &origin_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get the parent " "volinfo for the volume %s", snap_vol->volname); goto out; } /* Snaps available */ if (conf->snap_max_hard_limit < origin_vol->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 %d", snap_limit); } else { snap_limit = origin_vol->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 %d",snap_limit); } snprintf (key, sizeof (key), "%s.snaps-available", keyprefix); if (snap_limit > origin_vol->snap_count) ret = dict_set_int32 (dict, key, snap_limit - origin_vol->snap_count); else ret = dict_set_int32 (dict, key, 0); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set available snaps"); goto out; } /* Parent volume name */ value = gf_strdup (snap_vol->parent_volname); if (!value) goto out; snprintf (key, sizeof (key), "%s.origin-volname", keyprefix); ret = dict_set_dynstr (dict, key, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set parent " "volume name in dictionary: %s", key); goto out; } value = NULL; ret = 0; /* Success */ out: if (value) GF_FREE (value); return ret; } static int glusterd_snapshot_get_snap_detail (dict_t *dict, glusterd_snap_t *snap, char *keyprefix, glusterd_volinfo_t *volinfo) { int ret = -1; int volcount = 0; char key[PATH_MAX] = {0,}; char *value = NULL; char *timestr = NULL; struct tm *tmptr = NULL; glusterd_volinfo_t *snap_vol = NULL; glusterd_volinfo_t *tmp_vol = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (dict); GF_ASSERT (snap); GF_ASSERT (keyprefix); /* Snap Name */ value = gf_strdup (snap->snapname); if (!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 (snap->snap_id)); if (NULL == value) { ret = -1; goto out; } snprintf (key, sizeof (key), "%s.snap-id", keyprefix); 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 (&(snap->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, PATH_MAX, gf_gld_mt_char); if (NULL == timestr) { ret = -1; goto out; } /* Format time into string */ ret = strftime (timestr, PATH_MAX, "%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; } snprintf (key, sizeof (key), "%s.snap-time", keyprefix); 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 snap description is provided then add that into dictionary */ if (NULL != snap->description) { value = gf_strdup (snap->description); if (NULL == value) { ret = -1; goto out; } snprintf (key, sizeof (key), "%s.snap-desc", keyprefix); 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; } value = NULL; } /* Snap status */ snprintf (key, sizeof (key), "%s.snap-status", keyprefix); switch (snap->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; } if (volinfo) { volcount = 1; snprintf (key, sizeof (key), "%s.vol%d", keyprefix, volcount); ret = glusterd_snapshot_get_snapvol_detail (dict, volinfo, key, 0); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get volume detail %s for snap %s", snap_vol->volname, snap->snapname); goto out; } goto done; } list_for_each_entry_safe (snap_vol, tmp_vol, &snap->volumes, vol_list) { volcount++; snprintf (key, sizeof (key), "%s.vol%d", keyprefix, volcount); ret = glusterd_snapshot_get_snapvol_detail (dict, snap_vol, key, 1); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get volume detail %s for snap %s", snap_vol->volname, snap->snapname); goto out; } } done: snprintf (key, sizeof (key), "%s.vol-count", keyprefix); ret = dict_set_int32 (dict, key, volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", key); goto out; } ret = 0; /* Success */ out: if (value) GF_FREE (value); if (timestr) GF_FREE(timestr); return ret; } static int glusterd_snapshot_get_all_snap_info (dict_t *dict) { int ret = -1; int snapcount = 0; char key[PATH_MAX] = {0,}; glusterd_snap_t *snap = NULL; glusterd_snap_t *tmp_snap = NULL; glusterd_conf_t *priv = NULL; xlator_t *this = NULL; this = THIS; priv = this->private; GF_ASSERT (priv); /* General parameter validation */ GF_ASSERT (dict); list_for_each_entry_safe (snap, tmp_snap, &priv->snapshots, snap_list) { snapcount++; snprintf (key, sizeof (key), "snap%d", snapcount); ret = glusterd_snapshot_get_snap_detail (dict, snap, key, NULL); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapdetail for snap %s", snap->snapname); goto out; } } ret = dict_set_int32 (dict, "snap-count", snapcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); goto out; } ret = 0; /* Success */ out: return ret; } int glusterd_snapshot_get_info_by_volume (dict_t *dict, char *volname, char *err_str, size_t len) { int ret = -1; int snapcount = 0; int snap_limit = 0; char *value = NULL; char key[PATH_MAX] = ""; glusterd_volinfo_t *volinfo = NULL; glusterd_volinfo_t *snap_vol = NULL; glusterd_volinfo_t *tmp_vol = NULL; glusterd_conf_t *conf = NULL; xlator_t *this = NULL; this = THIS; conf = this->private; GF_ASSERT (conf); GF_ASSERT (dict); GF_ASSERT (volname); ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, len, "Volume (%s) does not exist", volname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } /* Snaps available */ 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 %d", 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 %d",snap_limit); } if (snap_limit > volinfo->snap_count) ret = dict_set_int32 (dict, "snaps-available", snap_limit - volinfo->snap_count); else ret = dict_set_int32 (dict, "snaps-available", 0); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set available snaps"); goto out; } /* Origin volume name */ value = gf_strdup (volinfo->volname); if (!value) goto out; ret = dict_set_dynstr (dict, "origin-volname", value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set parent " "volume name in dictionary: %s", key); goto out; } value = NULL; list_for_each_entry_safe (snap_vol, tmp_vol, &volinfo->snap_volumes, snapvol_list) { snapcount++; snprintf (key, sizeof (key), "snap%d", snapcount); ret = glusterd_snapshot_get_snap_detail (dict, snap_vol->snapshot, key, snap_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snapdetail for snap %s", snap_vol->snapshot->snapname); goto out; } } ret = dict_set_int32 (dict, "snap-count", snapcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); goto out; } ret = 0; out: if (value) GF_FREE (value); return ret; } /* This function will be called from RPC handler routine. * This function is responsible for getting the requested * snapshot info 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_info (rpcsvc_request_t *req, glusterd_op_t op, dict_t *dict, char *err_str, size_t len) { int ret = -1; int8_t snap_driven = 1; char *volname = NULL; char *snapname = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; int32_t cmd = GF_SNAP_INFO_TYPE_ALL; this = THIS; GF_ASSERT (this); GF_VALIDATE_OR_GOTO (this->name, req, out); GF_VALIDATE_OR_GOTO (this->name, dict, out); ret = dict_get_int32 (dict, "cmd", &cmd); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get type " "of snapshot info"); goto out; } switch (cmd) { case GF_SNAP_INFO_TYPE_ALL : { ret = glusterd_snapshot_get_all_snap_info (dict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get info of all snaps"); goto out; } break; } case GF_SNAP_INFO_TYPE_SNAP : { ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap name"); goto out; } ret = dict_set_int32 (dict, "snap-count", 1); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); goto out; } snap = glusterd_find_snap_by_name (snapname); if (!snap) { snprintf (err_str, len, "Snap (%s) does not exist", snapname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); ret = -1; goto out; } ret = glusterd_snapshot_get_snap_detail (dict, snap, "snap1", NULL); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snap detail of snap " "%s", snap->snapname); goto out; } break; } case GF_SNAP_INFO_TYPE_VOL : { ret = dict_get_str (dict, "volname", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volname"); goto out; } ret = glusterd_snapshot_get_info_by_volume (dict, volname, err_str, len); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume info of volume " "%s", volname); goto out; } snap_driven = 0; break; } } ret = dict_set_int8 (dict, "snap-driven", snap_driven); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snap-driven"); 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: return ret; } /* Function glusterd_snapshot_get_list1 sets all the snapshot names in the dictionary */ int glusterd_snapshot_get_list1 (dict_t *dict) { int ret = -1; int snapcount = 0; char *snapname = NULL; char key[PATH_MAX] = {0,}; glusterd_snap_t *snap = NULL; glusterd_snap_t *tmp_snap = NULL; glusterd_conf_t *priv = NULL; xlator_t *this = NULL; this = THIS; priv = this->private; GF_ASSERT (priv); GF_ASSERT (dict); list_for_each_entry_safe (snap, tmp_snap, &priv->snapshots, snap_list) { snapcount++; snapname = gf_strdup (snap->snapname); if (!snapname) { gf_log (this->name, GF_LOG_ERROR, "strdup failed"); ret = -1; goto out; } snprintf (key, sizeof (key), "snapname%d", snapcount); ret = dict_set_dynstr (dict, key, snapname); if (ret) { GF_FREE (snapname); gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", key); goto out; } } ret = dict_set_int32 (dict, "snap-count", snapcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); goto out; } ret = 0; out: return ret; } /* Function glusterd_snapshot_get_list2 sets all the snapshot names under a given volume in the dictionary */ int glusterd_snapshot_get_list2 (dict_t *dict, glusterd_volinfo_t *volinfo) { int ret = -1; int snapcount = 0; char *snapname = NULL; char key[PATH_MAX] = {0,}; glusterd_volinfo_t *snap_vol = NULL; glusterd_volinfo_t *tmp_vol = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (dict); GF_ASSERT (volinfo); list_for_each_entry_safe (snap_vol, tmp_vol, &volinfo->snap_volumes, snapvol_list) { snapcount++; snapname = gf_strdup (snap_vol->snapshot->snapname); if (!snapname) { gf_log (this->name, GF_LOG_ERROR, "strdup failed"); ret = -1; goto out; } snprintf (key, sizeof (key), "snapname%d", snapcount); ret = dict_set_dynstr (dict, key, snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "set %s", key); GF_FREE (snapname); goto out; } } ret = dict_set_int32 (dict, "snap-count", snapcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); goto out; } ret = 0; out: return ret; } 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; char *volname = NULL; glusterd_volinfo_t *volinfo = NULL; xlator_t *this = NULL; this = THIS; GF_VALIDATE_OR_GOTO (this->name, req, out); GF_VALIDATE_OR_GOTO (this->name, dict, out); /* Ignore error for getting volname as it is optional */ ret = dict_get_str (dict, "volname", &volname); if (NULL == volname) { ret = glusterd_snapshot_get_list1 (dict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snapshot list"); goto out; } } else { ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { snprintf (err_str, len, "Volume (%s) does not exist", volname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } ret = glusterd_snapshot_get_list2 (dict, volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snapshot list for volume %s", volname); 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: 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->snapname); 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 mgmt_v3 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 *snapname = NULL; int64_t volcount = 0; xlator_t *this = NULL; char key[PATH_MAX] = ""; char *username = NULL; char *password = NULL; uuid_t *uuid_ptr = 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; } if (volcount <= 0) { gf_log (this->name, GF_LOG_ERROR, "Invalid volume count %ld " "supplied", volcount); ret = -1; goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get the snapname"); goto out; } uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); if (!uuid_ptr) { gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } uuid_generate (*uuid_ptr); ret = dict_set_bin (dict, "snap-id", uuid_ptr, sizeof(uuid_t)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snap-id"); GF_FREE (uuid_ptr); goto out; } uuid_ptr = NULL; ret = dict_set_int64 (dict, "snap-time", (int64_t)time(NULL)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snap-time"); 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; } uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); if (!uuid_ptr) { gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); ret = -1; goto out; } snprintf (key, sizeof(key) - 1, "vol%d_volid", i); uuid_generate (*uuid_ptr); ret = dict_set_bin (dict, key, uuid_ptr, sizeof(uuid_t)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snap_volid"); GF_FREE (uuid_ptr); goto out; } } ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to initiate snap " "phases"); } out: return ret; } /* This is a snapshot restore handler function. This function will be * executed in the originator node. This function is responsible for * 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; char *snapname = NULL; glusterd_conf_t *conf = NULL; xlator_t *this = NULL; glusterd_snap_t *snap = NULL; glusterd_volinfo_t *snap_volinfo = NULL; int32_t i = 0; char key[PATH_MAX] = ""; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_ASSERT (req); GF_ASSERT (dict); GF_ASSERT (err_str); ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to " "get snapname"); goto out; } snap = glusterd_find_snap_by_name (snapname); if (!snap) { snprintf (err_str, len, "Snap (%s) does not exist", snapname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); ret = -1; goto out; } list_for_each_entry (snap_volinfo, &snap->volumes, vol_list) { i++; snprintf (key, sizeof (key), "volname%d", i); ret = dict_set_str (dict, key, snap_volinfo->parent_volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Could not set " "parent volume name"); goto out; } } ret = dict_set_int32 (dict, "volcount", i); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Could not save volume count"); 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_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; } glusterd_snap_t* glusterd_create_snap_object (dict_t *dict) { char *snapname = NULL; uuid_t *snap_id = NULL; char *description = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; int ret = -1; int64_t time_stamp = 0; this = THIS; priv = this->private; GF_ASSERT (dict); /* Fetch snapname, description, id and time from dict */ ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snapname"); goto out; } /* Ignore ret value for description*/ ret = dict_get_str (dict, "description", &description); ret = dict_get_bin (dict, "snap-id", (void **)&snap_id); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snap_id"); goto out; } ret = dict_get_int64 (dict, "snap-time", &time_stamp); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snap-time"); goto out; } if (time_stamp <= 0) { ret = -1; gf_log (this->name, GF_LOG_ERROR, "Invalid time-stamp: %ld", time_stamp); goto out; } list_for_each_entry (snap, &priv->snapshots, snap_list) { if (!strcmp (snap->snapname, snapname) || !uuid_compare (snap->snap_id, *snap_id)) { gf_log (THIS->name, GF_LOG_ERROR, "Found duplicate snap %s (%s)", snap->snapname, uuid_utoa (snap->snap_id)); ret = -1; break; } } if (ret) { snap = NULL; goto out; } snap = glusterd_new_snap_object (); if (!snap) { gf_log (this->name, GF_LOG_ERROR, "Could not create " "the snap object for snap %s", snapname); goto out; } strcpy (snap->snapname, snapname); uuid_copy (snap->snap_id, *snap_id); snap->time_stamp = (time_t)time_stamp; snap->snap_status = GD_SNAP_STATUS_IN_USE; if (description) { snap->description = gf_strdup (description); if (snap->description == NULL) { gf_log (this->name, GF_LOG_ERROR, "Saving the Snap Description Failed"); ret = -1; goto out; } } ret = glusterd_store_snap (snap); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Could not store snap" "object %s", snap->snapname); goto out; } list_add_order (&snap->snap_list, &priv->snapshots, glusterd_compare_snap_time); gf_log (this->name, GF_LOG_TRACE, "Snap %s added to the list", snap->snapname); ret = 0; out: if (ret) { if (snap) glusterd_snap_remove (snap, _gf_true,_gf_true); snap=NULL; } return snap; } /* This function is called to get the device path of the snap lvm. Usually if /dev/mapper/- 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 snap[PATH_MAX] = ""; char msg[1024] = ""; char volgroup[PATH_MAX] = ""; char *snap_device = NULL; xlator_t *this = NULL; runner_t runner = {0,}; char *ptr = NULL; int ret = -1; this = THIS; GF_ASSERT (this); if (!device) { gf_log (this->name, GF_LOG_ERROR, "device is NULL"); goto out; } if (!snapname) { gf_log (this->name, GF_LOG_ERROR, "snapname is NULL"); goto out; } runinit (&runner); runner_add_args (&runner, "/sbin/lvs", "--noheadings", "-o", "vg_name", device, NULL); runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); snprintf (msg, sizeof (msg), "Get volume group for device %s", device); runner_log (&runner, this->name, GF_LOG_DEBUG, msg); ret = runner_start (&runner); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume group " "for device %s", device); runner_end (&runner); goto out; } ptr = fgets(volgroup, sizeof(volgroup), runner_chio (&runner, STDOUT_FILENO)); if (!ptr || !strlen(volgroup)) { gf_log (this->name, GF_LOG_ERROR, "Failed to get volume group " "for snap %s", snapname); runner_end (&runner); ret = -1; goto out; } runner_end (&runner); strcpy(snap, "/dev/"); strcat(snap, gf_trim(volgroup)); strcat(snap, "/"); strcat(snap, snapname); snap_device = gf_strdup (snap); if (!snap_device) { gf_log (this->name, GF_LOG_WARNING, "Cannot copy the " "snapshot device name for snapname: %s)", snapname); } out: 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 */ char * glusterd_take_lvm_snapshot (glusterd_volinfo_t *snap_vol, glusterd_brickinfo_t *brickinfo) { char msg[NAME_MAX] = ""; char buf[PATH_MAX] = ""; char *snap_device = NULL; char *ptr = NULL; char *device = NULL; int ret = -1; gf_boolean_t match = _gf_false; runner_t runner = {0,}; xlator_t *this = NULL; this = THIS; 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; } /* Figuring out if setactivationskip flag is supported or not */ runinit (&runner); snprintf (msg, sizeof (msg), "running lvcreate help"); runner_add_args (&runner, "/sbin/lvcreate", "--help", NULL); runner_log (&runner, "", GF_LOG_DEBUG, msg); runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); ret = runner_start (&runner); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to run lvcreate help"); runner_end (&runner); goto out; } /* Looking for setactivationskip in lvcreate --help */ do { ptr = fgets(buf, sizeof(buf), runner_chio (&runner, STDOUT_FILENO)); if (ptr) { if (strstr(buf, "setactivationskip")) { match = _gf_true; break; } } } while (ptr != NULL); runner_end (&runner); /* Takng the actual snapshot */ runinit (&runner); snprintf (msg, sizeof (msg), "taking snapshot of the brick %s:%s", brickinfo->hostname, brickinfo->path); if (match == _gf_true) runner_add_args (&runner, "/sbin/lvcreate", "-s", device, "--setactivationskip", "n", "--name", snap_vol->volname, NULL); else runner_add_args (&runner, "/sbin/lvcreate", "-s", device, "--name", snap_vol->volname, NULL); runner_log (&runner, "", GF_LOG_DEBUG, msg); ret = runner_start (&runner); 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); runner_end (&runner); goto out; } runner_end (&runner); snap_device = glusterd_build_snap_device_path (device, snap_vol->volname); if (!snap_device) { gf_log (this->name, GF_LOG_WARNING, "Cannot copy the snapshot " "device name for snap %s (volume id: %s)", snap_vol->snapshot->snapname, snap_vol->volname); ret = -1; goto out; } out: return snap_device; } 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_dir = NULL; char snap_brick_mount_path[PATH_MAX] = ""; char *tmp = NULL; char snap_brick_path[PATH_MAX] = ""; char msg[1024] = ""; char *mnt_pt = NULL; struct mntent *entry = NULL; FILE *mtab = NULL; struct stat statbuf = {0, }; runner_t runner = {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", snap_mount_folder, 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; } glusterd_volinfo_t * glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap, dict_t *dict, int64_t volcount) { char *snap_brick_mount_path = ""; char key[PATH_MAX] = ""; char *device = 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_vol = NULL; uuid_t *snap_volid = 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 (origin_vol); GF_ASSERT (dict); /* fetch username, password and vol_id from dict*/ snprintf (key, sizeof(key), "volume%ld_username", volcount); ret = dict_get_str (dict, key, &username); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get %s for " "snap %s", key, snap->snapname); goto out; } snprintf (key, sizeof(key), "volume%ld_password", volcount); ret = dict_get_str (dict, key, &password); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get %s for " "snap %s", key, snap->snapname); goto out; } snprintf (key, sizeof(key) - 1, "vol%ld_volid", volcount); 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; } /* We are not setting the username and password here as * we need to set the user name and password passed in * the dictionary */ ret = glusterd_volinfo_dup (origin_vol, &snap_vol, _gf_false); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to duplicate volinfo " "for the snapshot %s", snap->snapname); goto out; } /* uuid is used as lvm snapshot name. This will avoid restrictions on snapshot names provided by user */ GLUSTERD_GET_UUID_NOHYPHEN (snap_vol->volname, *snap_volid); uuid_copy (snap_vol->volume_id, *snap_volid); snap_vol->is_snap_volume = _gf_true; strcpy (snap_vol->parent_volname, origin_vol->volname); snap_vol->snapshot = snap; snap->snap_volume = snap_vol; glusterd_auth_set_username (snap_vol, username); glusterd_auth_set_password (snap_vol, password); /* Adding snap brickinfos to the snap volinfo */ brick_count = 0; list_for_each_entry (brickinfo, &origin_vol->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->snapname); goto out; } snprintf (key, sizeof(key) - 1, "vol%ld.brick%d", volcount, brick_count); ret = dict_get_ptr (dict, key, (void **)&snap_brick_mount_path); if (ret) { gf_log ("", GF_LOG_ERROR, "Unable to fetch " "snap mount path (%s)", key); 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); list_add_tail (&snap_brickinfo->brick_list, &snap_vol->bricks); brick_count++; } list_for_each_entry (brickinfo, &origin_vol->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, origin_vol->volname, snap->snapname); continue; } device = glusterd_take_lvm_snapshot (snap_vol, brickinfo); /* 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 (!device) { 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_vol, brickinfo); GF_FREE (device); device = NULL; if (ret) { gf_log (this->name, GF_LOG_ERROR, "not able to" " create the brickinfo for the snap %s" ", volume %s", snap->snapname, origin_vol->volname); goto out; } } /*TODO: the quorum check of the snap volume here */ ret = glusterd_store_volinfo (snap_vol, GLUSTERD_VOLINFO_VER_AC_INCREMENT); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to store snapshot " "volinfo (%s) for snap %s", snap_vol->volname, snap->snapname); goto out; } ret = generate_snap_brick_volfiles (origin_vol, snap_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "generating the brick " "volfiles for the snap %s (volume: %s) failed", snap->snapname, origin_vol->volname); goto out; } ret = generate_snap_client_volfiles (origin_vol, snap_vol, 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", snap->snapname, origin_vol->volname); goto out; } ret = generate_snap_client_volfiles (origin_vol, snap_vol, 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", snap->snapname, origin_vol->volname); goto out; } ret = glusterd_list_add_snapvol (origin_vol, snap_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "could not add the snap " "volume %s to the list", snap_vol->volname); goto out; } list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { if (uuid_compare (brickinfo->uuid, MY_UUID)) continue; ret = glusterd_brick_start (snap_vol, 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, snap->snapname, origin_vol->volname); goto out; } } snap_vol->status = GLUSTERD_STATUS_STARTED; ret = glusterd_store_volinfo (snap_vol, GLUSTERD_VOLINFO_VER_AC_INCREMENT); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to store snap volinfo"); goto out; } out: if (ret) { if (snap_vol) glusterd_snap_volume_remove (snap_vol, _gf_true, _gf_true); snap_vol = NULL; } return snap_vol; } /* 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 volcount = 0; char *snapname = NULL; char *volname = NULL; char key[PATH_MAX] = ""; glusterd_snap_t *snap = NULL; glusterd_volinfo_t *snap_vol = NULL; glusterd_volinfo_t *tmp = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (req); GF_ASSERT (dict); GF_ASSERT (err_str); ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get snapname"); goto out; } snap = glusterd_find_snap_by_name (snapname); if (!snap){ snprintf (err_str, len, "Snap (%s) does not exist", snapname); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); ret = -1; goto out; } /* Set volnames in the dict to get mgmt_v3 lock */ list_for_each_entry_safe (snap_vol, tmp, &snap->volumes, vol_list) { volcount++; volname = gf_strdup (snap_vol->parent_volname); if (!volname) { ret = -1; gf_log (this->name, GF_LOG_ERROR, "strdup failed"); goto out; } snprintf (key, sizeof (key), "volname%ld", volcount); ret = dict_set_dynstr (dict, key, volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set " "volume name in dictionary"); GF_FREE (volname); goto out; } volname = NULL; } ret = dict_set_int64 (dict, "volcount", volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set volcount"); 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 *snapname = NULL; xlator_t *this = NULL; glusterd_snap_t *snap = NULL; this = THIS; if (!dict || !op_errstr) { gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret){ gf_log (this->name, GF_LOG_ERROR, "Getting the snap name " "failed"); goto out; } snap = glusterd_find_snap_by_name (snapname); if (!snap){ gf_log (this->name, GF_LOG_ERROR, "Snap %s does not exist", snapname); ret = -1; 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; glusterd_conf_t *priv = NULL; xlator_t *this = NULL; this = THIS; priv = this->private; GF_ASSERT (priv); 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, &priv->snapshots, 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->snapname); 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 *snapname = NULL; char *dup_snapname = NULL; glusterd_snap_t *snap = NULL; xlator_t *this = NULL; this = THIS; if (!dict || !op_errstr) { gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret){ gf_log (this->name, GF_LOG_ERROR, "Getting the snap name " "failed"); goto out; } snap = glusterd_find_snap_by_name (snapname); if (!snap){ gf_log (this->name, GF_LOG_ERROR, "Snap %s does not exist", snapname); ret = -1; goto out; } ret = glusterd_snap_remove (snap, _gf_true, _gf_false); if (ret){ gf_log (this->name, GF_LOG_ERROR, "Failed to remove snap %s", snapname); goto out; } dup_snapname = gf_strdup (snapname); if (!dup_snapname) { gf_log (this->name, GF_LOG_ERROR, "Strdup failed"); ret = -1; goto out; } ret = dict_set_dynstr (rsp_dict, "snapname", dup_snapname); if (ret){ gf_log (this->name, GF_LOG_ERROR, "Failed to set the snapname"); GF_FREE (dup_snapname); goto out; } ret = 0; out: return ret; } int32_t glusterd_snapshot_create_commit (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int ret = -1; int64_t i = 0; int64_t volcount = 0; char *snapname = NULL; char *volname = NULL; char *tmp_name = NULL; char key[PATH_MAX] = ""; xlator_t *this = NULL; glusterd_snap_t *snap = NULL; glusterd_volinfo_t *origin_vol = NULL; glusterd_volinfo_t *snap_vol = NULL; glusterd_conf_t *priv = NULL; this = THIS; priv = this->private; GF_ASSERT(priv); ret = dict_get_int64 (dict, "volcount", &volcount); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "get the volume count"); goto out; } ret = dict_get_str (dict, "snapname", &snapname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snapname"); goto out; } tmp_name = gf_strdup (snapname); if (!tmp_name) { gf_log (this->name, GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } ret = dict_set_dynstr (rsp_dict, "snapname", tmp_name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set snapname in rsp_dict"); GF_FREE (tmp_name); goto out; } tmp_name = NULL; snap = glusterd_create_snap_object (dict); if (!snap) { gf_log (this->name, GF_LOG_ERROR, "creating the" "snap object %s failed", snapname); ret = -1; goto out; } for (i = 1; i <= volcount; i++) { snprintf (key, sizeof (key), "volname%ld", i); 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, &origin_vol); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get the volinfo for " "the volume %s", volname); goto out; } /* TODO: Create a stub where the bricks are added parallely by worker threads so that the snap creating happens parallely. */ snap_vol = glusterd_do_snap_vol (origin_vol, snap, dict, i); if (!snap_vol) { ret = -1; gf_log (this->name, GF_LOG_WARNING, "taking the " "snapshot of the volume %s failed", volname); goto out; } } ret = 0; out: if (ret) { if (snap) glusterd_snap_remove (snap, _gf_true, _gf_true); snap=NULL; } 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_INFO: ret = glusterd_handle_snapshot_info (req, cli_op, dict, err_str, sizeof (err_str)); if (ret) { gf_log (this->name, GF_LOG_WARNING, "Snapshot info " "failed"); } 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); }