pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
pcmk_cluster_queries.c
Go to the documentation of this file.
1/*
2 * Copyright 2020-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
12#include <glib.h> // gboolean, GMainLoop, etc.
13#include <libxml/tree.h> // xmlNode
14
15#include <pacemaker.h>
16#include <pacemaker-internal.h>
17
18#include <crm/crm.h>
19#include <crm/cib.h>
20#include <crm/cib/internal.h>
21#include <crm/msg_xml.h>
23#include <crm/common/xml.h>
25#include <crm/common/iso8601.h>
28#include <crm/common/mainloop.h>
29
30#define DEFAULT_MESSAGE_TIMEOUT_MS 30000
31
32
33typedef struct {
34 pcmk__output_t *out;
35 GMainLoop *mainloop;
36 int rc;
37 guint message_timer_id;
38 guint message_timeout_ms;
39 enum pcmk_pacemakerd_state pcmkd_state;
40} data_t;
41
42static void
43quit_main_loop(data_t *data)
44{
45 if (data->mainloop != NULL) {
46 GMainLoop *mloop = data->mainloop;
47
48 data->mainloop = NULL; // Don't re-enter this block
49 pcmk_quit_main_loop(mloop, 10);
50 g_main_loop_unref(mloop);
51 }
52}
53
54static gboolean
55admin_message_timeout(gpointer user_data)
56{
57 data_t *data = user_data;
58 pcmk__output_t *out = data->out;
59
60 out->err(out, "error: No reply received from controller before timeout (%dms)",
61 data->message_timeout_ms);
62 data->message_timer_id = 0;
63 data->rc = ETIMEDOUT;
64 quit_main_loop(data);
65 return FALSE; // Tells glib to remove source
66}
67
68static void
69start_main_loop(data_t *data)
70{
71 if (data->message_timeout_ms < 1) {
72 data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
73 }
74
75 data->rc = ECONNRESET; // For unexpected disconnects
76 data->mainloop = g_main_loop_new(NULL, FALSE);
77 data->message_timer_id = g_timeout_add(data->message_timeout_ms,
78 admin_message_timeout,
79 data);
80 g_main_loop_run(data->mainloop);
81}
82
83static void
84event_done(data_t *data, pcmk_ipc_api_t *api)
85{
87 quit_main_loop(data);
88}
89
91controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
92{
93 pcmk__output_t *out = data->out;
94 pcmk_controld_api_reply_t *reply = event_data;
95
96 switch (event_type) {
98 if (data->rc == ECONNRESET) { // Unexpected
99 out->err(out, "error: Lost connection to controller");
100 }
101 event_done(data, controld_api);
102 return NULL;
103
105 break;
106
107 default:
108 return NULL;
109 }
110
111 if (data->message_timer_id != 0) {
112 g_source_remove(data->message_timer_id);
113 data->message_timer_id = 0;
114 }
115
116 if (status != CRM_EX_OK) {
117 out->err(out, "error: Bad reply from controller: %s",
118 crm_exit_str(status));
119 data->rc = EBADMSG;
120 event_done(data, controld_api);
121 return NULL;
122 }
123
124 if (reply->reply_type != pcmk_controld_reply_ping) {
125 out->err(out, "error: Unknown reply type %d from controller",
126 reply->reply_type);
127 data->rc = EBADMSG;
128 event_done(data, controld_api);
129 return NULL;
130 }
131
132 return reply;
133}
134
135static void
136controller_status_event_cb(pcmk_ipc_api_t *controld_api,
137 enum pcmk_ipc_event event_type, crm_exit_t status,
138 void *event_data, void *user_data)
139{
140 data_t *data = user_data;
141 pcmk__output_t *out = data->out;
142 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
143 event_type, status, event_data);
144
145 if (reply != NULL) {
146 out->message(out, "health",
147 reply->data.ping.sys_from,
148 reply->host_from,
149 reply->data.ping.fsa_state,
150 reply->data.ping.result);
151 data->rc = pcmk_rc_ok;
152 }
153
154 event_done(data, controld_api);
155}
156
157static void
158designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
159 enum pcmk_ipc_event event_type, crm_exit_t status,
160 void *event_data, void *user_data)
161{
162 data_t *data = user_data;
163 pcmk__output_t *out = data->out;
164 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
165 event_type, status, event_data);
166
167 if (reply != NULL) {
168 out->message(out, "dc", reply->host_from);
169 data->rc = pcmk_rc_ok;
170 }
171
172 event_done(data, controld_api);
173}
174
175static void
176pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
177 enum pcmk_ipc_event event_type, crm_exit_t status,
178 void *event_data, void *user_data)
179{
180 data_t *data = user_data;
181 pcmk__output_t *out = data->out;
182 pcmk_pacemakerd_api_reply_t *reply = event_data;
183
184 switch (event_type) {
186 if (data->rc == ECONNRESET) { // Unexpected
187 out->err(out, "error: Lost connection to pacemakerd");
188 }
189 event_done(data, pacemakerd_api);
190 return;
191
193 break;
194
195 default:
196 return;
197 }
198
199 if (data->message_timer_id != 0) {
200 g_source_remove(data->message_timer_id);
201 data->message_timer_id = 0;
202 }
203
204 if (status != CRM_EX_OK) {
205 out->err(out, "error: Bad reply from pacemakerd: %s",
206 crm_exit_str(status));
207 event_done(data, pacemakerd_api);
208 data->rc = EBADMSG;
209 return;
210 }
211
213 out->err(out, "error: Unknown reply type %d from pacemakerd",
214 reply->reply_type);
215 event_done(data, pacemakerd_api);
216 data->rc = EBADMSG;
217 return;
218 }
219
220 // Parse desired information from reply
221 data->pcmkd_state = reply->data.ping.state;
222 if (reply->data.ping.status == pcmk_rc_ok) {
223 crm_time_t *when = crm_time_new(NULL);
224 char *when_s = NULL;
225
226 crm_time_set_timet(when, &reply->data.ping.last_good);
227 when_s = crm_time_as_string(when,
231
232 out->message(out, "pacemakerd-health",
233 reply->data.ping.sys_from, reply->data.ping.state, NULL,
234 when_s);
235
236 crm_time_free(when);
237 free(when_s);
238
239 } else {
240 out->message(out, "pacemakerd-health",
241 reply->data.ping.sys_from, reply->data.ping.state,
242 "query failed", NULL);
243 }
244 data->rc = pcmk_rc_ok;
245 event_done(data, pacemakerd_api);
246}
247
248static pcmk_ipc_api_t *
249ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
250 enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
251{
252 int rc;
253 pcmk__output_t *out = data->out;
254 pcmk_ipc_api_t *api = NULL;
255
256 rc = pcmk_new_ipc_api(&api, server);
257 if (api == NULL) {
258 out->err(out, "error: Could not connect to %s: %s",
259 pcmk_ipc_name(api, true),
260 pcmk_rc_str(rc));
261 data->rc = rc;
262 return NULL;
263 }
264 if (cb != NULL) {
266 }
267
268 rc = pcmk_connect_ipc(api, dispatch_type);
269 if (rc != pcmk_rc_ok) {
270 if ((rc == EREMOTEIO) && eremoteio_ok) {
271 /* EREMOTEIO may be expected and acceptable for some callers.
272 * Preserve the return code in case callers need to handle it
273 * specially.
274 */
275 } else {
276 out->err(out, "error: Could not connect to %s: %s",
277 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
278 }
279 data->rc = rc;
281 return NULL;
282 }
283
284 return api;
285}
286
287int
288pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
289{
290 data_t data = {
291 .out = out,
292 .mainloop = NULL,
293 .rc = pcmk_rc_ok,
294 .message_timer_id = 0,
295 .message_timeout_ms = message_timeout_ms,
296 .pcmkd_state = pcmk_pacemakerd_state_invalid,
297 };
298 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
299 pcmk_ipc_api_t *controld_api = NULL;
300
301 if (message_timeout_ms == 0) {
302 dispatch_type = pcmk_ipc_dispatch_sync;
303 }
304 controld_api = ipc_connect(&data, pcmk_ipc_controld,
305 controller_status_event_cb, dispatch_type,
306 false);
307
308 if (controld_api != NULL) {
309 int rc = pcmk_controld_api_ping(controld_api, dest_node);
310 if (rc != pcmk_rc_ok) {
311 out->err(out, "error: Could not ping controller API: %s",
312 pcmk_rc_str(rc));
313 data.rc = rc;
314 }
315
316 if (dispatch_type == pcmk_ipc_dispatch_main) {
317 start_main_loop(&data);
318 }
319
320 pcmk_free_ipc_api(controld_api);
321 }
322
323 return data.rc;
324}
325
326int
327pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
328{
329 pcmk__output_t *out = NULL;
330 int rc = pcmk_rc_ok;
331
332 rc = pcmk__xml_output_new(&out, xml);
333 if (rc != pcmk_rc_ok) {
334 return rc;
335 }
336
338
339 rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
340 pcmk__xml_output_finish(out, xml);
341 return rc;
342}
343
344int
345pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
346{
347 data_t data = {
348 .out = out,
349 .mainloop = NULL,
350 .rc = pcmk_rc_ok,
351 .message_timer_id = 0,
352 .message_timeout_ms = message_timeout_ms,
353 .pcmkd_state = pcmk_pacemakerd_state_invalid,
354 };
355 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
356 pcmk_ipc_api_t *controld_api = NULL;
357
358 if (message_timeout_ms == 0) {
359 dispatch_type = pcmk_ipc_dispatch_sync;
360 }
361 controld_api = ipc_connect(&data, pcmk_ipc_controld,
362 designated_controller_event_cb, dispatch_type,
363 false);
364
365 if (controld_api != NULL) {
366 int rc = pcmk_controld_api_ping(controld_api, NULL);
367 if (rc != pcmk_rc_ok) {
368 out->err(out, "error: Could not ping controller API: %s",
369 pcmk_rc_str(rc));
370 data.rc = rc;
371 }
372
373 if (dispatch_type == pcmk_ipc_dispatch_main) {
374 start_main_loop(&data);
375 }
376
377 pcmk_free_ipc_api(controld_api);
378 }
379
380 return data.rc;
381}
382
383int
384pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
385{
386 pcmk__output_t *out = NULL;
387 int rc = pcmk_rc_ok;
388
389 rc = pcmk__xml_output_new(&out, xml);
390 if (rc != pcmk_rc_ok) {
391 return rc;
392 }
393
395
396 rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
397 pcmk__xml_output_finish(out, xml);
398 return rc;
399}
400
424int
425pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
426 guint message_timeout_ms,
427 enum pcmk_pacemakerd_state *state)
428{
429 data_t data = {
430 .out = out,
431 .mainloop = NULL,
433 .message_timer_id = 0,
434 .message_timeout_ms = message_timeout_ms,
435 .pcmkd_state = pcmk_pacemakerd_state_invalid,
436 };
437 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
438 pcmk_ipc_api_t *pacemakerd_api = NULL;
439
440 if (message_timeout_ms == 0) {
441 dispatch_type = pcmk_ipc_dispatch_sync;
442 }
443 pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
444 pacemakerd_event_cb, dispatch_type, true);
445
446 if (pacemakerd_api != NULL) {
447 int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
448 if (rc != pcmk_rc_ok) {
449 out->err(out, "error: Could not ping launcher API: %s",
450 pcmk_rc_str(rc));
451 data.rc = rc;
452 }
453
454 if (dispatch_type == pcmk_ipc_dispatch_main) {
455 start_main_loop(&data);
456 }
457 pcmk_free_ipc_api(pacemakerd_api);
458 }
459
460 if (state != NULL) {
461 *state = data.pcmkd_state;
462 }
463 return data.rc;
464}
465
466// Documented in header
467int
468pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
469 unsigned int message_timeout_ms)
470{
471 pcmk__output_t *out = NULL;
472 int rc = pcmk_rc_ok;
473
474 rc = pcmk__xml_output_new(&out, xml);
475 if (rc != pcmk_rc_ok) {
476 return rc;
477 }
478
480
481 rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms,
482 NULL);
483 pcmk__xml_output_finish(out, xml);
484 return rc;
485}
486
487/* user data for looping through remote node xpath searches */
488struct node_data {
489 pcmk__output_t *out;
490 int found;
491 const char *field; /* XML attribute to check for node name */
492 const char *type;
493 gboolean bash_export;
494};
495
496static void
497remote_node_print_helper(xmlNode *result, void *user_data)
498{
499 struct node_data *data = user_data;
500 pcmk__output_t *out = data->out;
502 const char *id = crm_element_value(result, data->field);
503
504 // node name and node id are the same for remote/guest nodes
505 out->message(out, "crmadmin-node", data->type,
506 name ? name : id,
507 id,
508 data->bash_export);
509 data->found++;
510}
511
512// \return Standard Pacemaker return code
513int
514pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
515{
516 xmlNode *xml_node = NULL;
517 int rc;
518
519 rc = cib__signon_query(NULL, &xml_node);
520
521 if (rc == pcmk_rc_ok) {
522 struct node_data data = {
523 .out = out,
524 .found = 0,
525 .bash_export = bash_export
526 };
527
528 out->begin_list(out, NULL, NULL, "nodes");
529
530 if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
531 node_types = NULL;
532 }
533
534 if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
535 data.field = "id";
536 data.type = "cluster";
538 remote_node_print_helper, &data);
539 }
540
541 if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
542 data.field = "value";
543 data.type = "guest";
545 remote_node_print_helper, &data);
546 }
547
548 if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
549 data.field = "id";
550 data.type = "remote";
552 remote_node_print_helper, &data);
553 }
554
555 out->end_list(out);
556
557 if (data.found == 0) {
558 out->info(out, "No nodes configured");
559 }
560
561 free_xml(xml_node);
562 }
563
564 return rc;
565}
566
567int
568pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
569{
570 pcmk__output_t *out = NULL;
571 int rc = pcmk_rc_ok;
572
573 rc = pcmk__xml_output_new(&out, xml);
574 if (rc != pcmk_rc_ok) {
575 return rc;
576 }
577
579
580 rc = pcmk__list_nodes(out, node_types, FALSE);
581 pcmk__xml_output_finish(out, xml);
582 return rc;
583}
int cib__signon_query(cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:719
const char * name
Definition: cib.c:24
Cluster Configuration.
enum crm_ais_msg_types type
Definition: cpg.c:3
char data[0]
Definition: cpg.c:10
A dumping ground.
void(* pcmk_ipc_callback_t)(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data, void *user_data)
Callback function type for Pacemaker daemon IPC APIs.
Definition: ipc.h:111
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:545
int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
Connect to a Pacemaker daemon via IPC.
Definition: ipc_client.c:486
pcmk_ipc_event
Possible event types that an IPC event callback can be called for.
Definition: ipc.h:80
@ pcmk_ipc_event_reply
Daemon's reply to client IPC request.
Definition: ipc.h:83
@ pcmk_ipc_event_disconnect
Termination of IPC connection.
Definition: ipc.h:82
pcmk_ipc_server
Available IPC interfaces.
Definition: ipc.h:69
@ pcmk_ipc_pacemakerd
Launcher.
Definition: ipc.h:75
@ pcmk_ipc_controld
Controller.
Definition: ipc.h:72
const char * pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
Get the IPC name used with an IPC API connection.
Definition: ipc_client.c:243
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:202
pcmk_ipc_dispatch
How IPC replies should be dispatched.
Definition: ipc.h:88
@ pcmk_ipc_dispatch_sync
Sending a command will wait for any reply.
Definition: ipc.h:91
@ pcmk_ipc_dispatch_main
Attach IPC to GMainLoop for dispatch.
Definition: ipc.h:89
int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server)
Create a new object for using Pacemaker daemon IPC.
Definition: ipc_client.c:47
void pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb, void *user_data)
Register a callback for IPC API events.
Definition: ipc_client.c:596
IPC commands for Pacemaker controller.
int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
Ask the controller for status.
Definition: ipc_controld.c:414
@ pcmk_controld_reply_ping
Definition: ipc_controld.h:36
IPC commands for Pacemakerd.
@ pcmk_pacemakerd_reply_ping
pcmk_pacemakerd_state
@ pcmk_pacemakerd_state_invalid
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
ISO_8601 Date handling.
void crm_time_set_timet(crm_time_t *target, const time_t *source)
Definition: iso8601.c:1259
#define crm_time_log_timeofday
Definition: iso8601.h:68
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140
char * crm_time_as_string(const crm_time_t *dt, int flags)
Definition: iso8601.c:500
#define crm_time_log_with_timezone
Definition: iso8601.h:69
#define crm_time_log_date
Definition: iso8601.h:67
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
struct crm_time_s crm_time_t
Definition: iso8601.h:32
Wrappers for and extensions to glib mainloop.
void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
Drain some remaining main loop events then quit it.
Definition: mainloop.c:1417
#define XML_ATTR_UNAME
Definition: msg_xml.h:157
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
Formatted output for pacemaker tools.
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:201
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:223
High Level API.
int pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
Get nodes list.
int pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
Get controller status.
int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
Get designated controller.
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
#define DEFAULT_MESSAGE_TIMEOUT_MS
int pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name, unsigned int message_timeout_ms)
Get and output pacemakerd status.
int pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name, guint message_timeout_ms, enum pcmk_pacemakerd_state *state)
int pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
int pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
pcmk__action_result_t result
Definition: pcmk_fence.c:35
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2195
#define EREMOTEIO
Definition: portability.h:135
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
@ CRM_EX_OK
Success.
Definition: results.h:234
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_ipc_unresponsive
Definition: results.h:125
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:615
enum crm_exit_e crm_exit_t
@ pcmk__str_regex
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1103
This structure contains everything that makes up a single output formatter.
void(* end_list)(pcmk__output_t *out)
int(* message)(pcmk__output_t *out, const char *message_id,...)
int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
const char * host_from
Name of node that sent reply.
Definition: ipc_controld.h:61
struct pcmk_controld_api_reply_t::@1::@4 ping
enum pcmk_controld_api_reply reply_type
Definition: ipc_controld.h:59
union pcmk_controld_api_reply_t::@1 data
enum pcmk_pacemakerd_api_reply reply_type
union pcmk_pacemakerd_api_reply_t::@5 data
struct pcmk_pacemakerd_api_reply_t::@5::@6 ping
enum pcmk_pacemakerd_state state
Wrappers for and extensions to libxml2.
void crm_foreach_xpath_result(xmlNode *xml, const char *xpath, void(*helper)(xmlNode *, void *), void *user_data)
Run a supplied function for each result of an xpath search.
Definition: xpath.c:173
void free_xml(xmlNode *child)
Definition: xml.c:885
#define PCMK__XP_REMOTE_NODE_CONFIG
Definition: xml_internal.h:140
#define PCMK__XP_MEMBER_NODE_CONFIG
Definition: xml_internal.h:129
#define PCMK__XP_GUEST_NODE_CONFIG
Definition: xml_internal.h:134