root/lib/cluster/cluster.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__cluster_get_xml_id
  2. pcmk_cluster_connect
  3. pcmk_cluster_disconnect
  4. pcmk_cluster_new
  5. pcmk_cluster_free
  6. pcmk_cluster_set_destroy_fn
  7. pcmk__cluster_send_message
  8. pcmk__cluster_node_name
  9. pcmk__cluster_local_node_name
  10. pcmk__node_name_from_uuid
  11. pcmk_cluster_layer_text
  12. pcmk_get_cluster_layer
  13. crm_cluster_connect
  14. name_for_cluster_type
  15. get_cluster_type

   1 /*
   2  * Copyright 2004-2025 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 a node's XML ID in the CIB, setting it if not already set
  38  *
  39  * \param[in,out] node  Node to check
  40  *
  41  * \return CIB XML ID of \p node if known, otherwise \c NULL
  42  */
  43 const char *
  44 pcmk__cluster_get_xml_id(pcmk__node_status_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
  47 
  48     if (node == NULL) {
  49         return NULL;
  50     }
  51     if (node->xml_id != NULL) {
  52         return node->xml_id;
  53     }
  54 
  55     // xml_id is always set when a Pacemaker Remote node entry is created
  56     CRM_CHECK(!pcmk_is_set(node->flags, pcmk__node_status_remote), return NULL);
  57 
  58     switch (cluster_layer) {
  59 #if SUPPORT_COROSYNC
  60         case pcmk_cluster_layer_corosync:
  61             node->xml_id = pcmk__corosync_uuid(node);
  62             return node->xml_id;
  63 #endif  // SUPPORT_COROSYNC
  64 
  65         default:
  66             crm_err("Unsupported cluster layer %s",
  67                     pcmk_cluster_layer_text(cluster_layer));
  68             return NULL;
  69     }
  70 }
  71 
  72 /*!
  73  * \internal
  74  * \brief Connect to the cluster layer
  75  *
  76  * \param[in,out] cluster  Initialized cluster object to connect
  77  *
  78  * \return Standard Pacemaker return code
  79  */
  80 int
  81 pcmk_cluster_connect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
  84     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
  85 
  86     if (cluster == NULL) {
  87         return EINVAL;
  88     }
  89 
  90     // cts-lab looks for this message
  91     crm_notice("Connecting to %s cluster layer", cluster_layer_s);
  92 
  93     switch (cluster_layer) {
  94 #if SUPPORT_COROSYNC
  95         case pcmk_cluster_layer_corosync:
  96             return pcmk__corosync_connect(cluster);
  97 #endif // SUPPORT_COROSYNC
  98 
  99         default:
 100             break;
 101     }
 102 
 103     crm_err("Failed to connect to unsupported cluster layer %s",
 104             cluster_layer_s);
 105     return EPROTONOSUPPORT;
 106 }
 107 
 108 /*!
 109  * \brief Disconnect from the cluster layer
 110  *
 111  * \param[in,out] cluster  Cluster object to disconnect
 112  *
 113  * \return Standard Pacemaker return code
 114  */
 115 int
 116 pcmk_cluster_disconnect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
 119     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 120 
 121     crm_info("Disconnecting from %s cluster layer", cluster_layer_s);
 122 
 123     switch (cluster_layer) {
 124 #if SUPPORT_COROSYNC
 125         case pcmk_cluster_layer_corosync:
 126             pcmk__corosync_disconnect(cluster);
 127             pcmk__cluster_destroy_node_caches();
 128             return pcmk_rc_ok;
 129 #endif // SUPPORT_COROSYNC
 130 
 131         default:
 132             break;
 133     }
 134 
 135     crm_err("Failed to disconnect from unsupported cluster layer %s",
 136             cluster_layer_s);
 137     return EPROTONOSUPPORT;
 138 }
 139 
 140 /*!
 141  * \brief Allocate a new \p pcmk_cluster_t object
 142  *
 143  * \return A newly allocated \p pcmk_cluster_t object (guaranteed not \c NULL)
 144  * \note The caller is responsible for freeing the return value using
 145  *       \p pcmk_cluster_free().
 146  */
 147 pcmk_cluster_t *
 148 pcmk_cluster_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 149 {
 150     pcmk_cluster_t *cluster = pcmk__assert_alloc(1, sizeof(pcmk_cluster_t));
 151 
 152     cluster->priv = pcmk__assert_alloc(1, sizeof(pcmk__cluster_private_t));
 153     cluster->priv->server = pcmk__parse_server(crm_system_name);
 154     return cluster;
 155 }
 156 
 157 /*!
 158  * \brief Free a \p pcmk_cluster_t object and its dynamically allocated members
 159  *
 160  * \param[in,out] cluster  Cluster object to free
 161  */
 162 void
 163 pcmk_cluster_free(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 164 {
 165     if (cluster == NULL) {
 166         return;
 167     }
 168     election_fini(cluster);
 169     free(cluster->priv->node_xml_id);
 170     free(cluster->priv->node_name);
 171     free(cluster->priv);
 172     free(cluster);
 173 }
 174 
 175 /*!
 176  * \brief Set the destroy function for a cluster object
 177  *
 178  * \param[in,out] cluster  Cluster object
 179  * \param[in]     fn       Destroy function to set
 180  *
 181  * \return Standard Pacemaker return code
 182  */
 183 int
 184 pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer))
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     if (cluster == NULL) {
 187         return EINVAL;
 188     }
 189     cluster->destroy = fn;
 190     return pcmk_rc_ok;
 191 }
 192 
 193 /*!
 194  * \internal
 195  * \brief Send an XML message via the cluster messaging layer
 196  *
 197  * \param[in] node     Cluster node to send message to
 198  * \param[in] service  Message type to use in message host info
 199  * \param[in] data     XML message to send
 200  *
 201  * \return \c true on success, or \c false otherwise
 202  */
 203 bool
 204 pcmk__cluster_send_message(const pcmk__node_status_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 205                            enum pcmk_ipc_server service, const xmlNode *data)
 206 {
 207     // @TODO Return standard Pacemaker return code
 208     switch (pcmk_get_cluster_layer()) {
 209 #if SUPPORT_COROSYNC
 210         case pcmk_cluster_layer_corosync:
 211             return pcmk__cpg_send_xml(data, node, service);
 212 #endif  // SUPPORT_COROSYNC
 213 
 214         default:
 215             break;
 216     }
 217     return false;
 218 }
 219 
 220 /*!
 221  * \internal
 222  * \brief Get the node name corresponding to a cluster-layer node ID
 223  *
 224  * Get the node name from the cluster layer if possible. Otherwise, if for the
 225  * local node, call \c uname() and get the \c nodename member from the
 226  * <tt>struct utsname</tt> object.
 227  *
 228  * \param[in] nodeid  Node ID to check (or 0 for the local node)
 229  *
 230  * \return Node name corresponding to \p nodeid
 231  *
 232  * \note This will fatally exit if \c uname() fails to get the local node name
 233  *       or we run out of memory.
 234  * \note The caller is responsible for freeing the return value using \c free().
 235  */
 236 char *
 237 pcmk__cluster_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239     char *name = NULL;
 240     const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
 241     const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 242 
 243     switch (cluster_layer) {
 244 #if SUPPORT_COROSYNC
 245         case pcmk_cluster_layer_corosync:
 246             name = pcmk__corosync_name(0, nodeid);
 247             if (name != NULL) {
 248                 return name;
 249             }
 250             break;
 251 #endif // SUPPORT_COROSYNC
 252 
 253         default:
 254             crm_err("Unsupported cluster layer: %s", cluster_layer_s);
 255             break;
 256     }
 257 
 258     if (nodeid == 0) {
 259         struct utsname hostinfo;
 260 
 261         crm_notice("Could not get local node name from %s cluster layer, "
 262                    "defaulting to local hostname",
 263                    cluster_layer_s);
 264 
 265         if (uname(&hostinfo) < 0) {
 266             // @TODO Maybe let the caller decide what to do
 267             crm_err("Failed to get the local hostname");
 268             crm_exit(CRM_EX_FATAL);
 269         }
 270         return pcmk__str_copy(hostinfo.nodename);
 271     }
 272 
 273     crm_notice("Could not obtain a node name for node with "
 274                PCMK_XA_ID "=%" PRIu32,
 275                nodeid);
 276     return NULL;
 277 }
 278 
 279 /*!
 280  * \internal
 281  * \brief Get the local node's cluster-layer node name
 282  *
 283  * If getting the node name from the cluster layer is impossible, call
 284  * \c uname() and get the \c nodename member from the <tt>struct utsname</tt>
 285  * object.
 286  *
 287  * \return Local node's name
 288  *
 289  * \note This will fatally exit if \c uname() fails to get the local node name
 290  *       or we run out of memory.
 291  */
 292 const char *
 293 pcmk__cluster_local_node_name(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 294 {
 295     // @TODO Refactor to avoid trivially leaking name at exit
 296     static char *name = NULL;
 297 
 298     if (name == NULL) {
 299         name = pcmk__cluster_node_name(0);
 300     }
 301     return name;
 302 }
 303 
 304 /*!
 305  * \internal
 306  * \brief Get the node name corresonding to a node UUID
 307  *
 308  * Look for the UUID in both the remote node cache and the cluster member cache.
 309  *
 310  * \param[in] uuid  UUID to search for
 311  *
 312  * \return Node name corresponding to \p uuid if found, or \c NULL otherwise
 313  */
 314 const char *
 315 pcmk__node_name_from_uuid(const char *uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 316 {
 317     /* @TODO There are too many functions in libcrmcluster that look up a node
 318      * from the node caches (possibly creating a cache entry if none exists).
 319      * There are at least the following:
 320      * * pcmk__cluster_lookup_remote_node()
 321      * * pcmk__get_node()
 322      * * pcmk__node_name_from_uuid()
 323      * * pcmk__search_node_caches()
 324      *
 325      * There's a lot of duplication among them, but they all do slightly
 326      * different things. We should try to clean them up and consolidate them to
 327      * the extent possible, likely with new helper functions.
 328      */
 329     GHashTableIter iter;
 330     pcmk__node_status_t *node = NULL;
 331 
 332     CRM_CHECK(uuid != NULL, return NULL);
 333 
 334     // Remote nodes have the same uname and uuid
 335     if (g_hash_table_lookup(pcmk__remote_peer_cache, uuid)) {
 336         return uuid;
 337     }
 338 
 339     g_hash_table_iter_init(&iter, pcmk__peer_cache);
 340     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 341         if (pcmk__str_eq(uuid, pcmk__cluster_get_xml_id(node),
 342                          pcmk__str_none)) {
 343             return node->name;
 344         }
 345     }
 346     return NULL;
 347 }
 348 
 349 /*!
 350  * \brief Get a log-friendly string equivalent of a cluster layer
 351  *
 352  * \param[in] layer  Cluster layer
 353  *
 354  * \return Log-friendly string corresponding to \p layer
 355  */
 356 const char *
 357 pcmk_cluster_layer_text(enum pcmk_cluster_layer layer)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 {
 359     switch (layer) {
 360         case pcmk_cluster_layer_corosync:
 361             return "corosync";
 362         case pcmk_cluster_layer_unknown:
 363             return "unknown";
 364         case pcmk_cluster_layer_invalid:
 365             return "invalid";
 366         default:
 367             crm_err("Invalid cluster layer: %d", layer);
 368             return "invalid";
 369     }
 370 }
 371 
 372 /*!
 373  * \brief Get and validate the local cluster layer
 374  *
 375  * If a cluster layer is not configured via the \c PCMK__ENV_CLUSTER_TYPE local
 376  * option, this will try to detect an active cluster from among the supported
 377  * cluster layers.
 378  *
 379  * \return Local cluster layer
 380  *
 381  * \note This will fatally exit if the configured cluster layer is invalid.
 382  */
 383 enum pcmk_cluster_layer
 384 pcmk_get_cluster_layer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 385 {
 386     static enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown;
 387     const char *cluster = NULL;
 388 
 389     // Cluster layer is stable once set
 390     if (cluster_layer != pcmk_cluster_layer_unknown) {
 391         return cluster_layer;
 392     }
 393 
 394     cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE);
 395 
 396     if (cluster != NULL) {
 397         crm_info("Verifying configured cluster layer '%s'", cluster);
 398         cluster_layer = pcmk_cluster_layer_invalid;
 399 
 400 #if SUPPORT_COROSYNC
 401         if (pcmk__str_eq(cluster, PCMK_VALUE_COROSYNC, pcmk__str_casei)) {
 402             cluster_layer = pcmk_cluster_layer_corosync;
 403         }
 404 #endif  // SUPPORT_COROSYNC
 405 
 406         if (cluster_layer == pcmk_cluster_layer_invalid) {
 407             crm_notice("This installation does not support the '%s' cluster "
 408                        "infrastructure: terminating",
 409                        cluster);
 410             crm_exit(CRM_EX_FATAL);
 411         }
 412         crm_info("Assuming an active '%s' cluster", cluster);
 413 
 414     } else {
 415         // Nothing configured, so test supported cluster layers
 416 #if SUPPORT_COROSYNC
 417         crm_debug("Testing with Corosync");
 418         if (pcmk__corosync_is_active()) {
 419             cluster_layer = pcmk_cluster_layer_corosync;
 420         }
 421 #endif  // SUPPORT_COROSYNC
 422 
 423         if (cluster_layer == pcmk_cluster_layer_unknown) {
 424             crm_notice("Could not determine the current cluster layer");
 425         } else {
 426             crm_info("Detected an active '%s' cluster",
 427                      pcmk_cluster_layer_text(cluster_layer));
 428         }
 429     }
 430 
 431     return cluster_layer;
 432 }
 433 
 434 // Deprecated functions kept only for backward API compatibility
 435 // LCOV_EXCL_START
 436 
 437 #include <crm/cluster/compat.h>
 438 
 439 gboolean
 440 crm_cluster_connect(pcmk_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 441 {
 442     if (cluster == NULL) {
 443         return FALSE;
 444     }
 445     if (cluster->priv == NULL) {
 446         /* sbd (as of at least 1.5.2) doesn't call pcmk_cluster_new() to
 447          * allocate the pcmk_cluster_t
 448          */
 449         cluster->priv = pcmk__assert_alloc(1, sizeof(pcmk__cluster_private_t));
 450     }
 451     return pcmk_cluster_connect(cluster) == pcmk_rc_ok;
 452 }
 453 
 454 const char *
 455 name_for_cluster_type(enum cluster_type_e type)
     /* [previous][next][first][last][top][bottom][index][help] */
 456 {
 457     switch (type) {
 458         case pcmk_cluster_corosync:
 459             return "corosync";
 460         case pcmk_cluster_unknown:
 461             return "unknown";
 462         case pcmk_cluster_invalid:
 463             return "invalid";
 464     }
 465     crm_err("Invalid cluster type: %d", type);
 466     return "invalid";
 467 }
 468 
 469 enum cluster_type_e
 470 get_cluster_type(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 471 {
 472     return (enum cluster_type_e) pcmk_get_cluster_layer();
 473 }
 474 
 475 // LCOV_EXCL_STOP
 476 // End deprecated API

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