Beispiel #1
0
/*!
 * \internal
 * \brief Handle cluster communication related to pacemaker_remote node leaving
 *
 * \param[in] node_name  Name of lost node
 */
static void
remote_node_down(const char *node_name)
{
    xmlNode *update;
    int call_id = 0;
    int call_opt = crmd_cib_smart_opt();
    crm_node_t *node;

    /* Clear all node attributes */
    update_attrd_remote_node_removed(node_name, NULL);

    /* Ensure node is in the remote peer cache with lost state */
    node = crm_remote_peer_get(node_name);
    CRM_CHECK(node != NULL, return);
    crm_update_peer_state(__FUNCTION__, node, CRM_NODE_LOST, 0);

    /* Notify DC */
    send_remote_state_message(node_name, FALSE);

    /* Update CIB node state */
    update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
    do_update_node_cib(node, node_update_cluster, update, __FUNCTION__);
    fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, call_id, NULL);
    if (call_id < 0) {
        crm_perror(LOG_ERR, "%s CIB node state update", node_name);
    }
    free_xml(update);
}
Beispiel #2
0
/*!
 * \internal
 * \brief Handle cluster communication related to pacemaker_remote node leaving
 *
 * \param[in] node_name  Name of lost node
 */
static void
remote_node_down(const char *node_name)
{
    xmlNode *update;
    int call_id = 0;
    int call_opt = crmd_cib_smart_opt();
    crm_node_t *node;

    /* Purge node from attrd's memory */
    update_attrd_remote_node_removed(node_name, NULL);

    /* Purge node's operation history and transient attributes from CIB */
    erase_status_tag(node_name, XML_CIB_TAG_LRM, call_opt);
    erase_status_tag(node_name, XML_TAG_TRANSIENT_NODEATTRS, call_opt);

    /* Ensure node is in the remote peer cache with lost state */
    node = crm_remote_peer_get(node_name);
    CRM_CHECK(node != NULL, return);
    crm_update_peer_state(__FUNCTION__, node, CRM_NODE_LOST, 0);

    /* Notify DC */
    send_remote_state_message(node_name, FALSE);

    /* Update CIB node state */
    update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
    do_update_node_cib(node, node_update_cluster, update, __FUNCTION__);
    fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, call_id, NULL);
    if (call_id < 0) {
        crm_perror(LOG_ERR, "%s CIB node state update", node_name);
    }
    free_xml(update);
}
Beispiel #3
0
/*!
 * \internal
 * \brief Handle cluster communication related to pacemaker_remote node joining
 *
 * \param[in] node_name  Name of newly integrated pacemaker_remote node
 */
