diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h
index 3a8b2242f7..bf15f96376 100644
--- a/include/crm/cluster/internal.h
+++ b/include/crm/cluster/internal.h
@@ -1,474 +1,476 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #ifndef CRM_CLUSTER_INTERNAL__H
 #  define CRM_CLUSTER_INTERNAL__H
 
 #  include <crm/cluster.h>
 
 #  define AIS_IPC_NAME  "ais-crm-ipc"
 #  define AIS_IPC_MESSAGE_SIZE 8192*128
 #  define CRM_MESSAGE_IPC_ACK	0
 
 #  ifndef INTERFACE_MAX
 #    define INTERFACE_MAX 2     /* from the private coroapi.h header */
 #  endif
 
 typedef struct crm_ais_host_s AIS_Host;
 typedef struct crm_ais_msg_s AIS_Message;
 
 struct crm_ais_host_s {
     uint32_t id;
     uint32_t pid;
     gboolean local;
     enum crm_ais_msg_types type;
     uint32_t size;
     char uname[MAX_NAME];
 
 } __attribute__ ((packed));
 
 struct crm_ais_msg_s {
     cs_ipc_header_response_t header __attribute__ ((aligned(8)));
     uint32_t id;
     gboolean is_compressed;
 
     AIS_Host host;
     AIS_Host sender;
 
     uint32_t size;
     uint32_t compressed_size;
     /* 584 bytes */
     char data[0];
 
 } __attribute__ ((packed));
 
 struct crm_ais_nodeid_resp_s {
     cs_ipc_header_response_t header __attribute__ ((aligned(8)));
     uint32_t id;
     uint32_t counter;
     char uname[MAX_NAME];
     char cname[MAX_NAME];
 } __attribute__ ((packed));
 
 struct crm_ais_quorum_resp_s {
     cs_ipc_header_response_t header __attribute__ ((aligned(8)));
     uint64_t id;
     uint32_t votes;
     uint32_t expected_votes;
     uint32_t quorate;
 } __attribute__ ((packed));
 
 /* *INDENT-OFF* */
 enum crm_proc_flag {
     crm_proc_none      = 0x00000001,
     /* These values are sent over the network by the legacy plugin
      * Therefor changing any of these values is going to break compatability
      *
      * So don't
      */
 
     /* 3 messaging types */
     crm_proc_heartbeat = 0x01000000,
     crm_proc_plugin    = 0x00000002,
     crm_proc_cpg       = 0x04000000,
 
     crm_proc_lrmd      = 0x00000010,
     crm_proc_cib       = 0x00000100,
     crm_proc_crmd      = 0x00000200,
     crm_proc_attrd     = 0x00001000,
 
     crm_proc_stonithd  = 0x00002000,
     crm_proc_stonith_ng= 0x00100000,
 
     crm_proc_pe        = 0x00010000,
     crm_proc_te        = 0x00020000,
 
     crm_proc_mgmtd     = 0x00040000,
 };
 /* *INDENT-ON* */
 
 /*!
  * \internal
  * \brief Return the process bit corresponding to the current cluster stack
  *
  * \return Process flag if detectable, otherwise 0
  */
 static inline uint32_t
 crm_get_cluster_proc()
 {
     switch (get_cluster_type()) {
         case pcmk_cluster_corosync:
         case pcmk_cluster_cman:
             return crm_proc_cpg;
 
         case pcmk_cluster_heartbeat:
             return crm_proc_heartbeat;
 
         case pcmk_cluster_classic_ais:
             return crm_proc_plugin;
 
         default:
             break;
     }
     return crm_proc_none;
 }
 
 static inline const char *
 peer2text(enum crm_proc_flag proc)
 {
     const char *text = "unknown";
 
     if (proc == (crm_proc_crmd | crm_get_cluster_proc())) {
         return "peer";
     }
 
     switch (proc) {
         case crm_proc_none:
             text = "none";
             break;
         case crm_proc_plugin:
             text = "ais";
             break;
         case crm_proc_heartbeat:
             text = "heartbeat";
             break;
         case crm_proc_cib:
             text = "cib";
             break;
         case crm_proc_crmd:
             text = "crmd";
             break;
         case crm_proc_pe:
             text = "pengine";
             break;
         case crm_proc_te:
             text = "tengine";
             break;
         case crm_proc_lrmd:
             text = "lrmd";
             break;
         case crm_proc_attrd:
             text = "attrd";
             break;
         case crm_proc_stonithd:
             text = "stonithd";
             break;
         case crm_proc_stonith_ng:
             text = "stonith-ng";
             break;
         case crm_proc_mgmtd:
             text = "mgmtd";
             break;
         case crm_proc_cpg:
             text = "corosync-cpg";
             break;
     }
     return text;
 }
 
 static inline enum crm_proc_flag
 text2proc(const char *proc)
 {
     /* We only care about these two so far */
 
     if (proc && strcmp(proc, "cib") == 0) {
         return crm_proc_cib;
     } else if (proc && strcmp(proc, "crmd") == 0) {
         return crm_proc_crmd;
     }
 
     return crm_proc_none;
 }
 
 static inline const char *
 ais_dest(const struct crm_ais_host_s *host)
 {
     if (host->local) {
         return "local";
     } else if (host->size > 0) {
         return host->uname;
     } else {
         return "<all>";
     }
 }
 
 #  define ais_data_len(msg) (msg->is_compressed?msg->compressed_size:msg->size)
 
 static inline AIS_Message *
 ais_msg_copy(const AIS_Message * source)
 {
     AIS_Message *target = malloc(sizeof(AIS_Message) + ais_data_len(source));
 
     if(target) {
         memcpy(target, source, sizeof(AIS_Message));
         memcpy(target->data, source->data, ais_data_len(target));
     }
     return target;
 }
 
 /*
 typedef enum {
    CS_OK = 1,
    CS_ERR_LIBRARY = 2,
    CS_ERR_VERSION = 3,
    CS_ERR_INIT = 4,
    CS_ERR_TIMEOUT = 5,
    CS_ERR_TRY_AGAIN = 6,
    CS_ERR_INVALID_PARAM = 7,
    CS_ERR_NO_MEMORY = 8,
    CS_ERR_BAD_HANDLE = 9,
    CS_ERR_BUSY = 10,
    CS_ERR_ACCESS = 11,
    CS_ERR_NOT_EXIST = 12,
    CS_ERR_NAME_TOO_LONG = 13,
    CS_ERR_EXIST = 14,
    CS_ERR_NO_SPACE = 15,
    CS_ERR_INTERRUPT = 16,
    CS_ERR_NAME_NOT_FOUND = 17,
    CS_ERR_NO_RESOURCES = 18,
    CS_ERR_NOT_SUPPORTED = 19,
    CS_ERR_BAD_OPERATION = 20,
    CS_ERR_FAILED_OPERATION = 21,
    CS_ERR_MESSAGE_ERROR = 22,
    CS_ERR_QUEUE_FULL = 23,
    CS_ERR_QUEUE_NOT_AVAILABLE = 24,
    CS_ERR_BAD_FLAGS = 25,
    CS_ERR_TOO_BIG = 26,
    CS_ERR_NO_SECTIONS = 27,
    CS_ERR_CONTEXT_NOT_FOUND = 28,
    CS_ERR_TOO_MANY_GROUPS = 30,
    CS_ERR_SECURITY = 100
 } cs_error_t;
  */
 static inline const char *
 ais_error2text(int error)
 {
     const char *text = "unknown";
 
 #  if SUPPORT_COROSYNC
     switch (error) {
         case CS_OK:
             text = "OK";
             break;
         case CS_ERR_LIBRARY:
             text = "Library error";
             break;
         case CS_ERR_VERSION:
             text = "Version error";
             break;
         case CS_ERR_INIT:
             text = "Initialization error";
             break;
         case CS_ERR_TIMEOUT:
             text = "Timeout";
             break;
         case CS_ERR_TRY_AGAIN:
             text = "Try again";
             break;
         case CS_ERR_INVALID_PARAM:
             text = "Invalid parameter";
             break;
         case CS_ERR_NO_MEMORY:
             text = "No memory";
             break;
         case CS_ERR_BAD_HANDLE:
             text = "Bad handle";
             break;
         case CS_ERR_BUSY:
             text = "Busy";
             break;
         case CS_ERR_ACCESS:
             text = "Access error";
             break;
         case CS_ERR_NOT_EXIST:
             text = "Doesn't exist";
             break;
         case CS_ERR_NAME_TOO_LONG:
             text = "Name too long";
             break;
         case CS_ERR_EXIST:
             text = "Exists";
             break;
         case CS_ERR_NO_SPACE:
             text = "No space";
             break;
         case CS_ERR_INTERRUPT:
             text = "Interrupt";
             break;
         case CS_ERR_NAME_NOT_FOUND:
             text = "Name not found";
             break;
         case CS_ERR_NO_RESOURCES:
             text = "No resources";
             break;
         case CS_ERR_NOT_SUPPORTED:
             text = "Not supported";
             break;
         case CS_ERR_BAD_OPERATION:
             text = "Bad operation";
             break;
         case CS_ERR_FAILED_OPERATION:
             text = "Failed operation";
             break;
         case CS_ERR_MESSAGE_ERROR:
             text = "Message error";
             break;
         case CS_ERR_QUEUE_FULL:
             text = "Queue full";
             break;
         case CS_ERR_QUEUE_NOT_AVAILABLE:
             text = "Queue not available";
             break;
         case CS_ERR_BAD_FLAGS:
             text = "Bad flags";
             break;
         case CS_ERR_TOO_BIG:
             text = "To big";
             break;
         case CS_ERR_NO_SECTIONS:
             text = "No sections";
             break;
     }
 #  endif
     return text;
 }
 
 static inline const char *
 msg_type2text(enum crm_ais_msg_types type)
 {
     const char *text = "unknown";
 
     switch (type) {
         case crm_msg_none:
             text = "unknown";
             break;
         case crm_msg_ais:
             text = "ais";
             break;
         case crm_msg_cib:
             text = "cib";
             break;
         case crm_msg_crmd:
             text = "crmd";
             break;
         case crm_msg_pe:
             text = "pengine";
             break;
         case crm_msg_te:
             text = "tengine";
             break;
         case crm_msg_lrmd:
             text = "lrmd";
             break;
         case crm_msg_attrd:
             text = "attrd";
             break;
         case crm_msg_stonithd:
             text = "stonithd";
             break;
         case crm_msg_stonith_ng:
             text = "stonith-ng";
             break;
     }
     return text;
 }
 
 enum crm_ais_msg_types text2msg_type(const char *text);
 char *get_ais_data(const AIS_Message * msg);
 gboolean check_message_sanity(const AIS_Message * msg, const char *data);
 
 #  if SUPPORT_HEARTBEAT
 extern ll_cluster_t *heartbeat_cluster;
 gboolean send_ha_message(ll_cluster_t * hb_conn, xmlNode * msg,
                          const char *node, gboolean force_ordered);
 gboolean ha_msg_dispatch(ll_cluster_t * cluster_conn, gpointer user_data);
 
 gboolean register_heartbeat_conn(crm_cluster_t * cluster);
 xmlNode *convert_ha_message(xmlNode * parent, HA_Message * msg, const char *field);
 gboolean ccm_have_quorum(oc_ed_t event);
 const char *ccm_event_name(oc_ed_t event);
 crm_node_t *crm_update_ccm_node(const oc_ev_membership_t * oc, int offset, const char *state,
                                 uint64_t seq);
 
 gboolean heartbeat_initialize_nodelist(void *cluster, gboolean force_member, xmlNode * xml_parent);
 #  endif
 
 #  if SUPPORT_COROSYNC
 
 gboolean send_cpg_iov(struct iovec * iov);
 
 #    if SUPPORT_PLUGIN
 char *classic_node_name(uint32_t nodeid);
 void plugin_handle_membership(AIS_Message *msg);
 bool send_plugin_text(int class, struct iovec *iov);
 #    else
 char *corosync_node_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid);
 char *corosync_cluster_name(void);
 int corosync_cmap_has_config(const char *prefix);
 #    endif
 
 gboolean corosync_initialize_nodelist(void *cluster, gboolean force_member, xmlNode * xml_parent);
 
 gboolean send_cluster_message_cs(xmlNode * msg, gboolean local,
                                  crm_node_t * node, enum crm_ais_msg_types dest);
 
 enum cluster_type_e find_corosync_variant(void);
 
 void terminate_cs_connection(crm_cluster_t * cluster);
 gboolean init_cs_connection(crm_cluster_t * cluster);
 gboolean init_cs_connection_once(crm_cluster_t * cluster);
 #  endif
 
 #  ifdef SUPPORT_CMAN
 char *cman_node_name(uint32_t nodeid);
 #  endif
 
 enum crm_quorum_source {
     crm_quorum_cman,
     crm_quorum_corosync,
     crm_quorum_pacemaker,
 };
 
 int get_corosync_id(int id, const char *uuid);
 char *get_corosync_uuid(crm_node_t *peer);
 enum crm_quorum_source get_quorum_source(void);
 
 void crm_update_peer_proc(const char *source, crm_node_t * peer, uint32_t flag, const char *status);
 
 crm_node_t *crm_update_peer(const char *source, 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);
 
 void crm_update_peer_expected(const char *source, crm_node_t * node, const char *expected);
 void crm_update_peer_state(const char *source, crm_node_t * node, const char *state,
                            int membership);
