diff options
Diffstat (limited to 'xlators/mgmt/glusterd/src/glusterd-peer-utils.c')
| -rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-peer-utils.c | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/xlators/mgmt/glusterd/src/glusterd-peer-utils.c b/xlators/mgmt/glusterd/src/glusterd-peer-utils.c new file mode 100644 index 00000000000..18d355cb186 --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-peer-utils.c @@ -0,0 +1,1058 @@ +/* + 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 "glusterd-peer-utils.h" +#include "glusterd-store.h" +#include "glusterd-server-quorum.h" +#include "glusterd-messages.h" +#include <glusterfs/common-utils.h> +#include "glusterd-utils.h" + +void +glusterd_peerinfo_destroy(struct rcu_head *head) +{ + int32_t ret = -1; + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_peer_hostname_t *hostname = NULL; + glusterd_peer_hostname_t *tmp = NULL; + + /* This works as rcu_head is the first member of gd_rcu_head */ + peerinfo = caa_container_of((gd_rcu_head *)head, glusterd_peerinfo_t, + rcu_head); + + /* Set THIS to the saved this. Needed by some functions below */ + THIS = peerinfo->rcu_head.this; + + CDS_INIT_LIST_HEAD(&peerinfo->uuid_list); + + ret = glusterd_store_delete_peerinfo(peerinfo); + if (ret) { + gf_msg("glusterd", GF_LOG_ERROR, errno, GD_MSG_PEERINFO_DELETE_FAIL, + "Deleting peer info failed"); + } + + GF_FREE(peerinfo->hostname); + peerinfo->hostname = NULL; + + cds_list_for_each_entry_safe(hostname, tmp, &peerinfo->hostnames, + hostname_list) + { + glusterd_peer_hostname_free(hostname); + } + + glusterd_sm_tr_log_delete(&peerinfo->sm_log); + pthread_mutex_unlock(&peerinfo->delete_lock); + pthread_mutex_destroy(&peerinfo->delete_lock); + GF_FREE(peerinfo); + + peerinfo = NULL; + + return; +} + +int32_t +glusterd_peerinfo_cleanup(glusterd_peerinfo_t *peerinfo) +{ + GF_ASSERT(peerinfo); + gf_boolean_t quorum_action = _gf_false; + glusterd_conf_t *priv = THIS->private; + + if (pthread_mutex_trylock(&peerinfo->delete_lock)) { + /* Someone else is already deleting the peer, so give up */ + return 0; + } + + if (peerinfo->quorum_contrib != QUORUM_NONE) + quorum_action = _gf_true; + if (peerinfo->rpc) { + peerinfo->rpc = glusterd_rpc_clnt_unref(priv, peerinfo->rpc); + peerinfo->rpc = NULL; + } + + cds_list_del_rcu(&peerinfo->uuid_list); + /* Saving THIS, as it is needed by the callback function */ + peerinfo->rcu_head.this = THIS; + call_rcu(&peerinfo->rcu_head.head, glusterd_peerinfo_destroy); + + if (quorum_action) + /* coverity[SLEEP] */ + glusterd_do_quorum_action(); + return 0; +} + +/* gd_peerinfo_find_from_hostname iterates over all the addresses saved for each + * peer and matches it to @hoststr. + * Returns the matched peer if found else returns NULL + */ +static glusterd_peerinfo_t * +gd_peerinfo_find_from_hostname(const char *hoststr) +{ + xlator_t *this = THIS; + glusterd_conf_t *priv = NULL; + glusterd_peerinfo_t *peer = NULL; + glusterd_peerinfo_t *found = NULL; + glusterd_peer_hostname_t *tmphost = NULL; + + GF_ASSERT(this != NULL); + priv = this->private; + GF_VALIDATE_OR_GOTO(this->name, (priv != NULL), out); + + GF_VALIDATE_OR_GOTO(this->name, (hoststr != NULL), out); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peer, &priv->peers, uuid_list) + { + cds_list_for_each_entry_rcu(tmphost, &peer->hostnames, hostname_list) + { + if (!strncasecmp(tmphost->hostname, hoststr, 1024)) { + gf_msg_debug(this->name, 0, "Friend %s found.. state: %d", + tmphost->hostname, peer->state.state); + found = peer; /* Probably needs to be + dereferenced*/ + goto unlock; + } + } + } +unlock: + RCU_READ_UNLOCK; +out: + return found; +} + +/* gd_peerinfo_find_from_addrinfo iterates over all the addresses saved for each + * peer, resolves them and compares them to @addr. + * + * + * NOTE: As getaddrinfo is a blocking call and is being performed multiple times + * in this function, it could lead to the calling thread to be blocked for + * significant amounts of time. + * + * Returns the matched peer if found else returns NULL + */ +static glusterd_peerinfo_t * +gd_peerinfo_find_from_addrinfo(const struct addrinfo *addr) +{ + xlator_t *this = THIS; + glusterd_conf_t *conf = NULL; + glusterd_peerinfo_t *peer = NULL; + glusterd_peerinfo_t *found = NULL; + glusterd_peer_hostname_t *address = NULL; + int ret = 0; + struct addrinfo *paddr = NULL; + struct addrinfo *tmp = NULL; + + GF_ASSERT(this != NULL); + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, (conf != NULL), out); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peer, &conf->peers, uuid_list) + { + cds_list_for_each_entry_rcu(address, &peer->hostnames, hostname_list) + { + /* TODO: Cache the resolved addrinfos to improve + * performance + */ + ret = getaddrinfo(address->hostname, NULL, NULL, &paddr); + if (ret) { + /* Don't fail if getaddrinfo fails, continue + * onto the next address + */ + gf_msg_trace(this->name, 0, "getaddrinfo for %s failed (%s)", + address->hostname, gai_strerror(ret)); + continue; + } + + for (tmp = paddr; tmp != NULL; tmp = tmp->ai_next) { + if (gf_compare_sockaddr(addr->ai_addr, tmp->ai_addr)) { + found = peer; /* (de)referenced? */ + break; + } + } + + freeaddrinfo(paddr); + if (found) + goto unlock; + } + } +unlock: + RCU_READ_UNLOCK; +out: + return found; +} + +/* glusterd_peerinfo_find_by_hostname searches for a peer which matches the + * hostname @hoststr and if found returns the pointer to peerinfo object. + * Returns NULL otherwise. + * + * It first attempts a quick search by string matching @hoststr. If that fails, + * it'll attempt a more thorough match by resolving the addresses and matching + * the resolved addrinfos. + */ +glusterd_peerinfo_t * +glusterd_peerinfo_find_by_hostname(const char *hoststr) +{ + int ret = -1; + struct addrinfo *addr = NULL; + struct addrinfo *p = NULL; + xlator_t *this = THIS; + glusterd_peerinfo_t *peerinfo = NULL; + + GF_ASSERT(hoststr); + + peerinfo = gd_peerinfo_find_from_hostname(hoststr); + if (peerinfo) + return peerinfo; + + ret = getaddrinfo(hoststr, NULL, NULL, &addr); + if (ret != 0) { + gf_msg(this->name, GF_LOG_ERROR, ret, GD_MSG_GETADDRINFO_FAIL, + "error in getaddrinfo: %s\n", gai_strerror(ret)); + goto out; + } + + for (p = addr; p != NULL; p = p->ai_next) { + peerinfo = gd_peerinfo_find_from_addrinfo(p); + if (peerinfo) { + freeaddrinfo(addr); + return peerinfo; + } + } + +out: + gf_msg_debug(this->name, 0, "Unable to find friend: %s", hoststr); + if (addr) + freeaddrinfo(addr); + return NULL; +} + +int +glusterd_hostname_to_uuid(char *hostname, uuid_t uuid) +{ + GF_ASSERT(hostname); + GF_ASSERT(uuid); + + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_conf_t *priv = NULL; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + priv = this->private; + GF_ASSERT(priv); + + peerinfo = glusterd_peerinfo_find_by_hostname(hostname); + if (peerinfo) { + ret = 0; + gf_uuid_copy(uuid, peerinfo->uuid); + } else { + if (gf_is_local_addr(hostname)) { + gf_uuid_copy(uuid, MY_UUID); + ret = 0; + } else { + ret = -1; + } + } + + gf_msg_debug(this->name, 0, "returning %d", ret); + return ret; +} + +/* glusterd_peerinfo_find_by_uuid searches for a peer which matches the + * uuid @uuid and if found returns the pointer to peerinfo object. + * Returns NULL otherwise. + */ +glusterd_peerinfo_t * +glusterd_peerinfo_find_by_uuid(uuid_t uuid) +{ + glusterd_conf_t *priv = NULL; + glusterd_peerinfo_t *entry = NULL; + glusterd_peerinfo_t *found = NULL; + xlator_t *this = THIS; + glusterd_friend_sm_state_t state; + + GF_ASSERT(this); + + if (gf_uuid_is_null(uuid)) + return NULL; + + priv = this->private; + + GF_ASSERT(priv); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(entry, &priv->peers, uuid_list) + { + if (!gf_uuid_compare(entry->uuid, uuid)) { + found = entry; /* Probably should be rcu_dereferenced */ + state = found->state.state; + break; + } + } + RCU_READ_UNLOCK; + + if (found) + gf_msg_debug(this->name, 0, "Friend found... state: %s", + glusterd_friend_sm_state_name_get(state)); + else + gf_msg_debug(this->name, 0, "Friend with uuid: %s, not found", + uuid_utoa(uuid)); + return found; +} + +/* glusterd_peerinfo_find will search for a peer matching either @uuid or + * @hostname and return a pointer to the peerinfo object + * Returns NULL otherwise. + */ +glusterd_peerinfo_t * +glusterd_peerinfo_find(uuid_t uuid, const char *hostname) +{ + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = THIS; + + GF_ASSERT(this); + + if (uuid) { + peerinfo = glusterd_peerinfo_find_by_uuid(uuid); + + if (peerinfo) { + return peerinfo; + } else { + gf_msg_debug(this->name, 0, "Unable to find peer by uuid: %s", + uuid_utoa(uuid)); + } + } + + if (hostname) { + peerinfo = glusterd_peerinfo_find_by_hostname(hostname); + + if (peerinfo) { + return peerinfo; + } else { + gf_msg_debug(this->name, 0, "Unable to find hostname: %s", + hostname); + } + } + return NULL; +} + +/* glusterd_peerinfo_new will create a new peerinfo object and set it's members + * values using the passed parameters. + * @hostname is added as the first entry in peerinfo->hostnames list and also + * set to peerinfo->hostname. + * It returns a pointer to peerinfo object if successful and returns NULL + * otherwise. The caller should take care of freeing the created peerinfo + * object. + */ +glusterd_peerinfo_t * +glusterd_peerinfo_new(glusterd_friend_sm_state_t state, uuid_t *uuid, + const char *hostname, int port) +{ + glusterd_peerinfo_t *new_peer = NULL; + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + + this = THIS; + GF_ASSERT(this); + conf = this->private; + GF_ASSERT(conf); + + new_peer = GF_CALLOC(1, sizeof(*new_peer), gf_gld_mt_peerinfo_t); + if (!new_peer) { + gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL); + goto out; + } + + CDS_INIT_LIST_HEAD(&new_peer->uuid_list); + + new_peer->state.state = state; + + CDS_INIT_LIST_HEAD(&new_peer->hostnames); + if (hostname) { + ret = gd_add_address_to_peer(new_peer, hostname); + if (ret) + goto out; + /* Also set it to peerinfo->hostname. Doing this as we use + * peerinfo->hostname in a lot of places and is really hard to + * get everything right + */ + new_peer->hostname = gf_strdup(hostname); + } + + if (uuid) { + gf_uuid_copy(new_peer->uuid, *uuid); + } + + ret = glusterd_sm_tr_log_init( + &new_peer->sm_log, glusterd_friend_sm_state_name_get, + glusterd_friend_sm_event_name_get, GLUSTERD_TR_LOG_SIZE); + if (ret) + goto out; + + if (new_peer->state.state == GD_FRIEND_STATE_BEFRIENDED) + new_peer->quorum_contrib = QUORUM_WAITING; + new_peer->port = port; + + pthread_mutex_init(&new_peer->delete_lock, NULL); + + new_peer->generation = uatomic_add_return(&conf->generation, 1); +out: + if (ret && new_peer) { + glusterd_peerinfo_cleanup(new_peer); + new_peer = NULL; + } + return new_peer; +} + +/* Check if the all peers are connected and befriended, except the peer + * specified (the peer being detached) + */ +gf_boolean_t +glusterd_chk_peers_connected_befriended(uuid_t skip_uuid) +{ + gf_boolean_t ret = _gf_true; + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_conf_t *priv = NULL; + + priv = THIS->private; + GF_ASSERT(priv); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peerinfo, &priv->peers, uuid_list) + { + if (!gf_uuid_is_null(skip_uuid) && + !gf_uuid_compare(skip_uuid, peerinfo->uuid)) + continue; + + if ((GD_FRIEND_STATE_BEFRIENDED != peerinfo->state.state) || + !(peerinfo->connected)) { + ret = _gf_false; + break; + } + } + RCU_READ_UNLOCK; + + gf_msg_debug(THIS->name, 0, "Returning %s", (ret ? "TRUE" : "FALSE")); + return ret; +} + +/* Return hostname for given uuid if it exists + * else return NULL + */ +char * +glusterd_uuid_to_hostname(uuid_t uuid) +{ + char *hostname = NULL; + glusterd_conf_t *priv = NULL; + glusterd_peerinfo_t *entry = NULL; + + priv = THIS->private; + GF_ASSERT(priv); + + if (!gf_uuid_compare(MY_UUID, uuid)) { + hostname = gf_strdup("localhost"); + return hostname; + } + RCU_READ_LOCK; + if (!cds_list_empty(&priv->peers)) { + cds_list_for_each_entry_rcu(entry, &priv->peers, uuid_list) + { + if (!gf_uuid_compare(entry->uuid, uuid)) { + hostname = gf_strdup(entry->hostname); + break; + } + } + } + RCU_READ_UNLOCK; + + return hostname; +} + +char * +gd_peer_uuid_str(glusterd_peerinfo_t *peerinfo) +{ + if ((peerinfo == NULL) || gf_uuid_is_null(peerinfo->uuid)) + return NULL; + + if (peerinfo->uuid_str[0] == '\0') + uuid_utoa_r(peerinfo->uuid, peerinfo->uuid_str); + + return peerinfo->uuid_str; +} + +gf_boolean_t +glusterd_are_all_peers_up() +{ + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + gf_boolean_t peers_up = _gf_false; + + this = THIS; + GF_VALIDATE_OR_GOTO("glusterd", this, out); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, conf, out); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list) + { + if (!peerinfo->connected) { + RCU_READ_UNLOCK; + goto out; + } + } + RCU_READ_UNLOCK; + + peers_up = _gf_true; + +out: + return peers_up; +} + +gf_boolean_t +glusterd_are_vol_all_peers_up(glusterd_volinfo_t *volinfo, + struct cds_list_head *peers, char **down_peerstr) +{ + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + gf_boolean_t ret = _gf_false; + + cds_list_for_each_entry(brickinfo, &volinfo->bricks, brick_list) + { + if (!gf_uuid_compare(brickinfo->uuid, MY_UUID)) + continue; + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peerinfo, peers, uuid_list) + { + if (gf_uuid_compare(peerinfo->uuid, brickinfo->uuid)) + continue; + + /*Found peer who owns the brick, return false + * if peer is not connected or not friend */ + if (!(peerinfo->connected) || + (peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)) { + *down_peerstr = gf_strdup(peerinfo->hostname); + RCU_READ_UNLOCK; + gf_msg_debug(THIS->name, 0, "Peer %s is down. ", *down_peerstr); + goto out; + } + } + RCU_READ_UNLOCK; + } + + ret = _gf_true; +out: + gf_msg_debug("glusterd", 0, "Returning %d", ret); + return ret; +} + +int32_t +glusterd_peer_hostname_new(const char *hostname, + glusterd_peer_hostname_t **name) +{ + glusterd_peer_hostname_t *peer_hostname = NULL; + int32_t ret = -1; + + GF_ASSERT(hostname); + GF_ASSERT(name); + xlator_t *this = THIS; + GF_ASSERT(this); + + peer_hostname = GF_CALLOC(1, sizeof(*peer_hostname), + gf_gld_mt_peer_hostname_t); + + if (!peer_hostname) { + gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL); + goto out; + } + + peer_hostname->hostname = gf_strdup(hostname); + CDS_INIT_LIST_HEAD(&peer_hostname->hostname_list); + + *name = peer_hostname; + ret = 0; + +out: + gf_msg_debug("glusterd", 0, "Returning %d", ret); + return ret; +} + +void +glusterd_peer_hostname_free(glusterd_peer_hostname_t *name) +{ + if (!name) + return; + + cds_list_del_init(&name->hostname_list); + + GF_FREE(name->hostname); + name->hostname = NULL; + + GF_FREE(name); + + return; +} + +gf_boolean_t +gd_peer_has_address(glusterd_peerinfo_t *peerinfo, const char *address) +{ + glusterd_peer_hostname_t *hostname = NULL; + + GF_VALIDATE_OR_GOTO("glusterd", (peerinfo != NULL), out); + GF_VALIDATE_OR_GOTO("glusterd", (address != NULL), out); + + cds_list_for_each_entry(hostname, &peerinfo->hostnames, hostname_list) + { + if (strcmp(hostname->hostname, address) == 0) { + return _gf_true; + } + } + +out: + return _gf_false; +} + +int +gd_add_address_to_peer(glusterd_peerinfo_t *peerinfo, const char *address) +{ + int ret = -1; + glusterd_peer_hostname_t *hostname = NULL; + + GF_VALIDATE_OR_GOTO("glusterd", (peerinfo != NULL), out); + GF_VALIDATE_OR_GOTO("glusterd", (address != NULL), out); + + if (gd_peer_has_address(peerinfo, address)) { + ret = 0; + goto out; + } + + ret = glusterd_peer_hostname_new(address, &hostname); + if (ret) + goto out; + + cds_list_add_tail_rcu(&hostname->hostname_list, &peerinfo->hostnames); + + ret = 0; +out: + return ret; +} + +/* gd_add_friend_to_dict() adds details of @friend into @dict with the given + * @prefix. All the parameters are compulsory. + * + * The complete address list is added to the dict only if the cluster op-version + * is >= GD_OP_VERSION_3_6_0 + */ +int +gd_add_friend_to_dict(glusterd_peerinfo_t *friend, dict_t *dict, + const char *prefix) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + char key[100] = { + 0, + }; + glusterd_peer_hostname_t *address = NULL; + int count = 0; + + this = THIS; + GF_VALIDATE_OR_GOTO("glusterd", (this != NULL), out); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, (conf != NULL), out); + + GF_VALIDATE_OR_GOTO(this->name, (friend != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out); + + snprintf(key, sizeof(key), "%s.uuid", prefix); + ret = dict_set_dynstr_with_alloc(dict, key, uuid_utoa(friend->uuid)); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Failed to set key %s in dict", key); + goto out; + } + + /* Setting the first hostname from the list with this key for backward + * compatibility + */ + snprintf(key, sizeof(key), "%s.hostname", prefix); + address = cds_list_entry(&friend->hostnames, glusterd_peer_hostname_t, + hostname_list); + ret = dict_set_dynstr_with_alloc(dict, key, address->hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Failed to set key %s in dict", key); + goto out; + } + + if (conf->op_version < GD_OP_VERSION_3_6_0) { + ret = 0; + goto out; + } + + address = NULL; + count = 0; + cds_list_for_each_entry(address, &friend->hostnames, hostname_list) + { + GF_VALIDATE_OR_GOTO(this->name, (address != NULL), out); + + snprintf(key, sizeof(key), "%s.hostname%d", prefix, count); + ret = dict_set_dynstr_with_alloc(dict, key, address->hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Failed to set key %s in dict", key); + goto out; + } + count++; + } + ret = snprintf(key, sizeof(key), "%s.address-count", prefix); + ret = dict_set_int32n(dict, key, ret, count); + if (ret) + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Failed to set key %s in dict", key); + +out: + gf_msg_debug(this ? this->name : "glusterd", 0, "Returning %d", ret); + return ret; +} + +/* gd_update_peerinfo_from_dict will update the hostnames for @peerinfo from + * peer details with @prefix in @dict. + * Returns 0 on success and -1 on failure. + */ +int +gd_update_peerinfo_from_dict(glusterd_peerinfo_t *peerinfo, dict_t *dict, + const char *prefix) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + char key[100] = { + 0, + }; + char *hostname = NULL; + int count = 0; + int i = 0; + + this = THIS; + GF_ASSERT(this != NULL); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, (conf != NULL), out); + + GF_VALIDATE_OR_GOTO(this->name, (peerinfo != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out); + + ret = snprintf(key, sizeof(key), "%s.hostname", prefix); + ret = dict_get_strn(dict, key, ret, &hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED, + "Key %s not present in " + "dictionary", + key); + goto out; + } + ret = gd_add_address_to_peer(peerinfo, hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_ADD_ADDRESS_TO_PEER_FAIL, + "Could not add address to peer"); + goto out; + } + /* Also set peerinfo->hostname to the first address */ + if (peerinfo->hostname != NULL) + GF_FREE(peerinfo->hostname); + peerinfo->hostname = gf_strdup(hostname); + + if (conf->op_version < GD_OP_VERSION_3_6_0) { + ret = 0; + goto out; + } + + ret = snprintf(key, sizeof(key), "%s.address-count", prefix); + ret = dict_get_int32n(dict, key, ret, &count); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED, + "Key %s not present in " + "dictionary", + key); + goto out; + } + hostname = NULL; + for (i = 0; i < count; i++) { + ret = snprintf(key, sizeof(key), "%s.hostname%d", prefix, i); + ret = dict_get_strn(dict, key, ret, &hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED, + "Key %s not present " + "in dictionary", + key); + goto out; + } + ret = gd_add_address_to_peer(peerinfo, hostname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_ADD_ADDRESS_TO_PEER_FAIL, + "Could not add address to peer"); + goto out; + } + + hostname = NULL; + } + +out: + gf_msg_debug(this->name, 0, "Returning %d", ret); + return ret; +} + +/* gd_peerinfo_from_dict creates a peerinfo object from details of peer with + * @prefix in @dict. + * Returns a pointer to the created peerinfo object on success, and NULL on + * failure. + */ +glusterd_peerinfo_t * +gd_peerinfo_from_dict(dict_t *dict, const char *prefix) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + glusterd_peerinfo_t *new_peer = NULL; + char key[64] = { + 0, + }; + char *uuid_str = NULL; + + this = THIS; + GF_VALIDATE_OR_GOTO("glusterd", (this != NULL), out); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, (conf != NULL), out); + + GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out); + + new_peer = glusterd_peerinfo_new(GD_FRIEND_STATE_DEFAULT, NULL, NULL, 0); + if (new_peer == NULL) { + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_PEERINFO_CREATE_FAIL, + "Could not create peerinfo " + "object"); + goto out; + } + + ret = snprintf(key, sizeof(key), "%s.uuid", prefix); + ret = dict_get_strn(dict, key, ret, &uuid_str); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED, + "Key %s not present in " + "dictionary", + key); + goto out; + } + gf_uuid_parse(uuid_str, new_peer->uuid); + + ret = gd_update_peerinfo_from_dict(new_peer, dict, prefix); + +out: + if ((ret != 0) && (new_peer != NULL)) { + glusterd_peerinfo_cleanup(new_peer); + new_peer = NULL; + } + + return new_peer; +} + +static int +gd_add_peer_hostnames_to_dict(glusterd_peerinfo_t *peerinfo, dict_t *dict, + const char *prefix) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + char key[64] = { + 0, + }; + glusterd_peer_hostname_t *addr = NULL; + int count = 0; + + this = THIS; + GF_ASSERT(this != NULL); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, (conf != NULL), out); + + if (conf->op_version < GD_OP_VERSION_3_6_0) { + ret = 0; + goto out; + } + + GF_VALIDATE_OR_GOTO(this->name, (peerinfo != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out); + GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out); + + cds_list_for_each_entry(addr, &peerinfo->hostnames, hostname_list) + { + snprintf(key, sizeof(key), "%s.hostname%d", prefix, count); + ret = dict_set_dynstr_with_alloc(dict, key, addr->hostname); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Key=%s", key, NULL); + goto out; + } + count++; + } + + ret = snprintf(key, sizeof(key), "%s.hostname_count", prefix); + ret = dict_set_int32n(dict, key, ret, count); + +out: + return ret; +} + +int +gd_add_peer_detail_to_dict(glusterd_peerinfo_t *peerinfo, dict_t *friends, + int count) +{ + int ret = -1; + char key[32] = { + 0, + }; + int keylen; + char *peer_uuid_str = NULL; + + xlator_t *this = THIS; + GF_ASSERT(this); + GF_ASSERT(peerinfo); + GF_ASSERT(friends); + + peer_uuid_str = gd_peer_uuid_str(peerinfo); + keylen = snprintf(key, sizeof(key), "friend%d.uuid", count); + ret = dict_set_strn(friends, key, keylen, peer_uuid_str); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s", + key, NULL); + goto out; + } + + keylen = snprintf(key, sizeof(key), "friend%d.hostname", count); + ret = dict_set_strn(friends, key, keylen, peerinfo->hostname); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s", + key, NULL); + goto out; + } + + keylen = snprintf(key, sizeof(key), "friend%d.port", count); + ret = dict_set_int32n(friends, key, keylen, peerinfo->port); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s", + key, NULL); + goto out; + } + + keylen = snprintf(key, sizeof(key), "friend%d.stateId", count); + ret = dict_set_int32n(friends, key, keylen, peerinfo->state.state); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, + "Key=%s in dict", key, NULL); + goto out; + } + + keylen = snprintf(key, sizeof(key), "friend%d.state", count); + ret = dict_set_strn( + friends, key, keylen, + glusterd_friend_sm_state_name_get(peerinfo->state.state)); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "key=%s", + key, NULL); + goto out; + } + + keylen = snprintf(key, sizeof(key), "friend%d.connected", count); + ret = dict_set_int32n(friends, key, keylen, (int32_t)peerinfo->connected); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s", + key, NULL); + goto out; + } + + snprintf(key, sizeof(key), "friend%d", count); + ret = gd_add_peer_hostnames_to_dict(peerinfo, friends, key); + +out: + return ret; +} + +/* glusterd_peerinfo_find_by_generation searches for a peer which has the + * generation number @generation and if found returns the pointer to peerinfo + * object. Returns NULL otherwise. + */ +glusterd_peerinfo_t * +glusterd_peerinfo_find_by_generation(uint32_t generation) +{ + glusterd_conf_t *priv = NULL; + glusterd_peerinfo_t *entry = NULL; + glusterd_peerinfo_t *found = NULL; + xlator_t *this = THIS; + glusterd_friend_sm_state_t state; + + GF_ASSERT(this); + + priv = this->private; + + GF_ASSERT(priv); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(entry, &priv->peers, uuid_list) + { + if (entry->generation == generation) { + found = entry; /* Probably should be rcu_dereferenced */ + state = found->state.state; + break; + } + } + RCU_READ_UNLOCK; + + if (found) + gf_msg_debug(this->name, 0, "Friend found... state: %s", + glusterd_friend_sm_state_name_get(state)); + else + gf_msg_debug(this->name, 0, + "Friend with generation: %" PRIu32 ", not found", + generation); + return found; +} + +int +glusterd_get_peers_count() +{ + int count = 0; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + glusterd_peerinfo_t *peer = NULL; + + this = THIS; + GF_VALIDATE_OR_GOTO("glusterd", this, out); + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, conf, out); + + RCU_READ_LOCK; + cds_list_for_each_entry_rcu(peer, &conf->peers, uuid_list) count++; + RCU_READ_UNLOCK; + +out: + return count; +} |
