pacemaker  2.1.0-7c3f660
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 
32 typedef 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 
40 static void
41 quit_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 
52 static gboolean
53 admin_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 
66 static void
67 start_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 
81 static void
82 event_done(data_t *data, pcmk_ipc_api_t *api)
83 {
85  quit_main_loop(data);
86 }
87 
89 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)
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 
133 static void
134 controller_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 
155 static void
156 designated_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 
173 static void
174 pacemakerd_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 
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  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 
239 static pcmk_ipc_api_t *
240 ipc_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) {
256  pcmk_register_ipc_callback(api, cb, data);
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 
270 int
271 pcmk__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 
297 int
298 pcmk_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 
315 int
316 pcmk__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 
342 int
343 pcmk_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 
360 int
361 pcmk__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 
387 int
388 pcmk_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 */
406 struct 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 
414 static void
415 remote_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
431 int
432 pcmk__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  return pcmk_legacy2rc(rc);
444  }
445 
446  rc = the_cib->cmds->query(the_cib, NULL, &xml_node,
448  if (rc == pcmk_ok) {
449  struct node_data data = {
450  .out = out,
451  .found = 0,
452  .BASH_EXPORT = BASH_EXPORT
453  };
454 
455  out->begin_list(out, NULL, NULL, "nodes");
456 
457  if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
458  node_types = NULL;
459  }
460 
461  if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
462  data.field = "id";
463  data.type = "cluster";
465  remote_node_print_helper, &data);
466  }
467 
468  if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
469  data.field = "value";
470  data.type = "guest";
472  remote_node_print_helper, &data);
473  }
474 
475  if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
476  data.field = "id";
477  data.type = "remote";
479  remote_node_print_helper, &data);
480  }
481 
482  out->end_list(out);
483 
484  if (data.found == 0) {
485  out->info(out, "No nodes configured");
486  }
487 
488  free_xml(xml_node);
489  }
490  the_cib->cmds->signoff(the_cib);
491  return pcmk_legacy2rc(rc);
492 }
493 
494 int
495 pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
496 {
497  pcmk__output_t *out = NULL;
498  int rc = pcmk_rc_ok;
499 
500  rc = pcmk__out_prologue(&out, xml);
501  if (rc != pcmk_rc_ok) {
502  return rc;
503  }
504 
506 
507  rc = pcmk__list_nodes(out, node_types, FALSE);
508  pcmk__out_epilogue(out, xml, rc);
509  return rc;
510 }
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:92
A dumping ground.
char data[0]
Definition: cpg.c:55
enum pcmk_controld_api_reply reply_type
Definition: ipc_controld.h:58
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
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT)
cib_t * cib_new(void)
Definition: cib_client.c:292
#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:452
Launcher.
Definition: ipc.h:74
const char * host_from
Name of node that sent reply.
Definition: ipc_controld.h:60
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:396
int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
Get pacemakerd status.
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
int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
Get designated controller.
int(* message)(pcmk__output_t *out, const char *message_id,...)
High Level API.
char * crm_system_name
Definition: utils.c:54
enum crm_exit_e crm_exit_t
int(*) void(* err)(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:420
int pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
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)
struct pcmk_controld_api_reply_t::@0::@3 ping
void(* end_list)(pcmk__output_t *out)
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:68
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:526
int rc
Definition: pcmk_fence.c:35
cib_api_operations_t * cmds
Definition: cib_types.h:147
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:530
void crm_time_set_timet(crm_time_t *target, time_t *source)
Definition: iso8601.c:1255
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:1496
#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:241
Wrappers for and extensions to libxml2.
#define XML_ATTR_UNAME
Definition: msg_xml.h:151
ISO_8601 Date handling.
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:450
IPC commands for Pacemakerd.
void free_xml(xmlNode *child)
Definition: xml.c:823
#define crm_time_log_with_timezone
Definition: iso8601.h:68
int pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
Get nodes list.
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:497
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
int pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
Get controller status.
pcmk_ipc_event
Possible event types that an IPC event callback can be called for.
Definition: ipc.h:79
Attach IPC to GMainLoop for dispatch.
Definition: ipc.h:88
Termination of IPC connection.
Definition: ipc.h:81
Controller.
Definition: ipc.h:71
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:93
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:200
void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
Drain some remaining main loop events then quit it.
Definition: mainloop.c:1439
This structure contains everything that makes up a single output formatter.
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition: cib_types.h:73
int pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1097
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
#define pcmk_ok
Definition: results.h:67
int(* signoff)(cib_t *cib)
Definition: cib_types.h:76
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:82
char * name
Definition: pcmk_fence.c:31
#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:561
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:511
#define crm_time_log_date
Definition: iso8601.h:66
int pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
#define DEFAULT_MESSAGE_TIMEOUT_MS
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141