pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
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/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} data_t;
40
41static void
42quit_main_loop(data_t *data)
43{
44 if (data->mainloop != NULL) {
45 GMainLoop *mloop = data->mainloop;
46
47 data->mainloop = NULL; // Don't re-enter this block
48 pcmk_quit_main_loop(mloop, 10);
49 g_main_loop_unref(mloop);
50 }
51}
52
53static gboolean
54admin_message_timeout(gpointer user_data)
55{
56 data_t *data = user_data;
57 pcmk__output_t *out = data->out;
58
59 out->err(out, "error: No reply received from controller before timeout (%dms)",
60 data->message_timeout_ms);
61 data->message_timer_id = 0;
62 data->rc = ETIMEDOUT;
63 quit_main_loop(data);
64 return FALSE; // Tells glib to remove source
65}
66
67static void
68start_main_loop(data_t *data)
69{
70 if (data->message_timeout_ms < 1) {
71 data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
72 }
73
74 data->rc = ECONNRESET; // For unexpected disconnects
75 data->mainloop = g_main_loop_new(NULL, FALSE);
76 data->message_timer_id = g_timeout_add(data->message_timeout_ms,
77 admin_message_timeout,
78 data);
79 g_main_loop_run(data->mainloop);
80}
81
82static void
83event_done(data_t *data, pcmk_ipc_api_t *api)
84{
86 quit_main_loop(data);
87}
88
90controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
91{
92 pcmk__output_t *out = data->out;
93 pcmk_controld_api_reply_t *reply = event_data;
94
95 switch (event_type) {
97 if (data->rc == ECONNRESET) { // Unexpected
98 out->err(out, "error: Lost connection to controller");
99 }
100 event_done(data, controld_api);
101 return NULL;
102
104 break;
105
106 default:
107 return NULL;
108 }
109
110 if (data->message_timer_id != 0) {
111 g_source_remove(data->message_timer_id);
112 data->message_timer_id = 0;
113 }
114
115 if (status != CRM_EX_OK) {
116 out->err(out, "error: Bad reply from controller: %s",
117 crm_exit_str(status));
118 data->rc = EBADMSG;
119 event_done(data, controld_api);
120 return NULL;
121 }
122
123 if (reply->reply_type != pcmk_controld_reply_ping) {
124 out->err(out, "error: Unknown reply type %d from controller",
125 reply->reply_type);
126 data->rc = EBADMSG;
127 event_done(data, controld_api);
128 return NULL;
129 }
130
131 return reply;
132}
133
134static void
135controller_status_event_cb(pcmk_ipc_api_t *controld_api,
136 enum pcmk_ipc_event event_type, crm_exit_t status,
137 void *event_data, void *user_data)
138{
139 data_t *data = user_data;
140 pcmk__output_t *out = data->out;
141 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
142 event_type, status, event_data);
143
144 if (reply != NULL) {
145 out->message(out, "health",
146 reply->data.ping.sys_from,
147 reply->host_from,
148 reply->data.ping.fsa_state,
149 reply->data.ping.result);
150 data->rc = pcmk_rc_ok;
151 }
152
153 event_done(data, controld_api);
154}
155
156static void
157designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
158 enum pcmk_ipc_event event_type, crm_exit_t status,
159 void *event_data, void *user_data)
160{
161 data_t *data = user_data;
162 pcmk__output_t *out = data->out;
163 pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
164 event_type, status, event_data);
165
166 if (reply != NULL) {
167 out->message(out, "dc", reply->host_from);
168 data->rc = pcmk_rc_ok;
169 }
170
171 event_done(data, controld_api);
172}
173
174static void
175pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
176 enum pcmk_ipc_event event_type, crm_exit_t status,
177 void *event_data, void *user_data)
178{
179 data_t *data = user_data;
180 pcmk__output_t *out = data->out;
181 pcmk_pacemakerd_api_reply_t *reply = event_data;
182
183 crm_time_t *crm_when;
184 char *pinged_buf = NULL;
185
186 switch (event_type) {
188 if (data->rc == ECONNRESET) { // Unexpected
189 out->err(out, "error: Lost connection to pacemakerd");
190 }
191 event_done(data, pacemakerd_api);
192 return;
193
195 break;
196
197 default:
198 return;
199 }
200
201 if (data->message_timer_id != 0) {
202 g_source_remove(data->message_timer_id);
203 data->message_timer_id = 0;
204 }
205
206 if (status != CRM_EX_OK) {
207 out->err(out, "error: Bad reply from pacemakerd: %s",
208 crm_exit_str(status));
209 event_done(data, pacemakerd_api);
210 return;
211 }
212
214 out->err(out, "error: Unknown reply type %d from pacemakerd",
215 reply->reply_type);
216 event_done(data, pacemakerd_api);
217 return;
218 }
219
220 // Parse desired information from reply
221 crm_when = crm_time_new(NULL);
222 crm_time_set_timet(crm_when, &reply->data.ping.last_good);
223 pinged_buf = crm_time_as_string(crm_when,
226
227 out->message(out, "pacemakerd-health",
228 reply->data.ping.sys_from,
229 (reply->data.ping.status == pcmk_rc_ok)?
231 reply->data.ping.state):"query failed",
232 (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
233 data->rc = pcmk_rc_ok;
234 crm_time_free(crm_when);
235 free(pinged_buf);
236
237 event_done(data, pacemakerd_api);
238}
239
240static pcmk_ipc_api_t *
241ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb)
242{
243 int rc;
244 pcmk__output_t *out = data->out;
245 pcmk_ipc_api_t *api = NULL;
246
247
248 rc = pcmk_new_ipc_api(&api, server);
249 if (api == NULL) {
250 out->err(out, "error: Could not connect to %s: %s",
251 pcmk_ipc_name(api, true),
252 pcmk_rc_str(rc));
253 data->rc = rc;
254 return NULL;
255 }
256 if (cb != NULL) {
258 }
260 if (rc != pcmk_rc_ok) {
261 out->err(out, "error: Could not connect to %s: %s",
262 pcmk_ipc_name(api, true),
263 pcmk_rc_str(rc));
264 data->rc = rc;
265 return NULL;
266 }
267
268 return api;
269}
270
271int
272pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
273{
274 data_t data = {
275 .out = out,
276 .mainloop = NULL,
277 .rc = pcmk_rc_ok,
278 .message_timer_id = 0,
279 .message_timeout_ms = message_timeout_ms
280 };
281 pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, controller_status_event_cb);
282
283 if (controld_api != NULL) {
284 int rc = pcmk_controld_api_ping(controld_api, dest_node);
285 if (rc != pcmk_rc_ok) {
286 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
287 data.rc = rc;
288 }
289
290 start_main_loop(&data);
291
292 pcmk_free_ipc_api(controld_api);
293 }
294
295 return data.rc;
296}
297
298int
299pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
300{
301 pcmk__output_t *out = NULL;
302 int rc = pcmk_rc_ok;
303
304 rc = pcmk__out_prologue(&out, xml);
305 if (rc != pcmk_rc_ok) {
306 return rc;
307 }
308
310
311 rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
312 pcmk__out_epilogue(out, xml, rc);
313 return rc;
314}
315
316int
317pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
318{
319 data_t data = {
320 .out = out,
321 .mainloop = NULL,
322 .rc = pcmk_rc_ok,
323 .message_timer_id = 0,
324 .message_timeout_ms = message_timeout_ms
325 };
326 pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, designated_controller_event_cb);
327
328 if (controld_api != NULL) {
329 int rc = pcmk_controld_api_ping(controld_api, NULL);
330 if (rc != pcmk_rc_ok) {
331 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
332 data.rc = rc;
333 }
334
335 start_main_loop(&data);
336
337 pcmk_free_ipc_api(controld_api);
338 }
339
340 return data.rc;
341}
342
343int
344pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
345{
346 pcmk__output_t *out = NULL;
347 int rc = pcmk_rc_ok;
348
349 rc = pcmk__out_prologue(&out, xml);
350 if (rc != pcmk_rc_ok) {
351 return rc;
352 }
353
355
356 rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
357 pcmk__out_epilogue(out, xml, rc);
358 return rc;
359}
360
361int
362pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
363{
364 data_t data = {
365 .out = out,
366 .mainloop = NULL,
367 .rc = pcmk_rc_ok,
368 .message_timer_id = 0,
369 .message_timeout_ms = message_timeout_ms
370 };
371 pcmk_ipc_api_t *pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd, pacemakerd_event_cb);
372
373 if (pacemakerd_api != NULL) {
374 int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
375 if (rc != pcmk_rc_ok) {
376 out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
377 data.rc = rc;
378 }
379
380 start_main_loop(&data);
381
382 pcmk_free_ipc_api(pacemakerd_api);
383 }
384
385 return data.rc;
386}
387
388int
389pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
390{
391 pcmk__output_t *out = NULL;
392 int rc = pcmk_rc_ok;
393
394 rc = pcmk__out_prologue(&out, xml);
395 if (rc != pcmk_rc_ok) {
396 return rc;
397 }
398
400
401 rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms);
402 pcmk__out_epilogue(out, xml, rc);
403 return rc;
404}
405
406/* user data for looping through remote node xpath searches */
407struct node_data {
408 pcmk__output_t *out;
409 int found;
410 const char *field; /* XML attribute to check for node name */
411 const char *type;
412 gboolean BASH_EXPORT;
413};
414
415static void
416remote_node_print_helper(xmlNode *result, void *user_data)
417{
418 struct node_data *data = user_data;
419 pcmk__output_t *out = data->out;
421 const char *id = crm_element_value(result, data->field);
422
423 // node name and node id are the same for remote/guest nodes
424 out->message(out, "crmadmin-node", data->type,
425 name ? name : id,
426 id,
427 data->BASH_EXPORT);
428 data->found++;
429}
430
431// \return Standard Pacemaker return code
432int
433pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
434{
435 xmlNode *xml_node = NULL;
436 int rc;
437
438 rc = cib__signon_query(NULL, &xml_node);
439
440 if (rc == pcmk_rc_ok) {
441 struct node_data data = {
442 .out = out,
443 .found = 0,
444 .BASH_EXPORT = BASH_EXPORT
445 };
446
447 out->begin_list(out, NULL, NULL, "nodes");
448
449 if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
450 node_types = NULL;
451 }
452
453 if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
454 data.field = "id";
455 data.type = "cluster";
457 remote_node_print_helper, &data);
458 }
459
460 if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
461 data.field = "value";
462 data.type = "guest";
464 remote_node_print_helper, &data);
465 }
466
467 if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
468 data.field = "id";
469 data.type = "remote";
471 remote_node_print_helper, &data);
472 }
473
474 out->end_list(out);
475
476 if (data.found == 0) {
477 out->info(out, "No nodes configured");
478 }
479
480 free_xml(xml_node);
481 }
482
483 return rc;
484}
485
486int
487pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
488{
489 pcmk__output_t *out = NULL;
490 int rc = pcmk_rc_ok;
491
492 rc = pcmk__out_prologue(&out, xml);
493 if (rc != pcmk_rc_ok) {
494 return rc;
495 }
496
498
499 rc = pcmk__list_nodes(out, node_types, FALSE);
500 pcmk__out_epilogue(out, xml, rc);
501 return rc;
502}
int cib__signon_query(cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:716
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:543
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:484
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:242
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
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:201
@ 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:593
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.
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:496
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140
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: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:1437
#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:529
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)
pcmk__action_result_t result
Definition: pcmk_fence.c:34
int pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml)
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:1920
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:370
@ CRM_EX_OK
Success.
Definition: results.h:239
@ pcmk_rc_ok
Definition: results.h:146
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:506
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:1101
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.
Definition: ipc_controld.h:61
enum pcmk_controld_api_reply reply_type
Definition: ipc_controld.h:59
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:824
#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