static void
remote_node_up(const char *node_name)
{
    int call_opt, call_id = 0;
    xmlNode *update, *state;
    crm_node_t *node;

    CRM_CHECK(node_name != NULL, return);
    crm_info("Announcing pacemaker_remote node %s", node_name);

    /* Clear node's operation history. The node's transient attributes should
     * and normally will be cleared when the node leaves, but since remote node
     * state has a number of corner cases, clear them here as well, to be sure.
     */
    call_opt = crmd_cib_smart_opt();
    erase_status_tag(node_name, XML_CIB_TAG_LRM, call_opt);
    erase_status_tag(node_name, XML_TAG_TRANSIENT_NODEATTRS, call_opt);

    /* Clear node's probed attribute */
    update_attrd(node_name, CRM_OP_PROBED, NULL, NULL, TRUE);

    /* Ensure node is in the remote peer cache with member status */
    node = crm_remote_peer_get(node_name);
    CRM_CHECK(node != NULL, return);
    crm_update_peer_state(__FUNCTION__, node, CRM_NODE_MEMBER, 0);

    /* pacemaker_remote nodes don't participate in the membership layer,
     * so cluster nodes don't automatically get notified when they come and go.
     * We send a cluster message to the DC, and update the CIB node state entry,
     * so the DC will get it sooner (via message) or later (via CIB refresh),
     * and any other interested parties can query the CIB.
     */
    send_remote_state_message(node_name, TRUE);

    update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
    state = create_node_state_update(node, node_update_cluster, update,
                                     __FUNCTION__);

    /* Clear the XML_NODE_IS_FENCED flag in the node state. If the node ever
     * needs to be fenced, this flag will allow various actions to determine
     * whether the fencing has happened yet.
     */
    crm_xml_add(state, XML_NODE_IS_FENCED, "0");

    /* TODO: If the remote connection drops, and this (async) CIB update either
     * failed or has not yet completed, later actions could mistakenly think the
     * node has already been fenced (if the XML_NODE_IS_FENCED attribute was
     * previously set, because it won't have been cleared). This could prevent
     * actual fencing or allow recurring monitor failures to be cleared too
     * soon. Ideally, we wouldn't rely on the CIB for the fenced status.
     */
    fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, call_id, NULL);
    if (call_id < 0) {
        crm_perror(LOG_WARNING, "%s CIB node state setup", node_name);
    }
    free_xml(update);
}
Beispiel #4
0
/*	 A_DC_TAKEOVER	*/
void
do_dc_takeover(long long action,
               enum crmd_fsa_cause cause,
               enum crmd_fsa_state cur_state,
               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
    int rc = pcmk_ok;
    xmlNode *cib = NULL;
    GListPtr gIter = NULL;
    const char *cluster_type = name_for_cluster_type(get_cluster_type());

    crm_info("Taking over DC status for this partition");
    set_bit(fsa_input_register, R_THE_DC);

    for (gIter = stonith_cleanup_list; gIter != NULL; gIter = gIter->next) {
        char *target = gIter->data;
        crm_node_t *target_node = crm_get_peer(0, target);
        const char *uuid = crm_peer_uuid(target_node);

        crm_notice("Marking %s, target of a previous stonith action, as clean", target);
        send_stonith_update(NULL, target, uuid);
        free(target);
    }
    g_list_free(stonith_cleanup_list);
    stonith_cleanup_list = NULL;

#if SUPPORT_COROSYNC
    if (is_classic_ais_cluster()) {
        send_ais_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais);
    }
