diff --git a/lib/common/membership.c b/lib/common/membership.c index 1c2ffa5dde..b57ee0d27a 100644 --- a/lib/common/membership.c +++ b/lib/common/membership.c @@ -1,510 +1,510 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include GHashTable *crm_peer_id_cache = NULL; GHashTable *crm_peer_cache = NULL; unsigned long long crm_peer_seq = 0; gboolean crm_have_quorum = FALSE; gboolean crm_is_member_active(const crm_node_t *node) { if(node && safe_str_eq(node->state, CRM_NODE_MEMBER)) { return TRUE; } return FALSE; } gboolean crm_is_full_member(const crm_node_t *node) { if(crm_is_member_active(node) && (node->processes & crm_proc_crmd)) { return TRUE; } return FALSE; } static gboolean crm_reap_dead_member( gpointer key, gpointer value, gpointer user_data) { crm_node_t *node = value; crm_node_t *search = user_data; if(search != NULL && node->id != search->id) { return FALSE; } else if(crm_is_member_active(value) == FALSE) { crm_notice("Removing %s/%u from the membership list", node->uname, node->id); return TRUE; } return FALSE; } guint reap_crm_member(uint32_t id) { int matches = 0; crm_node_t *node = g_hash_table_lookup(crm_peer_id_cache, GUINT_TO_POINTER(id)); if(node == NULL) { crm_info("Peer %u is unknown", id); } else if(crm_is_member_active(node)) { crm_warn("Peer %u/%s is still active", id, node->uname); } else { if(g_hash_table_remove(crm_peer_id_cache, GUINT_TO_POINTER(id))) { crm_notice("Removed dead peer %u from the uuid cache", id); } else { crm_warn("Peer %u/%s was not removed", id, node->uname); } matches = g_hash_table_foreach_remove( crm_peer_cache, crm_reap_dead_member, node); crm_notice("Removed %d dead peers with id=%u from the membership list", matches, id); } return matches; } static void crm_count_member( gpointer key, gpointer value, gpointer user_data) { guint *count = user_data; - if(crm_is_member_active(value)) { + if(crm_is_full_member(value)) { *count = *count + 1; } } guint crm_active_members(void) { guint count = 0; g_hash_table_foreach(crm_peer_cache, crm_count_member, &count); return count; } struct peer_count_s { uint32_t peer; guint count; }; static void crm_count_peer( gpointer key, gpointer value, gpointer user_data) { crm_node_t *node = value; struct peer_count_s *search = user_data; if(crm_is_member_active(node) && (node->processes & search->peer)) { search->count = search->count + 1; } } guint crm_active_peers(uint32_t peer) { struct peer_count_s search; search.count = 0; search.peer = peer; g_hash_table_foreach(crm_peer_cache, crm_count_peer, &search); return search.count; } void destroy_crm_node(gpointer data) { crm_node_t *node = data; crm_debug_2("Destroying entry for node %u", node->id); crm_free(node->addr); crm_free(node->uname); crm_free(node->state); crm_free(node->uuid); crm_free(node); } void crm_peer_init(void) { static gboolean initialized = FALSE; if(initialized) { return; } initialized = TRUE; crm_peer_destroy(); if(crm_peer_cache == NULL) { crm_peer_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, destroy_crm_node); } if(crm_peer_id_cache == NULL) { crm_peer_id_cache = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, NULL); } } void crm_peer_destroy(void) { if(crm_peer_cache != NULL) { g_hash_table_destroy(crm_peer_cache); crm_peer_cache = NULL; } if(crm_peer_id_cache != NULL) { g_hash_table_destroy(crm_peer_id_cache); crm_peer_id_cache = NULL; } } void (*crm_status_callback)(enum crm_status_type, crm_node_t*, const void*) = NULL; void crm_set_status_callback( void (*dispatch)(enum crm_status_type,crm_node_t*, const void*)) { crm_status_callback = dispatch; } static crm_node_t *crm_new_peer(unsigned int id, const char *uname) { crm_node_t *node = NULL; CRM_CHECK(uname != NULL || id > 0, return NULL); crm_debug("Creating entry for node %s/%u", uname, id); crm_malloc0(node, sizeof(crm_node_t)); node->state = crm_strdup("unknown"); if(id > 0) { node->id = id; crm_info("Node %s now has id: %u", crm_str(uname), id); g_hash_table_replace(crm_peer_id_cache, GUINT_TO_POINTER(node->id), node); } if(uname) { node->uname = crm_strdup(uname); CRM_ASSERT(node->uname != NULL); crm_info("Node %u is now known as %s", id, node->uname); g_hash_table_replace(crm_peer_cache, node->uname, node); if(is_openais_cluster()) { node->uuid = crm_strdup(node->uname); } if(crm_status_callback) { crm_status_callback(crm_status_uname, node, NULL); } } return node; } crm_node_t *crm_get_peer(unsigned int id, const char *uname) { crm_node_t *node = NULL; if(uname != NULL) { node = g_hash_table_lookup(crm_peer_cache, uname); } if(node == NULL && id > 0) { node = g_hash_table_lookup(crm_peer_id_cache, GUINT_TO_POINTER(id)); if(node && node->uname && uname) { crm_crit("Node %s and %s share the same cluster node id '%u'!", node->uname, uname, id); /* NOTE: Calling crm_new_peer() means the entry in * crm_peer_id_cache will point to the new entity */ /* TODO: Replace the old uname instead? */ node = crm_new_peer(id, uname); CRM_ASSERT(node->uname != NULL); } } if(node && uname && node->uname == NULL) { node->uname = crm_strdup(uname); crm_info("Node %u is now known as %s", id, uname); g_hash_table_insert(crm_peer_cache, node->uname, node); if(crm_status_callback) { crm_status_callback(crm_status_uname, node, NULL); } } if(node && id > 0 && id != node->id) { g_hash_table_remove(crm_peer_id_cache, GUINT_TO_POINTER(node->id)); g_hash_table_insert(crm_peer_id_cache, GUINT_TO_POINTER(id), node); node->id = id; crm_info("Node %s now has id: %u", crm_str(uname), id); } return node; } crm_node_t *crm_update_peer( unsigned int id, uint64_t born, uint64_t seen, int32_t votes, uint32_t children, const char *uuid, const char *uname, const char *addr, const char *state) { gboolean state_changed = FALSE; gboolean addr_changed = FALSE; gboolean procs_changed = FALSE; gboolean votes_changed = FALSE; crm_node_t *node = NULL; CRM_CHECK(uname != NULL || id > 0, return NULL); CRM_ASSERT(crm_peer_cache != NULL); CRM_ASSERT(crm_peer_id_cache != NULL); node = crm_get_peer(id, uname); if(node == NULL) { node = crm_new_peer(id, uname); /* do it now so we don't get '(new)' everywhere */ node->votes = votes; node->processes = children; if(addr) { node->addr = crm_strdup(addr); } } if(votes > 0 && node->votes != votes) { votes_changed = TRUE; node->votes = votes; } if(node->uuid == NULL) { if(uuid != NULL) { node->uuid = crm_strdup(uuid); } else if(node->uname != NULL && is_openais_cluster()) { node->uuid = crm_strdup(node->uname); } } if(children > 0 && children != node->processes) { uint32_t last = node->processes; node->processes = children; procs_changed = TRUE; if(crm_status_callback) { crm_status_callback(crm_status_processes, node, &last); } } if(born != 0) { node->born = born; } if(state != NULL && safe_str_neq(node->state, state)) { char *last = node->state; node->state = crm_strdup(state); state_changed = TRUE; if(crm_status_callback) { crm_status_callback(crm_status_nstate, node, last); } crm_free(last); } if(seen != 0 && crm_is_member_active(node)) { node->last_seen = seen; } if(addr != NULL) { if(node->addr == NULL || crm_str_eq(node->addr, addr, FALSE) == FALSE) { addr_changed = TRUE; crm_free(node->addr); node->addr = crm_strdup(addr); } } if(state_changed || addr_changed || votes_changed || procs_changed) { crm_info("Node %s: id=%u state=%s%s addr=%s%s votes=%d%s born="U64T" seen="U64T" proc=%.32x%s", node->uname, node->id, node->state, state_changed?" (new)":"", node->addr, addr_changed?" (new)":"", node->votes, votes_changed?" (new)":"", node->born, node->last_seen, node->processes, procs_changed?" (new)":"" ); } return node; } crm_node_t *crm_update_ais_node(xmlNode *member, long long seq) { const char *id_s = crm_element_value(member, "id"); const char *addr = crm_element_value(member, "addr"); const char *uname = crm_element_value(member, "uname"); const char *state = crm_element_value(member, "state"); const char *born_s = crm_element_value(member, "born"); const char *seen_s = crm_element_value(member, "seen"); const char *votes_s = crm_element_value(member, "votes"); const char *procs_s = crm_element_value(member, "processes"); int votes = crm_int_helper(votes_s, NULL); unsigned int id = crm_int_helper(id_s, NULL); unsigned int procs = crm_int_helper(procs_s, NULL); /* TODO: These values will contain garbage if version < 0.7.1 */ uint64_t born = crm_int_helper(born_s, NULL); uint64_t seen = crm_int_helper(seen_s, NULL); return crm_update_peer(id, born, seen, votes, procs, uname, uname, addr, state); } crm_node_t *crm_update_cman_node(xmlNode *member, long long seq) { const char *id_s = crm_element_value(member, "id"); const char *uname = crm_element_value(member, "uname"); const char *procs_s = crm_element_value(member, "processes"); unsigned int id = crm_int_helper(id_s, NULL); unsigned int procs = crm_int_helper(procs_s, NULL); crm_info("Updating peer processes for %s", crm_str(uname)); return crm_update_peer(id, 0, 0, 0, procs, uname, uname, NULL, NULL); } #if SUPPORT_HEARTBEAT crm_node_t *crm_update_ccm_node( const oc_ev_membership_t *oc, int offset, const char *state, uint64_t seq) { crm_node_t *node = NULL; const char *uuid = NULL; CRM_CHECK(oc->m_array[offset].node_uname != NULL, return NULL); uuid = get_uuid(oc->m_array[offset].node_uname); node = crm_update_peer(oc->m_array[offset].node_id, oc->m_array[offset].node_born_on, seq, -1, 0, uuid, oc->m_array[offset].node_uname, NULL, state); if(safe_str_eq(CRM_NODE_ACTIVE, state)) { /* Heartbeat doesn't send status notifications for nodes that were already part of the cluster */ crm_update_peer_proc( oc->m_array[offset].node_uname, crm_proc_ais, ONLINESTATUS); /* Nor does it send status notifications for processes that were already active */ crm_update_peer_proc( oc->m_array[offset].node_uname, crm_proc_crmd, ONLINESTATUS); } return node; } #endif void crm_update_peer_proc(const char *uname, uint32_t flag, const char *status) { uint32_t last = 0; crm_node_t *node = NULL; gboolean changed = FALSE; CRM_ASSERT(crm_peer_cache != NULL); CRM_CHECK(uname != NULL, return); node = g_hash_table_lookup(crm_peer_cache, uname); CRM_CHECK(node != NULL, crm_err("Could not set %s.%s to %s", uname, peer2text(flag), status); return); last = node->processes; if(safe_str_eq(status, ONLINESTATUS)) { if((node->processes & flag) == 0) { set_bit_inplace(node->processes, flag); changed = TRUE; } } else if(node->processes & flag) { clear_bit_inplace(node->processes, flag); changed = TRUE; } if(changed) { crm_info("%s.%s is now %s", uname, peer2text(flag), status); if(crm_status_callback) { crm_status_callback(crm_status_processes, node, &last); } } } static int crm_terminate_member_common(int nodeid, const char *uname, IPC_Channel *cluster, int *connection) { crm_node_t *node = NULL; gboolean success = FALSE; const char *reason = "Cluster connection failed"; node = crm_get_peer(nodeid, uname); if(cluster == NULL) { reason = "No connection to the cluster"; } else if(node == NULL) { if(uname) { crm_err("Nothing known about node uname=%s", uname); } else if(nodeid > 0) { crm_err("Nothing known about node id=%d", nodeid); } else { crm_err("A node id or uname is required, got %d/%p", nodeid, uname); } return -1; } else { time_t now = time(NULL); char *now_s = crm_itoa(now); if(cluster) { success = attrd_update(cluster, 'U', node->uname, "terminate", now_s, XML_CIB_TAG_STATUS, NULL, NULL); } else { success = attrd_update_no_mainloop(connection, 'U', node->uname, "terminate", now_s, XML_CIB_TAG_STATUS, NULL, NULL); } crm_free(now_s); } if(success) { crm_info("Requested that node %d/%s be terminated", nodeid, node->uname); return 1; } crm_err("Could not terminate node %d/%s: %s", nodeid, node->uname, reason); return 0; } int crm_terminate_member(int nodeid, const char *uname, IPC_Channel *cluster) { if(cluster != NULL) { return crm_terminate_member_common(nodeid, uname, cluster, NULL); } crm_err("Could not terminate node %d/%s: No cluster connection", nodeid, uname); return 0; } int crm_terminate_member_no_mainloop(int nodeid, const char *uname, int *connection) { return crm_terminate_member_common(nodeid, uname, NULL, connection); }