diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
commit | e5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch) | |
tree | a6716c9275b4b413f6c9194798b34b91affb3cc7 /daemons/controld/controld_callbacks.c | |
parent | Initial commit. (diff) | |
download | pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip |
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'daemons/controld/controld_callbacks.c')
-rw-r--r-- | daemons/controld/controld_callbacks.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c new file mode 100644 index 0000000..d578adc --- /dev/null +++ b/daemons/controld/controld_callbacks.c @@ -0,0 +1,367 @@ +/* + * Copyright 2004-2022 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <sys/param.h> +#include <string.h> + +#include <crm/crm.h> +#include <crm/msg_xml.h> +#include <crm/common/xml.h> +#include <crm/cluster.h> +#include <crm/cib.h> + +#include <pacemaker-controld.h> + +/* From join_dc... */ +extern gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source); + +void +crmd_ha_msg_filter(xmlNode * msg) +{ + if (AM_I_DC) { + const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM); + + if (pcmk__str_eq(sys_from, CRM_SYSTEM_DC, pcmk__str_casei)) { + const char *from = crm_element_value(msg, F_ORIG); + + if (!pcmk__str_eq(from, controld_globals.our_nodename, + pcmk__str_casei)) { + int level = LOG_INFO; + const char *op = crm_element_value(msg, F_CRM_TASK); + + /* make sure the election happens NOW */ + if (controld_globals.fsa_state != S_ELECTION) { + ha_msg_input_t new_input; + + level = LOG_WARNING; + new_input.msg = msg; + register_fsa_error_adv(C_FSA_INTERNAL, I_ELECTION, NULL, &new_input, + __func__); + } + + do_crm_log(level, "Another DC detected: %s (op=%s)", from, op); + goto done; + } + } + + } else { + const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); + + if (pcmk__str_eq(sys_to, CRM_SYSTEM_DC, pcmk__str_casei)) { + return; + } + } + + /* crm_log_xml_trace(msg, "HA[inbound]"); */ + route_message(C_HA_MESSAGE, msg); + + done: + controld_trigger_fsa(); +} + +/*! + * \internal + * \brief Check whether a node is online + * + * \param[in] node Node to check + * + * \retval -1 if completely dead + * \retval 0 if partially alive + * \retval 1 if completely alive + */ +static int +node_alive(const crm_node_t *node) +{ + if (pcmk_is_set(node->flags, crm_remote_node)) { + // Pacemaker Remote nodes can't be partially alive + return pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei) ? 1: -1; + + } else if (crm_is_peer_active(node)) { + // Completely up cluster node: both cluster member and peer + return 1; + + } else if (!pcmk_is_set(node->processes, crm_get_cluster_proc()) + && !pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei)) { + // Completely down cluster node: neither cluster member nor peer + return -1; + } + + // Partially up cluster node: only cluster member or only peer + return 0; +} + +#define state_text(state) ((state)? (const char *)(state) : "in unknown state") + +void +peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data) +{ + uint32_t old = 0; + bool appeared = FALSE; + bool is_remote = pcmk_is_set(node->flags, crm_remote_node); + + /* The controller waits to receive some information from the membership + * layer before declaring itself operational. If this is being called for a + * cluster node, indicate that we have it. + */ + if (!is_remote) { + controld_set_fsa_input_flags(R_PEER_DATA); + } + + if (type == crm_status_processes + && pcmk_is_set(node->processes, crm_get_cluster_proc()) + && !AM_I_DC + && !is_remote) { + /* + * This is a hack until we can send to a nodeid and/or we fix node name lookups + * These messages are ignored in crmd_ha_msg_filter() + */ + xmlNode *query = create_request(CRM_OP_HELLO, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL); + + crm_debug("Sending hello to node %u so that it learns our node name", node->id); + send_cluster_message(node, crm_msg_crmd, query, FALSE); + + free_xml(query); + } + + if (node->uname == NULL) { + return; + } + + switch (type) { + case crm_status_uname: + /* If we've never seen the node, then it also won't be in the status section */ + crm_info("%s node %s is now %s", + (is_remote? "Remote" : "Cluster"), + node->uname, state_text(node->state)); + return; + + case crm_status_nstate: + /* This callback should not be called unless the state actually + * changed, but here's a failsafe just in case. + */ + CRM_CHECK(!pcmk__str_eq(data, node->state, pcmk__str_casei), + return); + + crm_info("%s node %s is now %s (was %s)", + (is_remote? "Remote" : "Cluster"), + node->uname, state_text(node->state), state_text(data)); + + if (pcmk__str_eq(CRM_NODE_MEMBER, node->state, pcmk__str_casei)) { + appeared = TRUE; + if (!is_remote) { + remove_stonith_cleanup(node->uname); + } + } else { + controld_remove_failed_sync_node(node->uname); + controld_remove_voter(node->uname); + } + + crmd_alert_node_event(node); + break; + + case crm_status_processes: + CRM_CHECK(data != NULL, return); + old = *(const uint32_t *)data; + appeared = pcmk_is_set(node->processes, crm_get_cluster_proc()); + + { + const char *dc_s = controld_globals.dc_name; + + if ((dc_s == NULL) && AM_I_DC) { + dc_s = "true"; + } + + crm_info("Node %s is %s a peer " CRM_XS + " DC=%s old=%#07x new=%#07x", + node->uname, (appeared? "now" : "no longer"), + pcmk__s(dc_s, "<none>"), old, node->processes); + } + + if (!pcmk_is_set((node->processes ^ old), crm_get_cluster_proc())) { + /* Peer status did not change. This should not be possible, + * since we don't track process flags other than peer status. + */ + crm_trace("Process flag %#7x did not change from %#7x to %#7x", + crm_get_cluster_proc(), old, node->processes); + return; + + } + + if (!appeared) { + node->peer_lost = time(NULL); + controld_remove_failed_sync_node(node->uname); + controld_remove_voter(node->uname); + } + + if (!pcmk_is_set(controld_globals.fsa_input_register, + R_CIB_CONNECTED)) { + crm_trace("Ignoring peer status change because not connected to CIB"); + return; + + } else if (controld_globals.fsa_state == S_STOPPING) { + crm_trace("Ignoring peer status change because stopping"); + return; + } + + if (!appeared + && pcmk__str_eq(node->uname, controld_globals.our_nodename, + pcmk__str_casei)) { + /* Did we get evicted? */ + crm_notice("Our peer connection failed"); + register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ERROR, NULL); + + } else if (pcmk__str_eq(node->uname, controld_globals.dc_name, + pcmk__str_casei) + && !crm_is_peer_active(node)) { + /* Did the DC leave us? */ + crm_notice("Our peer on the DC (%s) is dead", + controld_globals.dc_name); + register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL); + + /* @COMPAT DC < 1.1.13: If a DC shuts down normally, we don't + * want to fence it. Newer DCs will send their shutdown request + * to all peers, who will update the DC's expected state to + * down, thus avoiding fencing. We can safely erase the DC's + * transient attributes when it leaves in that case. However, + * the only way to avoid fencing older DCs is to leave the + * transient attributes intact until it rejoins. + */ + if (compare_version(controld_globals.dc_version, "3.0.9") > 0) { + controld_delete_node_state(node->uname, + controld_section_attrs, + cib_scope_local); + } + + } else if (AM_I_DC + || pcmk_is_set(controld_globals.flags, controld_dc_left) + || (controld_globals.dc_name == NULL)) { + /* This only needs to be done once, so normally the DC should do + * it. However if there is no DC, every node must do it, since + * there is no other way to ensure some one node does it. + */ + if (appeared) { + te_trigger_stonith_history_sync(FALSE); + } else { + controld_delete_node_state(node->uname, + controld_section_attrs, + cib_scope_local); + } + } + break; + } + + if (AM_I_DC) { + xmlNode *update = NULL; + int flags = node_update_peer; + int alive = node_alive(node); + pcmk__graph_action_t *down = match_down_event(node->uuid); + + crm_trace("Alive=%d, appeared=%d, down=%d", + alive, appeared, (down? down->id : -1)); + + if (appeared && (alive > 0) && !is_remote) { + register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL); + } + + if (down) { + const char *task = crm_element_value(down->xml, XML_LRM_ATTR_TASK); + + if (pcmk__str_eq(task, CRM_OP_FENCE, pcmk__str_casei)) { + + /* tengine_stonith_callback() confirms fence actions */ + crm_trace("Updating CIB %s fencer reported fencing of %s complete", + (pcmk_is_set(down->flags, pcmk__graph_action_confirmed)? "after" : "before"), node->uname); + + } else if (!appeared && pcmk__str_eq(task, CRM_OP_SHUTDOWN, pcmk__str_casei)) { + + // Shutdown actions are immediately confirmed (i.e. no_wait) + if (!is_remote) { + flags |= node_update_join | node_update_expected; + crmd_peer_down(node, FALSE); + check_join_state(controld_globals.fsa_state, __func__); + } + if (alive >= 0) { + crm_info("%s of peer %s is in progress " CRM_XS " action=%d", + task, node->uname, down->id); + } else { + crm_notice("%s of peer %s is complete " CRM_XS " action=%d", + task, node->uname, down->id); + pcmk__update_graph(controld_globals.transition_graph, down); + trigger_graph(); + } + + } else { + crm_trace("Node %s is %s, was expected to %s (op %d)", + node->uname, + ((alive > 0)? "alive" : + ((alive < 0)? "dead" : "partially alive")), + task, down->id); + } + + } else if (appeared == FALSE) { + if ((controld_globals.transition_graph == NULL) + || (controld_globals.transition_graph->id == -1)) { + crm_info("Stonith/shutdown of node %s is unknown to the " + "current DC", node->uname); + } else { + crm_warn("Stonith/shutdown of node %s was not expected", + node->uname); + } + if (!is_remote) { + crm_update_peer_join(__func__, node, crm_join_none); + check_join_state(controld_globals.fsa_state, __func__); + } + abort_transition(INFINITY, pcmk__graph_restart, "Node failure", + NULL); + fail_incompletable_actions(controld_globals.transition_graph, + node->uuid); + + } else { + crm_trace("Node %s came up, was not expected to be down", + node->uname); + } + + if (is_remote) { + /* A pacemaker_remote node won't have its cluster status updated + * in the CIB by membership-layer callbacks, so do it here. + */ + flags |= node_update_cluster; + + /* Trigger resource placement on newly integrated nodes */ + if (appeared) { + abort_transition(INFINITY, pcmk__graph_restart, + "Pacemaker Remote node integrated", NULL); + } + } + + /* Update the CIB node state */ + update = create_node_state_update(node, flags, NULL, __func__); + if (update == NULL) { + crm_debug("Node state update not yet possible for %s", node->uname); + } else { + fsa_cib_anon_update(XML_CIB_TAG_STATUS, update); + } + free_xml(update); + } + + controld_trigger_fsa(); +} + +gboolean +crm_fsa_trigger(gpointer user_data) +{ + crm_trace("Invoked (queue len: %d)", + g_list_length(controld_globals.fsa_message_queue)); + s_crmd_fsa(C_FSA_INTERNAL); + crm_trace("Exited (queue len: %d)", + g_list_length(controld_globals.fsa_message_queue)); + return TRUE; +} |