From d15ad38e8623f510fb1e121a8ff0d845a99238e4 Mon Sep 17 00:00:00 2001 From: Rajesh Joseph Date: Tue, 15 Oct 2013 17:10:52 +0530 Subject: mgmt/glusterd: Snapshot list support Handles snapshot list command issued by cli. Details of all the snapshots will be sent back to the caller in required format. Change-Id: I01e512290548007c06e90b40a59cdde048fab954 Signed-off-by: Rajesh Joseph --- rpc/xdr/src/cli1-xdr.c | 11 + rpc/xdr/src/cli1-xdr.h | 9 + rpc/xdr/src/cli1-xdr.x | 7 + xlators/mgmt/glusterd/src/glusterd-snapshot.c | 1023 ++++++++++++++++++++++++- xlators/mgmt/glusterd/src/glusterd.h | 1 + 5 files changed, 1032 insertions(+), 19 deletions(-) diff --git a/rpc/xdr/src/cli1-xdr.c b/rpc/xdr/src/cli1-xdr.c index 7d85b43c1..f0f08d334 100644 --- a/rpc/xdr/src/cli1-xdr.c +++ b/rpc/xdr/src/cli1-xdr.c @@ -178,6 +178,17 @@ xdr_gf_cli_status_type (XDR *xdrs, gf_cli_status_type *objp) return TRUE; } +bool_t +xdr_gf1_cli_snapshot (XDR *xdrs, gf1_cli_snapshot *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + bool_t xdr_gf_cli_req (XDR *xdrs, gf_cli_req *objp) { diff --git a/rpc/xdr/src/cli1-xdr.h b/rpc/xdr/src/cli1-xdr.h index d418fabf3..bdc76b638 100644 --- a/rpc/xdr/src/cli1-xdr.h +++ b/rpc/xdr/src/cli1-xdr.h @@ -169,6 +169,13 @@ enum gf_cli_status_type { }; typedef enum gf_cli_status_type gf_cli_status_type; +enum gf1_cli_snapshot { + GF_SNAP_OPTION_TYPE_NONE = 0, + GF_SNAP_OPTION_TYPE_CREATE = 1, + GF_SNAP_OPTION_TYPE_LIST = 2, +}; +typedef enum gf1_cli_snapshot gf1_cli_snapshot; + struct gf_cli_req { struct { u_int dict_len; @@ -280,6 +287,7 @@ extern bool_t xdr_gf1_cli_gsync_set (XDR *, gf1_cli_gsync_set*); extern bool_t xdr_gf1_cli_stats_op (XDR *, gf1_cli_stats_op*); extern bool_t xdr_gf1_cli_top_op (XDR *, gf1_cli_top_op*); extern bool_t xdr_gf_cli_status_type (XDR *, gf_cli_status_type*); +extern bool_t xdr_gf1_cli_snapshot (XDR *, gf1_cli_snapshot*); extern bool_t xdr_gf_cli_req (XDR *, gf_cli_req*); extern bool_t xdr_gf_cli_rsp (XDR *, gf_cli_rsp*); extern bool_t xdr_gf1_cli_peer_list_req (XDR *, gf1_cli_peer_list_req*); @@ -308,6 +316,7 @@ extern bool_t xdr_gf1_cli_gsync_set (); extern bool_t xdr_gf1_cli_stats_op (); extern bool_t xdr_gf1_cli_top_op (); extern bool_t xdr_gf_cli_status_type (); +extern bool_t xdr_gf1_cli_snapshot (); extern bool_t xdr_gf_cli_req (); extern bool_t xdr_gf_cli_rsp (); extern bool_t xdr_gf1_cli_peer_list_req (); diff --git a/rpc/xdr/src/cli1-xdr.x b/rpc/xdr/src/cli1-xdr.x index cc7ca8e24..b8dff5694 100644 --- a/rpc/xdr/src/cli1-xdr.x +++ b/rpc/xdr/src/cli1-xdr.x @@ -122,6 +122,13 @@ enum gf_cli_status_type { GF_CLI_STATUS_SHD = 0x1000 /*1000000000000*/ }; +/* Identifiers for snapshot clis */ +enum gf1_cli_snapshot { + GF_SNAP_OPTION_TYPE_NONE, + GF_SNAP_OPTION_TYPE_CREATE, + GF_SNAP_OPTION_TYPE_LIST +}; + struct gf_cli_req { opaque dict<>; } ; diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot.c b/xlators/mgmt/glusterd/src/glusterd-snapshot.c index 1c0ea0859..1c1e28a86 100644 --- a/xlators/mgmt/glusterd/src/glusterd-snapshot.c +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2012 Red Hat, Inc. + 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 @@ -338,6 +338,1002 @@ 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) { @@ -407,6 +1403,13 @@ glusterd_handle_snapshot_fn (rpcsvc_request_t *req) 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: @@ -427,21 +1430,3 @@ glusterd_handle_snapshot (rpcsvc_request_t *req) { return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); } - -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; -} diff --git a/xlators/mgmt/glusterd/src/glusterd.h b/xlators/mgmt/glusterd/src/glusterd.h index 724618b0e..d51f8d2ce 100644 --- a/xlators/mgmt/glusterd/src/glusterd.h +++ b/xlators/mgmt/glusterd/src/glusterd.h @@ -335,6 +335,7 @@ struct glusterd_snap_ { struct list_head snap_list; char snap_name[256]; uuid_t snap_id; + char cg_name[256]; uuid_t cg_id; char *description; time_t time_stamp; -- cgit