/* 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-jarvis.h" #include "syscall.h" #include "cli1-xdr.h" #include "xdr-generic.h" #ifdef GF_LINUX_HOST_OS #include #endif int glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { char *volname = NULL; glusterd_volinfo_t *volinfo = NULL; int64_t volume_count = 0; char volname_buf[PATH_MAX] = {0, }; int64_t i = 0; int snap_command = 0; xlator_t *this = NULL; int ret = -1; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (rsp_dict); //not sure if this is needed, verify. ret = dict_get_int32 (dict, "type", &snap_command); if (ret) { gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " "the snapshot command"); goto out; } switch (snap_command) { case (GF_SNAP_OPTION_TYPE_CREATE): { ret = dict_get_int64 (dict, "volcount", &volume_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "get the volume count"); goto out; } for (i = 0; i < volume_count; i++) { snprintf (volname_buf, sizeof (volname_buf), "volname%ld", i+1); ret = dict_get_str (dict, volname_buf, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get volume name"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get the volinfo for " "the volume %s", volname); goto out; } if (glusterd_is_defrag_on (volinfo)) { gf_log (this->name, GF_LOG_WARNING, "rebalance process is running " "for the volume %s", volname); goto out; } //Also check whether geo replication is running } break; } default: gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); goto out; break; } ret = 0; out: return ret; } glusterd_snap_t* glusterd_new_snap_object() { 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); gf_log (THIS->name, GF_LOG_DEBUG, "Snap %s added @ %"PRIu64, snap->snap_name, count); ret = 0; } unlock: UNLOCK (&volinfo->lock); out: return ret; } 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); } 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 (&cg->cg_list, &last->cg_list); gf_log (THIS->name, GF_LOG_DEBUG, "Added CG %s (%s) @ %"PRIu64, cg->cg_name, uuid_utoa(cg->cg_id), count); ret = 0; out: 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.snap-name", 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; ssize_t index = -1; glusterd_snap_t *entry = NULL; glusterd_snap_t *tmp = NULL; xlator_t *this = NULL; char *value = NULL; char key[256]; this = THIS; /* General parameter validation */ GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (keyprefix); GF_ASSERT (volinfo); value = gf_strdup (volinfo->volname); if (NULL == value) { goto out; } /* First set the volume name */ ret = snprintf (key, sizeof (key), "%s.vol-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 volume name"); goto out; } /* Ownership of value transferred to dict. Therefore we must initalize * it to NULL */ value = NULL; /* New entries are always added to the end of snap_list and we need to * display the list in LIFO (Last-In-First-Out) order. Therefore insert * the entries in reverse order into the dictionary. */ list_for_each_entry_safe_reverse (entry, tmp, &volinfo->snaps, snap_list) { ++index; ret = snprintf (key, sizeof (key), "%s.snap%ld", keyprefix, index); if (ret < 0) { /* Only negative value is error */ goto out; } /* 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.snap0", 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 = 0; break; /* Found the snap */ } /* If all the snap is written into the dictionary then write the * snap count into the dictionary */ if (0 == ret) { ++index; /* To get count increment index by 1*/ 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); } 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]; 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) { 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, &(cg->volumes[i]), NULL, detail); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "snaplist for %s volume", cg->volumes[i].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) { 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) { gf_log (this->name, GF_LOG_ERROR, "Failed to get " "%s CG", cgname); 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]; 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 " "volname for %s", key); goto out; } /* Now for each volume get the snap list */ 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_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) { 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 *err_str = "Operation failed"; char key[256]; 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, "snap-name", &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, "cg-name", &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.cg0", keyprefix); if (ret < 0) { /* Only negative value is error */ goto out; } ret = glusterd_snapshot_cg_get_snaplist (dict, key, cgname, detail); 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; } /* this should be the last thing to be done. 1. Do op stage related checks such as whether volume is there or not etc 2. Do quorum checks. 3. Then do this and take the snapshot OR take the snapshot and build the snap object (Can be decided later) */ int32_t glusterd_snap_create (glusterd_volinfo_t *volinfo, glusterd_volinfo_t *snap_volinfo, char *description, uuid_t cg_id) { glusterd_snap_t *snap = NULL; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; int ret = -1; uuid_t snap_uuid; this = THIS; priv = this->private; if (!volinfo) { gf_log (this->name, GF_LOG_ERROR, "volinfo is NULL"); goto out; } snap = glusterd_new_snap_object (); if (!snap) { gf_log (this->name, GF_LOG_ERROR, "could not create " "the snap object fot the volume %s (snap: %s)", volinfo->volname, snap_volinfo->volname); goto out; } // for now ignore if description is not strduped if (description) snap->description = gf_strdup (description); snap->time_stamp = time (NULL); uuid_generate (snap_uuid); uuid_copy (snap->snap_id, snap_uuid); if (!uuid_is_null (cg_id)) uuid_copy (snap->cg_id, cg_id); snap->snap_volume = snap_volinfo; uuid_copy (snap_volinfo->volume_id, snap_uuid); strcpy (snap->snap_name, snap_volinfo->volname); //TODO: replace strcpy with strncpy ret = glusterd_add_snap (volinfo, snap); if (ret) { gf_log (this->name, GF_LOG_ERROR, "could not add the snap " "object %s to the snap list of the volume %s", snap_volinfo->volname, volinfo->volname); goto out; } out: if (ret) { if (snap) { list_del_init (&snap->snap_list); LOCK_DESTROY (&snap->lock); GF_FREE (snap->description); GF_FREE (snap->snap_volume); GF_FREE (snap); } } return ret; } int glusterd_remove_snapshot (glusterd_brickinfo_t *brickinfo, char *volname, 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; priv = this->private; if (!brickinfo) { gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); goto out; } snprintf (msg, sizeof(msg), "remove snapshot of the brick %s:%s, " "device: %s", brickinfo->hostname, brickinfo->path, snap_device); runner_add_args (&runner, "/sbin/lvmremove", snap_device); runner_log (&runner, "", GF_LOG_DEBUG, msg); //let glusterd get blocked till snapshot is over synclock_unlock (&priv->big_lock); ret = runner_run (&runner); synclock_lock (&priv->big_lock); if (ret) { gf_log (this->name, GF_LOG_ERROR, "removing snapshot of the " "brick (%s:%s) of device %s failed", brickinfo->hostname, brickinfo->path, snap_device); goto out; } out: return ret; } int32_t glusterd_brick_snapshot_remove (glusterd_volinfo_t *snap_volinfo, glusterd_volinfo_t *actual_volinfo, char *name) { char *mnt_pt = NULL; struct mntent *entry = NULL; int32_t ret = -1; glusterd_brickinfo_t *brickinfo = NULL; xlator_t *this = NULL; FILE *mtab = NULL; this = THIS; GF_ASSERT (this); 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) { ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); if (ret) goto out; entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); if (!entry) { ret = -1; goto out; } ret = glusterd_remove_snapshot (brickinfo, actual_volinfo->volname, name, entry->mnt_fsname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "remove the snapshot %s (%s)", brickinfo->path, entry->mnt_fsname); goto out; } } ret = 0; out: if (mtab) endmntent (mtab); return ret; } /* This function is called to get the device path of the snap lvm. Usually if /dev//- 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, "--name", snapname); runner_log (&runner, "", GF_LOG_DEBUG, msg); //let glusterd get blocked till snapshot is over synclock_unlock (&priv->big_lock); ret = runner_run (&runner); synclock_lock (&priv->big_lock); if (ret) { gf_log (this->name, GF_LOG_ERROR, "taking snapshot of the " "brick (%s:%s) of device %s failed", brickinfo->hostname, brickinfo->path, device); goto out; } gf_log (this->name, GF_LOG_INFO, "device: %s", device); if (device) { tmp = glusterd_build_snap_device_path (device, snapname); *snap_device = tmp; if (!*snap_device) { gf_log (this->name, GF_LOG_WARNING, "cannot copy the " "snapshot device name (volname: %s, " "snapname: %s)", volname, snapname); ret = -1; goto out; } } out: return ret; } int32_t glusterd_snap_brick_create (char *device, glusterd_volinfo_t *snap_volinfo, glusterd_brickinfo_t *original_brickinfo) { int32_t ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; glusterd_brickinfo_t *snap_brickinfo = NULL; char snap_brick_mount_path[PATH_MAX] = {0, }; char *tmp = NULL; char *mnt_pt = NULL; struct mntent *entry = NULL; FILE *mtab = NULL; this = THIS; priv = this->private; if (!device) { gf_log (this->name, GF_LOG_ERROR, "device is NULL"); goto out; } if (!snap_volinfo) { gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); goto out; } if (!original_brickinfo) { gf_log (this->name, GF_LOG_ERROR, "original brickinfo is NULL" "(snap: %s)", snap_volinfo->volname); goto out; } tmp = gf_strdup (device); if (!tmp) { gf_log (this->name, GF_LOG_INFO, "out of memory"); goto out; } glusterd_replace_slash_with_hyphen (tmp); if (tmp[0] == '-') tmp[0] = '/'; snprintf (snap_brick_mount_path, sizeof (snap_brick_mount_path), "%s" "/%s%s-brick", GLUSTERD_DEFAULT_SNAPS_BRICK_DIR, snap_volinfo->volname, tmp); ret = glusterd_get_brick_root (original_brickinfo->path, &mnt_pt); if (ret) goto out; entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); if (!entry) { ret = -1; goto out; } ret = mkdir_p (snap_brick_mount_path, 0777, _gf_true); if (ret) { gf_log (this->name, GF_LOG_ERROR, "creating the brick directory" " %s for the snapshot %s(device: %s) failed", snap_brick_mount_path, snap_volinfo->volname, device); goto out; } /* mount the snap logical device on the directory inside /var/run/gluster/snaps//@snap_brick_mount_path */ ret = mount (device, snap_brick_mount_path, entry->mnt_type, MS_MGC_VAL, "nouuid"); if (ret) { gf_log (this->name, GF_LOG_ERROR, "mounting the snapshot " "logical device %s failed (error: %s)", device, strerror (errno)); goto out; } ret = glusterd_brickinfo_new (&snap_brickinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "initializing the brick for the snap " "volume failed (snapname: %s)", snap_volinfo->volname); goto out; } strcpy (snap_brickinfo->hostname, original_brickinfo->hostname); strcpy (snap_brickinfo->path, snap_brick_mount_path); LOCK (&snap_volinfo->lock); { list_add_tail (&snap_brickinfo->brick_list, &snap_volinfo->bricks); } UNLOCK (&snap_volinfo->lock); out: GF_FREE (tmp); if (ret) { umount (snap_brick_mount_path); if (snap_brickinfo) glusterd_brickinfo_delete (snap_brickinfo); } if (mtab) endmntent (mtab); return ret; } /* TODO: lvm uses '-' as the delimter for differentiating the logical volume name and the volume group name. So as of now, if the snapname given from cli contains '-', it confuses lvm. Handle it. */ int32_t glusterd_do_snap (glusterd_volinfo_t *volinfo, char *name, dict_t *dict, gf_boolean_t cg, uuid_t cg_id) { int32_t ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; glusterd_brickinfo_t *brickinfo = NULL; char *device = NULL; char snapname[PATH_MAX] = {0, }; char tmp[2046] = {0, }; glusterd_volinfo_t *snap_volume = NULL; char *description = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); strcpy (snapname, name); if (cg) { snprintf (tmp, sizeof (tmp), "%s-snap", volinfo->volname); strcat (snapname, tmp); } ret = glusterd_volinfo_dup (volinfo, &snap_volume); strcpy (snap_volume->volname, snapname); list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { if (!glusterd_is_brick_started (brickinfo)) continue; ret = glusterd_take_snapshot (brickinfo, volinfo->volname, snapname, dict, &device); /* continue with the snapshot even though snapshot on one of the bricks fails. At the end check whether the snapshot volume meets quorum or not. If so, then the snapshot can be treated as success. If not, undo the changes and return failure to cli. */ if (ret) continue; /*create the complete brick here and add it to the volinfo */ ret = glusterd_snap_brick_create (device, snap_volume, brickinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "not able to" " create the brickinfo for the snap %s" ", volume %s", snapname, volinfo->volname); goto out; } } //TODO: the quorum check of the snap volume here ret = dict_get_str (dict, "snap-description", &description); // for now continue the snap, if getting description fails. ret = glusterd_snap_create (volinfo, snap_volume, description, cg_id); if (ret) { gf_log (this->name, GF_LOG_ERROR, "creating the" "snap object failed for the volume %s", volinfo->volname); goto out; } ret = glusterd_store_perform_snap_store (volinfo); if (ret) { gf_log (this->name, GF_LOG_WARNING, "could not do volume store" " after taking the snapshot (volume: %s)", volinfo->volname); goto out; } 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); if (ret) { gf_log (this->name, GF_LOG_ERROR, "generating the trusted " "client volfiles for the snap %s (volume: %s) failed", snapname, volinfo->volname); goto out; } ret = generate_snap_client_volfiles (volinfo, snap_volume, GF_CLIENT_OTHER); if (ret) { 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->volumes); list_for_each_entry (brickinfo, &snap_volume->bricks, brick_list) { ret = glusterd_snap_brick_start (volinfo, snap_volume, brickinfo, _gf_true); if (ret) { gf_log (this->name, GF_LOG_WARNING, "starting the " "brick %s:%s for the snap %s (volume: %s) " "failed", brickinfo->hostname, brickinfo->path, snapname, volinfo->volname); goto out; } } out: if (ret) glusterd_volinfo_delete (snap_volume); return ret; } int32_t glusterd_do_snap_remove (glusterd_volinfo_t *volinfo, char *name, dict_t *dict) { int32_t ret = -1; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; glusterd_snap_t *snap = NULL; glusterd_snap_cg_t *cg = NULL; int i = 0; this = THIS; priv = this->private; if (!volinfo) { gf_log (this->name, GF_LOG_ERROR, "volinfo NULL"); goto out; } if (!name) { gf_log (this->name, GF_LOG_ERROR, "name is NULL (volume: %s)", volinfo->volname); goto out; } snap = glusterd_find_snap_by_name (volinfo, name); if (!snap) { cg = glusterd_find_snap_cg_by_name (priv, volinfo->volname); if (!cg) { gf_log (this->name, GF_LOG_ERROR, "could not find " "the snap or the cg object by the name %s", name); goto out; } } if (snap) { ret = glusterd_brick_snapshot_remove (snap->snap_volume, volinfo, name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "removing the bricks" " snapshots for the snap %s (volume: %s) " "failed", name, volinfo->volname); goto out; } } if (cg) { for (i = 0; i < cg->volume_count ; i++) { ret = glusterd_brick_snapshot_remove (&cg->volumes[i], volinfo, name); if (ret) { gf_log (this->name, GF_LOG_ERROR, "removing the" " bricks snapshots for the snap %s " "(volume: %s) failed", name, volinfo->volname); goto out; } } } ret = 0; out: return ret; } /* This function helps in generating the names for either the snapshot (if only one volume name is given in the snap create command) or the consistency group (if multiple volume names are given in the snaap create command). Apart from that, it also helps in generating the names for the snaps of the individual volumes in a consistency group. */ char * generate_snapname (char *volname, char *name, gf_boolean_t volume_from_cg) { char internal_snapname[PATH_MAX] = {0, }; char timestr[256] = {0, }; int ret = -1; char *snapname = NULL; struct timeval tv = {0, }; xlator_t *this = NULL; int i = 0; this = THIS; GF_ASSERT (this); if (name) { GF_ASSERT (volname); if (volume_from_cg) { snprintf (internal_snapname, sizeof (internal_snapname), "%s_%s", volname, name); } else { snprintf (internal_snapname, sizeof (internal_snapname), "%s", name); } } else { ret = gettimeofday (&tv, NULL); if (ret) { gf_log (this->name, GF_LOG_ERROR, "getting time failed. snapname is not given via" "cli. "); goto out; } gf_time_fmt (timestr, sizeof (timestr), tv.tv_sec, gf_timefmt_FT); snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), ".%"GF_PRI_SUSECONDS, tv.tv_usec); for (i = 0; i < strlen (timestr); i++) { if (timestr[i] == ' ' || timestr[i] == ':' || timestr[i] == '.' || timestr[i] == '-') timestr[i] = '_'; } snprintf (internal_snapname, sizeof (internal_snapname), "%s%s", (volume_from_cg)?"cg_":"", timestr); } snapname = gf_strdup (internal_snapname); out: return snapname; } /* name can be either the snapname if @volnames contains only one volume or cg name if there are multiple volume names in volnames string */ int32_t glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int ret = -1; int i = 0; int snap_command = 0; int64_t volume_count = 0; gf_boolean_t is_cg = _gf_false; char *name = NULL; char *volname = NULL; char *tmp = NULL; char volname_buf[PATH_MAX] = {0, }; xlator_t *this = NULL; glusterd_volinfo_t *volinfo = NULL; glusterd_snap_cg_t *cg = NULL; glusterd_conf_t *priv = NULL; uuid_t cg_id; this = THIS; GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (rsp_dict); //not sure if this is needed, verify. priv = this->private; GF_ASSERT (priv); ret = dict_get_int32 (dict, "type", &snap_command); if (ret) { gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " "the snapshot command"); goto out; } switch (snap_command) { case (GF_SNAP_OPTION_TYPE_CREATE): { ret = dict_get_int64 (dict, "volcount", &volume_count); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to " "get the volume count"); goto out; } //snap-name should not be set if volume_count > 1 ret = dict_get_str (dict, "snap-name", &name); if (volume_count > 1 && !ret) GF_ASSERT (0); if (volume_count > 1) { is_cg = _gf_true; ret = dict_get_str (dict, "cg-name", &name); uuid_generate (cg_id); } else if (volume_count == 1) { ret = dict_get_str (dict, "snap-name", &name); } if (!name) { name = generate_snapname (volname, NULL, is_cg); if (!name) { gf_log (this->name, GF_LOG_ERROR, "strdup of internal snapname" " ((%s) failed for the " "volume %s", name, volname); goto out; } } for (i = 1; i < volume_count + 1; i++) { snprintf (volname_buf, sizeof (volname_buf), "volname%d", i); ret = dict_get_str (dict, volname_buf, &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get volume name"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get the volinfo for " "the volume %s", volname); goto out; } tmp = generate_snapname (volname, name, is_cg); if (!tmp) { gf_log (this->name, GF_LOG_ERROR, "strdup " "failed (%s)", name); goto out; } /* TODO: Create a stub where the bricks are added parallely by worker threads so that the snap creating happens parallely. */ ret = glusterd_do_snap (volinfo, tmp, dict, is_cg, cg_id); if (ret) { gf_log (this->name, GF_LOG_WARNING, "taking the " "snapshot of the volume %s failed", volname); goto out; } } break; } default: gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); goto out; break; } if (volume_count > 1) { cg = glusterd_new_snap_cg_object (volume_count); if (!cg) { gf_log (this->name, GF_LOG_ERROR, "cannot create the " "consistency group %s", name); goto out; } uuid_copy (cg->cg_id, cg_id); ret = glusterd_add_snap_cg (priv, cg); if (ret) { gf_log (this->name, GF_LOG_ERROR, "could not add the" " consistency group %s to the glusterd list ", name); cg = glusterd_remove_snap_cg_by_name (priv, name); if (!cg) gf_log (this->name, GF_LOG_WARNING, "cannot " "find the consistency group %s", name); goto out; } } ret = 0; out: return ret; } int glusterd_handle_snapshot_fn (rpcsvc_request_t *req) { int32_t ret = 0; dict_t *dict = NULL; gf_cli_req cli_req = {{0},}; glusterd_op_t cli_op = GD_OP_SNAP; int type = 0; glusterd_conf_t *conf = NULL; char *host_uuid = NULL; char err_str[2048] = {0,}; xlator_t *this = NULL; GF_ASSERT (req); this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); ret = xdr_to_generic (req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req); if (ret < 0) { req->rpc_err = GARBAGE_ARGS; goto out; } if (cli_req.dict.dict_len > 0) { dict = dict_new (); if (!dict) goto out; ret = dict_unserialize (cli_req.dict.dict_val, cli_req.dict.dict_len, &dict); if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "failed to " "unserialize req-buffer to dictionary"); snprintf (err_str, sizeof (err_str), "Unable to decode " "the command"); goto out; } dict->extra_stdfree = cli_req.dict.dict_val; host_uuid = gf_strdup (uuid_utoa(MY_UUID)); if (host_uuid == NULL) { snprintf (err_str, sizeof (err_str), "Failed to get " "the uuid of local glusterd"); ret = -1; goto out; } ret = dict_set_dynstr (dict, "host-uuid", host_uuid); if (ret) goto out; } else { gf_log (this->name, GF_LOG_ERROR, "request dict length is %d", cli_req.dict.dict_len); goto out; } ret = dict_get_int32 (dict, "type", &type); if (ret < 0) { snprintf (err_str, sizeof (err_str), "Command type not found"); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } switch (type) { case GF_SNAP_OPTION_TYPE_CREATE: ret = glusterd_jarvis_initiate_snap_phases (req, cli_op, dict); break; case GF_SNAP_OPTION_TYPE_LIST: ret = glusterd_handle_snapshot_list (req, cli_op, dict); break; case GF_SNAP_OPTION_TYPE_RESTORE: case GF_SNAP_OPTION_TYPE_DELETE: case GF_SNAP_OPTION_TYPE_START: case GF_SNAP_OPTION_TYPE_STOP: case GF_SNAP_OPTION_TYPE_STATUS: case GF_SNAP_OPTION_TYPE_CONFIG: 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) { GF_FREE (host_uuid); if (err_str[0] == '\0') snprintf (err_str, sizeof (err_str), "Operation failed"); ret = glusterd_op_send_cli_response (cli_op, ret, 0, req, dict, err_str); } return ret; } int glusterd_handle_snapshot (rpcsvc_request_t *req) { return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); }