root/lib/cluster/cluster.c

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

DEFINITIONS

This source file includes following definitions.
  1. crm_peer_uuid
  2. crm_cluster_connect
  3. crm_cluster_disconnect
  4. pcmk_cluster_new
  5. pcmk_cluster_free
  6. send_cluster_message
  7. get_local_node_name
  8. get_node_name
  9. crm_peer_uname
  10. set_uuid
  11. name_for_cluster_type
  12. get_cluster_type
  13. is_corosync_cluster

   1 /*
   2  * Copyright 2004-2022 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 <stdio.h>
  14 #include <unistd.h>
  15 #include <string.h>
  16 #include <stdlib.h>
  17 #include <time.h>
  18 #include <sys/param.h>
  19 #include <sys/types.h>
  20 
  21 #include <crm/crm.h>
  22 #include <crm/msg_xml.h>
  23 
  24 #include <crm/common/ipc.h>
  25 #include <crm/cluster/internal.h>
  26 #include "crmcluster_private.h"
  27 
  28 CRM_TRACE_INIT_DATA(cluster);
  29 
  30 /*!
  31  * \brief Get (and set if needed) a node's UUID
  32  *
  33  * \param[in,out] peer  Node to check
  34  *
  35  * \return Node UUID of \p peer, or NULL if unknown
  36  */
  37 const char *
  38 crm_peer_uuid(crm_node_t *peer)
     /* [previous][next][first][last][top][bottom][index][help] */
  39 {
  40     char *uuid = NULL;
  41 
  42     // Check simple cases first, to avoid any calls that might block
  43     if (peer == NULL) {
  44         return NULL;
  45     }
  46     if (peer->uuid != NULL) {
  47         return peer->uuid;
  48     }
  49 
  50     switch (get_cluster_type()) {
  51         case pcmk_cluster_corosync:
  52 #if SUPPORT_COROSYNC
  53             uuid = pcmk__corosync_uuid(peer);
  54 #endif
  55             break;
  56 
  57         case pcmk_cluster_unknown:
  58         case pcmk_cluster_invalid:
  59             crm_err("Unsupported cluster type");
  60             break;
  61     }
  62 
  63     peer->uuid = uuid;
  64     return peer->uuid;
  65 }
  66 
  67 /*!
  68  * \brief Connect to the cluster layer
  69  *
  70  * \param[in,out] Initialized cluster object to connect
  71  *
  72  * \return TRUE on success, otherwise FALSE
  73  */
  74 gboolean
  75 crm_cluster_connect(crm_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     enum cluster_type_e type = get_cluster_type();
  78 
  79     crm_notice("Connecting to %s cluster infrastructure",
  80                name_for_cluster_type(type));
  81     switch (type) {
  82         case pcmk_cluster_corosync:
  83 #if SUPPORT_COROSYNC
  84             crm_peer_init();
  85             return pcmk__corosync_connect(cluster);
  86 #else
  87             break;
  88 #endif // SUPPORT_COROSYNC
  89         default:
  90             break;
  91     }
  92     return FALSE;
  93 }
  94 
  95 /*!
  96  * \brief Disconnect from the cluster layer
  97  *
  98  * \param[in,out] cluster  Cluster object to disconnect
  99  */
 100 void
 101 crm_cluster_disconnect(crm_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103     enum cluster_type_e type = get_cluster_type();
 104 
 105     crm_info("Disconnecting from %s cluster infrastructure",
 106              name_for_cluster_type(type));
 107     switch (type) {
 108         case pcmk_cluster_corosync:
 109 #if SUPPORT_COROSYNC
 110             crm_peer_destroy();
 111             pcmk__corosync_disconnect(cluster);
 112 #endif // SUPPORT_COROSYNC
 113             break;
 114         default:
 115             break;
 116     }
 117 }
 118 
 119 /*!
 120  * \brief Allocate a new \p crm_cluster_t object
 121  *
 122  * \return A newly allocated \p crm_cluster_t object (guaranteed not \p NULL)
 123  * \note The caller is responsible for freeing the return value using
 124  *       \p pcmk_cluster_free().
 125  */
 126 crm_cluster_t *
 127 pcmk_cluster_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129     crm_cluster_t *cluster = calloc(1, sizeof(crm_cluster_t));
 130 
 131     CRM_ASSERT(cluster != NULL);
 132     return cluster;
 133 }
 134 
 135 /*!
 136  * \brief Free a \p crm_cluster_t object and its dynamically allocated members
 137  *
 138  * \param[in,out] cluster  Cluster object to free
 139  */
 140 void
 141 pcmk_cluster_free(crm_cluster_t *cluster)
     /* [previous][next][first][last][top][bottom][index][help] */
 142 {
 143     if (cluster == NULL) {
 144         return;
 145     }
 146     free(cluster->uuid);
 147     free(cluster->uname);
 148     free(cluster);
 149 }
 150 
 151 /*!
 152  * \brief Send an XML message via the cluster messaging layer
 153  *
 154  * \param[in] node     Cluster node to send message to
 155  * \param[in] service  Message type to use in message host info
 156  * \param[in] data     XML message to send
 157  * \param[in] ordered  Ignored for currently supported messaging layers
 158  *
 159  * \return TRUE on success, otherwise FALSE
 160  */
 161 gboolean
 162 send_cluster_message(const crm_node_t *node, enum crm_ais_msg_types service,
     /* [previous][next][first][last][top][bottom][index][help] */
 163                      xmlNode *data, gboolean ordered)
 164 {
 165     switch (get_cluster_type()) {
 166         case pcmk_cluster_corosync:
 167 #if SUPPORT_COROSYNC
 168             return pcmk__cpg_send_xml(data, node, service);
 169 #endif
 170             break;
 171         default:
 172             break;
 173     }
 174     return FALSE;
 175 }
 176 
 177 /*!
 178  * \brief Get the local node's name
 179  *
 180  * \return Local node's name
 181  * \note This will fatally exit if local node name cannot be known.
 182  */
 183 const char *
 184 get_local_node_name(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     static char *name = NULL;
 187 
 188     if (name == NULL) {
 189         name = get_node_name(0);
 190     }
 191     return name;
 192 }
 193 
 194 /*!
 195  * \brief Get the node name corresponding to a cluster node ID
 196  *
 197  * \param[in] nodeid  Node ID to check (or 0 for local node)
 198  *
 199  * \return Node name corresponding to \p nodeid
 200  * \note This will fatally exit if \p nodeid is 0 and local node name cannot be
 201  *       known.
 202  */
 203 char *
 204 get_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 205 {
 206     char *name = NULL;
 207     enum cluster_type_e stack = get_cluster_type();
 208 
 209     switch (stack) {
 210         case pcmk_cluster_corosync:
 211 #if SUPPORT_COROSYNC
 212             name = pcmk__corosync_name(0, nodeid);
 213             break;
 214 #endif // SUPPORT_COROSYNC
 215 
 216         default:
 217             crm_err("Unknown cluster type: %s (%d)", name_for_cluster_type(stack), stack);
 218     }
 219 
 220     if ((name == NULL) && (nodeid == 0)) {
 221         name = pcmk_hostname();
 222         if (name == NULL) {
 223             // @TODO Maybe let the caller decide what to do
 224             crm_err("Could not obtain the local %s node name",
 225                     name_for_cluster_type(stack));
 226             crm_exit(CRM_EX_FATAL);
 227         }
 228         crm_notice("Defaulting to uname -n for the local %s node name",
 229                    name_for_cluster_type(stack));
 230     }
 231 
 232     if (name == NULL) {
 233         crm_notice("Could not obtain a node name for %s node with id %u",
 234                    name_for_cluster_type(stack), nodeid);
 235     }
 236     return name;
 237 }
 238 
 239 /*!
 240  * \brief Get the node name corresponding to a node UUID
 241  *
 242  * \param[in] uuid  UUID of desired node
 243  *
 244  * \return name of desired node
 245  *
 246  * \note This relies on the remote peer cache being populated with all
 247  *       remote nodes in the cluster, so callers should maintain that cache.
 248  */
 249 const char *
 250 crm_peer_uname(const char *uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252     GHashTableIter iter;
 253     crm_node_t *node = NULL;
 254 
 255     CRM_CHECK(uuid != NULL, return NULL);
 256 
 257     /* remote nodes have the same uname and uuid */
 258     if (g_hash_table_lookup(crm_remote_peer_cache, uuid)) {
 259         return uuid;
 260     }
 261 
 262     /* avoid blocking calls where possible */
 263     g_hash_table_iter_init(&iter, crm_peer_cache);
 264     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 265         if (pcmk__str_eq(node->uuid, uuid, pcmk__str_casei)) {
 266             if (node->uname != NULL) {
 267                 return node->uname;
 268             }
 269             break;
 270         }
 271     }
 272     node = NULL;
 273 
 274     if (is_corosync_cluster()) {
 275         long long id;
 276 
 277         if ((pcmk__scan_ll(uuid, &id, 0LL) != pcmk_rc_ok)
 278             || (id < 1LL) || (id > UINT32_MAX))  {
 279             crm_err("Invalid Corosync node ID '%s'", uuid);
 280             return NULL;
 281         }
 282 
 283         node = pcmk__search_cluster_node_cache((uint32_t) id, NULL);
 284         if (node != NULL) {
 285             crm_info("Setting uuid for node %s[%u] to %s",
 286                      node->uname, node->id, uuid);
 287             node->uuid = strdup(uuid);
 288             return node->uname;
 289         }
 290         return NULL;
 291     }
 292 
 293     return NULL;
 294 }
 295 
 296 /*!
 297  * \brief Add a node's UUID as an XML attribute
 298  *
 299  * \param[in,out] xml   XML element to add UUID to
 300  * \param[in]     attr  XML attribute name to set
 301  * \param[in,out] node  Node whose UUID should be used as attribute value
 302  */
 303 void
 304 set_uuid(xmlNode *xml, const char *attr, crm_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306     crm_xml_add(xml, attr, crm_peer_uuid(node));
 307 }
 308 
 309 /*!
 310  * \brief  Get a log-friendly string equivalent of a cluster type
 311  *
 312  * \param[in] type  Cluster type
 313  *
 314  * \return Log-friendly string corresponding to \p type
 315  */
 316 const char *
 317 name_for_cluster_type(enum cluster_type_e type)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319     switch (type) {
 320         case pcmk_cluster_corosync:
 321             return "corosync";
 322         case pcmk_cluster_unknown:
 323             return "unknown";
 324         case pcmk_cluster_invalid:
 325             return "invalid";
 326     }
 327     crm_err("Invalid cluster type: %d", type);
 328     return "invalid";
 329 }
 330 
 331 /*!
 332  * \brief Get (and validate) the local cluster type
 333  *
 334  * \return Local cluster type
 335  * \note This will fatally exit if the local cluster type is invalid.
 336  */
 337 enum cluster_type_e
 338 get_cluster_type(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 339 {
 340     bool detected = false;
 341     const char *cluster = NULL;
 342     static enum cluster_type_e cluster_type = pcmk_cluster_unknown;
 343 
 344     /* Return the previous calculation, if any */
 345     if (cluster_type != pcmk_cluster_unknown) {
 346         return cluster_type;
 347     }
 348 
 349     cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE);
 350 
 351 #if SUPPORT_COROSYNC
 352     /* If nothing is defined in the environment, try corosync (if supported) */
 353     if (cluster == NULL) {
 354         crm_debug("Testing with Corosync");
 355         cluster_type = pcmk__corosync_detect();
 356         if (cluster_type != pcmk_cluster_unknown) {
 357             detected = true;
 358             goto done;
 359         }
 360     }
 361 #endif
 362 
 363     /* Something was defined in the environment, test it against what we support */
 364     crm_info("Verifying cluster type: '%s'",
 365              ((cluster == NULL)? "-unspecified-" : cluster));
 366     if (cluster == NULL) {
 367 
 368 #if SUPPORT_COROSYNC
 369     } else if (pcmk__str_eq(cluster, "corosync", pcmk__str_casei)) {
 370         cluster_type = pcmk_cluster_corosync;
 371 #endif
 372 
 373     } else {
 374         cluster_type = pcmk_cluster_invalid;
 375         goto done; /* Keep the compiler happy when no stacks are supported */
 376     }
 377 
 378   done:
 379     if (cluster_type == pcmk_cluster_unknown) {
 380         crm_notice("Could not determine the current cluster type");
 381 
 382     } else if (cluster_type == pcmk_cluster_invalid) {
 383         crm_notice("This installation does not support the '%s' cluster infrastructure: terminating.",
 384                    cluster);
 385         crm_exit(CRM_EX_FATAL);
 386 
 387     } else {
 388         crm_info("%s an active '%s' cluster",
 389                  (detected? "Detected" : "Assuming"),
 390                  name_for_cluster_type(cluster_type));
 391     }
 392 
 393     return cluster_type;
 394 }
 395 
 396 /*!
 397  * \brief Check whether the local cluster is a Corosync cluster
 398  *
 399  * \return TRUE if the local cluster is a Corosync cluster, otherwise FALSE
 400  */
 401 gboolean
 402 is_corosync_cluster(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404     return get_cluster_type() == pcmk_cluster_corosync;
 405 }

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