diff options
Diffstat (limited to 'xlators/mgmt/glusterd/src/glusterd-volgen.c')
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-volgen.c | 514 |
1 files changed, 507 insertions, 7 deletions
diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c index 1bc0db6ce..3b8e7648c 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.c +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c @@ -28,6 +28,8 @@ #include "logging.h" #include "dict.h" #include "graph-utils.h" +#include "glusterd-store.h" +#include "glusterd-hooks.h" #include "trie.h" #include "glusterd-mem-types.h" #include "cli1-xdr.h" @@ -3175,7 +3177,8 @@ glusterd_generate_brick_volfile (glusterd_volinfo_t *volinfo, static int glusterd_generate_snap_brick_volfile (glusterd_volinfo_t *volinfo, glusterd_brickinfo_t *brickinfo, - glusterd_volinfo_t *snap_volinfo) + glusterd_volinfo_t *snap_volinfo, + gf_boolean_t vol_restore) { volgen_graph_t graph = {0,}; char filename[PATH_MAX] = {0,}; @@ -3189,7 +3192,16 @@ glusterd_generate_snap_brick_volfile (glusterd_volinfo_t *volinfo, GF_ASSERT (brickinfo); GLUSTERD_REMOVE_SLASH_FROM_PATH (brickinfo->path, brick); - GLUSTERD_GET_SNAP_DIR (path, volinfo, snap_volinfo->volname, priv); + /* If the brick volfile is generated as part of a restore + * operation then we should be generating the brick volfile + * in the origin volume folder instead of snap volume folder. + */ + if (vol_restore) { + GLUSTERD_GET_VOLUME_DIR (path, volinfo, priv); + } else { + GLUSTERD_GET_SNAP_DIR (path, volinfo, + snap_volinfo->volname, priv); + } snprintf (filename, PATH_MAX, "%s/%s.%s.%s.vol", path, snap_volinfo->volname, @@ -3285,8 +3297,10 @@ generate_snap_brick_volfiles (glusterd_volinfo_t *volinfo, "Found a brick - %s:%s", brickinfo->hostname, brickinfo->path); - ret = glusterd_generate_snap_brick_volfile (volinfo, brickinfo, - snap_volinfo); + ret = glusterd_generate_snap_brick_volfile (volinfo, + brickinfo, + snap_volinfo, + _gf_false); if (ret) goto out; } @@ -3382,7 +3396,8 @@ out: int generate_snap_client_volfiles (glusterd_volinfo_t *actual_volinfo, glusterd_volinfo_t *snap_volinfo, - glusterd_client_type_t client_type) + glusterd_client_type_t client_type, + gf_boolean_t vol_restore) { char filepath[PATH_MAX] = {0,}; int ret = -1; @@ -3416,6 +3431,18 @@ generate_snap_client_volfiles (glusterd_volinfo_t *actual_volinfo, dict = dict_new (); if (!dict) goto out; + + /* If the brick volfile is generated as part of a restore + * operation then we should be generating the brick volfile + * in the origin volume folder instead of snap volume folder. + */ + if (vol_restore) { + GLUSTERD_GET_VOLUME_DIR (path, actual_volinfo, conf); + } else { + GLUSTERD_GET_SNAP_DIR (path, actual_volinfo, + snap_volinfo->volname, conf); + } + for (i = 0; types[i]; i++) { memset (filepath, 0, sizeof (filepath)); ret = dict_set_str (dict, "client-transport-type", types[i]); @@ -3427,8 +3454,6 @@ generate_snap_client_volfiles (glusterd_volinfo_t *actual_volinfo, if (ret) goto out; - GLUSTERD_GET_SNAP_DIR (path, actual_volinfo, - snap_volinfo->volname,conf); if (client_type == GF_CLIENT_TRUSTED) { if ((actual_volinfo->transport_type == GF_TRANSPORT_BOTH_TCP_RDMA) && @@ -4115,3 +4140,478 @@ gd_is_boolean_option (char *key) return _gf_false; } + +/* This is a utility function which will create backup of + * origin volume file and then replace the origin volume + * file with a sym-link to snap volume file. + * + * @param orig_vol volinfo of origin volume + * @param orig_file file belonging to origin volume + * @param snap_file corresponding file in snap volume + * + * @return 0 on success and negative value on failure. + */ +static int +gd_backup_and_restore_snap_files (glusterd_volinfo_t *orig_vol, + const char *orig_file, + const char *snap_file) +{ + int ret = -1; + char tmppath[PATH_MAX] = {0,}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (orig_vol); + GF_ASSERT (orig_file); + GF_ASSERT (snap_file); + + /* If the volume is already a restored volume then we + * need not create a backup of files belonging to origin + * volume. + * We can simply delete the sym-link created by previous + * restore. + */ + if (orig_vol->is_volume_restored) { + ret = remove (orig_file); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "remove %s file", orig_file); + goto out; + } + } else { + snprintf (tmppath, sizeof (tmppath), "%s.origin", + orig_file); + /* TODO: These temp files need to be deleted once the restored + * operation is successful + */ + + /* Create backup of origin volume file */ + ret = rename (orig_file, tmppath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to rename " + "file from %s to %s", orig_file, tmppath); + goto out; + } + } + + /* Create sym-link to snap file */ + ret = symlink (snap_file, orig_file); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to create sym-link " + "to %s file", snap_file); + goto out; + } + + ret = 0; +out: + + return ret; +} + +/* This is a utility function which will create a backup copy + * of origin volume. + * + * @param origvol volinfo of origin volume + * @param fileformat This string provides formatting information + * for the file to be backed up. The format + * string should be of <string>%s<string> format. + * Where <string> can be any string and this + * function will replace %s with origin volume + * name. e.g. "trusted-%s-fuse.vol". + * @param volpath volume path + * + * @return 0 on success and negative value on failure. + */ +static inline int +gd_format_path_and_backup_snap_files (glusterd_volinfo_t *origvol, + const char *fileformat, + const char *volpath) +{ + int ret = -1; + char origfile[PATH_MAX] = {0,}; + char formatstr[PATH_MAX] = {0,}; + char tmppath[PATH_MAX] = {0,}; + + snprintf (formatstr, sizeof (formatstr), "%s/%s", volpath, fileformat); + snprintf (origfile, sizeof (origfile), formatstr, origvol->volname); + + /* If the volume is already a restored volume then we + * need not create a backup of files belonging to origin + * volume. + * We can simply delete the sym-link created by previous + * restore. + */ + if (origvol->is_volume_restored) { + ret = remove (origfile); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to " + "remove %s file", origfile); + } + goto out; + } + + snprintf (tmppath, sizeof (tmppath), "%s.origin", origfile); + + /* Create a backup of this file */ + ret = rename (origfile, tmppath); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "failed to restore %s file", + origfile); + } + +out: + return ret; +} + +/* This is a utility function which will backup the origin volume + * brick folder and then create sym link to the snap_vol brick + * folder. + * + * @param orig_vol volinfo of origin volume + * @param snap_vol volinfo of snap volume + * + * @return 0 on success and negative value on failure. + */ +static int +gd_restore_snap_brick_vol_files (glusterd_volinfo_t *orig_vol, + glusterd_volinfo_t *snap_vol) +{ + int ret = -1; + char path[PATH_MAX] = {0,}; + char brick[PATH_MAX] = {0,}; + char oldfilename[PATH_MAX] = {0,}; + char newfilename[PATH_MAX] = {0,}; + glusterd_brickinfo_t *brickinfo = NULL; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + + GLUSTERD_GET_VOLUME_DIR (path, orig_vol, conf); + + /* brick count of origin volume and snapshot volume might be + * different. Therefore we need to run two different loop to + * restore snap brick vol files. + */ + list_for_each_entry (brickinfo, &orig_vol->bricks, brick_list) { + GLUSTERD_REMOVE_SLASH_FROM_PATH (brickinfo->path, brick); + + snprintf (oldfilename, sizeof (oldfilename), "%s/%s.%s.%s.vol", + path, orig_vol->volname, brickinfo->hostname, + brick); + + /* If the volume is already a restored volume then we + * need not create a backup of files belonging to origin + * volume. + * We can simply delete the sym-link created by previous + * restore. + */ + if (orig_vol->is_volume_restored) { + ret = remove (oldfilename); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "remove %s file", oldfilename); + goto out; + } + continue; + } + + snprintf (newfilename, sizeof (newfilename), "%s.origin", + oldfilename); + + /* Create a backup of origin volume file */ + ret = rename (oldfilename, newfilename); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to rename " + "file from %s to %s", oldfilename, + newfilename); + goto out; + } + } + + /* Recreate brick files for origin volume */ + list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { + ret = glusterd_generate_snap_brick_volfile (orig_vol, + brickinfo, + snap_vol, + _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to generate " + "brick volfile"); + goto out; + } + } + + ret = 0; +out: + return ret; +} + + +/* This function will update the in-memory representation + * of the origin volume with the snap volume details. + * + * @param orig_vol origin volume volinfo + * @param snap_vol snap volume volinfo + * + * @return 0 on success and negative value on failure. + */ +static int +gd_update_origin_volume (glusterd_volinfo_t *orig_vol, + glusterd_volinfo_t *snap_vol) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + GF_ASSERT (orig_vol); + GF_ASSERT (snap_vol); + + /* Update the in-memory volinfo from the store */ + ret = glusterd_store_update_volinfo (orig_vol, NULL); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to update " + "volinfo for %s volume", orig_vol->volname); + goto out; + } + + /* Reset the port number to 0 */ + orig_vol->port = 0; + /* Reset the volume status to stopped */ + glusterd_set_volume_status (orig_vol, GLUSTERD_STATUS_STOPPED); + + /* Stop the snap volume */ + list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { + ret = glusterd_snap_brick_stop (orig_vol, snap_vol, + brickinfo, _gf_false); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to stop " + " %s brick", brickinfo->path); + goto out; + } + } + + /* Set the volume status to stopped for the snap volume */ + glusterd_set_volume_status (snap_vol, GLUSTERD_STATUS_STOPPED); + + /* The in-memory brick list for the origin volume should be + * replaced with the brick list of snap volume. Therefore + * first delete all the bricks from the origin volume and + * then read the brick list from the store which is already + * updated + */ + + /* Delete previous brick infos */ + ret = glusterd_volume_brickinfos_delete (orig_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to delete " + "brickinfos for %s volume", orig_vol->volname); + goto out; + } + + /* Retrieve brick list */ + ret = glusterd_store_retrieve_bricks (orig_vol, NULL); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to retrieve " + "bricks info from store for %s volume", + orig_vol->volname); + goto out; + } + +out: + return ret; +} + +/* This function will restore origin volume to it it's snap. + * The restore operation will simply replace the Gluster origin + * volume with the snap volume. + * + * @param orig_vol volinfo of origin volume + * @param snap_vol volinfo of snapshot volume + * + * @return 0 on success and negative value on error + */ +int +gd_restore_snap_volume (glusterd_volinfo_t *orig_vol, + glusterd_volinfo_t *snap_vol) +{ + int ret = -1; + unsigned int i = 0; + char snappath[PATH_MAX] = {0,}; + char volpath[PATH_MAX] = {0,}; + char oldfilename[PATH_MAX] = {0,}; + char newfilename[PATH_MAX] = {0,}; + char *types[] = {NULL, NULL, NULL}; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + gf_transport_type type = GF_TRANSPORT_TCP; + char tmpname[GLUSTERD_MAX_VOLUME_NAME] = {0,}; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + + GF_VALIDATE_OR_GOTO (this->name, orig_vol, out); + GF_VALIDATE_OR_GOTO (this->name, snap_vol, out); + + GLUSTERD_GET_SNAP_DIR (snappath, orig_vol, snap_vol->volname, conf); + GLUSTERD_GET_VOLUME_DIR (volpath, orig_vol, conf); + + /* As mentioned earlier the snapshot restore is done by replacing + * the origin volume with the snapshot volume. To do so we have to + * replace all backend files belonging to origin volume with the + * snapshot volume. And once all the backend files are updated + * update the in-memory structure of the origin volume. Also delete + * the snapshot volume. + * TODO: deleting of snapshot volume can be controlled by user + * controllable option. + */ + + /* Backup and restore info file */ + ret = gd_backup_and_restore_snap_files (orig_vol, + orig_vol->shandle->path, + snap_vol->shandle->path); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to restore info " + "file for %s volume", orig_vol->volname); + goto out; + } + + GLUSTERD_GET_BRICK_DIR (oldfilename, orig_vol, conf); + GLUSTERD_GET_SNAP_BRICK_DIR (newfilename, orig_vol, + snap_vol->volname, conf); + + /* Backup and restore bricks folder and it's contents */ + ret = gd_backup_and_restore_snap_files (orig_vol, oldfilename, + newfilename); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to restore bricks " + "folder for %s volume", orig_vol->volname); + goto out; + } + + enumerate_transport_reqs (orig_vol->transport_type, types); + + + for (i = 0; NULL != types[i]; i++) { + type = transport_str_to_type (types[i]); + + if ((orig_vol->transport_type == GF_TRANSPORT_BOTH_TCP_RDMA) && + type == GF_TRANSPORT_RDMA) { + /* Backup trusted rdma-fuse.vol file */ + ret = gd_format_path_and_backup_snap_files (orig_vol, + "trusted-%s.rdma-fuse.vol", + volpath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + " backup trusted*.rdma-fuse.vol file"); + goto out; + } + + /* Backup rdma-fuse.vol file */ + ret = gd_format_path_and_backup_snap_files (orig_vol, + "%s.rdma-fuse.vol", volpath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + " backup rdma-fuse.vol file"); + goto out; + } + } else { + /* Backup trusted fuse.vol file */ + ret = gd_format_path_and_backup_snap_files (orig_vol, + "trusted-%s-fuse.vol", volpath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + " backup trusted*-fuse.vol file"); + goto out; + } + + /* Backup fuse.vol file */ + ret = gd_format_path_and_backup_snap_files (orig_vol, + "%s-fuse.vol", volpath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + " backup fuse.vol file"); + goto out; + } + } + } + + /* The restored client and brick vol files should have all + * the entries present in snap vol files but the volume name + * for all these entries should still be the origin volume + * name. + * As of now these volfile generation takes volume name + * directly from the volinfo. Therefore instead of changing + * all the generate function simply replace the snap volume + * name with the origin volume name. And once the vol files + * are generated restore the volume name. + */ + strncpy (tmpname, snap_vol->volname, sizeof(tmpname)); + strncpy (snap_vol->volname, orig_vol->volname, + sizeof(snap_vol->volname)); + + /* Generate trusted client vol file */ + ret = generate_snap_client_volfiles (orig_vol, snap_vol, + GF_CLIENT_TRUSTED, _gf_true); + if (ret) { + /* Restore the snap volume name */ + strncpy (snap_vol->volname, tmpname, sizeof(snap_vol->volname)); + gf_log (this->name, GF_LOG_ERROR, "Failed to generated " + " trusted client vol file for %s volume", + orig_vol->volname); + goto out; + } + + ret = generate_snap_client_volfiles (orig_vol, snap_vol, + GF_CLIENT_OTHER, _gf_true); + if (ret) { + /* Restore the snap volume name */ + strncpy (snap_vol->volname, tmpname, sizeof(snap_vol->volname)); + gf_log (this->name, GF_LOG_ERROR, "Failed to generated " + " client vol file for %s volume", orig_vol->volname); + goto out; + } + + ret = gd_restore_snap_brick_vol_files (orig_vol, snap_vol); + if (ret) { + /* Restore the snap volume name */ + strncpy (snap_vol->volname, tmpname, sizeof(snap_vol->volname)); + gf_log (this->name, GF_LOG_ERROR, "Failed to generated " + " brick vol files for %s volume", orig_vol->volname); + goto out; + } + + /* Restore the snap volume name */ + strncpy (snap_vol->volname, tmpname, sizeof(snap_vol->volname)); + + /* Update the in-memory structure of origin volume */ + ret = gd_update_origin_volume (orig_vol, snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to update " + "origin volume %s", orig_vol->volname); + goto out; + } + + /* Update status to restored */ + orig_vol->is_volume_restored = _gf_true; + + ret = glusterd_store_volinfo (orig_vol, GLUSTERD_VOLINFO_VER_AC_NONE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to store volume " + "info of %s volume", orig_vol->volname); + goto out; + } +out: + return ret; +} |