root/daemons/controld/controld_callbacks.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. crmd_ha_msg_filter
  2. node_alive
  3. peer_update_callback
  4. crm_fsa_trigger

   1 /*
   2  * Copyright 2004-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <sys/param.h>
  13 #include <string.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/xml.h>
  17 #include <crm/cluster.h>
  18 #include <crm/cib.h>
  19 
  20 #include <pacemaker-controld.h>
  21 
  22 /* From join_dc... */
  23 extern gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source);
  24 
  25 void
  26 crmd_ha_msg_filter(xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     if (AM_I_DC) {
  29         const char *sys_from = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
  30 
  31         if (pcmk__str_eq(sys_from, CRM_SYSTEM_DC, pcmk__str_casei)) {
  32             const char *from = crm_element_value(msg, PCMK__XA_SRC);
  33 
  34             if (!pcmk__str_eq(from, controld_globals.our_nodename,
  35                               pcmk__str_casei)) {
  36                 int level = LOG_INFO;
  37                 const char *op = crm_element_value(msg, PCMK__XA_CRM_TASK);
  38 
  39                 /* make sure the election happens NOW */
  40                 if (controld_globals.fsa_state != S_ELECTION) {
  41                     ha_msg_input_t new_input;
  42 
  43                     level = LOG_WARNING;
  44                     new_input.msg = msg;
  45                     register_fsa_error_adv(C_FSA_INTERNAL, I_ELECTION, NULL, &new_input,
  46                                            __func__);
  47                 }
  48 
  49                 do_crm_log(level, "Another DC detected: %s (op=%s)", from, op);
  50                 goto done;
  51             }
  52         }
  53 
  54     } else {
  55         const char *sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
  56 
  57         if (pcmk__str_eq(sys_to, CRM_SYSTEM_DC, pcmk__str_casei)) {
  58             return;
  59         }
  60     }
  61 
  62     /* crm_log_xml_trace(msg, "HA[inbound]"); */
  63     route_message(C_HA_MESSAGE, msg);
  64 
  65   done:
  66     controld_trigger_fsa();
  67 }
  68 
  69 /*!
  70  * \internal
  71  * \brief Check whether a node is online
  72  *
  73  * \param[in] node  Node to check
  74  *
  75  * \retval -1 if completely dead
  76  * \retval  0 if partially alive
  77  * \retval  1 if completely alive
  78  */
  79 static int
  80 node_alive(const crm_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     if (pcmk_is_set(node->flags, crm_remote_node)) {
  83         // Pacemaker Remote nodes can't be partially alive
  84         return pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei) ? 1: -1;
  85 
  86     } else if (pcmk__cluster_is_node_active(node)) {
  87         // Completely up cluster node: both cluster member and peer
  88         return 1;
  89 
  90     } else if (!pcmk_is_set(node->processes, crm_get_cluster_proc())
  91                && !pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
  92         // Completely down cluster node: neither cluster member nor peer
  93         return -1;
  94     }
  95 
  96     // Partially up cluster node: only cluster member or only peer
  97     return 0;
  98 }
  99 
 100 #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
 101 
 102 void
 103 peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     uint32_t old = 0;
 106     bool appeared = FALSE;
 107     bool is_remote = pcmk_is_set(node->flags, crm_remote_node);
 108 
 109     controld_node_pending_timer(node);
 110 
 111     /* The controller waits to receive some information from the membership
 112      * layer before declaring itself operational. If this is being called for a
 113      * cluster node, indicate that we have it.
 114      */
 115     if (!is_remote) {
 116         controld_set_fsa_input_flags(R_PEER_DATA);
 117     }
 118 
 119     if (type == crm_status_processes
 120         && pcmk_is_set(node->processes, crm_get_cluster_proc())
 121         && !AM_I_DC
 122         && !is_remote) {
 123         /*
 124          * This is a hack until we can send to a nodeid and/or we fix node name lookups
 125          * These messages are ignored in crmd_ha_msg_filter()
 126          */
 127         xmlNode *query = create_request(CRM_OP_HELLO, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
 128 
 129         crm_debug("Sending hello to node %u so that it learns our node name", node->id);
 130         pcmk__cluster_send_message(node, crm_msg_crmd, query);
 131 
 132         free_xml(query);
 133     }
 134 
 135     if (node->uname == NULL) {
 136         return;
 137     }
 138 
 139     switch (type) {
 140         case crm_status_uname:
 141             /* If we've never seen the node, then it also won't be in the status section */
 142             crm_info("%s node %s is now %s",
 143                      (is_remote? "Remote" : "Cluster"),
 144                      node->uname, state_text(node->state));
 145             return;
 146 
 147         case crm_status_nstate:
 148             /* This callback should not be called unless the state actually
 149              * changed, but here's a failsafe just in case.
 150              */
 151             CRM_CHECK(!pcmk__str_eq(data, node->state, pcmk__str_casei),
 152                       return);
 153 
 154             crm_info("%s node %s is now %s (was %s)",
 155                      (is_remote? "Remote" : "Cluster"),
 156                      node->uname, state_text(node->state), state_text(data));
 157 
 158             if (pcmk__str_eq(CRM_NODE_MEMBER, node->state, pcmk__str_casei)) {
 159                 appeared = TRUE;
 160                 if (!is_remote) {
 161                     remove_stonith_cleanup(node->uname);
 162                 }
 163             } else {
 164                 controld_remove_failed_sync_node(node->uname);
 165                 controld_remove_voter(node->uname);
 166             }
 167 
 168             crmd_alert_node_event(node);
 169             break;
 170 
 171         case crm_status_processes:
 172             CRM_CHECK(data != NULL, return);
 173             old = *(const uint32_t *)data;
 174             appeared = pcmk_is_set(node->processes, crm_get_cluster_proc());
 175 
 176             {
 177                 const char *dc_s = controld_globals.dc_name;
 178 
 179                 if ((dc_s == NULL) && AM_I_DC) {
 180                     dc_s = PCMK_VALUE_TRUE;
 181                 }
 182 
 183                 crm_info("Node %s is %s a peer " CRM_XS
 184                          " DC=%s old=%#07x new=%#07x",
 185                          node->uname, (appeared? "now" : "no longer"),
 186                          pcmk__s(dc_s, "<none>"), old, node->processes);
 187             }
 188 
 189             if (!pcmk_is_set((node->processes ^ old), crm_get_cluster_proc())) {
 190                 /* Peer status did not change. This should not be possible,
 191                  * since we don't track process flags other than peer status.
 192                  */
 193                 crm_trace("Process flag %#7x did not change from %#7x to %#7x",
 194                           crm_get_cluster_proc(), old, node->processes);
 195                 return;
 196 
 197             }
 198 
 199             if (!appeared) {
 200                 node->peer_lost = time(NULL);
 201                 controld_remove_failed_sync_node(node->uname);
 202                 controld_remove_voter(node->uname);
 203             }
 204 
 205             if (!pcmk_is_set(controld_globals.fsa_input_register,
 206                              R_CIB_CONNECTED)) {
 207                 crm_trace("Ignoring peer status change because not connected to CIB");
 208                 return;
 209 
 210             } else if (controld_globals.fsa_state == S_STOPPING) {
 211                 crm_trace("Ignoring peer status change because stopping");
 212                 return;
 213             }
 214 
 215             if (!appeared
 216                 && pcmk__str_eq(node->uname, controld_globals.our_nodename,
 217                                 pcmk__str_casei)) {
 218                 /* Did we get evicted? */
 219                 crm_notice("Our peer connection failed");
 220                 register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ERROR, NULL);
 221 
 222             } else if (pcmk__str_eq(node->uname, controld_globals.dc_name,
 223                                     pcmk__str_casei)
 224                        && !pcmk__cluster_is_node_active(node)) {
 225                 /* Did the DC leave us? */
 226                 crm_notice("Our peer on the DC (%s) is dead",
 227                            controld_globals.dc_name);
 228                 register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL);
 229 
 230                 /* @COMPAT DC < 1.1.13: If a DC shuts down normally, we don't
 231                  * want to fence it. Newer DCs will send their shutdown request
 232                  * to all peers, who will update the DC's expected state to
 233                  * down, thus avoiding fencing. We can safely erase the DC's
 234                  * transient attributes when it leaves in that case. However,
 235                  * the only way to avoid fencing older DCs is to leave the
 236                  * transient attributes intact until it rejoins.
 237                  */
 238                 if (compare_version(controld_globals.dc_version, "3.0.9") > 0) {
 239                     controld_delete_node_state(node->uname,
 240                                                controld_section_attrs,
 241                                                cib_scope_local);
 242                 }
 243 
 244             } else if (AM_I_DC
 245                        || pcmk_is_set(controld_globals.flags, controld_dc_left)
 246                        || (controld_globals.dc_name == NULL)) {
 247                 /* This only needs to be done once, so normally the DC should do
 248                  * it. However if there is no DC, every node must do it, since
 249                  * there is no other way to ensure some one node does it.
 250                  */
 251                 if (appeared) {
 252                     te_trigger_stonith_history_sync(FALSE);
 253                 } else {
 254                     controld_delete_node_state(node->uname,
 255                                                controld_section_attrs,
 256                                                cib_scope_local);
 257                 }
 258             }
 259             break;
 260     }
 261 
 262     if (AM_I_DC) {
 263         xmlNode *update = NULL;
 264         int flags = node_update_peer;
 265         int alive = node_alive(node);
 266         pcmk__graph_action_t *down = match_down_event(node->uuid);
 267 
 268         crm_trace("Alive=%d, appeared=%d, down=%d",
 269                   alive, appeared, (down? down->id : -1));
 270 
 271         if (appeared && (alive > 0) && !is_remote) {
 272             register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL);
 273         }
 274 
 275         if (down) {
 276             const char *task = crm_element_value(down->xml, PCMK_XA_OPERATION);
 277 
 278             if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) {
 279 
 280                 /* tengine_stonith_callback() confirms fence actions */
 281                 crm_trace("Updating CIB %s fencer reported fencing of %s complete",
 282                           (pcmk_is_set(down->flags, pcmk__graph_action_confirmed)? "after" : "before"), node->uname);
 283 
 284             } else if (!appeared && pcmk__str_eq(task, PCMK_ACTION_DO_SHUTDOWN,
 285                                                  pcmk__str_casei)) {
 286 
 287                 // Shutdown actions are immediately confirmed (i.e. no_wait)
 288                 if (!is_remote) {
 289                     flags |= node_update_join | node_update_expected;
 290                     crmd_peer_down(node, FALSE);
 291                     check_join_state(controld_globals.fsa_state, __func__);
 292                 }
 293                 if (alive >= 0) {
 294                     crm_info("%s of peer %s is in progress " CRM_XS " action=%d",
 295                              task, node->uname, down->id);
 296                 } else {
 297                     crm_notice("%s of peer %s is complete " CRM_XS " action=%d",
 298                                task, node->uname, down->id);
 299                     pcmk__update_graph(controld_globals.transition_graph, down);
 300                     trigger_graph();
 301                 }
 302 
 303             } else {
 304                 crm_trace("Node %s is %s, was expected to %s (op %d)",
 305                           node->uname,
 306                           ((alive > 0)? "alive" :
 307                            ((alive < 0)? "dead" : "partially alive")),
 308                           task, down->id);
 309             }
 310 
 311         } else if (appeared == FALSE) {
 312             if ((controld_globals.transition_graph == NULL)
 313                 || (controld_globals.transition_graph->id == -1)) {
 314                 crm_info("Stonith/shutdown of node %s is unknown to the "
 315                          "current DC", node->uname);
 316             } else {
 317                 crm_warn("Stonith/shutdown of node %s was not expected",
 318                          node->uname);
 319             }
 320             if (!is_remote) {
 321                 crm_update_peer_join(__func__, node, crm_join_none);
 322                 check_join_state(controld_globals.fsa_state, __func__);
 323             }
 324             abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
 325                              "Node failure", NULL);
 326             fail_incompletable_actions(controld_globals.transition_graph,
 327                                        node->uuid);
 328 
 329         } else {
 330             crm_trace("Node %s came up, was not expected to be down",
 331                       node->uname);
 332         }
 333 
 334         if (is_remote) {
 335             /* A pacemaker_remote node won't have its cluster status updated
 336              * in the CIB by membership-layer callbacks, so do it here.
 337              */
 338             flags |= node_update_cluster;
 339 
 340             /* Trigger resource placement on newly integrated nodes */
 341             if (appeared) {
 342                 abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
 343                                  "Pacemaker Remote node integrated", NULL);
 344             }
 345         }
 346 
 347         if (!appeared && (type == crm_status_processes)
 348             && (node->when_member > 1)) {
 349             /* The node left CPG but is still a cluster member. Set its
 350              * membership time to 1 to record it in the cluster state as a
 351              * boolean, so we don't fence it due to
 352              * PCMK_OPT_NODE_PENDING_TIMEOUT.
 353              */
 354             node->when_member = 1;
 355             flags |= node_update_cluster;
 356             controld_node_pending_timer(node);
 357         }
 358 
 359         /* Update the CIB node state */
 360         update = create_node_state_update(node, flags, NULL, __func__);
 361         if (update == NULL) {
 362             crm_debug("Node state update not yet possible for %s", node->uname);
 363         } else {
 364             fsa_cib_anon_update(PCMK_XE_STATUS, update);
 365         }
 366         free_xml(update);
 367     }
 368 
 369     controld_trigger_fsa();
 370 }
 371 
 372 gboolean
 373 crm_fsa_trigger(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 374 {
 375     crm_trace("Invoked (queue len: %d)",
 376               g_list_length(controld_globals.fsa_message_queue));
 377     s_crmd_fsa(C_FSA_INTERNAL);
 378     crm_trace("Exited  (queue len: %d)",
 379               g_list_length(controld_globals.fsa_message_queue));
 380     return TRUE;
 381 }

/* [previous][next][first][last][top][bottom][index][help] */