+void crm_reap_unseen_nodes(uint64_t ring_id);
+
 
 gboolean init_cman_connection(gboolean(*dispatch) (unsigned long long, gboolean),
                               void (*destroy) (gpointer));
 
 gboolean cluster_connect_quorum(gboolean(*dispatch) (unsigned long long, gboolean),
                                 void (*destroy) (gpointer));
 
 void set_node_uuid(const char *uname, const char *uuid);
 
 gboolean node_name_is_valid(const char *key, const char *name);
 
 crm_node_t * crm_find_peer_full(unsigned int id, const char *uname, int flags);
 crm_node_t * crm_find_peer(unsigned int id, const char *uname);
 
 #endif
diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c
index 278d41050d..93a10e2d23 100644
--- a/lib/cluster/corosync.c
+++ b/lib/cluster/corosync.c
@@ -1,636 +1,634 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 #include <bzlib.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 
 #include <crm/common/ipc.h>
 #include <crm/cluster/internal.h>
 #include <crm/common/mainloop.h>
 #include <sys/utsname.h>
 
 #include <qb/qbipcc.h>
 #include <qb/qbutil.h>
 
 #include <corosync/corodefs.h>
 #include <corosync/corotypes.h>
 #include <corosync/hdb.h>
 #include <corosync/cfg.h>
 #include <corosync/cmap.h>
 #include <corosync/quorum.h>
 
 #include <crm/msg_xml.h>
 
 quorum_handle_t pcmk_quorum_handle = 0;
 
 gboolean(*quorum_app_callback) (unsigned long long seq, gboolean quorate) = NULL;
 
 /*
  * CFG functionality stolen from node_name() in corosync-quorumtool.c
  * This resolves the first address assigned to a node and returns the name or IP address.
  */
 char *
 corosync_node_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid)
 {
     int lpc = 0;
     int rc = CS_OK;
     int retries = 0;
     char *name = NULL;
     cmap_handle_t local_handle = 0;
 
     /* nodeid == 0 == CMAN_NODEID_US */
     if (nodeid == 0) {
         nodeid = get_local_nodeid(0);
     }
 
     if (cmap_handle == 0 && local_handle == 0) {
         retries = 0;
         crm_trace("Initializing CMAP connection");
         do {
             rc = cmap_initialize(&local_handle);
             if (rc != CS_OK) {
                 retries++;
                 crm_debug("API connection setup failed: %s.  Retrying in %ds", cs_strerror(rc),
                           retries);
                 sleep(retries);
             }
 
         } while (retries < 5 && rc != CS_OK);
 
         if (rc != CS_OK) {
             crm_warn("Could not connect to Cluster Configuration Database API, error %s",
                      cs_strerror(rc));
             local_handle = 0;
         }
     }
 
     if (cmap_handle == 0) {
         cmap_handle = local_handle;
     }
 
     while (name == NULL && cmap_handle != 0) {
         uint32_t id = 0;
         char *key = NULL;
 
         key = crm_strdup_printf("nodelist.node.%d.nodeid", lpc);
         rc = cmap_get_uint32(cmap_handle, key, &id);
         crm_trace("Checking %u vs %u from %s", nodeid, id, key);
         free(key);
 
         if (rc != CS_OK) {
             break;
         }
 
         if (nodeid == id) {
             crm_trace("Searching for node name for %u in nodelist.node.%d %s", nodeid, lpc, name);
             if (name == NULL) {
                 key = crm_strdup_printf("nodelist.node.%d.ring0_addr", lpc);
                 cmap_get_string(cmap_handle, key, &name);
                 crm_trace("%s = %s", key, name);
 
                 if (node_name_is_valid(key, name) == FALSE) {
                     free(name);
                     name = NULL;
                 }
                 free(key);
             }
 
             if (name == NULL) {
                 key = crm_strdup_printf("nodelist.node.%d.name", lpc);
                 cmap_get_string(cmap_handle, key, &name);
                 crm_trace("%s = %s %d", key, name, rc);
                 free(key);
             }
             break;
         }
 
         lpc++;
     }
 
     if(local_handle) {
         cmap_finalize(local_handle);
     }
 
     if (name == NULL) {
         crm_info("Unable to get node name for nodeid %u", nodeid);
     }
     return name;
 }
 
 void
 terminate_cs_connection(crm_cluster_t *cluster)
 {
     crm_notice("Disconnecting from Corosync");
 
     cluster_disconnect_cpg(cluster);
 
     if (pcmk_quorum_handle) {
         crm_trace("Disconnecting quorum");
         quorum_finalize(pcmk_quorum_handle);
         pcmk_quorum_handle = 0;
 
     } else {
         crm_info("No Quorum connection");
     }
 }
 
 int ais_membership_timer = 0;
 gboolean ais_membership_force = FALSE;
 
 
 static int
 pcmk_quorum_dispatch(gpointer user_data)
 {
     int rc = 0;
 
     rc = quorum_dispatch(pcmk_quorum_handle, CS_DISPATCH_ALL);
     if (rc < 0) {
         crm_err("Connection to the Quorum API failed: %d", rc);
         pcmk_quorum_handle = 0;
         return -1;
     }
     return 0;
 }
 
 static void
 pcmk_quorum_notification(quorum_handle_t handle,
                          uint32_t quorate,
                          uint64_t ring_id, uint32_t view_list_entries, uint32_t * view_list)
 {
     int i;
     GHashTableIter iter;
     crm_node_t *node = NULL;
     static gboolean init_phase = TRUE;
 
     if (quorate != crm_have_quorum) {
         crm_notice("Membership " U64T ": quorum %s (%lu)", ring_id,
                    quorate ? "acquired" : "lost", (long unsigned int)view_list_entries);
         crm_have_quorum = quorate;
 
     } else {
         crm_info("Membership " U64T ": quorum %s (%lu)", ring_id,
                  quorate ? "retained" : "still lost", (long unsigned int)view_list_entries);
     }
 
     if (view_list_entries == 0 && init_phase) {
         crm_info("Corosync membership is still forming, ignoring");
         return;
     }
 
     init_phase = FALSE;
 
+    /* Reset last_seen for all cached nodes so we can tell which ones aren't
+     * in the view list */
     g_hash_table_iter_init(&iter, crm_peer_cache);
     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
         node->last_seen = 0;
     }
 
