/* 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 "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 "syscall.h" #include "cli1-xdr.h" #include "xdr-generic.h" 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 (&snap->snap_list, &last->snap_list); 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; } 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 *priv = NULL; char *host_uuid = NULL; char err_str[2048] = {0,}; xlator_t *this = NULL; GF_ASSERT (req); this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); ret = xdr_to_generic (req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req); if (ret < 0) { req->rpc_err = GARBAGE_ARGS; goto out; } if (cli_req.dict.dict_len) { 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; } else { dict->extra_stdfree = cli_req.dict.dict_val; } host_uuid = gf_strdup (uuid_utoa(MY_UUID)); if (host_uuid == NULL) { snprintf (err_str, sizeof (err_str), "Failed to get " "the uuid of local glusterd"); ret = -1; goto out; } ret = dict_set_dynstr (dict, "host-uuid", host_uuid); if (ret) goto out; } ret = dict_get_int32 (dict, "type", &type); if (ret < 0) { snprintf (err_str, sizeof (err_str), "Command type not found"); gf_log (this->name, GF_LOG_ERROR, "%s", err_str); goto out; } switch (type) { case GF_SNAP_OPTION_TYPE_CREATE: ret = glusterd_mgmt_v3_initiate_all_phases (req, cli_op, dict); break; case GF_SNAP_OPTION_TYPE_LIST: ret = glusterd_handle_snapshot_list (req, cli_op, dict); break; default: gf_log (this->name, GF_LOG_ERROR, "Unkown snapshot request " "type (%d)", type); ret = -1; /* Failure */ } out: if (ret) { 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); }