root/lib/cluster/cluster.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__cluster_parse_msg_type
  2. pcmk__cluster_node_uuid
  3. pcmk_cluster_connect
  4. pcmk_cluster_disconnect
  5. pcmk_cluster_new
  6. pcmk_cluster_free
  7. pcmk_cluster_set_destroy_fn
  8. pcmk__cluster_send_message
  9. pcmk__cluster_node_name
  10. pcmk__cluster_local_node_name
  11. pcmk__node_name_from_uuid
  12. pcmk_cluster_layer_text
  13. pcmk_get_cluster_layer
  14. set_uuid
  15. crm_cluster_connect
  16. crm_cluster_disconnect
  17. name_for_cluster_type
  18. get_cluster_type
  19. is_corosync_cluster
  20. send_cluster_message
  21. crm_peer_uuid
  22. get_node_name
  23. get_local_node_name
  24. crm_peer_uname

   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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <dlfcn.h>
  12 
  13 #include <inttypes.h>               // PRIu32
  14 #include <stdbool.h>
  15 #include <stdio.h>
  16 #include <unistd.h>
  17 #include <string.h>
  18 #include <stdlib.h>
  19 #include <time.h>
  20 #include <sys/param.h>
  21 #include <sys/types.h>
  22 #include <sys/utsname.h>            // uname()
  23 
  24 #include <glib.h>                   // gboolean
  25 
  26 #include <crm/crm.h>
  27 
  28 #include <crm/common/ipc.h>
  29 #include <crm/common/xml.h>
  30 #include <crm/cluster/internal.h>
  31 #include "crmcluster_private.h"
  32 
  33 CRM_TRACE_INIT_DATA(cluster);
  34 
  35 /*!
  36  * \internal
  37  * \brief Get the message type equivalent of a string
  38  *
  39  * \param[in] text  String of message type
  40  *
  41  * \return Message type equivalent of \p text
  42  */
  43 enum crm_ais_msg_types
  44 pcmk__cluster_parse_msg_type(const char *text)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46     CRM_CHECK(text != NULL, return crm_msg_none);
  47 
  48     text = pcmk__message_name(text);
  49 
  50     if (pcmk__str_eq(text, "ais", pcmk__str_none)) {
  51         return crm_msg_ais;
  52     }
  53     if (pcmk__str_eq(text, CRM_SYSTEM_CIB, pcmk__str_none)) {
  54         return crm_msg_cib;
  55     }
  56     if (pcmk__str_any_of(text, CRM_SYSTEM_CRMD, CRM_SYSTEM_DC, NULL)) {
  57         return crm_msg_crmd;
  58     }
  59     if (pcmk__str_eq(text, CRM_SYSTEM_TENGINE, pcmk__str_none)) {
  60         return crm_msg_te;
  61     }
  62     if (pcmk__str_eq(text, CRM_SYSTEM_PENGINE, pcmk__str_none)) {
  63         return crm_msg_pe;
  64     }
  65     if (pcmk__str_eq(text, CRM_SYSTEM_LRMD, pcmk__str_none)) {
  66         return crm_msg_lrmd;
  67     }
  68     if (pcmk__str_eq(text, CRM_SYSTEM_STONITHD, pcmk__str_none)) {
  69         return crm_msg_stonithd;
  70     }
  71     if (pcmk__str_eq(text, "stonith-ng", pcmk__str_none)) {
  72         return crm_msg_stonith_ng;
  73     }
  74     if (pcmk__str_eq(text, "attrd", pcmk__str_none)) {
  75         return crm_msg_attrd;
  76     }
  77     return crm_msg_none;
  78 }
  79 
  80 /*!
  81  * \internal
  82  * \brief Get a node's cluster-layer UUID, setting it if not already set
  83  *
  84  * \param[in,out] node  Node to check
  85  *
  86  * \return Cluster-layer node UUID of \p node, or \c NULL if unknown
  87  */
  88 const char *
  89 pcmk__cluster_node_uuid(crm_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
  92 
  93     if (node == NULL) {
  94         return NULL;
  95     }
  96     if (node->uuid != NULL) {
  97         return node->uuid;
  98     }
  99 
 100     switch (cluster_layer) {
 101 #if SUPPORT_COROSYNC
 102         case pcmk_cluster_layer_corosync:
 103             node->uuid = pcmk__corosync_uuid(node);
 104             return node->uuid;
 105 #endif  // SUPPORT_COROSYNC
 106 
 107         default:
 108             crm_err("Unsupported cluster layer %s",
 109                     pcmk_cluster_layer_text(cluster_layer));
 110             return NULL;
 111     }
 112 }
 113 
 114 /*!
 115  * \internal
 116  * \brief Connect to the cluster layer
 117  *
 118  * \param[in,out] cluster  Initialized cluster object to connect
 119  *
 120  * \return Standard Pacemaker return code
 121  */
 122 int
 123 pcmk_cluster_connect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
 126     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 127 
 128     // cts-lab looks for this message
 129     crm_notice("Connecting to %s cluster layer", cluster_layer_s);
 130 
 131     switch (cluster_layer) {
 132 #if SUPPORT_COROSYNC
 133         case pcmk_cluster_layer_corosync:
 134             return pcmk__corosync_connect(cluster);
 135 #endif // SUPPORT_COROSYNC
 136 
 137         default:
 138             break;
 139     }
 140 
 141     crm_err("Failed to connect to unsupported cluster layer %s",
 142             cluster_layer_s);
 143     return EPROTONOSUPPORT;
 144 }
 145 
 146 /*!
 147  * \brief Disconnect from the cluster layer
 148  *
 149  * \param[in,out] cluster  Cluster object to disconnect
 150  *
 151  * \return Standard Pacemaker return code
 152  */
 153 int
 154 pcmk_cluster_disconnect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
 157     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 158 
 159     crm_info("Disconnecting from %s cluster layer", cluster_layer_s);
 160 
 161     switch (cluster_layer) {
 162 #if SUPPORT_COROSYNC
 163         case pcmk_cluster_layer_corosync:
 164             pcmk__corosync_disconnect(cluster);
 165             pcmk__cluster_destroy_node_caches();
 166             return pcmk_rc_ok;
 167 #endif // SUPPORT_COROSYNC
 168 
 169         default:
 170             break;
 171     }
 172 
 173     crm_err("Failed to disconnect from unsupported cluster layer %s",
 174             cluster_layer_s);
 175     return EPROTONOSUPPORT;
 176 }
 177 
 178 /*!
 179  * \brief Allocate a new \p pcmk_cluster_t object
 180  *
 181  * \return A newly allocated \p pcmk_cluster_t object (guaranteed not \c NULL)
 182  * \note The caller is responsible for freeing the return value using
 183  *       \p pcmk_cluster_free().
 184  */
 185 pcmk_cluster_t *
 186 pcmk_cluster_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 187 {
 188     return (pcmk_cluster_t *) pcmk__assert_alloc(1, sizeof(pcmk_cluster_t));
 189 }
 190 
 191 /*!
 192  * \brief Free a \p pcmk_cluster_t object and its dynamically allocated members
 193  *
 194  * \param[in,out] cluster  Cluster object to free
 195  */
 196 void
 197 pcmk_cluster_free(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 {
 199     if (cluster == NULL) {
 200         return;
 201     }
 202     free(cluster->uuid);
 203     free(cluster->uname);
 204     free(cluster);
 205 }
 206 
 207 /*!
 208  * \brief Set the destroy function for a cluster object
 209  *
 210  * \param[in,out] cluster  Cluster object
 211  * \param[in]     fn       Destroy function to set
 212  *
 213  * \return Standard Pacemaker return code
 214  */
 215 int
 216 pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer))
     /* [previous][next][first][last][top][bottom][index][help] */
 217 {
 218     if (cluster == NULL) {
 219         return EINVAL;
 220     }
 221     cluster->destroy = fn;
 222     return pcmk_rc_ok;
 223 }
 224 
 225 /*!
 226  * \internal
 227  * \brief Send an XML message via the cluster messaging layer
 228  *
 229  * \param[in] node     Cluster node to send message to
 230  * \param[in] service  Message type to use in message host info
 231  * \param[in] data     XML message to send
 232  *
 233  * \return \c true on success, or \c false otherwise
 234  */
 235 bool
 236 pcmk__cluster_send_message(const crm_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 237                            enum crm_ais_msg_types service, const xmlNode *data)
 238 {
 239     // @TODO Return standard Pacemaker return code
 240     switch (pcmk_get_cluster_layer()) {
 241 #if SUPPORT_COROSYNC
 242         case pcmk_cluster_layer_corosync:
 243             return pcmk__cpg_send_xml(data, node, service);
 244 #endif  // SUPPORT_COROSYNC
 245 
 246         default:
 247             break;
 248     }
 249     return false;
 250 }
 251 
 252 /*!
 253  * \internal
 254  * \brief Get the node name corresponding to a cluster-layer node ID
 255  *
 256  * Get the node name from the cluster layer if possible. Otherwise, if for the
 257  * local node, call \c uname() and get the \c nodename member from the
 258  * <tt>struct utsname</tt> object.
 259  *
 260  * \param[in] nodeid  Node ID to check (or 0 for the local node)
 261  *
 262  * \return Node name corresponding to \p nodeid
 263  *
 264  * \note This will fatally exit if \c uname() fails to get the local node name
 265  *       or we run out of memory.
 266  * \note The caller is responsible for freeing the return value using \c free().
 267  */
 268 char *
 269 pcmk__cluster_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271     char *name = NULL;
 272     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
 273     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 274 
 275     switch (cluster_layer) {
 276 #if SUPPORT_COROSYNC
 277         case pcmk_cluster_layer_corosync:
 278             name = pcmk__corosync_name(0, nodeid);
 279             if (name != NULL) {
 280                 return name;
 281             }
 282             break;
 283 #endif // SUPPORT_COROSYNC
 284 
 285         default:
 286             crm_err("Unsupported cluster layer: %s", cluster_layer_s);
 287             break;
 288     }
 289 
 290     if (nodeid == 0) {
 291         struct utsname hostinfo;
 292 
 293         crm_notice("Could not get local node name from %s cluster layer, "
 294                    "defaulting to local hostname",
 295                    cluster_layer_s);
 296 
 297         if (uname(&hostinfo) < 0) {
 298             // @TODO Maybe let the caller decide what to do
 299             crm_err("Failed to get the local hostname");
 300             crm_exit(CRM_EX_FATAL);
 301         }
 302         return pcmk__str_copy(hostinfo.nodename);
 303     }
 304 
 305     crm_notice("Could not obtain a node name for node with "
 306                PCMK_XA_ID "=" PRIu32,
 307                nodeid);
 308     return NULL;
 309 }
 310 
 311 /*!
 312  * \internal
 313  * \brief Get the local node's cluster-layer node name
 314  *
 315  * If getting the node name from the cluster layer is impossible, call
 316  * \c uname() and get the \c nodename member from the <tt>struct utsname</tt>
 317  * object.
 318  *
 319  * \return Local node's name
 320  *
 321  * \note This will fatally exit if \c uname() fails to get the local node name
 322  *       or we run out of memory.
 323  */
 324 const char *
 325 pcmk__cluster_local_node_name(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     // @TODO Refactor to avoid trivially leaking name at exit
 328     static char *name = NULL;
 329 
 330     if (name == NULL) {
 331         name = pcmk__cluster_node_name(0);
 332     }
 333     return name;
 334 }
 335 
 336 /*!
 337  * \internal
 338  * \brief Get the node name corresonding to a node UUID
 339  *
 340  * Look for the UUID in both the remote node cache and the cluster member cache.
 341  *
 342  * \param[in] uuid  UUID to search for
 343  *
 344  * \return Node name corresponding to \p uuid if found, or \c NULL otherwise
 345  */
 346 const char *
 347 pcmk__node_name_from_uuid(const char *uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     /* @TODO There are too many functions in libcrmcluster that look up a node
 350      * from the node caches (possibly creating a cache entry if none exists).
 351      * There are at least the following:
 352      * * pcmk__cluster_lookup_remote_node()
 353      * * pcmk__get_node()
 354      * * pcmk__node_name_from_uuid()
 355      * * pcmk__search_node_caches()
 356      *
 357      * There's a lot of duplication among them, but they all do slightly
 358      * different things. We should try to clean them up and consolidate them to
 359      * the extent possible, likely with new helper functions.
 360      */
 361     GHashTableIter iter;
 362     crm_node_t *node = NULL;
 363 
 364     CRM_CHECK(uuid != NULL, return NULL);
 365 
 366     // Remote nodes have the same uname and uuid
 367     if (g_hash_table_lookup(crm_remote_peer_cache, uuid)) {
 368         return uuid;
 369     }
 370 
 371     g_hash_table_iter_init(&iter, crm_peer_cache);
 372     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 373         if (pcmk__str_eq(node->uuid, uuid, pcmk__str_casei)) {
 374             return node->uname;
 375         }
 376     }
 377     return NULL;
 378 }
 379 
 380 /*!
 381  * \brief Get a log-friendly string equivalent of a cluster layer
 382  *
 383  * \param[in] layer  Cluster layer
 384  *
 385  * \return Log-friendly string corresponding to \p layer
 386  */
 387 const char *
 388 pcmk_cluster_layer_text(enum pcmk_cluster_layer layer)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390     switch (layer) {
 391         case pcmk_cluster_layer_corosync:
 392             return "corosync";
 393         case pcmk_cluster_layer_unknown:
 394             return "unknown";
 395         case pcmk_cluster_layer_invalid:
 396             return "invalid";
 397         default:
 398             crm_err("Invalid cluster layer: %d", layer);
 399             return "invalid";
 400     }
 401 }
 402 
 403 /*!
 404  * \brief Get and validate the local cluster layer
 405  *
 406  * If a cluster layer is not configured via the \c PCMK__ENV_CLUSTER_TYPE local
 407  * option, this will try to detect an active cluster from among the supported
 408  * cluster layers.
 409  *
 410  * \return Local cluster layer
 411  *
 412  * \note This will fatally exit if the configured cluster layer is invalid.
 413  */
 414 enum pcmk_cluster_layer
 415 pcmk_get_cluster_layer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     static enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown;
 418     const char *cluster = NULL;
 419 
 420     // Cluster layer is stable once set
 421     if (cluster_layer != pcmk_cluster_layer_unknown) {
 422         return cluster_layer;
 423     }
 424 
 425     cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE);
 426 
 427     if (cluster != NULL) {
 428         crm_info("Verifying configured cluster layer '%s'", cluster);
 429         cluster_layer = pcmk_cluster_layer_invalid;
 430 
 431 #if SUPPORT_COROSYNC
 432         if (pcmk__str_eq(cluster, PCMK_VALUE_COROSYNC, pcmk__str_casei)) {
 433             cluster_layer = pcmk_cluster_layer_corosync;
 434         }
 435 #endif  // SUPPORT_COROSYNC
 436 
 437         if (cluster_layer == pcmk_cluster_layer_invalid) {
 438             crm_notice("This installation does not support the '%s' cluster "
 439                        "infrastructure: terminating",
 440                        cluster);
 441             crm_exit(CRM_EX_FATAL);
 442         }
 443         crm_info("Assuming an active '%s' cluster", cluster);
 444 
 445     } else {
 446         // Nothing configured, so test supported cluster layers
 447 #if SUPPORT_COROSYNC
 448         crm_debug("Testing with Corosync");
 449         if (pcmk__corosync_is_active()) {
 450             cluster_layer = pcmk_cluster_layer_corosync;
 451         }
 452 #endif  // SUPPORT_COROSYNC
 453 
 454         if (cluster_layer == pcmk_cluster_layer_unknown) {
 455             crm_notice("Could not determine the current cluster layer");
 456         } else {
 457             crm_info("Detected an active '%s' cluster",
 458                      pcmk_cluster_layer_text(cluster_layer));
 459         }
 460     }
 461 
 462     return cluster_layer;
 463 }
 464 
 465 // Deprecated functions kept only for backward API compatibility
 466 // LCOV_EXCL_START
 467 
 468 #include <crm/cluster/compat.h>
 469 
 470 void
 471 set_uuid(xmlNode *xml, const char *attr, crm_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 472 {
 473     crm_xml_add(xml, attr, pcmk__cluster_node_uuid(node));
 474 }
 475 
 476 gboolean
 477 crm_cluster_connect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 478 {
 479     return pcmk_cluster_connect(cluster) == pcmk_rc_ok;
 480 }
 481 
 482 void
 483 crm_cluster_disconnect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 484 {
 485     pcmk_cluster_disconnect(cluster);
 486 }
 487 
 488 const char *
 489 name_for_cluster_type(enum cluster_type_e type)
     /* [previous][next][first][last][top][bottom][index][help] */
 490 {
 491     switch (type) {
 492         case pcmk_cluster_corosync:
 493             return "corosync";
 494         case pcmk_cluster_unknown:
 495             return "unknown";
 496         case pcmk_cluster_invalid:
 497             return "invalid";
 498     }
 499     crm_err("Invalid cluster type: %d", type);
 500     return "invalid";
 501 }
 502 
 503 enum cluster_type_e
 504 get_cluster_type(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 505 {
 506     return (enum cluster_type_e) pcmk_get_cluster_layer();
 507 }
 508 
 509 gboolean
 510 is_corosync_cluster(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 511 {
 512     return pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync;
 513 }
 514 
 515 gboolean
 516 send_cluster_message(const crm_node_t *node, enum crm_ais_msg_types service,
     /* [previous][next][first][last][top][bottom][index][help] */
 517                      const xmlNode *data, gboolean ordered)
 518 {
 519     return pcmk__cluster_send_message(node, service, data);
 520 }
 521 
 522 const char *
 523 crm_peer_uuid(crm_node_t *peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 524 {
 525     return pcmk__cluster_node_uuid(peer);
 526 }
 527 
 528 char *
 529 get_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 530 {
 531     return pcmk__cluster_node_name(nodeid);
 532 }
 533 
 534 const char *
 535 get_local_node_name(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 536 {
 537     return pcmk__cluster_local_node_name();
 538 }
 539 
 540 const char *
 541 crm_peer_uname(const char *uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 542 {
 543     return pcmk__node_name_from_uuid(uuid);
 544 }
 545 
 546 // LCOV_EXCL_STOP
 547 // End deprecated API

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