root/daemons/controld/controld_membership.c

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

DEFINITIONS

This source file includes following definitions.
  1. reap_dead_nodes
  2. post_cache_update
  3. crmd_node_update_complete
  4. create_node_state_update
  5. remove_conflicting_node_callback
  6. search_conflicting_node_callback
  7. node_list_update_callback
  8. populate_cib_nodes
  9. cib_quorum_update_complete
  10. crm_update_quorum

   1 /*
   2  * Copyright 2004-2023 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 /* put these first so that uuid_t is defined without conflicts */
  11 #include <crm_internal.h>
  12 
  13 #include <string.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/msg_xml.h>
  17 #include <crm/common/xml.h>
  18 #include <crm/common/xml_internal.h>
  19 #include <crm/cluster/internal.h>
  20 
  21 #include <pacemaker-controld.h>
  22 
  23 void post_cache_update(int instance);
  24 
  25 extern gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source);
  26 
  27 static void
  28 reap_dead_nodes(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     crm_node_t *node = value;
  31 
  32     if (crm_is_peer_active(node) == FALSE) {
  33         crm_update_peer_join(__func__, node, crm_join_none);
  34 
  35         if(node && node->uname) {
  36             if (pcmk__str_eq(controld_globals.our_nodename, node->uname,
  37                              pcmk__str_casei)) {
  38                 crm_err("We're not part of the cluster anymore");
  39                 register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
  40 
  41             } else if (!AM_I_DC
  42                        && pcmk__str_eq(node->uname, controld_globals.dc_name,
  43                                        pcmk__str_casei)) {
  44                 crm_warn("Our DC node (%s) left the cluster", node->uname);
  45                 register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL);
  46             }
  47         }
  48 
  49         if ((controld_globals.fsa_state == S_INTEGRATION)
  50             || (controld_globals.fsa_state == S_FINALIZE_JOIN)) {
  51             check_join_state(controld_globals.fsa_state, __func__);
  52         }
  53         if ((node != NULL) && (node->uuid != NULL)) {
  54             fail_incompletable_actions(controld_globals.transition_graph,
  55                                        node->uuid);
  56         }
  57     }
  58 }
  59 
  60 void
  61 post_cache_update(int instance)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     xmlNode *no_op = NULL;
  64 
  65     crm_peer_seq = instance;
  66     crm_debug("Updated cache after membership event %d.", instance);
  67 
  68     g_hash_table_foreach(crm_peer_cache, reap_dead_nodes, NULL);
  69     controld_set_fsa_input_flags(R_MEMBERSHIP);
  70 
  71     if (AM_I_DC) {
  72         populate_cib_nodes(node_update_quick | node_update_cluster | node_update_peer |
  73                            node_update_expected, __func__);
  74     }
  75 
  76     /*
  77      * If we lost nodes, we should re-check the election status
  78      * Safe to call outside of an election
  79      */
  80     controld_set_fsa_action_flags(A_ELECTION_CHECK);
  81     controld_trigger_fsa();
  82 
  83     /* Membership changed, remind everyone we're here.
  84      * This will aid detection of duplicate DCs
  85      */
  86     no_op = create_request(CRM_OP_NOOP, NULL, NULL, CRM_SYSTEM_CRMD,
  87                            AM_I_DC ? CRM_SYSTEM_DC : CRM_SYSTEM_CRMD, NULL);
  88     send_cluster_message(NULL, crm_msg_crmd, no_op, FALSE);
  89     free_xml(no_op);
  90 }
  91 
  92 static void
  93 crmd_node_update_complete(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     fsa_data_t *msg_data = NULL;
  96 
  97     if (rc == pcmk_ok) {
  98         crm_trace("Node update %d complete", call_id);
  99 
 100     } else if(call_id < pcmk_ok) {
 101         crm_err("Node update failed: %s (%d)", pcmk_strerror(call_id), call_id);
 102         crm_log_xml_debug(msg, "failed");
 103         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 104 
 105     } else {
 106         crm_err("Node update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
 107         crm_log_xml_debug(msg, "failed");
 108         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 109     }
 110 }
 111 
 112 /*!
 113  * \internal
 114  * \brief Create an XML node state tag with updates
 115  *
 116  * \param[in,out] node    Node whose state will be used for update
 117  * \param[in]     flags   Bitmask of node_update_flags indicating what to update
 118  * \param[in,out] parent  XML node to contain update (or NULL)
 119  * \param[in]     source  Who requested the update (only used for logging)
 120  *
 121  * \return Pointer to created node state tag
 122  */
 123 xmlNode *
 124 create_node_state_update(crm_node_t *node, int flags, xmlNode *parent,
     /* [previous][next][first][last][top][bottom][index][help] */
 125                          const char *source)
 126 {
 127     const char *value = NULL;
 128     xmlNode *node_state;
 129 
 130     if (!node->state) {
 131         crm_info("Node update for %s cancelled: no state, not seen yet", node->uname);
 132        return NULL;
 133     }
 134 
 135     node_state = create_xml_node(parent, XML_CIB_TAG_STATE);
 136 
 137     if (pcmk_is_set(node->flags, crm_remote_node)) {
 138         pcmk__xe_set_bool_attr(node_state, XML_NODE_IS_REMOTE, true);
 139     }
 140 
 141     if (crm_xml_add(node_state, XML_ATTR_ID, crm_peer_uuid(node)) == NULL) {
 142         crm_info("Node update for %s cancelled: no ID", node->uname);
 143         free_xml(node_state);
 144         return NULL;
 145     }
 146 
 147     crm_xml_add(node_state, XML_ATTR_UNAME, node->uname);
 148 
 149     if ((flags & node_update_cluster) && node->state) {
 150         if (compare_version(controld_globals.dc_version, "3.18.0") >= 0) {
 151             // A value 0 means the node is not a cluster member.
 152             crm_xml_add_ll(node_state, PCMK__XA_IN_CCM, node->when_member);
 153 
 154         } else {
 155             pcmk__xe_set_bool_attr(node_state, PCMK__XA_IN_CCM,
 156                                    pcmk__str_eq(node->state, CRM_NODE_MEMBER,
 157                                                 pcmk__str_casei));
 158         }
 159     }
 160 
 161     if (!pcmk_is_set(node->flags, crm_remote_node)) {
 162         if (flags & node_update_peer) {
 163             if (compare_version(controld_globals.dc_version, "3.18.0") >= 0) {
 164                 // A value 0 means the peer is offline in CPG.
 165                 crm_xml_add_ll(node_state, PCMK__XA_CRMD, node->when_online);
 166 
 167             } else {
 168                 // @COMPAT DCs < 2.1.7 use online/offline rather than timestamp
 169                 value = OFFLINESTATUS;
 170                 if (pcmk_is_set(node->processes, crm_get_cluster_proc())) {
 171                     value = ONLINESTATUS;
 172                 }
 173                 crm_xml_add(node_state, PCMK__XA_CRMD, value);
 174             }
 175         }
 176 
 177         if (flags & node_update_join) {
 178             if (node->join <= crm_join_none) {
 179                 value = CRMD_JOINSTATE_DOWN;
 180             } else {
 181                 value = CRMD_JOINSTATE_MEMBER;
 182             }
 183             crm_xml_add(node_state, PCMK__XA_JOIN, value);
 184         }
 185 
 186         if (flags & node_update_expected) {
 187             crm_xml_add(node_state, PCMK__XA_EXPECTED, node->expected);
 188         }
 189     }
 190 
 191     crm_xml_add(node_state, XML_ATTR_ORIGIN, source);
 192 
 193     return node_state;
 194 }
 195 
 196 static void
 197 remove_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
     /* [previous][next][first][last][top][bottom][index][help] */
 198                                  xmlNode * output, void *user_data)
 199 {
 200     char *node_uuid = user_data;
 201 
 202     do_crm_log_unlikely(rc == 0 ? LOG_DEBUG : LOG_NOTICE,
 203                         "Deletion of the unknown conflicting node \"%s\": %s (rc=%d)",
 204                         node_uuid, pcmk_strerror(rc), rc);
 205 }
 206 
 207 static void
 208 search_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
     /* [previous][next][first][last][top][bottom][index][help] */
 209                                  xmlNode * output, void *user_data)
 210 {
 211     char *new_node_uuid = user_data;
 212     xmlNode *node_xml = NULL;
 213 
 214     if (rc != pcmk_ok) {
 215         if (rc != -ENXIO) {
 216             crm_notice("Searching conflicting nodes for %s failed: %s (%d)",
 217                        new_node_uuid, pcmk_strerror(rc), rc);
 218         }
 219         return;
 220 
 221     } else if (output == NULL) {
 222         return;
 223     }
 224 
 225     if (pcmk__xe_is(output, XML_CIB_TAG_NODE)) {
 226         node_xml = output;
 227 
 228     } else {
 229         node_xml = pcmk__xml_first_child(output);
 230     }
 231 
 232     for (; node_xml != NULL; node_xml = pcmk__xml_next(node_xml)) {
 233         const char *node_uuid = NULL;
 234         const char *node_uname = NULL;
 235         GHashTableIter iter;
 236         crm_node_t *node = NULL;
 237         gboolean known = FALSE;
 238 
 239         if (!pcmk__xe_is(node_xml, XML_CIB_TAG_NODE)) {
 240             continue;
 241         }
 242 
 243         node_uuid = crm_element_value(node_xml, XML_ATTR_ID);
 244         node_uname = crm_element_value(node_xml, XML_ATTR_UNAME);
 245 
 246         if (node_uuid == NULL || node_uname == NULL) {
 247             continue;
 248         }
 249 
 250         g_hash_table_iter_init(&iter, crm_peer_cache);
 251         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 252             if (node->uuid
 253                 && pcmk__str_eq(node->uuid, node_uuid, pcmk__str_casei)
 254                 && node->uname
 255                 && pcmk__str_eq(node->uname, node_uname, pcmk__str_casei)) {
 256 
 257                 known = TRUE;
 258                 break;
 259             }
 260         }
 261 
 262         if (known == FALSE) {
 263             cib_t *cib_conn = controld_globals.cib_conn;
 264             int delete_call_id = 0;
 265             xmlNode *node_state_xml = NULL;
 266 
 267             crm_notice("Deleting unknown node %s/%s which has conflicting uname with %s",
 268                        node_uuid, node_uname, new_node_uuid);
 269 
 270             delete_call_id = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_NODES,
 271                                                     node_xml, cib_scope_local);
 272             fsa_register_cib_callback(delete_call_id, strdup(node_uuid),
 273                                       remove_conflicting_node_callback);
 274 
 275             node_state_xml = create_xml_node(NULL, XML_CIB_TAG_STATE);
 276             crm_xml_add(node_state_xml, XML_ATTR_ID, node_uuid);
 277             crm_xml_add(node_state_xml, XML_ATTR_UNAME, node_uname);
 278 
 279             delete_call_id = cib_conn->cmds->remove(cib_conn,
 280                                                     XML_CIB_TAG_STATUS,
 281                                                     node_state_xml,
 282                                                     cib_scope_local);
 283             fsa_register_cib_callback(delete_call_id, strdup(node_uuid),
 284                                       remove_conflicting_node_callback);
 285             free_xml(node_state_xml);
 286         }
 287     }
 288 }
 289 
 290 static void
 291 node_list_update_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293     fsa_data_t *msg_data = NULL;
 294 
 295     if(call_id < pcmk_ok) {
 296         crm_err("Node list update failed: %s (%d)", pcmk_strerror(call_id), call_id);
 297         crm_log_xml_debug(msg, "update:failed");
 298         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 299 
 300     } else if(rc < pcmk_ok) {
 301         crm_err("Node update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
 302         crm_log_xml_debug(msg, "update:failed");
 303         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 304     }
 305 }
 306 
 307 void
 308 populate_cib_nodes(enum node_update_flags flags, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 309 {
 310     cib_t *cib_conn = controld_globals.cib_conn;
 311 
 312     int call_id = 0;
 313     gboolean from_hashtable = TRUE;
 314     xmlNode *node_list = create_xml_node(NULL, XML_CIB_TAG_NODES);
 315 
 316 #if SUPPORT_COROSYNC
 317     if (!pcmk_is_set(flags, node_update_quick) && is_corosync_cluster()) {
 318         from_hashtable = pcmk__corosync_add_nodes(node_list);
 319     }
 320 #endif
 321 
 322     if (from_hashtable) {
 323         GHashTableIter iter;
 324         crm_node_t *node = NULL;
 325         GString *xpath = NULL;
 326 
 327         g_hash_table_iter_init(&iter, crm_peer_cache);
 328         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 329             xmlNode *new_node = NULL;
 330 
 331             if ((node->uuid != NULL) && (node->uname != NULL)) {
 332                 crm_trace("Creating node entry for %s/%s", node->uname, node->uuid);
 333                 if (xpath == NULL) {
 334                     xpath = g_string_sized_new(512);
 335                 } else {
 336                     g_string_truncate(xpath, 0);
 337                 }
 338 
 339                 /* We need both to be valid */
 340                 new_node = create_xml_node(node_list, XML_CIB_TAG_NODE);
 341                 crm_xml_add(new_node, XML_ATTR_ID, node->uuid);
 342                 crm_xml_add(new_node, XML_ATTR_UNAME, node->uname);
 343 
 344                 /* Search and remove unknown nodes with the conflicting uname from CIB */
 345                 pcmk__g_strcat(xpath,
 346                                "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
 347                                "/" XML_CIB_TAG_NODES "/" XML_CIB_TAG_NODE
 348                                "[@" XML_ATTR_UNAME "='", node->uname, "']"
 349                                "[@" XML_ATTR_ID "!='", node->uuid, "']", NULL);
 350 
 351                 call_id = cib_conn->cmds->query(cib_conn,
 352                                                 (const char *) xpath->str,
 353                                                 NULL,
 354                                                 cib_scope_local|cib_xpath);
 355                 fsa_register_cib_callback(call_id, strdup(node->uuid),
 356                                           search_conflicting_node_callback);
 357             }
 358         }
 359 
 360         if (xpath != NULL) {
 361             g_string_free(xpath, TRUE);
 362         }
 363     }
 364 
 365     crm_trace("Populating <nodes> section from %s", from_hashtable ? "hashtable" : "cluster");
 366 
 367     if ((controld_update_cib(XML_CIB_TAG_NODES, node_list, cib_scope_local,
 368                              node_list_update_callback) == pcmk_rc_ok)
 369          && (crm_peer_cache != NULL) && AM_I_DC) {
 370         /*
 371          * There is no need to update the local CIB with our values if
 372          * we've not seen valid membership data
 373          */
 374         GHashTableIter iter;
 375         crm_node_t *node = NULL;
 376 
 377         free_xml(node_list);
 378         node_list = create_xml_node(NULL, XML_CIB_TAG_STATUS);
 379 
 380         g_hash_table_iter_init(&iter, crm_peer_cache);
 381         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 382             create_node_state_update(node, flags, node_list, source);
 383         }
 384 
 385         if (crm_remote_peer_cache) {
 386             g_hash_table_iter_init(&iter, crm_remote_peer_cache);
 387             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 388                 create_node_state_update(node, flags, node_list, source);
 389             }
 390         }
 391 
 392         controld_update_cib(XML_CIB_TAG_STATUS, node_list, cib_scope_local,
 393                             crmd_node_update_complete);
 394     }
 395     free_xml(node_list);
 396 }
 397 
 398 static void
 399 cib_quorum_update_complete(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 400 {
 401     fsa_data_t *msg_data = NULL;
 402 
 403     if (rc == pcmk_ok) {
 404         crm_trace("Quorum update %d complete", call_id);
 405 
 406     } else {
 407         crm_err("Quorum update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
 408         crm_log_xml_debug(msg, "failed");
 409         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 410     }
 411 }
 412 
 413 void
 414 crm_update_quorum(gboolean quorum, gboolean force_update)
     /* [previous][next][first][last][top][bottom][index][help] */
 415 {
 416     bool has_quorum = pcmk_is_set(controld_globals.flags, controld_has_quorum);
 417 
 418     if (quorum) {
 419         controld_set_global_flags(controld_ever_had_quorum);
 420 
 421     } else if (pcmk_all_flags_set(controld_globals.flags,
 422                                   controld_ever_had_quorum
 423                                   |controld_no_quorum_suicide)) {
 424         pcmk__panic(__func__);
 425     }
 426 
 427     if (AM_I_DC
 428         && ((has_quorum && !quorum) || (!has_quorum && quorum)
 429             || force_update)) {
 430         xmlNode *update = NULL;
 431 
 432         update = create_xml_node(NULL, XML_TAG_CIB);
 433         crm_xml_add_int(update, XML_ATTR_HAVE_QUORUM, quorum);
 434         crm_xml_add(update, XML_ATTR_DC_UUID, controld_globals.our_uuid);
 435 
 436         crm_debug("Updating quorum status to %s", pcmk__btoa(quorum));
 437         controld_update_cib(XML_TAG_CIB, update, cib_scope_local,
 438                             cib_quorum_update_complete);
 439         free_xml(update);
 440 
 441         /* Quorum changes usually cause a new transition via other activity:
 442          * quorum gained via a node joining will abort via the node join,
 443          * and quorum lost via a node leaving will usually abort via resource
 444          * activity and/or fencing.
 445          *
 446          * However, it is possible that nothing else causes a transition (e.g.
 447          * someone forces quorum via corosync-cmaptcl, or quorum is lost due to
 448          * a node in standby shutting down cleanly), so here ensure a new
 449          * transition is triggered.
 450          */
 451         if (quorum) {
 452             /* If quorum was gained, abort after a short delay, in case multiple
 453              * nodes are joining around the same time, so the one that brings us
 454              * to quorum doesn't cause all the remaining ones to be fenced.
 455              */
 456             abort_after_delay(INFINITY, pcmk__graph_restart, "Quorum gained",
 457                               5000);
 458         } else {
 459             abort_transition(INFINITY, pcmk__graph_restart, "Quorum lost",
 460                              NULL);
 461         }
 462     }
 463 
 464     if (quorum) {
 465         controld_set_global_flags(controld_has_quorum);
 466     } else {
 467         controld_clear_global_flags(controld_has_quorum);
 468     }
 469 }

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