+    /* Update the peer cache for each node in view list */
     for (i = 0; i < view_list_entries; i++) {
         uint32_t id = view_list[i];
-        char *name = NULL;
 
         crm_debug("Member[%d] %u ", i, id);
 
+        /* Get this node's peer cache entry (adding one if not already there) */
         node = crm_get_peer(id, NULL);
         if (node->uname == NULL) {
+            char *name = corosync_node_name(0, id);
+
             crm_info("Obtaining name for new node %u", id);
-            name = corosync_node_name(0, id);
             node = crm_get_peer(id, name);
+            free(name);
         }
 
+        /* Update the node state (including updating last_seen to ring_id) */
         crm_update_peer_state(__FUNCTION__, node, CRM_NODE_MEMBER, ring_id);
-        free(name);
     }
 
-    crm_trace("Reaping unseen nodes...");
-    g_hash_table_iter_init(&iter, crm_peer_cache);
-    while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
-        if (node->last_seen != ring_id && node->state) {
-            crm_update_peer_state(__FUNCTION__, node, CRM_NODE_LOST, 0);
-        } else if (node->last_seen != ring_id) {
-            crm_info("State of node %s[%u] is still unknown", node->uname, node->id);
-        }
-    }
+    /* Remove any peer cache entries we didn't update */
+    crm_reap_unseen_nodes(ring_id);
 
     if (quorum_app_callback) {
         quorum_app_callback(ring_id, quorate);
     }
 }
 
 quorum_callbacks_t quorum_callbacks = {
     .quorum_notify_fn = pcmk_quorum_notification,
 };
 
 gboolean
 cluster_connect_quorum(gboolean(*dispatch) (unsigned long long, gboolean),
                        void (*destroy) (gpointer))
 {
     int rc = -1;
     int fd = 0;
     int quorate = 0;
     uint32_t quorum_type = 0;
     struct mainloop_fd_callbacks quorum_fd_callbacks;
 
     quorum_fd_callbacks.dispatch = pcmk_quorum_dispatch;
     quorum_fd_callbacks.destroy = destroy;
 
     crm_debug("Configuring Pacemaker to obtain quorum from Corosync");
 
     rc = quorum_initialize(&pcmk_quorum_handle, &quorum_callbacks, &quorum_type);
     if (rc != CS_OK) {
         crm_err("Could not connect to the Quorum API: %d\n", rc);
         goto bail;
 
     } else if (quorum_type != QUORUM_SET) {
         crm_err("Corosync quorum is not configured\n");
         goto bail;
     }
 
     rc = quorum_getquorate(pcmk_quorum_handle, &quorate);
     if (rc != CS_OK) {
         crm_err("Could not obtain the current Quorum API state: %d\n", rc);
         goto bail;
     }
 
     crm_notice("Quorum %s", quorate ? "acquired" : "lost");
     quorum_app_callback = dispatch;
     crm_have_quorum = quorate;
 
     rc = quorum_trackstart(pcmk_quorum_handle, CS_TRACK_CHANGES | CS_TRACK_CURRENT);
     if (rc != CS_OK) {
         crm_err("Could not setup Quorum API notifications: %d\n", rc);
         goto bail;
     }
 
     rc = quorum_fd_get(pcmk_quorum_handle, &fd);
     if (rc != CS_OK) {
         crm_err("Could not obtain the Quorum API connection: %d\n", rc);
         goto bail;
     }
 
     mainloop_add_fd("quorum", G_PRIORITY_HIGH, fd, dispatch, &quorum_fd_callbacks);
 
     corosync_initialize_nodelist(NULL, FALSE, NULL);
 
   bail:
     if (rc != CS_OK) {
         quorum_finalize(pcmk_quorum_handle);
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 init_cs_connection(crm_cluster_t * cluster)
 {
     int retries = 0;
 
     while (retries < 5) {
         int rc = init_cs_connection_once(cluster);
 
         retries++;
 
         switch (rc) {
             case CS_OK:
                 return TRUE;
                 break;
             case CS_ERR_TRY_AGAIN:
             case CS_ERR_QUEUE_FULL:
                 sleep(retries);
                 break;
             default:
                 return FALSE;
         }
     }
 
     crm_err("Could not connect to corosync after %d retries", retries);
     return FALSE;
 }
 
 gboolean
 init_cs_connection_once(crm_cluster_t * cluster)
 {
     crm_node_t *peer = NULL;
     enum cluster_type_e stack = get_cluster_type();
 
     crm_peer_init();
 
     /* Here we just initialize comms */
     if (stack != pcmk_cluster_corosync) {
         crm_err("Invalid cluster type: %s (%d)", name_for_cluster_type(stack), stack);
         return FALSE;
     }
 
     if (cluster_connect_cpg(cluster) == FALSE) {
         return FALSE;
     }
     crm_info("Connection to '%s': established", name_for_cluster_type(stack));
 
     cluster->nodeid = get_local_nodeid(0);
     if(cluster->nodeid == 0) {
         crm_err("Could not establish local nodeid");
         return FALSE;
     }
 
     cluster->uname = get_node_name(0);
     if(cluster->uname == NULL) {
         crm_err("Could not establish local node name");
         return FALSE;
     }
 
     /* Ensure the local node always exists */
     peer = crm_get_peer(cluster->nodeid, cluster->uname);
     cluster->uuid = get_corosync_uuid(peer);
 
     return TRUE;
 }
 
 gboolean
 check_message_sanity(const AIS_Message * msg, const char *data)
 {
     gboolean sane = TRUE;
     int dest = msg->host.type;
     int tmp_size = msg->header.size - sizeof(AIS_Message);
 
     if (sane && msg->header.size == 0) {
         crm_warn("Message with no size");
         sane = FALSE;
     }
 
     if (sane && msg->header.error != CS_OK) {
         crm_warn("Message header contains an error: %d", msg->header.error);
         sane = FALSE;
     }
 
     if (sane && ais_data_len(msg) != tmp_size) {
         crm_warn("Message payload size is incorrect: expected %d, got %d", ais_data_len(msg),
                  tmp_size);
         sane = TRUE;
     }
 
     if (sane && ais_data_len(msg) == 0) {
         crm_warn("Message with no payload");
         sane = FALSE;
     }
 
     if (sane && data && msg->is_compressed == FALSE) {
         int str_size = strlen(data) + 1;
 
         if (ais_data_len(msg) != str_size) {
             int lpc = 0;
 
             crm_warn("Message payload is corrupted: expected %d bytes, got %d",
                      ais_data_len(msg), str_size);
             sane = FALSE;
             for (lpc = (str_size - 10); lpc < msg->size; lpc++) {
                 if (lpc < 0) {
                     lpc = 0;
                 }
                 crm_debug("bad_data[%d]: %d / '%c'", lpc, data[lpc], data[lpc]);
             }
         }
     }
 
     if (sane == FALSE) {
         crm_err("Invalid message %d: (dest=%s:%s, from=%s:%s.%u, compressed=%d, size=%d, total=%d)",
                 msg->id, ais_dest(&(msg->host)), msg_type2text(dest),
                 ais_dest(&(msg->sender)), msg_type2text(msg->sender.type),
                 msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size);
 
     } else {
         crm_trace
             ("Verified message %d: (dest=%s:%s, from=%s:%s.%u, compressed=%d, size=%d, total=%d)",
              msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)),
              msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed,
              ais_data_len(msg), msg->header.size);
     }
 
     return sane;
 }
 
 enum cluster_type_e
 find_corosync_variant(void)
 {
     int rc = CS_OK;
     cmap_handle_t handle;
 
     rc = cmap_initialize(&handle);
 
     switch(rc) {
         case CS_OK:
             break;
         case CS_ERR_SECURITY:
             crm_debug("Failed to initialize the cmap API: Permission denied (%d)", rc);
             /* It's there, we just can't talk to it.
              * Good enough for us to identify as 'corosync'
              */
             return pcmk_cluster_corosync;
 
         default:
             crm_info("Failed to initialize the cmap API: %s (%d)",
                      ais_error2text(rc), rc);
             return pcmk_cluster_unknown;
     }
 
     cmap_finalize(handle);
     return pcmk_cluster_corosync;
 }
 
 gboolean
 crm_is_corosync_peer_active(const crm_node_t * node)
 {
     if (node == NULL) {
         crm_trace("NULL");
         return FALSE;
 
     } else if (safe_str_neq(node->state, CRM_NODE_MEMBER)) {
         crm_trace("%s: state=%s", node->uname, node->state);
         return FALSE;
 
     } else if ((node->processes & crm_proc_cpg) == 0) {
         crm_trace("%s: processes=%.16x", node->uname, node->processes);
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 corosync_initialize_nodelist(void *cluster, gboolean force_member, xmlNode * xml_parent)
 {
     int lpc = 0;
     int rc = CS_OK;
     int retries = 0;
     gboolean any = FALSE;
     cmap_handle_t cmap_handle;
 
     do {
         rc = cmap_initialize(&cmap_handle);
         if (rc != CS_OK) {
             retries++;
             crm_debug("API connection setup failed: %s.  Retrying in %ds", cs_strerror(rc),
                       retries);
             sleep(retries);
         }
 
     } while (retries < 5 && rc != CS_OK);
 
     if (rc != CS_OK) {
         crm_warn("Could not connect to Cluster Configuration Database API, error %d", rc);
         return FALSE;
     }
 
     crm_peer_init();
     crm_trace("Initializing corosync nodelist");
     for (lpc = 0;; lpc++) {
         uint32_t nodeid = 0;
         char *name = NULL;
         char *key = NULL;
 
         key = crm_strdup_printf("nodelist.node.%d.nodeid", lpc);
         rc = cmap_get_uint32(cmap_handle, key, &nodeid);
         free(key);
 
         if (rc != CS_OK) {
             break;
         }
 
         name = corosync_node_name(cmap_handle, nodeid);
         if (name != NULL) {
             GHashTableIter iter;
             crm_node_t *node = NULL;
 
             g_hash_table_iter_init(&iter, crm_peer_cache);
             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
                 if(node && node->uname && strcasecmp(node->uname, name) == 0) {
                     if (node->id && node->id != nodeid) {
                         crm_crit("Nodes %u and %u share the same name '%s': shutting down", node->id,
                                  nodeid, name);
                         crm_exit(DAEMON_RESPAWN_STOP);
                     }
                 }
             }
         }
 
         if (nodeid > 0 || name != NULL) {
             crm_trace("Initializing node[%d] %u = %s", lpc, nodeid, name);
             crm_get_peer(nodeid, name);
         }
 
         if (nodeid > 0 && name != NULL) {
             any = TRUE;
 
             if (xml_parent) {
                 char buffer[64];
                 xmlNode *node = create_xml_node(xml_parent, XML_CIB_TAG_NODE);
 
                 if(snprintf(buffer, 63, "%u", nodeid) > 0) {
                     crm_xml_add(node, XML_ATTR_ID, buffer);
                 }
                 crm_xml_add(node, XML_ATTR_UNAME, name);
                 if (force_member) {
                     crm_xml_add(node, XML_ATTR_TYPE, CRM_NODE_MEMBER);
                 }
             }
         }
 
         free(name);
     }
     cmap_finalize(cmap_handle);
     return any;
 }
 
 char *
 corosync_cluster_name(void)
 {
     cmap_handle_t handle;
     char *cluster_name = NULL;
     int rc = CS_OK;
 
     rc = cmap_initialize(&handle);
     if (rc != CS_OK) {
         crm_info("Failed to initialize the cmap API: %s (%d)", ais_error2text(rc), rc);
         return NULL;
     }
 
     rc = cmap_get_string(handle, "totem.cluster_name", &cluster_name);
     if (rc != CS_OK) {
         crm_info("Cannot get totem.cluster_name: %s (%d)", ais_error2text(rc), rc);
 
     } else {
         crm_debug("cmap totem.cluster_name = '%s'", cluster_name);
     }
 
     cmap_finalize(handle);
 
     return cluster_name;
 }
 
 int
 corosync_cmap_has_config(const char *prefix)
 {
     int rc = CS_OK;
     int retries = 0;
     static int found = -1;
     cmap_handle_t cmap_handle;
     cmap_iter_handle_t iter_handle;
     char key_name[CMAP_KEYNAME_MAXLEN + 1];
 
     if(found != -1) {
         return found;
     }
 
     do {
         rc = cmap_initialize(&cmap_handle);
         if (rc != CS_OK) {
             retries++;
             crm_debug("API connection setup failed: %s.  Retrying in %ds", cs_strerror(rc),
                       retries);
             sleep(retries);
         }
 
     } while (retries < 5 && rc != CS_OK);
 
     if (rc != CS_OK) {
         crm_warn("Could not connect to Cluster Configuration Database API: %s (rc=%d)",
                  cs_strerror(rc), rc);
         return -1;
     }
 
     rc = cmap_iter_init(cmap_handle, prefix, &iter_handle);
     if (rc != CS_OK) {
         crm_warn("Failed to initialize iteration for corosync cmap '%s': %s (rc=%d)",
                  prefix, cs_strerror(rc), rc);
         goto bail;
     }
 
     found = 0;
     while ((rc = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL)) == CS_OK) {
         crm_trace("'%s' is configured in corosync cmap: %s", prefix, key_name);
         found++;
         break;
     }
     cmap_iter_finalize(cmap_handle, iter_handle);
 
 bail:
     cmap_finalize(cmap_handle);
 
     return found;
 }
