pacemaker  2.1.4-dc6eb4362
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 
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 } data_t;
40 
41 static void
42 quit_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 
53 static gboolean
54 admin_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 
67 static void
68 start_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 
82 static void
83 event_done(data_t *data, pcmk_ipc_api_t *api)
84 {
86  quit_main_loop(data);
87 }
88 
90 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)
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 
134 static void
135 controller_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 
156 static void
157 designated_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 
174 static void
175 pacemakerd_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 
213  if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
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 
240 static pcmk_ipc_api_t *
241 ipc_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 
271 int
272 pcmk__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 
298 int
299 pcmk_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 
316 int
317 pcmk__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 
343 int
344 pcmk_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 
361 int
362 pcmk__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 
388 int
389 pcmk_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 */
407 struct 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 
415 static void
416 remote_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
432 int
433 pcmk__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 
486 int
487 pcmk_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 }
void(* end_list)(pcmk__output_t *out)
A dumping ground.
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:67
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 * name
Definition: cib.c:24
int(* message)(pcmk__output_t *out, const char *message_id,...)
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
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
struct pcmk_pacemakerd_api_reply_t::@4::@5 ping
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
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:370
Wrappers for and extensions to glib mainloop.
union pcmk_controld_api_reply_t::@0 data
int pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml)
int cib__signon_query(cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:716
struct pcmk_controld_api_reply_t::@0::@3 ping
enum pcmk_pacemakerd_api_reply reply_type
Formatted output for pacemaker tools.
union pcmk_pacemakerd_api_reply_t::@4 data
#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:506
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
Get pacemakerd status.
void crm_time_set_timet(crm_time_t *target, time_t *source)
Definition: iso8601.c:1255
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:1920
#define PCMK__XP_REMOTE_NODE_CONFIG
Definition: xml_internal.h:140
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
Wrappers for and extensions to libxml2.
#define XML_ATTR_UNAME
Definition: msg_xml.h:157
ISO_8601 Date handling.
Success.
Definition: results.h:239
IPC commands for Pacemakerd.
void free_xml(xmlNode *child)
Definition: xml.c:824
#define crm_time_log_with_timezone
Definition: iso8601.h:68
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__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval)
IPC commands for Pacemaker controller.
char * crm_time_as_string(crm_time_t *dt, int flags)
Definition: iso8601.c:496
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
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:34
Controller.
Definition: ipc.h:72
int pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
const char * pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state)
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:201
void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
Drain some remaining main loop events then quit it.
Definition: mainloop.c:1437
This structure contains everything that makes up a single output formatter.
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1101
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
int pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:83
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:593
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:543
#define crm_time_log_date
Definition: iso8601.h:66
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