#endif

    if (voted != NULL) {
        crm_trace("Destroying voted hash");
        g_hash_table_destroy(voted);
        voted = NULL;
    }

    set_bit(fsa_input_register, R_JOIN_OK);
    set_bit(fsa_input_register, R_INVOKE_PE);

    fsa_cib_conn->cmds->set_master(fsa_cib_conn, cib_scope_local);

    cib = create_xml_node(NULL, XML_TAG_CIB);
    crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
    fsa_cib_update(XML_TAG_CIB, cib, cib_quorum_override, rc, NULL);
    fsa_register_cib_callback(rc, FALSE, NULL, feature_update_callback);

    update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
                         "dc-version", VERSION "-" BUILD_VERSION, FALSE, NULL);

    update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
                         "cluster-infrastructure", cluster_type, FALSE, NULL);

    mainloop_set_trigger(config_read);
    free_xml(cib);
}
Beispiel #5
0
/*	A_DC_JOIN_PROCESS_ACK	*/
void
do_dc_join_ack(long long action,
               enum crmd_fsa_cause cause,
               enum crmd_fsa_state cur_state,
               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
    int join_id = -1;
    int call_id = 0;
    ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg);

    const char *op = crm_element_value(join_ack->msg, F_CRM_TASK);
    const char *join_from = crm_element_value(join_ack->msg, F_CRM_HOST_FROM);
    crm_node_t *peer = crm_get_peer(0, join_from);

    if (safe_str_neq(op, CRM_OP_JOIN_CONFIRM) || peer == NULL) {
        crm_debug("Ignoring op=%s message from %s", op, join_from);
        return;
    }

    crm_trace("Processing ack from %s", join_from);
    crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id);

    if (peer->join != crm_join_finalized) {
        crm_info("Join not in progress: ignoring join-%d from %s (phase = %d)",
                 join_id, join_from, peer->join);
        return;

    } else if (join_id != current_join_id) {
        crm_err("Invalid response from %s: join-%d vs. join-%d",
                join_from, join_id, current_join_id);
        crm_update_peer_join(__FUNCTION__, peer, crm_join_nack);
        return;
    }

    crm_update_peer_join(__FUNCTION__, peer, crm_join_confirmed);

    crm_info("join-%d: Updating node state to %s for %s",
             join_id, CRMD_JOINSTATE_MEMBER, join_from);

    /* update CIB with the current LRM status from the node
     * We dont need to notify the TE of these updates, a transition will
     *   be started in due time
     */
    erase_status_tag(join_from, XML_CIB_TAG_LRM, cib_scope_local);
    fsa_cib_update(XML_CIB_TAG_STATUS, join_ack->xml,
                   cib_scope_local | cib_quorum_override | cib_can_create, call_id, NULL);
    fsa_register_cib_callback(call_id, FALSE, NULL, join_update_complete_callback);
    crm_debug("join-%d: Registered callback for LRM update %d", join_id, call_id);
}
Beispiel #6
0
void
crm_update_quorum(gboolean quorum, gboolean force_update)
{
    ever_had_quorum |= quorum;
    if (AM_I_DC && (force_update || fsa_has_quorum != quorum)) {
        int call_id = 0;
        xmlNode *update = NULL;
        int call_options = cib_scope_local | cib_quorum_override;

        update = create_xml_node(NULL, XML_TAG_CIB);
        crm_xml_add_int(update, XML_ATTR_HAVE_QUORUM, quorum);
        crm_xml_add(update, XML_ATTR_DC_UUID, fsa_our_uuid);

        fsa_cib_update(XML_TAG_CIB, update, call_options, call_id, NULL);
        crm_debug("Updating quorum status to %s (call=%d)", quorum ? "true" : "false", call_id);
        fsa_register_cib_callback(call_id, FALSE, NULL, cib_quorum_update_complete);
        free_xml(update);
    }
    fsa_has_quorum = quorum;
}
Beispiel #7
0
/*!
 * \internal
 * \brief Handle cluster communication related to pacemaker_remote node leaving
 *
 * \param[in] node_name  Name of lost node
 * \param[in] opts       Whether to keep or erase LRM history
 */
