diff options
| -rw-r--r-- | Makefile.am | 2 | ||||
| -rw-r--r-- | cli/src/cli-cmd-volume.c | 31 | ||||
| -rw-r--r-- | configure.ac | 2 | ||||
| -rw-r--r-- | glusterfs.spec.in | 1 | ||||
| -rw-r--r-- | heal/Makefile.am | 3 | ||||
| -rw-r--r-- | heal/src/Makefile.am | 28 | ||||
| -rw-r--r-- | heal/src/glfs-heal.c | 488 | 
7 files changed, 550 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am index 598ebb4103c..19f21fca8a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = autogen.sh \  	$(shell find $(top_srcdir)/tests -type f -print)  SUBDIRS = argp-standalone libglusterfs rpc api xlators glusterfsd \ -	$(FUSERMOUNT_SUBDIR) doc extras cli @SYNCDAEMON_SUBDIR@ +	$(FUSERMOUNT_SUBDIR) doc extras cli heal @SYNCDAEMON_SUBDIR@  pkgconfigdir = @pkgconfigdir@  pkgconfig_DATA = glusterfs-api.pc libgfchangelog.pc diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index 22bf66b4fb5..80b559dc69f 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -2033,6 +2033,10 @@ cli_cmd_volume_heal_cbk (struct cli_state *state, struct cli_cmd_word *word,          dict_t                  *options = NULL;          xlator_t                *this = NULL;          cli_local_t             *local = NULL; +        int                     heal_op = 0; +        runner_t                runner = {0}; +        char                    buff[PATH_MAX] = {0}; +        char                    *out = NULL;          this = THIS;          frame = create_frame (this, this->ctx->pool); @@ -2051,13 +2055,32 @@ cli_cmd_volume_heal_cbk (struct cli_state *state, struct cli_cmd_word *word,                  parse_error = 1;                  goto out;          } +        ret = dict_get_int32 (options, "heal-op", &heal_op); +        if (ret < 0) +                goto out; -        proc = &cli_rpc_prog->proctable[GLUSTER_CLI_HEAL_VOLUME]; +        if (heal_op == GF_AFR_OP_INDEX_SUMMARY) { +                runinit (&runner); +                runner_add_args (&runner, SBIN_DIR"/glfsheal", words[2], NULL); +                runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); +                ret = runner_start (&runner); +                if (ret == -1) +                        goto out; +                while ((out = fgets(buff, sizeof(buff), +                                   runner_chio (&runner, STDOUT_FILENO)))) { +                        printf ("%s", out); +                } -        CLI_LOCAL_INIT (local, words, frame, options); +                ret = runner_end (&runner); +                ret = WEXITSTATUS (ret); +        } else { +                proc = &cli_rpc_prog->proctable[GLUSTER_CLI_HEAL_VOLUME]; -        if (proc->fn) { -                ret = proc->fn (frame, THIS, options); +                CLI_LOCAL_INIT (local, words, frame, options); + +                if (proc->fn) { +                        ret = proc->fn (frame, THIS, options); +                }          }  out: diff --git a/configure.ac b/configure.ac index f1a9e6da267..37998e60d77 100644 --- a/configure.ac +++ b/configure.ac @@ -179,6 +179,8 @@ AC_CONFIG_FILES([Makefile  		geo-replication/Makefile  		geo-replication/src/Makefile  		geo-replication/syncdaemon/Makefile +                heal/Makefile +                heal/src/Makefile                  glusterfs.spec])  AC_CANONICAL_HOST diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 65e39b677e6..6b65957fb6a 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -789,6 +789,7 @@ fi  %endif  # binaries  %{_sbindir}/glusterd +%{_sbindir}/glfsheal  %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/storage*  %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/posix*  %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/protocol/server* diff --git a/heal/Makefile.am b/heal/Makefile.am new file mode 100644 index 00000000000..a985f42a877 --- /dev/null +++ b/heal/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src + +CLEANFILES = diff --git a/heal/src/Makefile.am b/heal/src/Makefile.am new file mode 100644 index 00000000000..2ea41b969f6 --- /dev/null +++ b/heal/src/Makefile.am @@ -0,0 +1,28 @@ +sbin_PROGRAMS = glfsheal + +glfsheal_SOURCES = glfs-heal.c + +glfsheal_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\ +		$(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ +		$(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ +		$(top_builddir)/api/src/libgfapi.la\ +		$(GF_GLUSTERFS_LIBS) $(XML_LIBS) + +glfsheal_LDFLAGS = $(GF_LDFLAGS) + +AM_CPPFLAGS = $(GF_CPPFLAGS) \ +	-I$(top_srcdir)/libglusterfs/src -I$(top_srcdir)/rpc/rpc-lib/src\ +	-I$(top_srcdir)/rpc/xdr/src\ +	-I$(top_srcdir)/api/src\ +	-DDATADIR=\"$(localstatedir)\" \ +	-DCONFDIR=\"$(sysconfdir)/glusterfs\" \ +	-DGSYNCD_PREFIX=\"$(libexecdir)/glusterfs\"\ +	-DSYNCDAEMON_COMPILE=$(SYNCDAEMON_COMPILE) -DSBIN_DIR=\"$(sbindir)\"\ +	$(XML_CPPFLAGS) + +AM_CFLAGS = -Wall $(GF_GLUSTERFS_CFLAGS) + +CLEANFILES = + +$(top_builddir)/libglusterfs/src/libglusterfs.la: +	$(MAKE) -C $(top_builddir)/libglusterfs/src/ all diff --git a/heal/src/glfs-heal.c b/heal/src/glfs-heal.c new file mode 100644 index 00000000000..9e2be12ab4c --- /dev/null +++ b/heal/src/glfs-heal.c @@ -0,0 +1,488 @@ +/* + Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> + 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. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include "glfs.h" +#include "glfs-handles.h" +#include "glfs-internal.h" +#include "syncop.h" +#include <string.h> +#include <time.h> + +int +glfsh_link_inode_update_loc (loc_t *loc, struct iatt *iattr) +{ +        inode_t       *link_inode = NULL; +        int           ret = -1; + +        link_inode = inode_link (loc->inode, NULL, NULL, iattr); +        if (link_inode == NULL) +                goto out; + +        inode_unref (loc->inode); +        loc->inode = link_inode; +        ret = 0; +out: +        return ret; +} + +int +glfsh_get_index_dir_loc (loc_t *rootloc, xlator_t *xl, loc_t *dirloc, +                         int32_t *op_errno) +{ +        void      *index_gfid = NULL; +        int       ret = 0; +        dict_t    *xattr = NULL; +        struct iatt   iattr = {0}; +        struct iatt   parent = {0}; + +        ret = syncop_getxattr (xl, rootloc, &xattr, GF_XATTROP_INDEX_GFID); +        if (ret < 0) { +                *op_errno = errno; +                goto out; +        } + +        ret = dict_get_ptr (xattr, GF_XATTROP_INDEX_GFID, &index_gfid); +        if (ret < 0) { +                *op_errno = EINVAL; +                goto out; +        } + +        uuid_copy (dirloc->gfid, index_gfid); +        dirloc->path = ""; +        dirloc->inode = inode_new (rootloc->inode->table); +        ret = syncop_lookup (xl, dirloc, NULL, +                             &iattr, NULL, &parent); +        dirloc->path = NULL; +        if (ret < 0) { +                *op_errno = errno; +                goto out; +        } +        ret = glfsh_link_inode_update_loc (dirloc, &iattr); +        if (ret) +                goto out; +        glfs_loc_touchup (dirloc); + +        return 0; +out: +        if (xattr) +                dict_unref (xattr); +        return ret; +} + +static int +_set_self_heal_vxattrs (dict_t *xattr_req) +{ +        int     ret = 0; + +        ret = dict_set_int32 (xattr_req, "dry-run-self-heal", 1); +        if (ret < 0) +                goto out; + +        ret = dict_set_int32 (xattr_req, "attempt-self-heal", 1); +        if (ret < 0) +                goto out; + +        ret = dict_set_int32 (xattr_req, "foreground-self-heal", 1); +        if (ret < 0) +                goto out; +out: +        return ret; +} + +static gf_boolean_t +_is_self_heal_pending (dict_t *xattr_rsp) +{ +        int     ret = 0; +        int     sh_pending = 0; + +        ret = dict_get_int32 (xattr_rsp, "metadata-self-heal-pending", +                              &sh_pending); +        if ((ret == 0) && sh_pending) { +                return _gf_true; +        } + +        ret = dict_get_int32 (xattr_rsp, "entry-self-heal-pending", +                              &sh_pending); +        if ((ret == 0) && sh_pending) { +                return _gf_true; +        } + +        ret = dict_get_int32 (xattr_rsp, "data-self-heal-pending", +                              &sh_pending); +        if ((ret == 0) && sh_pending) { +                return _gf_true; +        } +        return _gf_false; +} + +#define RESET_ENTRIES(loc, shf, ope, rsp, grsp) \ +        do {                                    \ +                loc_wipe (&loc);                \ +                shf = 0;                        \ +                ope = 0;                        \ +                if (rsp) {                      \ +                        dict_unref (rsp);       \ +                        rsp = NULL;             \ +                }                               \ +                if (grsp) {                     \ +                        dict_unref (grsp);      \ +                        grsp = NULL;            \ +                }                               \ +        } while (0) + +static int +glfsh_build_index_loc (loc_t *loc, char *name, loc_t *parent) +{ +        int             ret = 0; + +        uuid_copy (loc->pargfid, parent->inode->gfid); +        loc->path = ""; +        loc->name = name; +        loc->parent = inode_ref (parent->inode); +        if (!loc->parent) { +                loc->path = NULL; +                loc_wipe (loc); +                ret = -1; +        } +        return ret; +} + +void +glfsh_remove_stale_index (xlator_t *xl, loc_t *parent, char *fname) +{ +        int              ret = 0; +        loc_t            index_loc = {0}; + +        ret = glfsh_build_index_loc (&index_loc, fname, parent); +        if (ret) +                goto out; +        ret = syncop_unlink (xl, &index_loc); +        index_loc.path = NULL; +        loc_wipe (&index_loc); +out: +        return; +} +static int +glfsh_process_entries (xlator_t *xl, loc_t *parentloc, gf_dirent_t *entries, +                       off_t *offset, uint64_t *num_entries) +{ +        gf_dirent_t      *entry = NULL; +        gf_dirent_t      *tmp = NULL; +        int              ret = 0; +        loc_t            entry_loc = {0}; +        struct iatt      iattr = {0}; +        struct iatt      parent = {0}; +        dict_t          *xattr_req = NULL; +        dict_t          *xattr_rsp = NULL; +        dict_t          *getxattr_rsp = NULL; +        int32_t         sh_failed = 0; +        int32_t         sh_pending = 0; +        char            *path = NULL; +        int32_t         op_errno = 0; + +        xattr_req = dict_new (); +        if (!xattr_req) +                goto out; + +        list_for_each_entry_safe (entry, tmp, &entries->list, list) { +                *offset = entry->d_off; +                if ((strcmp (entry->d_name, ".") == 0) || +                    (strcmp (entry->d_name, "..") == 0)) +                        continue; + +                RESET_ENTRIES (entry_loc, sh_failed, op_errno, xattr_rsp, +                               getxattr_rsp); + +                ret = _set_self_heal_vxattrs (xattr_req); +                if (ret < 0) +                        goto out; + +                entry_loc.inode = inode_new (parentloc->inode->table); +                if (!entry_loc.inode) +                        goto out; + +                uuid_parse (entry->d_name, entry_loc.gfid); +                //TODO: put gfid-path +                glfs_loc_touchup (&entry_loc); +                ret = syncop_lookup (xl->parents->xlator, &entry_loc, xattr_req, +                                     &iattr, &xattr_rsp, &parent); +                if (ret < 0) +                        op_errno = errno; + +                if (op_errno == ENOENT || op_errno == ESTALE) { +                        glfsh_remove_stale_index (xl, parentloc, entry->d_name); +                        continue; +                } + +                ret = dict_get_int32 (xattr_rsp, "sh-failed", &sh_failed); + +                sh_pending = _is_self_heal_pending (xattr_rsp); +                //File/dir is undergoing I/O +                if (!op_errno && !sh_failed && !sh_pending) +                        continue; + +                ret = syncop_getxattr (xl, &entry_loc, &getxattr_rsp, +                                       GFID_TO_PATH_KEY); + +                ret = dict_get_str (getxattr_rsp, GFID_TO_PATH_KEY, &path); + + +                (*num_entries)++; +                printf ("%s\n", path ? path : uuid_utoa (entry_loc.gfid)); +        } +        ret = 0; +out: +        RESET_ENTRIES (entry_loc, sh_failed, op_errno, xattr_rsp, getxattr_rsp); +        return ret; +} + +static int +glfsh_crawl_directory (xlator_t   *readdir_xl, fd_t *fd, loc_t *loc) +{ +        off_t           offset   = 0; +        gf_dirent_t     entries; +        int             ret = 0; +        gf_boolean_t    free_entries = _gf_false; +        uint64_t        num_entries = 0; + +        INIT_LIST_HEAD (&entries.list); + +        while (1) { +                ret = syncop_readdir (readdir_xl, fd, 131072, offset, &entries); +                if (ret <= 0) +                        break; +                ret = 0; +                free_entries = _gf_true; + +                if (list_empty (&entries.list)) +                        goto out; + +                ret = glfsh_process_entries (readdir_xl, loc, &entries, &offset, +                                             &num_entries); +                if (ret < 0) +                        goto out; + +                gf_dirent_free (&entries); +                free_entries = _gf_false; +        } +        ret = 0; +out: +        if (free_entries) +                gf_dirent_free (&entries); +        if (ret < 0) { +                printf ("Failed to complete gathering info. " +                         "Number of entries so far: %"PRIu64"\n", num_entries); +        } else { +                printf ("Number of entries: %"PRIu64"\n", num_entries); +        } +        return ret; +} + +static int +glfsh_print_brick (xlator_t *xl, loc_t *rootloc) +{ +        int     ret = 0; +        dict_t  *xattr = NULL; +        char    *pathinfo = NULL; +        char    *brick_start = NULL; +        char    *brick_end = NULL; + +        ret = syncop_getxattr (xl, rootloc, &xattr, GF_XATTR_PATHINFO_KEY); +        if (ret < 0) +                goto out; + +        ret = dict_get_str (xattr, GF_XATTR_PATHINFO_KEY, &pathinfo); +        if (ret < 0) +                goto out; + +        brick_start = strchr (pathinfo, ':') + 1; +        brick_end = pathinfo + strlen (pathinfo) - 1; +        *brick_end = 0; +        printf ("Brick %s\n", brick_start); + +out: +        if (xattr) +                dict_unref (xattr); +        return ret; +} + +void +glfsh_print_brick_from_xl (xlator_t *xl) +{ +        char    *remote_host = NULL; +        char    *remote_subvol = NULL; +        int     ret = 0; + +        ret = dict_get_str (xl->options, "remote-host", &remote_host); +        if (ret < 0) +                goto out; + +        ret = dict_get_str (xl->options, "remote-subvolume", &remote_subvol); +        if (ret < 0) +                goto out; +out: +        if (ret < 0) { +                printf ("Brick - Not able to get brick information\n"); +        } else { +                printf ("Brick %s:%s\n", remote_host, remote_subvol); +        } +} + +void +glfsh_print_pending_heals (xlator_t *xl, loc_t *rootloc) +{ +        int ret = 0; +        loc_t   dirloc = {0}; +        fd_t    *fd = NULL; +        int32_t op_errno = 0; + +        ret = glfsh_print_brick (xl, rootloc); +        if (ret < 0) { +                glfsh_print_brick_from_xl (xl); +                printf ("Status: %s\n", strerror (errno)); +                goto out; +        } + +        ret = glfsh_get_index_dir_loc (rootloc, xl, &dirloc, &op_errno); +        if (ret < 0) { +                if (op_errno == ESTALE || op_errno == ENOENT) +                        printf ("Number of entries: 0\n"); +                goto out; +        } + +        fd = fd_anonymous (dirloc.inode); +        ret = glfsh_crawl_directory (xl, fd, &dirloc); +        if (fd) +                fd_unref (fd); +        if (ret < 0) +                printf ("Failed to find entries with pending self-heal\n"); +out: +        loc_wipe (&dirloc); +        return; +} + +static int +glfsh_validate_replicate_volume (xlator_t *xl) +{ +        gf_boolean_t    valid_replicate = _gf_false; +        gf_boolean_t    has_client = _gf_false; +        while (xl->next) +                xl = xl->next; + +        while (xl) { +                if (strcmp (xl->type, "protocol/client") == 0) { +                        has_client = _gf_true; +                        if (strcmp (xl->parents->xlator->type, +                                    "cluster/replicate") != 0) { +                                valid_replicate = _gf_false; +                                goto out; +                        } +                } + +                xl = xl->prev; +        } +        valid_replicate = _gf_true; +out: +        if (has_client && valid_replicate) +                return 0; +        return -1; +} + +int +main (int argc, char **argv) +{ +        glfs_t    *fs = NULL; +        int        ret = 0; +        char      *volname = NULL; +        xlator_t  *top_subvol = NULL; +        xlator_t  *xl = NULL; +        loc_t     rootloc = {0}; + +        if (argc != 2) { +                printf ("Usage: %s <volname>\n", argv[0]); +                ret = -1; +                goto out; +        } +        volname = argv[1]; + +        fs = glfs_new (volname); +        if (!fs) { +                ret = -1; +                printf ("Not able to initialize volume '%s'\n", volname); +                goto out; +        } + +        ret = glfs_set_volfile_server (fs, "tcp", "localhost", 24007); + +        ret = glfs_init (fs); +        if (ret < 0) { +                ret = -1; +                if (errno == ENOENT) { +                        printf ("Volume %s does not exist\n", volname); +                } else { +                        printf ("%s: Not able to fetch volfile from " +                                 "glusterd\n", volname); +                } +                goto out; +        } + +        sleep (2); +        __glfs_entry_fs (fs); +        top_subvol = glfs_active_subvol (fs); +        if (!top_subvol) { +                ret = -1; +                if (errno == ENOTCONN) { +                        printf ("Volume %s is not started (Or) All the bricks " +                                 "are not running.\n", volname); +                } else { +                        printf ("%s: Not able to mount the volume, %s\n", +                                 volname, strerror (errno)); +                } +                goto out; +        } + +        ret = glfsh_validate_replicate_volume (top_subvol); +        if (ret < 0) { +                printf ("Volume %s is not of type replicate\n", volname); +                goto out; +        } +        rootloc.inode = inode_ref (top_subvol->itable->root); +        glfs_loc_touchup (&rootloc); + +        xl = top_subvol; +        while (xl->next) +                xl = xl->next; + +        while (xl) { +                if (strcmp (xl->type, "protocol/client") == 0) { +                        glfsh_print_pending_heals (xl, &rootloc); +                        printf("\n"); +                } + +                xl = xl->prev; +        } + +        loc_wipe (&rootloc); +        glfs_subvol_done (fs, top_subvol); +        glfs_fini (fs); + +        return 0; +out: +        if (fs && top_subvol) +                glfs_subvol_done (fs, top_subvol); +        loc_wipe (&rootloc); +        if (fs) +                glfs_fini (fs); + +        return ret; +}  | 
