LCOV - code coverage report
Current view: top level - cluster - cluster.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 5 142 3.5 %
Date: 2024-05-07 11:09:47 Functions: 1 13 7.7 %

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

Generated by: LCOV version 1.14