pacemaker  2.1.5-b7adf64e51
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 
33 typedef 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 
42 static void
43 quit_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 
54 static gboolean
55 admin_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 
68 static void
69 start_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 
83 static void
84 event_done(data_t *data, pcmk_ipc_api_t *api)
85 {
87  quit_main_loop(data);
88 }
89 
91 controld_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 
135 static void
136 controller_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 
157 static void
158 designated_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 
175 static void
176 pacemakerd_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 
212  if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
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 
248 static pcmk_ipc_api_t *
249 ipc_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;
280  pcmk_free_ipc_api(api);
281  return NULL;
282  }
283 
284  return api;
285 }
286 
287 int
288 pcmk__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 
326 int
327 pcmk_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 
344 int
345 pcmk__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 
383 int
384 pcmk_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 
424 int
425 pcmk__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
467 int
468 pcmk_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 */
488 struct 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 
496 static void
497 remote_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
513 int
514 pcmk__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 
567 int
568 pcmk_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 }
void(* end_list)(pcmk__output_t *out)
A dumping ground.
struct pcmk_controld_api_reply_t::@1::@4 ping
union pcmk_controld_api_reply_t::@1 data
char data[0]
Definition: cpg.c:55
enum pcmk_controld_api_reply reply_type
Definition: ipc_controld.h:59
int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
Get designated controller.
#define crm_time_log_timeofday
Definition: iso8601.h:68
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
const char * name
Definition: cib.c:24
int(* message)(pcmk__output_t *out, const char *message_id,...)
Launcher.
Definition: ipc.h:75
const char * host_from
Name of node that sent reply.
Definition: ipc_controld.h:61
struct crm_time_s crm_time_t
Definition: iso8601.h:32
int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
Ask the controller for status.
Definition: ipc_controld.c:414
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
#define EREMOTEIO
Definition: portability.h:135
High Level API.
enum crm_exit_e crm_exit_t
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
enum crm_ais_msg_types type
Definition: cpg.c:48
struct pcmk_pacemakerd_api_reply_t::@5::@6 ping
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
Wrappers for and extensions to glib mainloop.
union pcmk_pacemakerd_api_reply_t::@5 data
int cib__signon_query(cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:719
char * crm_time_as_string(const crm_time_t *dt, int flags)
Definition: iso8601.c:500
enum pcmk_pacemakerd_api_reply reply_type
Formatted output for pacemaker tools.
#define PCMK__XP_GUEST_NODE_CONFIG
Definition: xml_internal.h:134
pcmk_ipc_server
Available IPC interfaces.
Definition: ipc.h:69
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:615
void crm_time_set_timet(crm_time_t *target, const time_t *source)
Definition: iso8601.c:1259
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:201
int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2195
#define PCMK__XP_REMOTE_NODE_CONFIG
Definition: xml_internal.h:140
Wrappers for and extensions to libxml2.
#define XML_ATTR_UNAME
Definition: msg_xml.h:157
ISO_8601 Date handling.
Success.
Definition: results.h:234
IPC commands for Pacemakerd.
void free_xml(xmlNode *child)
Definition: xml.c:885
#define crm_time_log_with_timezone
Definition: iso8601.h:69
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
IPC commands for Pacemaker controller.
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
pcmk_pacemakerd_state
pcmk_ipc_dispatch
How IPC replies should be dispatched.
Definition: ipc.h:88
pcmk_ipc_event
Possible event types that an IPC event callback can be called for.
Definition: ipc.h:80
Attach IPC to GMainLoop for dispatch.
Definition: ipc.h:89
Termination of IPC connection.
Definition: ipc.h:82
int pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
Get controller status.
pcmk__action_result_t result
Definition: pcmk_fence.c:35
Controller.
Definition: ipc.h:72
int pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
int pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name, guint message_timeout_ms, enum pcmk_pacemakerd_state *state)
Sending a command will wait for any reply.
Definition: ipc.h:91
Cluster Configuration.
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:202
void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
Drain some remaining main loop events then quit it.
Definition: mainloop.c:1417
This structure contains everything that makes up a single output formatter.
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
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1103
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
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 pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:223
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:83
int pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name, unsigned int message_timeout_ms)
Get and output pacemakerd status.
int pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
#define PCMK__XP_MEMBER_NODE_CONFIG
Definition: xml_internal.h:129
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
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:545
#define crm_time_log_date
Definition: iso8601.h:67
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
int pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
Get nodes list.
#define DEFAULT_MESSAGE_TIMEOUT_MS
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140