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
|