diff --git a/lib/cluster/membership.c b/lib/cluster/membership.c
index 8b4b5e32e2..f632af79b7 100644
--- a/lib/cluster/membership.c
+++ b/lib/cluster/membership.c
@@ -1,744 +1,787 @@
 /*
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include <crm_internal.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <glib.h>
 #include <crm/common/ipc.h>
 #include <crm/cluster/internal.h>
 #include <crm/msg_xml.h>
 #include <crm/stonith-ng.h>
 
 GHashTable *crm_peer_cache = NULL;
 GHashTable *crm_remote_peer_cache = NULL;
 unsigned long long crm_peer_seq = 0;
 gboolean crm_have_quorum = FALSE;
 
 int
 crm_remote_peer_cache_size(void)
 {
     if (crm_remote_peer_cache == NULL) {
         return 0;
     }
     return g_hash_table_size(crm_remote_peer_cache);
 }
 
 void
 crm_remote_peer_cache_add(const char *node_name)
 {
     crm_node_t *node = g_hash_table_lookup(crm_remote_peer_cache, node_name);
 
     if (node == NULL) {
             crm_trace("added %s to remote cache", node_name);
             node = calloc(1, sizeof(crm_node_t));
             node->flags = crm_remote_node;
             CRM_ASSERT(node);
             node->uname = strdup(node_name);
             node->uuid = strdup(node_name);
             node->state = strdup(CRM_NODE_MEMBER);
             g_hash_table_replace(crm_remote_peer_cache, node->uname, node);
     }
 }
 
 void
 crm_remote_peer_cache_remove(const char *node_name)
 {
     g_hash_table_remove(crm_remote_peer_cache, node_name);
 }
 
 static void
 remote_cache_refresh_helper(xmlNode *cib, const char *xpath, const char *field, int flags)
 {
     const char *remote = NULL;
     crm_node_t *node = NULL;
     xmlXPathObjectPtr xpathObj = NULL;
     int max = 0;
     int lpc = 0;
 
     xpathObj = xpath_search(cib, xpath);
     max = numXpathResults(xpathObj);
     for (lpc = 0; lpc < max; lpc++) {
         xmlNode *xml = getXpathResult(xpathObj, lpc);
 
         CRM_LOG_ASSERT(xml != NULL);
         if(xml != NULL) {
             remote = crm_element_value(xml, field);
         }
 
         if (remote) {
             crm_trace("added %s to remote cache", remote);
             node = calloc(1, sizeof(crm_node_t));
             node->flags = flags;
             CRM_ASSERT(node);
             node->uname = strdup(remote);
             node->uuid = strdup(remote);
             node->state = strdup(CRM_NODE_MEMBER);
             g_hash_table_replace(crm_remote_peer_cache, node->uname, node);
         }
     }
     freeXpathObject(xpathObj);
 }
 
 void crm_remote_peer_cache_refresh(xmlNode *cib)
 {
     const char *xpath = NULL;
 
     g_hash_table_remove_all(crm_remote_peer_cache);
 
     /* remote nodes associated with a cluster resource */
     xpath = "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE "//" XML_TAG_META_SETS "//" XML_CIB_TAG_NVPAIR "[@name='remote-node']";
     remote_cache_refresh_helper(cib, xpath, "value", crm_remote_node | crm_remote_container);
 
     /* baremetal nodes defined by connection resources*/
     xpath = "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE "[@type='remote'][@provider='pacemaker']";
     remote_cache_refresh_helper(cib, xpath, "id", crm_remote_node | crm_remote_baremetal);
 
     /* baremetal nodes we have seen in the config that may or may not have connection
      * resources associated with them anymore */
     xpath = "//" XML_TAG_CIB "//" XML_CIB_TAG_STATUS "//" XML_CIB_TAG_STATE "[@remote_node='true']";
     remote_cache_refresh_helper(cib, xpath, "id", crm_remote_node | crm_remote_baremetal);
 }
 
 gboolean
 crm_is_peer_active(const crm_node_t * node)
 {
     if(node == NULL) {
         return FALSE;
     }
 
     if (is_set(node->flags, crm_remote_node)) {
         /* remote nodes are never considered active members. This
          * guarantees they will never be considered for DC membership.*/
         return FALSE;
     }
 #if SUPPORT_COROSYNC
     if (is_openais_cluster()) {
         return crm_is_corosync_peer_active(node);
     }
 #endif
 #if SUPPORT_HEARTBEAT
     if (is_heartbeat_cluster()) {
         return crm_is_heartbeat_peer_active(node);
     }
 #endif
     crm_err("Unhandled cluster type: %s", name_for_cluster_type(get_cluster_type()));
     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) {
         return FALSE;
 
     } else if (search->id && node->id != search->id) {
         return FALSE;
 
     } else if (search->id == 0 && safe_str_neq(node->uname, search->uname)) {
         return FALSE;
 
     } else if (crm_is_peer_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, const char *name)
 {
     int matches = 0;
     crm_node_t search;
 
     if (crm_peer_cache == NULL) {
         crm_trace("Nothing to do, cache not initialized");
         return 0;
     }
 
     search.id = id;
     search.uname = name ? strdup(name) : NULL;
     matches = g_hash_table_foreach_remove(crm_peer_cache, crm_reap_dead_member, &search);
     if(matches) {
         crm_notice("Purged %d peers with id=%u and/or uname=%s from the membership cache",
                    matches, search.id, search.uname);
 
     } else {
         crm_info("No peers with id=%u and/or uname=%s exist", id, name);
     }
 
     free(search.uname);
     return matches;
 }
 
 static void
 crm_count_peer(gpointer key, gpointer value, gpointer user_data)
 {
     guint *count = user_data;
     crm_node_t *node = value;
 
     if (crm_is_peer_active(node)) {
         *count = *count + 1;
     }
 }
 
 guint
 crm_active_peers(void)
 {
     guint count = 0;
 
     if (crm_peer_cache) {
         g_hash_table_foreach(crm_peer_cache, crm_count_peer, &count);
     }
     return count;
 }
 
 static void
 destroy_crm_node(gpointer data)
 {
     crm_node_t *node = data;
 
     crm_trace("Destroying entry for node %u: %s", node->id, node->uname);
 
     free(node->addr);
     free(node->uname);
     free(node->state);
     free(node->uuid);
     free(node->expected);
     free(node);
 }
 
 void
 crm_peer_init(void)
 {
     if (crm_peer_cache == NULL) {
         crm_peer_cache = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, free, destroy_crm_node);
     }
 
     if (crm_remote_peer_cache == NULL) {
         crm_remote_peer_cache = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, destroy_crm_node);
     }
 }
 
 void
 crm_peer_destroy(void)
 {
     if (crm_peer_cache != NULL) {
         crm_trace("Destroying peer cache with %d members", g_hash_table_size(crm_peer_cache));
         g_hash_table_destroy(crm_peer_cache);
         crm_peer_cache = NULL;
     }
 
     if (crm_remote_peer_cache != NULL) {
         crm_trace("Destroying remote peer cache with %d members", g_hash_table_size(crm_remote_peer_cache));
         g_hash_table_destroy(crm_remote_peer_cache);
         crm_remote_peer_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 void crm_dump_peer_hash(int level, const char *caller)
 {
     GHashTableIter iter;
     const char *id = NULL;
     crm_node_t *node = NULL;
 
     g_hash_table_iter_init(&iter, crm_peer_cache);
     while (g_hash_table_iter_next(&iter, (gpointer *) &id, (gpointer *) &node)) {
         do_crm_log(level, "%s: Node %u/%s = %p - %s", caller, node->id, node->uname, node, id);
     }
 }
 
 static gboolean crm_hash_find_by_data(gpointer key, gpointer value, gpointer user_data)
 {
     if(value == user_data) {
         return TRUE;
     }
     return FALSE;
 }
 
 crm_node_t *
 crm_find_peer_full(unsigned int id, const char *uname, int flags)
 {
     crm_node_t *node = NULL;
 
     CRM_ASSERT(id > 0 || uname != NULL);
 
     crm_peer_init();
 
     if (flags & CRM_GET_PEER_REMOTE) {
         node = g_hash_table_lookup(crm_remote_peer_cache, uname);
     }
 
     if (node == NULL && (flags & CRM_GET_PEER_CLUSTER)) {
         node = crm_find_peer(id, uname);
     }
     return node;
 }
 
 crm_node_t *
 crm_get_peer_full(unsigned int id, const char *uname, int flags)
 {
     crm_node_t *node = NULL;
 
     CRM_ASSERT(id > 0 || uname != NULL);
 
     crm_peer_init();
 
     if (flags & CRM_GET_PEER_REMOTE) {
         node = g_hash_table_lookup(crm_remote_peer_cache, uname);
     }
 
     if (node == NULL && (flags & CRM_GET_PEER_CLUSTER)) {
         node = crm_get_peer(id, uname);
     }
     return node;
 }
 
 crm_node_t *
 crm_find_peer(unsigned int id, const char *uname)
 {
     GHashTableIter iter;
     crm_node_t *node = NULL;
     crm_node_t *by_id = NULL;
     crm_node_t *by_name = NULL;
 
     CRM_ASSERT(id > 0 || uname != NULL);
 
     crm_peer_init();
 
     if (uname != NULL) {
         g_hash_table_iter_init(&iter, crm_peer_cache);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
             if(node->uname && strcasecmp(node->uname, uname) == 0) {
                 crm_trace("Name match: %s = %p", node->uname, node);
                 by_name = node;
                 break;
             }
         }
     }
 
     if (id > 0) {
         g_hash_table_iter_init(&iter, crm_peer_cache);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
             if(node->id == id) {
                 crm_trace("ID match: %u = %p", node->id, node);
                 by_id = node;
                 break;
             }
         }
     }
 
     node = by_id; /* Good default */
     if(by_id == by_name) {
         /* Nothing to do if they match (both NULL counts) */
         crm_trace("Consistent: %p for %u/%s", by_id, id, uname);
 
     } else if(by_id == NULL && by_name) {
         crm_trace("Only one: %p for %u/%s", by_name, id, uname);
 
         if(id && by_name->id) {
             crm_dump_peer_hash(LOG_WARNING, __FUNCTION__);
             crm_crit("Node %u and %u share the same name '%s'",
                      id, by_name->id, uname);
             node = NULL; /* Create a new one */
 
         } else {
             node = by_name;
         }
 
     } else if(by_name == NULL && by_id) {
         crm_trace("Only one: %p for %u/%s", by_id, id, uname);
 
         if(uname && by_id->uname) {
             crm_dump_peer_hash(LOG_WARNING, __FUNCTION__);
             crm_crit("Node '%s' and '%s' share the same cluster nodeid %u: assuming '%s' is correct",
                      uname, by_id->uname, id, uname);
         }
 
     } else if(uname && by_id->uname) {
         if(safe_str_eq(uname, by_id->uname)) {
             crm_notice("Node '%s' has changed its ID from %u to %u", by_id->uname, by_name->id, by_id->id);
             g_hash_table_foreach_remove(crm_peer_cache, crm_hash_find_by_data, by_name);
 
         } else {
             crm_warn("Node '%s' and '%s' share the same cluster nodeid: %u %s", by_id->uname, by_name->uname, id, uname);
             crm_dump_peer_hash(LOG_INFO, __FUNCTION__);
             crm_abort(__FILE__, __FUNCTION__, __LINE__, "member weirdness", TRUE, TRUE);
         }
 
     } else if(id && by_name->id) {
         crm_warn("Node %u and %u share the same name: '%s'", by_id->id, by_name->id, uname);
 
     } else {
         /* Simple merge */
 
         /* Only corosync based clusters use nodeid's
          *
          * The functions that call crm_update_peer_state() only know nodeid
          * so 'by_id' is authorative when merging
          *
          * Same for crm_update_peer_proc()
          */
         crm_dump_peer_hash(LOG_DEBUG, __FUNCTION__);
 
         crm_info("Merging %p into %p", by_name, by_id);
         g_hash_table_foreach_remove(crm_peer_cache, crm_hash_find_by_data, by_name);
     }
 
     return node;
 }
 
 #if SUPPORT_COROSYNC
 static guint
 crm_remove_conflicting_peer(crm_node_t *node)
 {
     int matches = 0;
     GHashTableIter iter;
     crm_node_t *existing_node = NULL;
 
     if (node->id == 0 || node->uname == NULL) {
         return 0;
     }
 
 #  if !SUPPORT_PLUGIN
     if (corosync_cmap_has_config("nodelist") != 0) {
         return 0;
     }
 #  endif
 
     g_hash_table_iter_init(&iter, crm_peer_cache);
     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &existing_node)) {
         if (existing_node->id > 0
             && existing_node->id != node->id
             && existing_node->uname != NULL
             && strcasecmp(existing_node->uname, node->uname) == 0) {
 
             if (crm_is_peer_active(existing_node)) {
                 continue;
             }
 
             crm_warn("Removing cached offline node %u/%s which has conflicting uname with %u",
                      existing_node->id, existing_node->uname, node->id);
 
             g_hash_table_iter_remove(&iter);
             matches++;
         }
     }
 
     return matches;
 }
 #endif
 
 /* coverity[-alloc] Memory is referenced in one or both hashtables */
 crm_node_t *
 crm_get_peer(unsigned int id, const char *uname)
 {
     crm_node_t *node = NULL;
     char *uname_lookup = NULL;
 
     CRM_ASSERT(id > 0 || uname != NULL);
 
     crm_peer_init();
 
     node = crm_find_peer(id, uname);
 
     /* if uname wasn't provided, and find_peer did not turn up a uname based on id.
      * we need to do a lookup of the node name using the id in the cluster membership. */
     if ((node == NULL || node->uname == NULL) && (uname == NULL)) { 
         uname_lookup = get_node_name(id);
     }
 
     if (uname_lookup) {
         uname = uname_lookup;
         crm_trace("Inferred a name of '%s' for node %u", uname, id);
 
         /* try to turn up the node one more time now that we know the uname. */
         if (node == NULL) {
             node = crm_find_peer(id, uname);
         }
     }
 
 
     if (node == NULL) {
         char *uniqueid = crm_generate_uuid();
 
         node = calloc(1, sizeof(crm_node_t));
         CRM_ASSERT(node);
 
         crm_info("Created entry %s/%p for node %s/%u (%d total)",
                  uniqueid, node, uname, id, 1 + g_hash_table_size(crm_peer_cache));
         g_hash_table_replace(crm_peer_cache, uniqueid, node);
     }
 
     if(id > 0 && uname && (node->id == 0 || node->uname == NULL)) {
         crm_info("Node %u is now known as %s", id, uname);
     }
 
     if(id > 0 && node->id == 0) {
         node->id = id;
     }
 
     if(uname && node->uname == NULL) {
         int lpc, len = strlen(uname);
 
         for (lpc = 0; lpc < len; lpc++) {
             if (uname[lpc] >= 'A' && uname[lpc] <= 'Z') {
                 crm_warn("Node names with capitals are discouraged, consider changing '%s' to something else",
                          uname);
                 break;
             }
         }
 
         node->uname = strdup(uname);
         if (crm_status_callback) {
             crm_status_callback(crm_status_uname, node, NULL);
         }
 
 #if SUPPORT_COROSYNC
         if (is_openais_cluster()) {
             crm_remove_conflicting_peer(node);
         }
 #endif
     }
 
     if(node->uuid == NULL) {
         const char *uuid = crm_peer_uuid(node);
 
         if (uuid) {
             crm_info("Node %u has uuid %s", id, uuid);
 
         } else {
             crm_info("Cannot obtain a UUID for node %u/%s", id, node->uname);
         }
     }
 
     free(uname_lookup);
 
     return node;
 }
 
 crm_node_t *
 crm_update_peer(const char *source, 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)
 {
 #if SUPPORT_PLUGIN
     gboolean addr_changed = FALSE;
     gboolean votes_changed = FALSE;
 #endif
     crm_node_t *node = NULL;
 
     id = get_corosync_id(id, uuid);
     node = crm_get_peer(id, uname);
 
     CRM_ASSERT(node != NULL);
 
     if (node->uuid == NULL) {
         if (is_openais_cluster()) {
             /* Yes, overrule whatever was passed in */
             crm_peer_uuid(node);
 
         } else if (uuid != NULL) {
             node->uuid = strdup(uuid);
         }
     }
 
     if (children > 0) {
         crm_update_peer_proc(source, node, children, state);
     }
 
     if (state != NULL) {
         crm_update_peer_state(source, node, state, seen);
     }
 #if SUPPORT_HEARTBEAT
     if (born != 0) {
         node->born = born;
     }
 #endif
 
 #if SUPPORT_PLUGIN
     /* These were only used by the plugin */
     if (born != 0) {
         node->born = born;
     }
 
     if (votes > 0 && node->votes != votes) {
         votes_changed = TRUE;
         node->votes = votes;
     }
 
     if (addr != NULL) {
         if (node->addr == NULL || crm_str_eq(node->addr, addr, FALSE) == FALSE) {
             addr_changed = TRUE;
             free(node->addr);
             node->addr = strdup(addr);
         }
     }
     if (addr_changed || votes_changed) {
         crm_info("%s: Node %s: id=%u state=%s addr=%s%s votes=%d%s born=" U64T " seen=" U64T
                  " proc=%.32x", source, node->uname, node->id, node->state,
                  node->addr, addr_changed ? " (new)" : "", node->votes,
                  votes_changed ? " (new)" : "", node->born, node->last_seen, node->processes);
     }
 #endif
 
     return node;
 }
 
 void
 crm_update_peer_proc(const char *source, crm_node_t * node, uint32_t flag, const char *status)
 {
     uint32_t last = 0;
     gboolean changed = FALSE;
 
     CRM_CHECK(node != NULL, crm_err("%s: Could not set %s to %s for NULL",
                                     source, peer2text(flag), status); return);
 
     last = node->processes;
     if (status == NULL) {
         node->processes = flag;
         if (node->processes != last) {
             changed = TRUE;
         }
 
     } else if (safe_str_eq(status, ONLINESTATUS)) {
         if ((node->processes & flag) != flag) {
             set_bit(node->processes, flag);
             changed = TRUE;
         }
 #if SUPPORT_PLUGIN
     } else if (safe_str_eq(status, CRM_NODE_MEMBER)) {
         if (flag > 0 && node->processes != flag) {
             node->processes = flag;
             changed = TRUE;
         }
 #endif
 
     } else if (node->processes & flag) {
         clear_bit(node->processes, flag);
         changed = TRUE;
     }
 
     if (changed) {
         if (status == NULL && flag <= crm_proc_none) {
             crm_info("%s: Node %s[%u] - all processes are now offline", source, node->uname,
                      node->id);
         } else {
             crm_info("%s: Node %s[%u] - %s is now %s", source, node->uname, node->id,
                      peer2text(flag), status);
         }
 
         if (crm_status_callback) {
             crm_status_callback(crm_status_processes, node, &last);
         }
     } else {
         crm_trace("%s: Node %s[%u] - %s is unchanged (%s)", source, node->uname, node->id,
                   peer2text(flag), status);
     }
 }
 
 void
 crm_update_peer_expected(const char *source, crm_node_t * node, const char *expected)
 {
     char *last = NULL;
     gboolean changed = FALSE;
 
     CRM_CHECK(node != NULL, crm_err("%s: Could not set 'expected' to %s", source, expected);
               return);
 
     last = node->expected;
     if (expected != NULL && safe_str_neq(node->expected, expected)) {
         node->expected = strdup(expected);
         changed = TRUE;
     }
 
     if (changed) {
         crm_info("%s: Node %s[%u] - expected state is now %s (was %s)", source, node->uname, node->id,
                  expected, last);
         free(last);
     } else {
         crm_trace("%s: Node %s[%u] - expected state is unchanged (%s)", source, node->uname,
                   node->id, expected);
     }
 }
 
 void
 crm_update_peer_state(const char *source, crm_node_t * node, const char *state, int membership)
 {
     char *last = NULL;
     gboolean changed = FALSE;
 
     CRM_CHECK(node != NULL, crm_err("%s: Could not set 'state' to %s", source, state);
               return);
 
     last = node->state;
     if (state != NULL && safe_str_neq(node->state, state)) {
         node->state = strdup(state);
         changed = TRUE;
     }
 
     if (membership != 0 && safe_str_eq(node->state, CRM_NODE_MEMBER)) {
         node->last_seen = membership;
     }
 
     if (changed) {
         crm_notice("%s: Node %s[%u] - state is now %s (was %s)", source, node->uname, node->id, state, last);
         if (crm_status_callback) {
             enum crm_status_type status_type = crm_status_nstate;
             if (is_set(node->flags, crm_remote_node)) {
                 status_type = crm_status_rstate;
             }
             crm_status_callback(status_type, node, last);
         }
         free(last);
     } else {
         crm_trace("%s: Node %s[%u] - state is unchanged (%s)", source, node->uname, node->id,
                   state);
     }
 }
 
