summaryrefslogtreecommitdiffstats
path: root/lib/cluster/cluster.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
commite5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch)
treea6716c9275b4b413f6c9194798b34b91affb3cc7 /lib/cluster/cluster.c
parentInitial commit. (diff)
downloadpacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz
pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/cluster/cluster.c')
-rw-r--r--lib/cluster/cluster.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c
new file mode 100644
index 0000000..011e053
--- /dev/null
+++ b/lib/cluster/cluster.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2004-2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+#include <dlfcn.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <crm/crm.h>
+#include <crm/msg_xml.h>
+
+#include <crm/common/ipc.h>
+#include <crm/cluster/internal.h>
+#include "crmcluster_private.h"
+
+CRM_TRACE_INIT_DATA(cluster);
+
+/*!
+ * \brief Get (and set if needed) a node's UUID
+ *
+ * \param[in,out] peer Node to check
+ *
+ * \return Node UUID of \p peer, or NULL if unknown
+ */
+const char *
+crm_peer_uuid(crm_node_t *peer)
+{
+ char *uuid = NULL;
+
+ // Check simple cases first, to avoid any calls that might block
+ if (peer == NULL) {
+ return NULL;
+ }
+ if (peer->uuid != NULL) {
+ return peer->uuid;
+ }
+
+ switch (get_cluster_type()) {
+ case pcmk_cluster_corosync:
+#if SUPPORT_COROSYNC
+ uuid = pcmk__corosync_uuid(peer);
+#endif
+ break;
+
+ case pcmk_cluster_unknown:
+ case pcmk_cluster_invalid:
+ crm_err("Unsupported cluster type");
+ break;
+ }
+
+ peer->uuid = uuid;
+ return peer->uuid;
+}
+
+/*!
+ * \brief Connect to the cluster layer
+ *
+ * \param[in,out] Initialized cluster object to connect
+ *
+ * \return TRUE on success, otherwise FALSE
+ */
+gboolean
+crm_cluster_connect(crm_cluster_t *cluster)
+{
+ enum cluster_type_e type = get_cluster_type();
+
+ crm_notice("Connecting to %s cluster infrastructure",
+ name_for_cluster_type(type));
+ switch (type) {
+ case pcmk_cluster_corosync:
+#if SUPPORT_COROSYNC
+ crm_peer_init();
+ return pcmk__corosync_connect(cluster);
+#else
+ break;
+#endif // SUPPORT_COROSYNC
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/*!
+ * \brief Disconnect from the cluster layer
+ *
+ * \param[in,out] cluster Cluster object to disconnect
+ */
+void
+crm_cluster_disconnect(crm_cluster_t *cluster)
+{
+ enum cluster_type_e type = get_cluster_type();
+
+ crm_info("Disconnecting from %s cluster infrastructure",
+ name_for_cluster_type(type));
+ switch (type) {
+ case pcmk_cluster_corosync:
+#if SUPPORT_COROSYNC
+ crm_peer_destroy();
+ pcmk__corosync_disconnect(cluster);
+#endif // SUPPORT_COROSYNC
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * \brief Allocate a new \p crm_cluster_t object
+ *
+ * \return A newly allocated \p crm_cluster_t object (guaranteed not \p NULL)
+ * \note The caller is responsible for freeing the return value using
+ * \p pcmk_cluster_free().
+ */
+crm_cluster_t *
+pcmk_cluster_new(void)
+{
+ crm_cluster_t *cluster = calloc(1, sizeof(crm_cluster_t));
+
+ CRM_ASSERT(cluster != NULL);
+ return cluster;
+}
+
+/*!
+ * \brief Free a \p crm_cluster_t object and its dynamically allocated members
+ *
+ * \param[in,out] cluster Cluster object to free
+ */
+void
+pcmk_cluster_free(crm_cluster_t *cluster)
+{
+ if (cluster == NULL) {
+ return;
+ }
+ free(cluster->uuid);
+ free(cluster->uname);
+ free(cluster);
+}
+
+/*!
+ * \brief Send an XML message via the cluster messaging layer
+ *
+ * \param[in] node Cluster node to send message to
+ * \param[in] service Message type to use in message host info
+ * \param[in] data XML message to send
+ * \param[in] ordered Ignored for currently supported messaging layers
+ *
+ * \return TRUE on success, otherwise FALSE
+ */
+gboolean
+send_cluster_message(const crm_node_t *node, enum crm_ais_msg_types service,
+ xmlNode *data, gboolean ordered)
+{
+ switch (get_cluster_type()) {
+ case pcmk_cluster_corosync:
+#if SUPPORT_COROSYNC
+ return pcmk__cpg_send_xml(data, node, service);
+#endif
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/*!
+ * \brief Get the local node's name
+ *
+ * \return Local node's name
+ * \note This will fatally exit if local node name cannot be known.
+ */
+const char *
+get_local_node_name(void)
+{
+ static char *name = NULL;
+
+ if (name == NULL) {
+ name = get_node_name(0);
+ }
+ return name;
+}
+
+/*!
+ * \brief Get the node name corresponding to a cluster node ID
+ *
+ * \param[in] nodeid Node ID to check (or 0 for local node)
+ *
+ * \return Node name corresponding to \p nodeid
+ * \note This will fatally exit if \p nodeid is 0 and local node name cannot be
+ * known.
+ */
+char *
+get_node_name(uint32_t nodeid)
+{
+ char *name = NULL;
+ enum cluster_type_e stack = get_cluster_type();
+
+ switch (stack) {
+ case pcmk_cluster_corosync:
+#if SUPPORT_COROSYNC
+ name = pcmk__corosync_name(0, nodeid);
+ break;
+#endif // SUPPORT_COROSYNC
+
+ default:
+ crm_err("Unknown cluster type: %s (%d)", name_for_cluster_type(stack), stack);
+ }
+
+ if ((name == NULL) && (nodeid == 0)) {
+ name = pcmk_hostname();
+ if (name == NULL) {
+ // @TODO Maybe let the caller decide what to do
+ crm_err("Could not obtain the local %s node name",
+ name_for_cluster_type(stack));
+ crm_exit(CRM_EX_FATAL);
+ }
+ crm_notice("Defaulting to uname -n for the local %s node name",
+ name_for_cluster_type(stack));
+ }
+
+ if (name == NULL) {
+ crm_notice("Could not obtain a node name for %s node with id %u",
+ name_for_cluster_type(stack), nodeid);
+ }
+ return name;
+}
+
+/*!
+ * \brief Get the node name corresponding to a node UUID
+ *
+ * \param[in] uuid UUID of desired node
+ *
+ * \return name of desired node
+ *
+ * \note This relies on the remote peer cache being populated with all
+ * remote nodes in the cluster, so callers should maintain that cache.
+ */
+const char *
+crm_peer_uname(const char *uuid)
+{
+ GHashTableIter iter;
+ crm_node_t *node = NULL;
+
+ CRM_CHECK(uuid != NULL, return NULL);
+
+ /* remote nodes have the same uname and uuid */
+ if (g_hash_table_lookup(crm_remote_peer_cache, uuid)) {
+ return uuid;
+ }
+
+ /* avoid blocking calls where possible */
+ g_hash_table_iter_init(&iter, crm_peer_cache);
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
+ if (pcmk__str_eq(node->uuid, uuid, pcmk__str_casei)) {
+ if (node->uname != NULL) {
+ return node->uname;
+ }
+ break;
+ }
+ }
+ node = NULL;
+
+ if (is_corosync_cluster()) {
+ long long id;
+
+ if ((pcmk__scan_ll(uuid, &id, 0LL) != pcmk_rc_ok)
+ || (id < 1LL) || (id > UINT32_MAX)) {
+ crm_err("Invalid Corosync node ID '%s'", uuid);
+ return NULL;
+ }
+
+ node = pcmk__search_cluster_node_cache((uint32_t) id, NULL);
+ if (node != NULL) {
+ crm_info("Setting uuid for node %s[%u] to %s",
+ node->uname, node->id, uuid);
+ node->uuid = strdup(uuid);
+ return node->uname;
+ }
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Add a node's UUID as an XML attribute
+ *
+ * \param[in,out] xml XML element to add UUID to
+ * \param[in] attr XML attribute name to set
+ * \param[in,out] node Node whose UUID should be used as attribute value
+ */
+void
+set_uuid(xmlNode *xml, const char *attr, crm_node_t *node)
+{
+ crm_xml_add(xml, attr, crm_peer_uuid(node));
+}
+
+/*!
+ * \brief Get a log-friendly string equivalent of a cluster type
+ *
+ * \param[in] type Cluster type
+ *
+ * \return Log-friendly string corresponding to \p type
+ */
+const char *
+name_for_cluster_type(enum cluster_type_e type)
+{
+ switch (type) {
+ case pcmk_cluster_corosync:
+ return "corosync";
+ case pcmk_cluster_unknown:
+ return "unknown";
+ case pcmk_cluster_invalid:
+ return "invalid";
+ }
+ crm_err("Invalid cluster type: %d", type);
+ return "invalid";
+}
+
+/*!
+ * \brief Get (and validate) the local cluster type
+ *
+ * \return Local cluster type
+ * \note This will fatally exit if the local cluster type is invalid.
+ */
+enum cluster_type_e
+get_cluster_type(void)
+{
+ bool detected = false;
+ const char *cluster = NULL;
+ static enum cluster_type_e cluster_type = pcmk_cluster_unknown;
+
+ /* Return the previous calculation, if any */
+ if (cluster_type != pcmk_cluster_unknown) {
+ return cluster_type;
+ }
+
+ cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE);
+
+#if SUPPORT_COROSYNC
+ /* If nothing is defined in the environment, try corosync (if supported) */
+ if (cluster == NULL) {
+ crm_debug("Testing with Corosync");
+ cluster_type = pcmk__corosync_detect();
+ if (cluster_type != pcmk_cluster_unknown) {
+ detected = true;
+ goto done;
+ }
+ }
+#endif
+
+ /* Something was defined in the environment, test it against what we support */
+ crm_info("Verifying cluster type: '%s'",
+ ((cluster == NULL)? "-unspecified-" : cluster));
+ if (cluster == NULL) {
+
+#if SUPPORT_COROSYNC
+ } else if (pcmk__str_eq(cluster, "corosync", pcmk__str_casei)) {
+ cluster_type = pcmk_cluster_corosync;
+#endif
+
+ } else {
+ cluster_type = pcmk_cluster_invalid;
+ goto done; /* Keep the compiler happy when no stacks are supported */
+ }
+
+ done:
+ if (cluster_type == pcmk_cluster_unknown) {
+ crm_notice("Could not determine the current cluster type");
+
+ } else if (cluster_type == pcmk_cluster_invalid) {
+ crm_notice("This installation does not support the '%s' cluster infrastructure: terminating.",
+ cluster);
+ crm_exit(CRM_EX_FATAL);
+
+ } else {
+ crm_info("%s an active '%s' cluster",
+ (detected? "Detected" : "Assuming"),
+ name_for_cluster_type(cluster_type));
+ }
+
+ return cluster_type;
+}
+
+/*!
+ * \brief Check whether the local cluster is a Corosync cluster
+ *
+ * \return TRUE if the local cluster is a Corosync cluster, otherwise FALSE
+ */
+gboolean
+is_corosync_cluster(void)
+{
+ return get_cluster_type() == pcmk_cluster_corosync;
+}