root/lib/cluster/cluster.c

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

DEFINITIONS

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

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