static void
remote_node_down(const char *node_name, const enum down_opts opts)
{
    xmlNode *update;
    int call_id = 0;
    int call_opt = crmd_cib_smart_opt();
    crm_node_t *node;

    /* Purge node from attrd's memory */
    update_attrd_remote_node_removed(node_name, NULL);

    /* Purge node's transient attributes */
    erase_status_tag(node_name, XML_TAG_TRANSIENT_NODEATTRS, call_opt);

    /* Normally, the LRM operation history should be kept until the node comes
     * back up. However, after a successful fence, we want to clear it, so we
     * don't think resources are still running on the node.
     */
    if (opts == DOWN_ERASE_LRM) {
        erase_status_tag(node_name, XML_CIB_TAG_LRM, call_opt);
    }

    /* Ensure node is in the remote peer cache with lost state */
    node = crm_remote_peer_get(node_name);
    CRM_CHECK(node != NULL, return);
    crm_update_peer_state(__FUNCTION__, node, CRM_NODE_LOST, 0);

    /* Notify DC */
    send_remote_state_message(node_name, FALSE);

    /* Update CIB node state */
    update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
    create_node_state_update(node, node_update_cluster, update, __FUNCTION__);
    fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, call_id, NULL);
    if (call_id < 0) {
        crm_perror(LOG_ERR, "%s CIB node state update", node_name);
    }
    free_xml(update);
}
/*
	CRM_OP_JOIN_ACKNACKを送信後、ノードからCRM_OP_JOIN_CONFIRMメッセージが送られて来たときの処理
*/
void
do_dc_join_ack(long long action,
	       enum crmd_fsa_cause cause,
	       enum crmd_fsa_state cur_state,
	       enum crmd_fsa_input current_input,
	       fsa_data_t *msg_data)
{
	int join_id = -1;
	int call_id = 0;
	ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg);

	const char *join_id_s  = NULL;
	const char *join_state = NULL;
	const char *op         = crm_element_value(join_ack->msg, F_CRM_TASK);
	const char *join_from  = crm_element_value(join_ack->msg, F_CRM_HOST_FROM);

	if(safe_str_neq(op, CRM_OP_JOIN_CONFIRM)) {
		/* CRM_OP_JOIN_CONFIRMメッセージ以外のメッセージは無視してログを出力 */
		crm_debug("Ignoring op=%s message from %s", op, join_from);
		return;
	} 

	crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id);
	join_id_s = crm_element_value(join_ack->msg, F_CRM_JOIN_ID);

	/* now update them to "member" */
	
	crm_debug_2("Processing ack from %s", join_from);

	/* CRM_OP_JOIN_CONFIRMを送ってきたノードがfinalized_nodesハッシュテーブルに含まれるかサーチする */
	join_state = (const char *)
		g_hash_table_lookup(finalized_nodes, join_from);
	
	if(join_state == NULL) {
		/* 含まれていない場合は、無視 */
		crm_err("Join not in progress: ignoring join-%d from %s",
			join_id, join_from);
		return;
		
	} else if(safe_str_neq(join_state, CRMD_JOINSTATE_MEMBER)) {
		crm_err("Node %s wasnt invited to join the cluster",join_from);
		/* 含まれていて、CRMD_JOINSTATE_MEMBER状態の場合は、finalized_nodesハッシュテーブルからCRM_OP_JOIN_CONFIRMを送ってきたノードを削除 */
		g_hash_table_remove(finalized_nodes, join_from);
		return;
		
	} else if(join_id != current_join_id) {
		crm_err("Invalid response from %s: join-%d vs. join-%d",
			join_from, join_id, current_join_id);
		/* JOIN_IDが異なる場合も、finalized_nodesハッシュテーブルからCRM_OP_JOIN_CONFIRMを送ってきたノードを削除 */
		g_hash_table_remove(finalized_nodes, join_from);
		return;
	}
	
	/* 上記以外の場合も、finalized_nodesハッシュテーブルからCRM_OP_JOIN_CONFIRMを送ってきたノードを削除 */
	g_hash_table_remove(finalized_nodes, join_from);
	
	/* confirmed_nodesハッシュテーブルにRM_OP_JOIN_CONFIRMを送ってきたノードでサーチ */
	if(g_hash_table_lookup(confirmed_nodes, join_from) != NULL) {
		/* 既に登録済みの場合は、エラーメッセージを表示 */
		crm_err("join-%d: hash already contains confirmation from %s",
			join_id, join_from);
	}

	/* confirmed_nodesハッシュテーブルにCRM_OP_JOIN_CONFIRMを送ってきたノードを追加 */
	g_hash_table_insert(
		confirmed_nodes, crm_strdup(join_from), crm_strdup(join_id_s));

	/* join完了ログを出す */
 	crm_info("join-%d: Updating node state to %s for %s",
 		 join_id, CRMD_JOINSTATE_MEMBER, join_from);

	/* update CIB with the current LRM status from the node
	 * We dont need to notify the TE of these updates, a transition will
	 *   be started in due time
	 */
	/* CIBからCRM_OP_JOIN_CONFIRMを送ってきたノードのXML_CIB_TAG_LRM情報を消去 */
	erase_status_tag(join_from, XML_CIB_TAG_LRM, cib_scope_local);
	/* CIBを更新 */
	/* CRM_OP_JOIN_CONFIRMを送ってきたノードのXML_CIB_TAG_LRM情報も追加される */
	/* クラスタ構成ノードのLRMD情報が最新で更新 */
	fsa_cib_update(XML_CIB_TAG_STATUS, join_ack->xml,
		       cib_scope_local|cib_quorum_override|cib_can_create, call_id);
	/* CIBの更新コールバックをセット */
	add_cib_op_callback(
		fsa_cib_conn, call_id, FALSE, NULL, join_update_complete_callback);
 	crm_debug("join-%d: Registered callback for LRM update %d",
		  join_id, call_id);
}
Beispiel #9
0
/*	A_DC_JOIN_PROCESS_ACK	*/
void
do_dc_join_ack(long long action,
               enum crmd_fsa_cause cause,
               enum crmd_fsa_state cur_state,
               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
    int join_id = -1;
    int call_id = 0;
    ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg);

    const char *join_id_s = NULL;
    const char *join_state = NULL;
    const char *op = crm_element_value(join_ack->msg, F_CRM_TASK);
    const char *join_from = crm_element_value(join_ack->msg, F_CRM_HOST_FROM);

    if (safe_str_neq(op, CRM_OP_JOIN_CONFIRM)) {
        crm_debug("Ignoring op=%s message from %s", op, join_from);
        return;
    }

    crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id);
    join_id_s = crm_element_value(join_ack->msg, F_CRM_JOIN_ID);

    /* now update them to "member" */

    crm_trace("Processing ack from %s", join_from);

    join_state = (const char *)
        g_hash_table_lookup(finalized_nodes, join_from);

    if (join_state == NULL) {
        crm_err("Join not in progress: ignoring join-%d from %s", join_id, join_from);
        return;

    } else if (safe_str_neq(join_state, CRMD_JOINSTATE_MEMBER)) {
        crm_err("Node %s wasnt invited to join the cluster", join_from);
        g_hash_table_remove(finalized_nodes, join_from);
        return;

    } else if (join_id != current_join_id) {
        crm_err("Invalid response from %s: join-%d vs. join-%d",
                join_from, join_id, current_join_id);
        g_hash_table_remove(finalized_nodes, join_from);
        return;
    }

    g_hash_table_remove(finalized_nodes, join_from);

    if (g_hash_table_lookup(confirmed_nodes, join_from) != NULL) {
        crm_err("join-%d: hash already contains confirmation from %s", join_id, join_from);
    }

    g_hash_table_insert(confirmed_nodes, strdup(join_from), strdup(join_id_s));

    crm_info("join-%d: Updating node state to %s for %s",
             join_id, CRMD_JOINSTATE_MEMBER, join_from);

    /* update CIB with the current LRM status from the node
     * We dont need to notify the TE of these updates, a transition will
     *   be started in due time
     */
    erase_status_tag(join_from, XML_CIB_TAG_LRM, cib_scope_local);
    fsa_cib_update(XML_CIB_TAG_STATUS, join_ack->xml,
                   cib_scope_local | cib_quorum_override | cib_can_create, call_id, NULL);
    add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL, join_update_complete_callback);
    crm_debug("join-%d: Registered callback for LRM update %d", join_id, call_id);
}
/*
	DCノード開始処理
*/
void
do_dc_takeover(long long action,
	       enum crmd_fsa_cause cause,
	       enum crmd_fsa_state cur_state,
	       enum crmd_fsa_input current_input,
	       fsa_data_t *msg_data)
{
	int rc = cib_ok;
	xmlNode *cib = NULL;
	static const char *cluster_type = NULL;
	
	if(cluster_type == NULL) {
	    cluster_type = getenv("HA_cluster_type");
	}
	if(cluster_type == NULL) {
	    cluster_type = "Heartbeat";
	}
	
	crm_info("Taking over DC status for this partition");
	/* -------- ★★★★★★★★フラグ的な意味あいでの、DCノードはここでセット --------- */
	set_bit_inplace(fsa_input_register, R_THE_DC);

#if SUPPORT_AIS
	if(is_openais_cluster()) {
	    send_ais_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais);
	}
