root/lib/cluster/membership.c

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

DEFINITIONS

This source file includes following definitions.
  1. crm_remote_peer_cache_size
  2. crm_remote_peer_get
  3. crm_remote_peer_cache_remove
  4. remote_state_from_cib
  5. remote_cache_refresh_helper
  6. mark_dirty
  7. is_dirty
  8. crm_remote_peer_cache_refresh
  9. crm_is_peer_active
  10. crm_reap_dead_member
  11. reap_crm_member
  12. count_peer
  13. crm_active_peers
  14. destroy_crm_node
  15. crm_peer_init
  16. crm_peer_destroy
  17. crm_set_status_callback
  18. crm_set_autoreap
  19. dump_peer_hash
  20. hash_find_by_data
  21. pcmk__search_node_caches
  22. pcmk__get_peer_full
  23. crm_get_peer_full
  24. pcmk__search_cluster_node_cache
  25. remove_conflicting_peer
  26. pcmk__get_peer
  27. crm_get_peer
  28. update_peer_uname
  29. proc2text
  30. crm_update_peer_proc
  31. pcmk__update_peer_expected
  32. update_peer_state_iter
  33. pcmk__update_peer_state
  34. pcmk__reap_unseen_nodes
  35. find_known_node
  36. known_node_cache_refresh_helper
  37. refresh_known_node_cache
  38. pcmk__refresh_node_caches_from_cib
  39. pcmk__search_known_node_cache
  40. crm_terminate_member
  41. crm_terminate_member_no_mainloop

   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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #ifndef _GNU_SOURCE
  13 #  define _GNU_SOURCE
  14 #endif
  15 
  16 #include <sys/param.h>
  17 #include <sys/types.h>
  18 #include <stdio.h>
  19 #include <unistd.h>
  20 #include <string.h>
  21 #include <glib.h>
  22 #include <crm/common/ipc.h>
  23 #include <crm/common/xml_internal.h>
  24 #include <crm/cluster/internal.h>
  25 #include <crm/msg_xml.h>
  26 #include <crm/stonith-ng.h>
  27 #include "crmcluster_private.h"
  28 
  29 /* The peer cache remembers cluster nodes that have been seen.
  30  * This is managed mostly automatically by libcluster, based on
  31  * cluster membership events.
  32  *
  33  * Because cluster nodes can have conflicting names or UUIDs,
  34  * the hash table key is a uniquely generated ID.
  35  */
  36 GHashTable *crm_peer_cache = NULL;
  37 
  38 /*
  39  * The remote peer cache tracks pacemaker_remote nodes. While the
  40  * value has the same type as the peer cache's, it is tracked separately for
  41  * three reasons: pacemaker_remote nodes can't have conflicting names or UUIDs,
  42  * so the name (which is also the UUID) is used as the hash table key; there
  43  * is no equivalent of membership events, so management is not automatic; and
  44  * most users of the peer cache need to exclude pacemaker_remote nodes.
  45  *
  46  * That said, using a single cache would be more logical and less error-prone,
  47  * so it would be a good idea to merge them one day.
  48  *
  49  * libcluster provides two avenues for populating the cache:
  50  * crm_remote_peer_get() and crm_remote_peer_cache_remove() directly manage it,
  51  * while crm_remote_peer_cache_refresh() populates it via the CIB.
  52  */
  53 GHashTable *crm_remote_peer_cache = NULL;
  54 
  55 /*
  56  * The known node cache tracks cluster and remote nodes that have been seen in
  57  * the CIB. It is useful mainly when a caller needs to know about a node that
  58  * may no longer be in the membership, but doesn't want to add the node to the
  59  * main peer cache tables.
  60  */
  61 static GHashTable *known_node_cache = NULL;
  62 
  63 unsigned long long crm_peer_seq = 0;
  64 gboolean crm_have_quorum = FALSE;
  65 static gboolean crm_autoreap  = TRUE;
  66 
  67 // Flag setting and clearing for crm_node_t:flags
  68 
  69 #define set_peer_flags(peer, flags_to_set) do {                               \
  70         (peer)->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,     \
  71                                            "Peer", (peer)->uname,             \
  72                                            (peer)->flags, (flags_to_set),     \
  73                                            #flags_to_set);                    \
  74     } while (0)
  75 
  76 #define clear_peer_flags(peer, flags_to_clear) do {                           \
  77         (peer)->flags = pcmk__clear_flags_as(__func__, __LINE__,              \
  78                                              LOG_TRACE,                       \
  79                                              "Peer", (peer)->uname,           \
  80                                              (peer)->flags, (flags_to_clear), \
  81                                              #flags_to_clear);                \
  82     } while (0)
  83 
  84 static void update_peer_uname(crm_node_t *node, const char *uname);
  85 
  86 int
  87 crm_remote_peer_cache_size(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  88 {
  89     if (crm_remote_peer_cache == NULL) {
  90         return 0;
  91     }
  92     return g_hash_table_size(crm_remote_peer_cache);
  93 }
  94 
  95 /*!
  96  * \brief Get a remote node peer cache entry, creating it if necessary
  97  *
  98  * \param[in] node_name  Name of remote node
  99  *
 100  * \return Cache entry for node on success, NULL (and set errno) otherwise
 101  *
 102  * \note When creating a new entry, this will leave the node state undetermined,
 103  *       so the caller should also call pcmk__update_peer_state() if the state
 104  *       is known.
 105  */
 106 crm_node_t *
 107 crm_remote_peer_get(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 108 {
 109     crm_node_t *node;
 110 
 111     if (node_name == NULL) {
 112         errno = -EINVAL;
 113         return NULL;
 114     }
 115 
 116     /* Return existing cache entry if one exists */
 117     node = g_hash_table_lookup(crm_remote_peer_cache, node_name);
 118     if (node) {
 119         return node;
 120     }
 121 
 122     /* Allocate a new entry */
 123     node = calloc(1, sizeof(crm_node_t));
 124     if (node == NULL) {
 125         return NULL;
 126     }
 127 
 128     /* Populate the essential information */
 129     set_peer_flags(node, crm_remote_node);
 130     node->uuid = strdup(node_name);
 131     if (node->uuid == NULL) {
 132         free(node);
 133         errno = -ENOMEM;
 134         return NULL;
 135     }
 136 
 137     /* Add the new entry to the cache */
 138     g_hash_table_replace(crm_remote_peer_cache, node->uuid, node);
 139     crm_trace("added %s to remote cache", node_name);
 140 
 141     /* Update the entry's uname, ensuring peer status callbacks are called */
 142     update_peer_uname(node, node_name);
 143     return node;
 144 }
 145 
 146 void
 147 crm_remote_peer_cache_remove(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149     if (g_hash_table_remove(crm_remote_peer_cache, node_name)) {
 150         crm_trace("removed %s from remote peer cache", node_name);
 151     }
 152 }
 153 
 154 /*!
 155  * \internal
 156  * \brief Return node status based on a CIB status entry
 157  *
 158  * \param[in] node_state  XML of node state
 159  *
 160  * \return CRM_NODE_LOST if PCMK__XA_IN_CCM is false in node_state,
 161  *         CRM_NODE_MEMBER otherwise
 162  * \note Unlike most boolean XML attributes, this one defaults to true, for
 163  *       backward compatibility with older controllers that don't set it.
 164  */
 165 static const char *
 166 remote_state_from_cib(const xmlNode *node_state)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168     bool status = false;
 169 
 170     if ((pcmk__xe_get_bool_attr(node_state, PCMK__XA_IN_CCM,
 171                                 &status) == pcmk_rc_ok) && !status) {
 172         return CRM_NODE_LOST;
 173     } else {
 174         return CRM_NODE_MEMBER;
 175     }
 176 }
 177 
 178 /* user data for looping through remote node xpath searches */
 179 struct refresh_data {
 180     const char *field;  /* XML attribute to check for node name */
 181     gboolean has_state; /* whether to update node state based on XML */
 182 };
 183 
 184 /*!
 185  * \internal
 186  * \brief Process one pacemaker_remote node xpath search result
 187  *
 188  * \param[in] result     XML search result
 189  * \param[in] user_data  what to look for in the XML
 190  */
 191 static void
 192 remote_cache_refresh_helper(xmlNode *result, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 {
 194     const struct refresh_data *data = user_data;
 195     const char *remote = crm_element_value(result, data->field);
 196     const char *state = NULL;
 197     crm_node_t *node;
 198 
 199     CRM_CHECK(remote != NULL, return);
 200 
 201     /* Determine node's state, if the result has it */
 202     if (data->has_state) {
 203         state = remote_state_from_cib(result);
 204     }
 205 
 206     /* Check whether cache already has entry for node */
 207     node = g_hash_table_lookup(crm_remote_peer_cache, remote);
 208 
 209     if (node == NULL) {
 210         /* Node is not in cache, so add a new entry for it */
 211         node = crm_remote_peer_get(remote);
 212         CRM_ASSERT(node);
 213         if (state) {
 214             pcmk__update_peer_state(__func__, node, state, 0);
 215         }
 216 
 217     } else if (pcmk_is_set(node->flags, crm_node_dirty)) {
 218         /* Node is in cache and hasn't been updated already, so mark it clean */
 219         clear_peer_flags(node, crm_node_dirty);
 220         if (state) {
 221             pcmk__update_peer_state(__func__, node, state, 0);
 222         }
 223     }
 224 }
 225 
 226 static void
 227 mark_dirty(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 228 {
 229     set_peer_flags((crm_node_t *) value, crm_node_dirty);
 230 }
 231 
 232 static gboolean
 233 is_dirty(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235     return pcmk_is_set(((crm_node_t*)value)->flags, crm_node_dirty);
 236 }
 237 
 238 /*!
 239  * \brief Repopulate the remote peer cache based on CIB XML
 240  *
 241  * \param[in] xmlNode  CIB XML to parse
 242  */
 243 void
 244 crm_remote_peer_cache_refresh(xmlNode *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246     struct refresh_data data;
 247 
 248     crm_peer_init();
 249 
 250     /* First, we mark all existing cache entries as dirty,
 251      * so that later we can remove any that weren't in the CIB.
 252      * We don't empty the cache, because we need to detect changes in state.
 253      */
 254     g_hash_table_foreach(crm_remote_peer_cache, mark_dirty, NULL);
 255 
 256     /* Look for guest nodes and remote nodes in the status section */
 257     data.field = "id";
 258     data.has_state = TRUE;
 259     crm_foreach_xpath_result(cib, PCMK__XP_REMOTE_NODE_STATUS,
 260                              remote_cache_refresh_helper, &data);
 261 
 262     /* Look for guest nodes and remote nodes in the configuration section,
 263      * because they may have just been added and not have a status entry yet.
 264      * In that case, the cached node state will be left NULL, so that the
 265      * peer status callback isn't called until we're sure the node started
 266      * successfully.
 267      */
 268     data.field = "value";
 269     data.has_state = FALSE;
 270     crm_foreach_xpath_result(cib, PCMK__XP_GUEST_NODE_CONFIG,
 271                              remote_cache_refresh_helper, &data);
 272     data.field = "id";
 273     data.has_state = FALSE;
 274     crm_foreach_xpath_result(cib, PCMK__XP_REMOTE_NODE_CONFIG,
 275                              remote_cache_refresh_helper, &data);
 276 
 277     /* Remove all old cache entries that weren't seen in the CIB */
 278     g_hash_table_foreach_remove(crm_remote_peer_cache, is_dirty, NULL);
 279 }
 280 
 281 gboolean
 282 crm_is_peer_active(const crm_node_t * node)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     if(node == NULL) {
 285         return FALSE;
 286     }
 287 
 288     if (pcmk_is_set(node->flags, crm_remote_node)) {
 289         /* remote nodes are never considered active members. This
 290          * guarantees they will never be considered for DC membership.*/
 291         return FALSE;
 292     }
 293 #if SUPPORT_COROSYNC
 294     if (is_corosync_cluster()) {
 295         return crm_is_corosync_peer_active(node);
 296     }
 297 #endif
 298     crm_err("Unhandled cluster type: %s", name_for_cluster_type(get_cluster_type()));
 299     return FALSE;
 300 }
 301 
 302 static gboolean
 303 crm_reap_dead_member(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305     crm_node_t *node = value;
 306     crm_node_t *search = user_data;
 307 
 308     if (search == NULL) {
 309         return FALSE;
 310 
 311     } else if (search->id && node->id != search->id) {
 312         return FALSE;
 313 
 314     } else if (search->id == 0 && !pcmk__str_eq(node->uname, search->uname, pcmk__str_casei)) {
 315         return FALSE;
 316 
 317     } else if (crm_is_peer_active(value) == FALSE) {
 318         crm_info("Removing node with name %s and id %u from membership cache",
 319                  (node->uname? node->uname : "unknown"), node->id);
 320         return TRUE;
 321     }
 322     return FALSE;
 323 }
 324 
 325 /*!
 326  * \brief Remove all peer cache entries matching a node ID and/or uname
 327  *
 328  * \param[in] id    ID of node to remove (or 0 to ignore)
 329  * \param[in] name  Uname of node to remove (or NULL to ignore)
 330  *
 331  * \return Number of cache entries removed
 332  */
 333 guint
 334 reap_crm_member(uint32_t id, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 335 {
 336     int matches = 0;
 337     crm_node_t search = { 0, };
 338 
 339     if (crm_peer_cache == NULL) {
 340         crm_trace("Membership cache not initialized, ignoring purge request");
 341         return 0;
 342     }
 343 
 344     search.id = id;
 345     pcmk__str_update(&search.uname, name);
 346     matches = g_hash_table_foreach_remove(crm_peer_cache, crm_reap_dead_member, &search);
 347     if(matches) {
 348         crm_notice("Purged %d peer%s with id=%u%s%s from the membership cache",
 349                    matches, pcmk__plural_s(matches), search.id,
 350                    (search.uname? " and/or uname=" : ""),
 351                    (search.uname? search.uname : ""));
 352 
 353     } else {
 354         crm_info("No peers with id=%u%s%s to purge from the membership cache",
 355                  search.id, (search.uname? " and/or uname=" : ""),
 356                  (search.uname? search.uname : ""));
 357     }
 358 
 359     free(search.uname);
 360     return matches;
 361 }
 362 
 363 static void
 364 count_peer(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 365 {
 366     guint *count = user_data;
 367     crm_node_t *node = value;
 368 
 369     if (crm_is_peer_active(node)) {
 370         *count = *count + 1;
 371     }
 372 }
 373 
 374 guint
 375 crm_active_peers(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 376 {
 377     guint count = 0;
 378 
 379     if (crm_peer_cache) {
 380         g_hash_table_foreach(crm_peer_cache, count_peer, &count);
 381     }
 382     return count;
 383 }
 384 
 385 static void
 386 destroy_crm_node(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 387 {
 388     crm_node_t *node = data;
 389 
 390     crm_trace("Destroying entry for node %u: %s", node->id, node->uname);
 391 
 392     free(node->uname);
 393     free(node->state);
 394     free(node->uuid);
 395     free(node->expected);
 396     free(node->conn_host);
 397     free(node);
 398 }
 399 
 400 void
 401 crm_peer_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 402 {
 403     if (crm_peer_cache == NULL) {
 404         crm_peer_cache = pcmk__strikey_table(free, destroy_crm_node);
 405     }
 406 
 407     if (crm_remote_peer_cache == NULL) {
 408         crm_remote_peer_cache = pcmk__strikey_table(NULL, destroy_crm_node);
 409     }
 410 
 411     if (known_node_cache == NULL) {
 412         known_node_cache = pcmk__strikey_table(free, destroy_crm_node);
 413     }
 414 }
 415 
 416 void
 417 crm_peer_destroy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419     if (crm_peer_cache != NULL) {
 420         crm_trace("Destroying peer cache with %d members", g_hash_table_size(crm_peer_cache));
 421         g_hash_table_destroy(crm_peer_cache);
 422         crm_peer_cache = NULL;
 423     }
 424 
 425     if (crm_remote_peer_cache != NULL) {
 426         crm_trace("Destroying remote peer cache with %d members", g_hash_table_size(crm_remote_peer_cache));
 427         g_hash_table_destroy(crm_remote_peer_cache);
 428         crm_remote_peer_cache = NULL;
 429     }
 430 
 431     if (known_node_cache != NULL) {
 432         crm_trace("Destroying known node cache with %d members",
 433                   g_hash_table_size(known_node_cache));
 434         g_hash_table_destroy(known_node_cache);
 435         known_node_cache = NULL;
 436     }
 437 
 438 }
 439 
 440 static void (*peer_status_callback)(enum crm_status_type, crm_node_t *,
 441                                     const void *) = NULL;
 442 
 443 /*!
 444  * \brief Set a client function that will be called after peer status changes
 445  *
 446  * \param[in] dispatch  Pointer to function to use as callback
 447  *
 448  * \note Previously, client callbacks were responsible for peer cache
 449  *       management. This is no longer the case, and client callbacks should do
 450  *       only client-specific handling. Callbacks MUST NOT add or remove entries
 451  *       in the peer caches.
 452  */
 453 void
 454 crm_set_status_callback(void (*dispatch) (enum crm_status_type, crm_node_t *, const void *))
     /* [previous][next][first][last][top][bottom][index][help] */
 455 {
 456     peer_status_callback = dispatch;
 457 }
 458 
 459 /*!
 460  * \brief Tell the library whether to automatically reap lost nodes
 461  *
 462  * If TRUE (the default), calling crm_update_peer_proc() will also update the
 463  * peer state to CRM_NODE_MEMBER or CRM_NODE_LOST, and pcmk__update_peer_state()
 464  * will reap peers whose state changes to anything other than CRM_NODE_MEMBER.
 465  * Callers should leave this enabled unless they plan to manage the cache
 466  * separately on their own.
 467  *
 468  * \param[in] autoreap  TRUE to enable automatic reaping, FALSE to disable
 469  */
 470 void
 471 crm_set_autoreap(gboolean autoreap)
     /* [previous][next][first][last][top][bottom][index][help] */
 472 {
 473     crm_autoreap = autoreap;
 474 }
 475 
 476 static void
 477 dump_peer_hash(int level, const char *caller)
     /* [previous][next][first][last][top][bottom][index][help] */
 478 {
 479     GHashTableIter iter;
 480     const char *id = NULL;
 481     crm_node_t *node = NULL;
 482 
 483     g_hash_table_iter_init(&iter, crm_peer_cache);
 484     while (g_hash_table_iter_next(&iter, (gpointer *) &id, (gpointer *) &node)) {
 485         do_crm_log(level, "%s: Node %u/%s = %p - %s", caller, node->id, node->uname, node, id);
 486     }
 487 }
 488 
 489 static gboolean
 490 hash_find_by_data(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 491 {
 492     return value == user_data;
 493 }
 494 
 495 /*!
 496  * \internal
 497  * \brief Search caches for a node (cluster or Pacemaker Remote)
 498  *
 499  * \param[in] id     If not 0, cluster node ID to search for
 500  * \param[in] uname  If not NULL, node name to search for
 501  * \param[in] flags  Bitmask of enum crm_get_peer_flags
 502  *
 503  * \return Node cache entry if found, otherwise NULL
 504  */
 505 crm_node_t *
 506 pcmk__search_node_caches(unsigned int id, const char *uname, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 507 {
 508     crm_node_t *node = NULL;
 509 
 510     CRM_ASSERT(id > 0 || uname != NULL);
 511 
 512     crm_peer_init();
 513 
 514     if ((uname != NULL) && pcmk_is_set(flags, CRM_GET_PEER_REMOTE)) {
 515         node = g_hash_table_lookup(crm_remote_peer_cache, uname);
 516     }
 517 
 518     if ((node == NULL) && pcmk_is_set(flags, CRM_GET_PEER_CLUSTER)) {
 519         node = pcmk__search_cluster_node_cache(id, uname, NULL);
 520     }
 521     return node;
 522 }
 523 
 524 /*!
 525  * \brief Get a node cache entry (cluster or Pacemaker Remote)
 526  *
 527  * \param[in] id     If not 0, cluster node ID to search for
 528  * \param[in] uname  If not NULL, node name to search for
 529  * \param[in] uuid   If not NULL while id is 0, node UUID instead of cluster
 530  *                   node ID to search for
 531  * \param[in] flags  Bitmask of enum crm_get_peer_flags
 532  *
 533  * \return (Possibly newly created) node cache entry
 534  */
 535 crm_node_t *
 536 pcmk__get_peer_full(unsigned int id, const char *uname, const char *uuid,
     /* [previous][next][first][last][top][bottom][index][help] */
 537                     int flags)
 538 {
 539     crm_node_t *node = NULL;
 540 
 541     CRM_ASSERT(id > 0 || uname != NULL);
 542 
 543     crm_peer_init();
 544 
 545     if (pcmk_is_set(flags, CRM_GET_PEER_REMOTE)) {
 546         node = g_hash_table_lookup(crm_remote_peer_cache, uname);
 547     }
 548 
 549     if ((node == NULL) && pcmk_is_set(flags, CRM_GET_PEER_CLUSTER)) {
 550         node = pcmk__get_peer(id, uname, uuid);
 551     }
 552     return node;
 553 }
 554 
 555 /*!
 556  * \brief Get a node cache entry (cluster or Pacemaker Remote)
 557  *
 558  * \param[in] id     If not 0, cluster node ID to search for
 559  * \param[in] uname  If not NULL, node name to search for
 560  * \param[in] flags  Bitmask of enum crm_get_peer_flags
 561  *
 562  * \return (Possibly newly created) node cache entry
 563  */
 564 crm_node_t *
 565 crm_get_peer_full(unsigned int id, const char *uname, int flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 566 {
 567     return pcmk__get_peer_full(id, uname, NULL, flags);
 568 }
 569 
 570 /*!
 571  * \internal
 572  * \brief Search cluster node cache
 573  *
 574  * \param[in] id     If not 0, cluster node ID to search for
 575  * \param[in] uname  If not NULL, node name to search for
 576  * \param[in] uuid   If not NULL while id is 0, node UUID instead of cluster
 577  *                   node ID to search for
 578  *
 579  * \return Cluster node cache entry if found, otherwise NULL
 580  */
 581 crm_node_t *
 582 pcmk__search_cluster_node_cache(unsigned int id, const char *uname,
     /* [previous][next][first][last][top][bottom][index][help] */
 583                                 const char *uuid)
 584 {
 585     GHashTableIter iter;
 586     crm_node_t *node = NULL;
 587     crm_node_t *by_id = NULL;
 588     crm_node_t *by_name = NULL;
 589 
 590     CRM_ASSERT(id > 0 || uname != NULL);
 591 
 592     crm_peer_init();
 593 
 594     if (uname != NULL) {
 595         g_hash_table_iter_init(&iter, crm_peer_cache);
 596         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 597             if(node->uname && strcasecmp(node->uname, uname) == 0) {
 598                 crm_trace("Name match: %s = %p", node->uname, node);
 599                 by_name = node;
 600                 break;
 601             }
 602         }
 603     }
 604 
 605     if (id > 0) {
 606         g_hash_table_iter_init(&iter, crm_peer_cache);
 607         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 608             if(node->id == id) {
 609                 crm_trace("ID match: %u = %p", node->id, node);
 610                 by_id = node;
 611                 break;
 612             }
 613         }
 614 
 615     } else if (uuid != NULL) {
 616         g_hash_table_iter_init(&iter, crm_peer_cache);
 617         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 618             if (pcmk__str_eq(node->uuid, uuid, pcmk__str_casei)) {
 619                 crm_trace("UUID match: %s = %p", node->uuid, node);
 620                 by_id = node;
 621                 break;
 622             }
 623         }
 624     }
 625 
 626     node = by_id; /* Good default */
 627     if(by_id == by_name) {
 628         /* Nothing to do if they match (both NULL counts) */
 629         crm_trace("Consistent: %p for %u/%s", by_id, id, uname);
 630 
 631     } else if(by_id == NULL && by_name) {
 632         crm_trace("Only one: %p for %u/%s", by_name, id, uname);
 633 
 634         if(id && by_name->id) {
 635             dump_peer_hash(LOG_WARNING, __func__);
 636             crm_crit("Node %u and %u share the same name '%s'",
 637                      id, by_name->id, uname);
 638             node = NULL; /* Create a new one */
 639 
 640         } else {
 641             node = by_name;
 642         }
 643 
 644     } else if(by_name == NULL && by_id) {
 645         crm_trace("Only one: %p for %u/%s", by_id, id, uname);
 646 
 647         if(uname && by_id->uname) {
 648             dump_peer_hash(LOG_WARNING, __func__);
 649             crm_crit("Node '%s' and '%s' share the same cluster nodeid %u: assuming '%s' is correct",
 650                      uname, by_id->uname, id, uname);
 651         }
 652 
 653     } else if(uname && by_id->uname) {
 654         if(pcmk__str_eq(uname, by_id->uname, pcmk__str_casei)) {
 655             crm_notice("Node '%s' has changed its ID from %u to %u", by_id->uname, by_name->id, by_id->id);
 656             g_hash_table_foreach_remove(crm_peer_cache, hash_find_by_data, by_name);
 657 
 658         } else {
 659             crm_warn("Node '%s' and '%s' share the same cluster nodeid: %u %s", by_id->uname, by_name->uname, id, uname);
 660             dump_peer_hash(LOG_INFO, __func__);
 661             crm_abort(__FILE__, __func__, __LINE__, "member weirdness", TRUE,
 662                       TRUE);
 663         }
 664 
 665     } else if(id && by_name->id) {
 666         crm_warn("Node %u and %u share the same name: '%s'", by_id->id, by_name->id, uname);
 667 
 668     } else {
 669         /* Simple merge */
 670 
 671         /* Only corosync-based clusters use node IDs. The functions that call
 672          * pcmk__update_peer_state() and crm_update_peer_proc() only know
 673          * nodeid, so 'by_id' is authoritative when merging.
 674          */
 675         dump_peer_hash(LOG_DEBUG, __func__);
 676 
 677         crm_info("Merging %p into %p", by_name, by_id);
 678         g_hash_table_foreach_remove(crm_peer_cache, hash_find_by_data, by_name);
 679     }
 680 
 681     return node;
 682 }
 683 
 684 #if SUPPORT_COROSYNC
 685 static guint
 686 remove_conflicting_peer(crm_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 687 {
 688     int matches = 0;
 689     GHashTableIter iter;
 690     crm_node_t *existing_node = NULL;
 691 
 692     if (node->id == 0 || node->uname == NULL) {
 693         return 0;
 694     }
 695 
 696     if (!pcmk__corosync_has_nodelist()) {
 697         return 0;
 698     }
 699 
 700     g_hash_table_iter_init(&iter, crm_peer_cache);
 701     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &existing_node)) {
 702         if (existing_node->id > 0
 703             && existing_node->id != node->id
 704             && existing_node->uname != NULL
 705             && strcasecmp(existing_node->uname, node->uname) == 0) {
 706 
 707             if (crm_is_peer_active(existing_node)) {
 708                 continue;
 709             }
 710 
 711             crm_warn("Removing cached offline node %u/%s which has conflicting uname with %u",
 712                      existing_node->id, existing_node->uname, node->id);
 713 
 714             g_hash_table_iter_remove(&iter);
 715             matches++;
 716         }
 717     }
 718 
 719     return matches;
 720 }
 721 #endif
 722 
 723 /*!
 724  * \brief Get a cluster node cache entry
 725  *
 726  * \param[in] id     If not 0, cluster node ID to search for
 727  * \param[in] uname  If not NULL, node name to search for
 728  * \param[in] uuid   If not NULL while id is 0, node UUID instead of cluster
 729  *                   node ID to search for
 730  *
 731  * \return (Possibly newly created) cluster node cache entry
 732  */
 733 /* coverity[-alloc] Memory is referenced in one or both hashtables */
 734 crm_node_t *
 735 pcmk__get_peer(unsigned int id, const char *uname, const char *uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 736 {
 737     crm_node_t *node = NULL;
 738     char *uname_lookup = NULL;
 739 
 740     CRM_ASSERT(id > 0 || uname != NULL);
 741 
 742     crm_peer_init();
 743 
 744     node = pcmk__search_cluster_node_cache(id, uname, uuid);
 745 
 746     /* if uname wasn't provided, and find_peer did not turn up a uname based on id.
 747      * we need to do a lookup of the node name using the id in the cluster membership. */
 748     if ((node == NULL || node->uname == NULL) && (uname == NULL)) { 
 749         uname_lookup = get_node_name(id);
 750     }
 751 
 752     if (uname_lookup) {
 753         uname = uname_lookup;
 754         crm_trace("Inferred a name of '%s' for node %u", uname, id);
 755 
 756         /* try to turn up the node one more time now that we know the uname. */
 757         if (node == NULL) {
 758             node = pcmk__search_cluster_node_cache(id, uname, uuid);
 759         }
 760     }
 761 
 762 
 763     if (node == NULL) {
 764         char *uniqueid = crm_generate_uuid();
 765 
 766         node = calloc(1, sizeof(crm_node_t));
 767         CRM_ASSERT(node);
 768 
 769         crm_info("Created entry %s/%p for node %s/%u (%d total)",
 770                  uniqueid, node, uname, id, 1 + g_hash_table_size(crm_peer_cache));
 771         g_hash_table_replace(crm_peer_cache, uniqueid, node);
 772     }
 773 
 774     if(id > 0 && uname && (node->id == 0 || node->uname == NULL)) {
 775         crm_info("Node %u is now known as %s", id, uname);
 776     }
 777 
 778     if(id > 0 && node->id == 0) {
 779         node->id = id;
 780     }
 781 
 782     if (uname && (node->uname == NULL)) {
 783         update_peer_uname(node, uname);
 784     }
 785 
 786     if(node->uuid == NULL) {
 787         if (uuid == NULL) {
 788             uuid = crm_peer_uuid(node);
 789         }
 790 
 791         if (uuid) {
 792             crm_info("Node %u has uuid %s", id, uuid);
 793 
 794         } else {
 795             crm_info("Cannot obtain a UUID for node %u/%s", id, node->uname);
 796         }
 797     }
 798 
 799     free(uname_lookup);
 800 
 801     return node;
 802 }
 803 
 804 /*!
 805  * \brief Get a cluster node cache entry
 806  *
 807  * \param[in] id     If not 0, cluster node ID to search for
 808  * \param[in] uname  If not NULL, node name to search for
 809  *
 810  * \return (Possibly newly created) cluster node cache entry
 811  */
 812 /* coverity[-alloc] Memory is referenced in one or both hashtables */
 813 crm_node_t *
 814 crm_get_peer(unsigned int id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 815 {
 816     return pcmk__get_peer(id, uname, NULL);
 817 }
 818 
 819 /*!
 820  * \internal
 821  * \brief Update a node's uname
 822  *
 823  * \param[in,out] node   Node object to update
 824  * \param[in]     uname  New name to set
 825  *
 826  * \note This function should not be called within a peer cache iteration,
 827  *       because in some cases it can remove conflicting cache entries,
 828  *       which would invalidate the iterator.
 829  */
 830 static void
 831 update_peer_uname(crm_node_t *node, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 832 {
 833     CRM_CHECK(uname != NULL,
 834               crm_err("Bug: can't update node name without name"); return);
 835     CRM_CHECK(node != NULL,
 836               crm_err("Bug: can't update node name to %s without node", uname);
 837               return);
 838 
 839     if (pcmk__str_eq(uname, node->uname, pcmk__str_casei)) {
 840         crm_debug("Node uname '%s' did not change", uname);
 841         return;
 842     }
 843 
 844     for (const char *c = uname; *c; ++c) {
 845         if ((*c >= 'A') && (*c <= 'Z')) {
 846             crm_warn("Node names with capitals are discouraged, consider changing '%s'",
 847                      uname);
 848             break;
 849         }
 850     }
 851 
 852     pcmk__str_update(&node->uname, uname);
 853 
 854     if (peer_status_callback != NULL) {
 855         peer_status_callback(crm_status_uname, node, NULL);
 856     }
 857 
 858 #if SUPPORT_COROSYNC
 859     if (is_corosync_cluster() && !pcmk_is_set(node->flags, crm_remote_node)) {
 860         remove_conflicting_peer(node);
 861     }
 862 #endif
 863 }
 864 
 865 /*!
 866  * \internal
 867  * \brief Get log-friendly string equivalent of a process flag
 868  *
 869  * \param[in] proc  Process flag
 870  *
 871  * \return Log-friendly string equivalent of \p proc
 872  */
 873 static inline const char *
 874 proc2text(enum crm_proc_flag proc)
     /* [previous][next][first][last][top][bottom][index][help] */
 875 {
 876     const char *text = "unknown";
 877 
 878     switch (proc) {
 879         case crm_proc_none:
 880             text = "none";
 881             break;
 882         case crm_proc_based:
 883             text = "pacemaker-based";
 884             break;
 885         case crm_proc_controld:
 886             text = "pacemaker-controld";
 887             break;
 888         case crm_proc_schedulerd:
 889             text = "pacemaker-schedulerd";
 890             break;
 891         case crm_proc_execd:
 892             text = "pacemaker-execd";
 893             break;
 894         case crm_proc_attrd:
 895             text = "pacemaker-attrd";
 896             break;
 897         case crm_proc_fenced:
 898             text = "pacemaker-fenced";
 899             break;
 900         case crm_proc_cpg:
 901             text = "corosync-cpg";
 902             break;
 903     }
 904     return text;
 905 }
 906 
 907 /*!
 908  * \internal
 909  * \brief Update a node's process information (and potentially state)
 910  *
 911  * \param[in]     source  Caller's function name (for log messages)
 912  * \param[in,out] node    Node object to update
 913  * \param[in]     flag    Bitmask of new process information
 914  * \param[in]     status  node status (online, offline, etc.)
 915  *
 916  * \return NULL if any node was reaped from peer caches, value of node otherwise
 917  *
 918  * \note If this function returns NULL, the supplied node object was likely
 919  *       freed and should not be used again. This function should not be
 920  *       called within a cache iteration if reaping is possible, otherwise
 921  *       reaping could invalidate the iterator.
 922  */
 923 crm_node_t *
 924 crm_update_peer_proc(const char *source, crm_node_t * node, uint32_t flag, const char *status)
     /* [previous][next][first][last][top][bottom][index][help] */
 925 {
 926     uint32_t last = 0;
 927     gboolean changed = FALSE;
 928 
 929     CRM_CHECK(node != NULL, crm_err("%s: Could not set %s to %s for NULL",
 930                                     source, proc2text(flag), status);
 931                             return NULL);
 932 
 933     /* Pacemaker doesn't spawn processes on remote nodes */
 934     if (pcmk_is_set(node->flags, crm_remote_node)) {
 935         return node;
 936     }
 937 
 938     last = node->processes;
 939     if (status == NULL) {
 940         node->processes = flag;
 941         if (node->processes != last) {
 942             changed = TRUE;
 943         }
 944 
 945     } else if (pcmk__str_eq(status, ONLINESTATUS, pcmk__str_casei)) {
 946         if ((node->processes & flag) != flag) {
 947             node->processes = pcmk__set_flags_as(__func__, __LINE__,
 948                                                  LOG_TRACE, "Peer process",
 949                                                  node->uname, node->processes,
 950                                                  flag, "processes");
 951             changed = TRUE;
 952         }
 953 
 954     } else if (node->processes & flag) {
 955         node->processes = pcmk__clear_flags_as(__func__, __LINE__,
 956                                                LOG_TRACE, "Peer process",
 957                                                node->uname, node->processes,
 958                                                flag, "processes");
 959         changed = TRUE;
 960     }
 961 
 962     if (changed) {
 963         if (status == NULL && flag <= crm_proc_none) {
 964             crm_info("%s: Node %s[%u] - all processes are now offline", source, node->uname,
 965                      node->id);
 966         } else {
 967             crm_info("%s: Node %s[%u] - %s is now %s", source, node->uname, node->id,
 968                      proc2text(flag), status);
 969         }
 970 
 971         if (pcmk_is_set(node->processes, crm_get_cluster_proc())) {
 972             node->when_online = time(NULL);
 973 
 974         } else {
 975             node->when_online = 0;
 976         }
 977 
 978         /* Call the client callback first, then update the peer state,
 979          * in case the node will be reaped
 980          */
 981         if (peer_status_callback != NULL) {
 982             peer_status_callback(crm_status_processes, node, &last);
 983         }
 984 
 985         /* The client callback shouldn't touch the peer caches,
 986          * but as a safety net, bail if the peer cache was destroyed.
 987          */
 988         if (crm_peer_cache == NULL) {
 989             return NULL;
 990         }
 991 
 992         if (crm_autoreap) {
 993             const char *peer_state = NULL;
 994 
 995             if (pcmk_is_set(node->processes, crm_get_cluster_proc())) {
 996                 peer_state = CRM_NODE_MEMBER;
 997             } else {
 998                 peer_state = CRM_NODE_LOST;
 999             }
1000             node = pcmk__update_peer_state(__func__, node, peer_state, 0);
1001         }
1002     } else {
1003         crm_trace("%s: Node %s[%u] - %s is unchanged (%s)", source, node->uname, node->id,
1004                   proc2text(flag), status);
1005     }
1006     return node;
1007 }
1008 
1009 /*!
1010  * \internal
1011  * \brief Update a cluster node cache entry's expected join state
1012  *
1013  * \param[in]     source    Caller's function name (for logging)
1014  * \param[in,out] node      Node to update
1015  * \param[in]     expected  Node's new join state
1016  */
1017 void
1018 pcmk__update_peer_expected(const char *source, crm_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1019                            const char *expected)
1020 {
1021     char *last = NULL;
1022     gboolean changed = FALSE;
1023 
1024     CRM_CHECK(node != NULL, crm_err("%s: Could not set 'expected' to %s", source, expected);
1025               return);
1026 
1027     /* Remote nodes don't participate in joins */
1028     if (pcmk_is_set(node->flags, crm_remote_node)) {
1029         return;
1030     }
1031 
1032     last = node->expected;
1033     if (expected != NULL && !pcmk__str_eq(node->expected, expected, pcmk__str_casei)) {
1034         node->expected = strdup(expected);
1035         changed = TRUE;
1036     }
1037 
1038     if (changed) {
1039         crm_info("%s: Node %s[%u] - expected state is now %s (was %s)", source, node->uname, node->id,
1040                  expected, last);
1041         free(last);
1042     } else {
1043         crm_trace("%s: Node %s[%u] - expected state is unchanged (%s)", source, node->uname,
1044                   node->id, expected);
1045     }
1046 }
1047 
1048 /*!
1049  * \internal
1050  * \brief Update a node's state and membership information
1051  *
1052  * \param[in]     source      Caller's function name (for log messages)
1053  * \param[in,out] node        Node object to update
1054  * \param[in]     state       Node's new state
1055  * \param[in]     membership  Node's new membership ID
1056  * \param[in,out] iter        If not NULL, pointer to node's peer cache iterator
1057  *
1058  * \return NULL if any node was reaped, value of node otherwise
1059  *
1060  * \note If this function returns NULL, the supplied node object was likely
1061  *       freed and should not be used again. This function may be called from
1062  *       within a peer cache iteration if the iterator is supplied.
1063  */
1064 static crm_node_t *
1065 update_peer_state_iter(const char *source, crm_node_t *node, const char *state,
     /* [previous][next][first][last][top][bottom][index][help] */
1066                        uint64_t membership, GHashTableIter *iter)
1067 {
1068     gboolean is_member;
1069 
1070     CRM_CHECK(node != NULL,
1071               crm_err("Could not set state for unknown host to %s"
1072                       CRM_XS " source=%s", state, source);
1073               return NULL);
1074 
1075     is_member = pcmk__str_eq(state, CRM_NODE_MEMBER, pcmk__str_casei);
1076     if (is_member) {
1077         node->when_lost = 0;
1078         if (membership) {
1079             node->last_seen = membership;
1080         }
1081     }
1082 
1083     if (state && !pcmk__str_eq(node->state, state, pcmk__str_casei)) {
1084         char *last = node->state;
1085 
1086         if (is_member) {
1087              node->when_member = time(NULL);
1088 
1089         } else {
1090              node->when_member = 0;
1091         }
1092 
1093         node->state = strdup(state);
1094         crm_notice("Node %s state is now %s " CRM_XS
1095                    " nodeid=%u previous=%s source=%s", node->uname, state,
1096                    node->id, (last? last : "unknown"), source);
1097         if (peer_status_callback != NULL) {
1098             peer_status_callback(crm_status_nstate, node, last);
1099         }
1100         free(last);
1101 
1102         if (crm_autoreap && !is_member
1103             && !pcmk_is_set(node->flags, crm_remote_node)) {
1104             /* We only autoreap from the peer cache, not the remote peer cache,
1105              * because the latter should be managed only by
1106              * crm_remote_peer_cache_refresh().
1107              */
1108             if(iter) {
1109                 crm_notice("Purged 1 peer with id=%u and/or uname=%s from the membership cache", node->id, node->uname);
1110                 g_hash_table_iter_remove(iter);
1111 
1112             } else {
1113                 reap_crm_member(node->id, node->uname);
1114             }
1115             node = NULL;
1116         }
1117 
1118     } else {
1119         crm_trace("Node %s state is unchanged (%s) " CRM_XS
1120                   " nodeid=%u source=%s", node->uname, state, node->id, source);
1121     }
1122     return node;
1123 }
1124 
1125 /*!
1126  * \brief Update a node's state and membership information
1127  *
1128  * \param[in]     source      Caller's function name (for log messages)
1129  * \param[in,out] node        Node object to update
1130  * \param[in]     state       Node's new state
1131  * \param[in]     membership  Node's new membership ID
1132  *
1133  * \return NULL if any node was reaped, value of node otherwise
1134  *
1135  * \note If this function returns NULL, the supplied node object was likely
1136  *       freed and should not be used again. This function should not be
1137  *       called within a cache iteration if reaping is possible,
1138  *       otherwise reaping could invalidate the iterator.
1139  */
1140 crm_node_t *
1141 pcmk__update_peer_state(const char *source, crm_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1142                         const char *state, uint64_t membership)
1143 {
1144     return update_peer_state_iter(source, node, state, membership, NULL);
1145 }
1146 
1147 /*!
1148  * \internal
1149  * \brief Reap all nodes from cache whose membership information does not match
1150  *
1151  * \param[in] membership  Membership ID of nodes to keep
1152  */
1153 void
1154 pcmk__reap_unseen_nodes(uint64_t membership)
     /* [previous][next][first][last][top][bottom][index][help] */
1155 {
1156     GHashTableIter iter;
1157     crm_node_t *node = NULL;
1158 
1159     crm_trace("Reaping unseen nodes...");
1160     g_hash_table_iter_init(&iter, crm_peer_cache);
1161     while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&node)) {
1162         if (node->last_seen != membership) {
1163             if (node->state) {
1164                 /*
1165                  * Calling update_peer_state_iter() allows us to
1166                  * remove the node from crm_peer_cache without
1167                  * invalidating our iterator
1168                  */
1169                 update_peer_state_iter(__func__, node, CRM_NODE_LOST,
1170                                            membership, &iter);
1171 
1172             } else {
1173                 crm_info("State of node %s[%u] is still unknown",
1174                          node->uname, node->id);
1175             }
1176         }
1177     }
1178 }
1179 
1180 static crm_node_t *
1181 find_known_node(const char *id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
1182 {
1183     GHashTableIter iter;
1184     crm_node_t *node = NULL;
1185     crm_node_t *by_id = NULL;
1186     crm_node_t *by_name = NULL;
1187 
1188     if (uname) {
1189         g_hash_table_iter_init(&iter, known_node_cache);
1190         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
1191             if (node->uname && strcasecmp(node->uname, uname) == 0) {
1192                 crm_trace("Name match: %s = %p", node->uname, node);
1193                 by_name = node;
1194                 break;
1195             }
1196         }
1197     }
1198 
1199     if (id) {
1200         g_hash_table_iter_init(&iter, known_node_cache);
1201         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
1202             if(strcasecmp(node->uuid, id) == 0) {
1203                 crm_trace("ID match: %s= %p", id, node);
1204                 by_id = node;
1205                 break;
1206             }
1207         }
1208     }
1209 
1210     node = by_id; /* Good default */
1211     if (by_id == by_name) {
1212         /* Nothing to do if they match (both NULL counts) */
1213         crm_trace("Consistent: %p for %s/%s", by_id, id, uname);
1214 
1215     } else if (by_id == NULL && by_name) {
1216         crm_trace("Only one: %p for %s/%s", by_name, id, uname);
1217 
1218         if (id) {
1219             node = NULL;
1220 
1221         } else {
1222             node = by_name;
1223         }
1224 
1225     } else if (by_name == NULL && by_id) {
1226         crm_trace("Only one: %p for %s/%s", by_id, id, uname);
1227 
1228         if (uname) {
1229             node = NULL;
1230         }
1231 
1232     } else if (uname && by_id->uname
1233                && pcmk__str_eq(uname, by_id->uname, pcmk__str_casei)) {
1234         /* Multiple nodes have the same uname in the CIB.
1235          * Return by_id. */
1236 
1237     } else if (id && by_name->uuid
1238                && pcmk__str_eq(id, by_name->uuid, pcmk__str_casei)) {
1239         /* Multiple nodes have the same id in the CIB.
1240          * Return by_name. */
1241         node = by_name;
1242 
1243     } else {
1244         node = NULL;
1245     }
1246 
1247     if (node == NULL) {
1248         crm_debug("Couldn't find node%s%s%s%s",
1249                    id? " " : "",
1250                    id? id : "",
1251                    uname? " with name " : "",
1252                    uname? uname : "");
1253     }
1254 
1255     return node;
1256 }
1257 
1258 static void
1259 known_node_cache_refresh_helper(xmlNode *xml_node, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1260 {
1261     const char *id = crm_element_value(xml_node, XML_ATTR_ID);
1262     const char *uname = crm_element_value(xml_node, XML_ATTR_UNAME);
1263     crm_node_t * node =  NULL;
1264 
1265     CRM_CHECK(id != NULL && uname !=NULL, return);
1266     node = find_known_node(id, uname);
1267 
1268     if (node == NULL) {
1269         char *uniqueid = crm_generate_uuid();
1270 
1271         node = calloc(1, sizeof(crm_node_t));
1272         CRM_ASSERT(node != NULL);
1273 
1274         node->uname = strdup(uname);
1275         CRM_ASSERT(node->uname != NULL);
1276 
1277         node->uuid = strdup(id);
1278         CRM_ASSERT(node->uuid != NULL);
1279 
1280         g_hash_table_replace(known_node_cache, uniqueid, node);
1281 
1282     } else if (pcmk_is_set(node->flags, crm_node_dirty)) {
1283         pcmk__str_update(&node->uname, uname);
1284 
1285         /* Node is in cache and hasn't been updated already, so mark it clean */
1286         clear_peer_flags(node, crm_node_dirty);
1287     }
1288 
1289 }
1290 
1291 static void
1292 refresh_known_node_cache(xmlNode *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
1293 {
1294     crm_peer_init();
1295 
1296     g_hash_table_foreach(known_node_cache, mark_dirty, NULL);
1297 
1298     crm_foreach_xpath_result(cib, PCMK__XP_MEMBER_NODE_CONFIG,
1299                              known_node_cache_refresh_helper, NULL);
1300 
1301     /* Remove all old cache entries that weren't seen in the CIB */
1302     g_hash_table_foreach_remove(known_node_cache, is_dirty, NULL);
1303 }
1304 
1305 void
1306 pcmk__refresh_node_caches_from_cib(xmlNode *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
1307 {
1308     crm_remote_peer_cache_refresh(cib);
1309     refresh_known_node_cache(cib);
1310 }
1311 
1312 /*!
1313  * \internal
1314  * \brief Search known node cache
1315  *
1316  * \param[in] id     If not 0, cluster node ID to search for
1317  * \param[in] uname  If not NULL, node name to search for
1318  * \param[in] flags  Bitmask of enum crm_get_peer_flags
1319  *
1320  * \return Known node cache entry if found, otherwise NULL
1321  */
1322 crm_node_t *
1323 pcmk__search_known_node_cache(unsigned int id, const char *uname,
     /* [previous][next][first][last][top][bottom][index][help] */
1324                               uint32_t flags)
1325 {
1326     crm_node_t *node = NULL;
1327     char *id_str = NULL;
1328 
1329     CRM_ASSERT(id > 0 || uname != NULL);
1330 
1331     node = pcmk__search_node_caches(id, uname, flags);
1332 
1333     if (node || !(flags & CRM_GET_PEER_CLUSTER)) {
1334         return node;
1335     }
1336 
1337     if (id > 0) {
1338         id_str = crm_strdup_printf("%u", id);
1339     }
1340 
1341     node = find_known_node(id_str, uname);
1342 
1343     free(id_str);
1344     return node;
1345 }
1346 
1347 
1348 // Deprecated functions kept only for backward API compatibility
1349 // LCOV_EXCL_START
1350 
1351 #include <crm/cluster/compat.h>
1352 
1353 int
1354 crm_terminate_member(int nodeid, const char *uname, void *unused)
     /* [previous][next][first][last][top][bottom][index][help] */
1355 {
1356     return stonith_api_kick(nodeid, uname, 120, TRUE);
1357 }
1358 
1359 int
1360 crm_terminate_member_no_mainloop(int nodeid, const char *uname, int *connection)
     /* [previous][next][first][last][top][bottom][index][help] */
1361 {
1362     return stonith_api_kick(nodeid, uname, 120, TRUE);
1363 }
1364 
1365 // LCOV_EXCL_STOP
1366 // End deprecated API

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