pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_cluster_queries.c
Go to the documentation of this file.
1/*
2 * Copyright 2020-2021 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/msg_xml.h>
22#include <crm/common/xml.h>
24#include <crm/common/iso8601.h>
27#include <crm/common/mainloop.h>
28
29#define DEFAULT_MESSAGE_TIMEOUT_MS 30000
30
31
32typedef struct {
33 pcmk__output_t *out;
34 GMainLoop *mainloop;
35 int rc;
36 guint message_timer_id;
37 guint message_timeout_ms;
38} data_t;
39
40static void
41quit_main_loop(data_t *data)
42{
43 if (data->mainloop != NULL) {
44 GMainLoop *mloop = data->mainloop;
45
46 data->mainloop = NULL; // Don't re-enter this block
47 pcmk_quit_main_loop(mloop, 10);
48 g_main_loop_unref(mloop);
49 }
50}
51
52static gboolean
53admin_message_timeout(gpointer user_data)
54{
55 data_t *data = user_data;
56 pcmk__output_t *out = data->out;
57
58 out->err(out, "error: No reply received from controller before timeout (%dms)",
59 data->message_timeout_ms);
60 data->message_timer_id = 0;
61 data->rc = ETIMEDOUT;
62 quit_main_loop(data);
63 return FALSE; // Tells glib to remove source
64}
65
66static void
67start_main_loop(data_t *data)
68{
69 if (data->message_timeout_ms < 1) {
70 data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
71 }
72
73 data->rc = ECONNRESET; // For unexpected disconnects
74 data->mainloop = g_main_loop_new(NULL, FALSE);
75 data->message_timer_id = g_timeout_add(data->message_timeout_ms,
76 admin_message_timeout,
77 data);
78 g_main_loop_run(data->mainloop);
79}
80
81static void
82event_done(data_t *data, pcmk_ipc_api_t *api)
83{
85 quit_main_loop(data);
86}
87
89controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
90{
91 pcmk__output_t *out = data->out;
92 pcmk_controld_api_reply_t *reply = event_data;
93
94 switch (event_type) {
96 if (data->rc == ECONNRESET) { // Unexpected
97 out->err(out, "error: Lost connection to controller");
98 }
99 event_done(data, controld_api);
100 return NULL;
101
103 break;
104
105 default:
106 return NULL;
107 }
108
109 if (data->message_timer_id != 0) {
110 g_source_remove(data->message_timer_id);
111 data->message_timer_id = 0;
112 }
113
114 if (status != CRM_EX_OK) {
115 out->err(out, "error: Bad reply from controller: %s",
116 crm_exit_str(status));
117 data->rc = EBADMSG;
118 event_done(data, controld_api);
119 return NULL;
120 }
121
122 if (reply->reply_type != pcmk_controld_reply_ping) {
123 out->err(out, "error: Unknown reply type %d from controller",
124 reply->reply_type);
125 data->rc = EBADMSG;
126 event_done(data, controld_api);
127 return NULL;
128 }
129
130 return reply;
131}
132
133static void
134controller_status_event_cb(pcmk_ipc_api_t *controld_api,
135 enum pcmk_ipc_event event_type, crm_exit_t status,
136 void *event_data, void *user_data)
137{
138 data_t *data = user_data;
139 pcmk__output_t *out = data->out;
140 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
141 event_type, status, event_data);
142
143 if (reply != NULL) {
144 out->message(out, "health",
145 reply->data.ping.sys_from,
146 reply->host_from,
147 reply->data.ping.fsa_state,
148 reply->data.ping.result);
149 data->rc = pcmk_rc_ok;
150 }
151
152 event_done(data, controld_api);
153}
154
155static void
156designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
157 enum pcmk_ipc_event event_type, crm_exit_t status,
158 void *event_data, void *user_data)
159{
160 data_t *data = user_data;
161 pcmk__output_t *out = data->out;
162 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
163 event_type, status, event_data);
164
165 if (reply != NULL) {
166 out->message(out, "dc", reply->host_from);
167 data->rc = pcmk_rc_ok;
168 }
169
170 event_done(data, controld_api);
171}
172
173static void
174pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
175 enum pcmk_ipc_event event_type, crm_exit_t status,
176 void *event_data, void *user_data)
177{
178 data_t *data = user_data;
179 pcmk__output_t *out = data->out;
180 pcmk_pacemakerd_api_reply_t *reply = event_data;
181
182 crm_time_t *crm_when;
183 char *pinged_buf = NULL;
184
185 switch (event_type) {
187 if (data->rc == ECONNRESET) { // Unexpected
188 out->err(out, "error: Lost connection to pacemakerd");
189 }
190 event_done(data, pacemakerd_api);
191 return;
192
194 break;
195
196 default:
197 return;
198 }
199
200 if (data->message_timer_id != 0) {
201 g_source_remove(data->message_timer_id);
202 data->message_timer_id = 0;
203 }
204
205 if (status != CRM_EX_OK) {
206 out->err(out, "error: Bad reply from pacemakerd: %s",
207 crm_exit_str(status));
208 event_done(data, pacemakerd_api);
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 return;
217 }
218
219 // Parse desired information from reply
220 crm_when = crm_time_new(NULL);
221 crm_time_set_timet(crm_when, &reply->data.ping.last_good);
222 pinged_buf = crm_time_as_string(crm_when,
225
226 out->message(out, "pacemakerd-health",
227 reply->data.ping.sys_from,
228 (reply->data.ping.status == pcmk_rc_ok)?
230 reply->data.ping.state):"query failed",
231 (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
232 data->rc = pcmk_rc_ok;
233 crm_time_free(crm_when);
234 free(pinged_buf);
235
236 event_done(data, pacemakerd_api);
237}
238
239static pcmk_ipc_api_t *
240ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb)
241{
242 int rc;
243 pcmk__output_t *out = data->out;
244 pcmk_ipc_api_t *api = NULL;
245
246
247 rc = pcmk_new_ipc_api(&api, server);
248 if (api == NULL) {
249 out->err(out, "error: Could not connect to %s: %s",
250 pcmk_ipc_name(api, true),
251 pcmk_rc_str(rc));
252 data->rc = rc;
253 return NULL;
254 }
255 if (cb != NULL) {
257 }
259 if (rc != pcmk_rc_ok) {
260 out->err(out, "error: Could not connect to %s: %s",
261 pcmk_ipc_name(api, true),
262 pcmk_rc_str(rc));
263 data->rc = rc;
264 return NULL;
265 }
266
267 return api;
268}
269
270int
271pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
272{
273 data_t data = {
274 .out = out,
275 .mainloop = NULL,
276 .rc = pcmk_rc_ok,
277 .message_timer_id = 0,
278 .message_timeout_ms = message_timeout_ms
279 };
280 pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, controller_status_event_cb);
281
282 if (controld_api != NULL) {
283 int rc = pcmk_controld_api_ping(controld_api, dest_node);
284 if (rc != pcmk_rc_ok) {
285 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
286 data.rc = rc;
287 }
288
289 start_main_loop(&data);
290
291 pcmk_free_ipc_api(controld_api);
292 }
293
294 return data.rc;
295}
296
297int
298pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
299{
300 pcmk__output_t *out = NULL;
301 int rc = pcmk_rc_ok;
302
303 rc = pcmk__out_prologue(&out, xml);
304 if (rc != pcmk_rc_ok) {
305 return rc;
306 }
307
309
310 rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
311 pcmk__out_epilogue(out, xml, rc);
312 return rc;
313}
314
315int
316pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
317{
318 data_t data = {
319 .out = out,
320 .mainloop = NULL,
321 .rc = pcmk_rc_ok,
322 .message_timer_id = 0,
323 .message_timeout_ms = message_timeout_ms
324 };
325 pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, designated_controller_event_cb);
326
327 if (controld_api != NULL) {
328 int rc = pcmk_controld_api_ping(controld_api, NULL);
329 if (rc != pcmk_rc_ok) {
330 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
331 data.rc = rc;
332 }
333
334 start_main_loop(&data);
335
336 pcmk_free_ipc_api(controld_api);
337 }
338
339 return data.rc;
340}
341
342int
343pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
344{
345 pcmk__output_t *out = NULL;
346 int rc = pcmk_rc_ok;
347
348 rc = pcmk__out_prologue(&out, xml);
349 if (rc != pcmk_rc_ok) {
350 return rc;
351 }
352
354
355 rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
356 pcmk__out_epilogue(out, xml, rc);
357 return rc;
358}
359
360int
361pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
362{
363 data_t data = {
364 .out = out,
365 .mainloop = NULL,
366 .rc = pcmk_rc_ok,
367 .message_timer_id = 0,
368 .message_timeout_ms = message_timeout_ms
369 };
370 pcmk_ipc_api_t *pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd, pacemakerd_event_cb);
371
372 if (pacemakerd_api != NULL) {
373 int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
374 if (rc != pcmk_rc_ok) {
375 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
376 data.rc = rc;
377 }
378
379 start_main_loop(&data);
380
381 pcmk_free_ipc_api(pacemakerd_api);
382 }
383
384 return data.rc;
385}
386
387int
388pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
389{
390 pcmk__output_t *out = NULL;
391 int rc = pcmk_rc_ok;
392
393 rc = pcmk__out_prologue(&out, xml);
394 if (rc != pcmk_rc_ok) {
395 return rc;
396 }
397
399
400 rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms);
401 pcmk__out_epilogue(out, xml, rc);
402 return rc;
403}
404
405/* user data for looping through remote node xpath searches */
406struct node_data {
407 pcmk__output_t *out;
408 int found;
409 const char *field; /* XML attribute to check for node name */
410 const char *type;
411 gboolean BASH_EXPORT;
412};
413
414static void
415remote_node_print_helper(xmlNode *result, void *user_data)
416{
417 struct node_data *data = user_data;
418 pcmk__output_t *out = data->out;
419 const char *name = crm_element_value(result, XML_ATTR_UNAME);
420 const char *id = crm_element_value(result, data->field);
421
422 // node name and node id are the same for remote/guest nodes
423 out->message(out, "crmadmin-node", data->type,
424 name ? name : id,
425 id,
426 data->BASH_EXPORT);
427 data->found++;
428}
429
430// \return Standard Pacemaker return code
431int
432pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
433{
434 cib_t *the_cib = cib_new();
435 xmlNode *xml_node = NULL;
436 int rc;
437
438 if (the_cib == NULL) {
439 return ENOMEM;
440 }
441 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
442 if (rc != pcmk_ok) {
443 cib_delete(the_cib);
444 return pcmk_legacy2rc(rc);
445 }
446
447 rc = the_cib->cmds->query(the_cib, NULL, &xml_node,
449 if (rc == pcmk_ok) {
450 struct node_data data = {
451 .out = out,
452 .found = 0,
453 .BASH_EXPORT = BASH_EXPORT
454 };
455
456 out->begin_list(out, NULL, NULL, "nodes");
457
458 if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
459 node_types = NULL;
460 }
461
462 if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
463 data.field = "id";
464 data.type = "cluster";
466 remote_node_print_helper, &data);
467 }
468
469 if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
470 data.field = "value";
471 data.type = "guest";
473 remote_node_print_helper, &data);
474 }
475
476 if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
477 data.field = "id";
478 data.type = "remote";
480 remote_node_print_helper, &data);
481 }
482
483 out->end_list(out);
484
485 if (data.found == 0) {
486 out->info(out, "No nodes configured");
487 }
488
489 free_xml(xml_node);
490 }
491 the_cib->cmds->signoff(the_cib);
492 cib_delete(the_cib);
493 return pcmk_legacy2rc(rc);
494}
495
496int
497pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
498{
499 pcmk__output_t *out = NULL;
500 int rc = pcmk_rc_ok;
501
502 rc = pcmk__out_prologue(&out, xml);
503 if (rc != pcmk_rc_ok) {
504 return rc;
505 }
506
508
509 rc = pcmk__list_nodes(out, node_types, FALSE);
510 pcmk__out_epilogue(out, xml, rc);
511 return rc;
512}
Cluster Configuration.
void cib_delete(cib_t *cib)
Free all memory used by CIB connection.
Definition cib_client.c:442
cib_t * cib_new(void)
Definition cib_client.c:292
@ cib_command
Definition cib_types.h:43
@ cib_scope_local
Definition cib_types.h:59
@ cib_sync_call
Definition cib_types.h:61
enum crm_ais_msg_types type
Definition cpg.c:3
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
char * crm_system_name
Definition utils.c:54
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:110
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition ipc_client.c:511
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:452
const char * pcmk_ipc_name(pcmk_ipc_api_t *api, bool for_log)
Get the IPC name used with an IPC API connection.
Definition ipc_client.c:241
pcmk_ipc_event
Possible event types that an IPC event callback can be called for.
Definition ipc.h:79
@ pcmk_ipc_event_reply
Daemon's reply to client IPC request.
Definition ipc.h:82
@ pcmk_ipc_event_disconnect
Termination of IPC connection.
Definition ipc.h:81
pcmk_ipc_server
Available IPC interfaces.
Definition ipc.h:68
@ pcmk_ipc_pacemakerd
Launcher.
Definition ipc.h:74
@ pcmk_ipc_controld
Controller.
Definition ipc.h:71
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition ipc_client.c:200
@ pcmk_ipc_dispatch_main
Attach IPC to GMainLoop for dispatch.
Definition ipc.h:88
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:561
IPC commands for Pacemaker controller.
int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
Ask the controller for status.
@ pcmk_controld_reply_ping
IPC commands for Pacemakerd.
const char * pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state)
@ pcmk_pacemakerd_reply_ping
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
ISO_8601 Date handling.
#define crm_time_log_timeofday
Definition iso8601.h:67
char * crm_time_as_string(crm_time_t *dt, int flags)
Definition iso8601.c:497
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:141
void crm_time_set_timet(crm_time_t *target, time_t *source)
Definition iso8601.c:1255
#define crm_time_log_with_timezone
Definition iso8601.h:68
#define crm_time_log_date
Definition iso8601.h:66
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:93
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:1439
#define XML_ATTR_UNAME
Definition msg_xml.h:151
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:530
Formatted output for pacemaker tools.
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.
#define DEFAULT_MESSAGE_TIMEOUT_MS
int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
Get pacemakerd status.
int pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
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)
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
char * name
Definition pcmk_fence.c:31
int rc
Definition pcmk_fence.c:35
int pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml)
void pcmk__register_lib_messages(pcmk__output_t *out)
void pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval)
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:420
@ CRM_EX_OK
Definition results.h:217
@ pcmk_rc_ok
Definition results.h:142
#define pcmk_ok
Definition results.h:67
const char * crm_exit_str(crm_exit_t exit_code)
Definition results.c:526
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:450
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:1123
int(* signoff)(cib_t *cib)
Definition cib_types.h:76
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition cib_types.h:73
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition cib_types.h:92
cib_api_operations_t * cmds
Definition cib_types.h:147
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
union pcmk_controld_api_reply_t::@0 data
struct pcmk_controld_api_reply_t::@0::@3 ping
const char * host_from
Name of node that sent reply.
enum pcmk_controld_api_reply reply_type
enum pcmk_pacemakerd_api_reply reply_type
enum pcmk_pacemakerd_state state
union pcmk_pacemakerd_api_reply_t::@4 data
struct pcmk_pacemakerd_api_reply_t::@4::@5 ping
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:823
#define PCMK__XP_REMOTE_NODE_CONFIG
#define PCMK__XP_MEMBER_NODE_CONFIG
#define PCMK__XP_GUEST_NODE_CONFIG