+/*!
+ * \internal
+ * \brief Reap all nodes from cache whose membership information does not match
+ *
+ * \param[in] membership  Membership ID of nodes to keep
+ */
+void
+crm_reap_unseen_nodes(uint64_t membership)
+{
+    GHashTableIter iter;
+    crm_node_t *node = NULL;
+
+    crm_trace("Reaping unseen nodes...");
+    g_hash_table_iter_init(&iter, crm_peer_cache);
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&node)) {
+        if (node->last_seen != membership) {
+            if (node->state) {
+                /* crm_update_peer_state() cannot be called here, because that
+                 * might modify the peer cache, invalidating our iterator
+                 */
+                if (safe_str_eq(node->state, CRM_NODE_LOST)) {
+                    crm_trace("Node %s[%u] - state is unchanged (%s)",
+                              node->uname, node->id, CRM_NODE_LOST);
+                } else {
+                    char *last = node->state;
+
+                    node->state = strdup(CRM_NODE_LOST);
+                    crm_notice("Node %s[%u] - state is now %s (was %s)",
+                               node->uname, node->id, CRM_NODE_LOST, last);
+                    if (crm_status_callback) {
+                        crm_status_callback(crm_status_nstate, node, last);
+                    }
+                    free(last);
+                    g_hash_table_iter_remove(&iter);
+                }
+            } else {
+                crm_info("State of node %s[%u] is still unknown",
+                         node->uname, node->id);
+            }
+        }
+    }
+}
+
 int
 crm_terminate_member(int nodeid, const char *uname, void *unused)
 {
     /* Always use the synchronous, non-mainloop version */
     return stonith_api_kick(nodeid, uname, 120, TRUE);
 }
 
 int
 crm_terminate_member_no_mainloop(int nodeid, const char *uname, int *connection)
 {
     return stonith_api_kick(nodeid, uname, 120, TRUE);
 }