#endif
	
	if(voted != NULL) {
		/* votedハッシュテーブルを破棄する */
		crm_debug_2("Destroying voted hash");
		g_hash_table_destroy(voted);
		voted = NULL;
	}
	
	/* R_JOIN_OK, R_INVOKE_PEフラグセット */
	set_bit_inplace(fsa_input_register, R_JOIN_OK);
	set_bit_inplace(fsa_input_register, R_INVOKE_PE);
	
	/* CIBのset_master処理を実行する */
	fsa_cib_conn->cmds->set_master(fsa_cib_conn, cib_scope_local);
	
	/* 初期CIBのXMLを生成する */
	cib = create_xml_node(NULL, XML_TAG_CIB);
	/* 生成したXMLにCRM_FEATURE_SETをセットする */
	crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
	/* CIBを生成した初期XMLで更新する */
	fsa_cib_update(XML_TAG_CIB, cib, cib_quorum_override, rc);
	/* 更新コールバックをセットする */
	add_cib_op_callback(fsa_cib_conn, rc, FALSE, NULL, feature_update_callback);

	/* CIBの"dc-version","cluster-infrastructure"属性を更新する */
	update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
		    NULL, NULL, NULL, "dc-version", VERSION"-"BUILD_VERSION, FALSE);

	update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
		    NULL, NULL, NULL, "cluster-infrastructure", cluster_type, FALSE);
	
	/* config_readトリガーを叩く */
	mainloop_set_trigger(config_read);
	
	free_xml(cib);
}
Beispiel #11
0
void
populate_cib_nodes(enum node_update_flags flags, const char *source)
{
    int call_id = 0;
    gboolean from_hashtable = TRUE;
    int call_options = cib_scope_local | cib_quorum_override;
    xmlNode *node_list = create_xml_node(NULL, XML_CIB_TAG_NODES);

#if SUPPORT_HEARTBEAT
    if (is_not_set(flags, node_update_quick) && is_heartbeat_cluster()) {
        from_hashtable = heartbeat_initialize_nodelist(fsa_cluster_conn, FALSE, node_list);
    }
#endif

#if SUPPORT_COROSYNC
#  if !SUPPORT_PLUGIN
    if (is_not_set(flags, node_update_quick) && is_corosync_cluster()) {
        from_hashtable = corosync_initialize_nodelist(NULL, FALSE, node_list);
    }
#  endif
#endif

    if (from_hashtable) {
        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)) {
            xmlNode *new_node = NULL;

            crm_trace("Creating node entry for %s/%s", node->uname, node->uuid);
            if(node->uuid && node->uname) {
                /* We need both to be valid */
                new_node = create_xml_node(node_list, XML_CIB_TAG_NODE);
                crm_xml_add(new_node, XML_ATTR_ID, node->uuid);
                crm_xml_add(new_node, XML_ATTR_UNAME, node->uname);
            }
        }
    }

    crm_trace("Populating <nodes> section from %s", from_hashtable ? "hashtable" : "cluster");

    fsa_cib_update(XML_CIB_TAG_NODES, node_list, call_options, call_id, NULL);
    fsa_register_cib_callback(call_id, FALSE, NULL, node_list_update_callback);

    free_xml(node_list);

    if (call_id >= pcmk_ok && crm_peer_cache != NULL && AM_I_DC) {
        /*
         * There is no need to update the local CIB with our values if
         * we've not seen valid membership data
         */
        GHashTableIter iter;
        crm_node_t *node = NULL;

        node_list = create_xml_node(NULL, XML_CIB_TAG_STATUS);

        g_hash_table_iter_init(&iter, crm_peer_cache);
        while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
            do_update_node_cib(node, flags, node_list, source);
        }

        if (crm_remote_peer_cache) {
            g_hash_table_iter_init(&iter, crm_remote_peer_cache);
            while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
                do_update_node_cib(node, flags, node_list, source);
            }
        }

        fsa_cib_update(XML_CIB_TAG_STATUS, node_list, call_options, call_id, NULL);
        fsa_register_cib_callback(call_id, FALSE, NULL, crmd_node_update_complete);
        last_peer_update = call_id;

        free_xml(node_list);
    }
}