This source file includes following definitions.
- command_cb
- quit_main_loop
- controller_event_cb
- pacemakerd_event_cb
- list_nodes
- build_arg_context
- main
- do_work
- admin_message_timeout
- do_find_node_list
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <stdbool.h>
14 #include <stdlib.h>
15
16 #include <glib.h>
17 #include <libxml/tree.h>
18
19 #include <crm/crm.h>
20 #include <crm/cib.h>
21 #include <crm/msg_xml.h>
22 #include <crm/common/cmdline_internal.h>
23 #include <crm/common/xml.h>
24 #include <crm/common/iso8601.h>
25 #include <crm/common/ipc_controld.h>
26 #include <crm/common/ipc_pacemakerd.h>
27 #include <crm/common/mainloop.h>
28
29 #define SUMMARY "query and manage the Pacemaker controller"
30
31 #define DEFAULT_MESSAGE_TIMEOUT_MS 30000
32
33 static guint message_timer_id = 0;
34 static guint message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
35 static GMainLoop *mainloop = NULL;
36
37 bool need_controld_api = true;
38 bool need_pacemakerd_api = false;
39
40 bool do_work(pcmk_ipc_api_t *api);
41 void do_find_node_list(xmlNode *xml_node);
42 static char *ipc_name = NULL;
43
44 gboolean admin_message_timeout(gpointer data);
45
46 static enum {
47 cmd_none,
48 cmd_shutdown,
49 cmd_health,
50 cmd_elect_dc,
51 cmd_whois_dc,
52 cmd_list_nodes,
53 cmd_pacemakerd_health,
54 } command = cmd_none;
55
56 static gboolean BE_VERBOSE = FALSE;
57 static gboolean BASH_EXPORT = FALSE;
58 static gboolean BE_SILENT = FALSE;
59 static char *dest_node = NULL;
60 static crm_exit_t exit_code = CRM_EX_OK;
61
62
63 struct {
64 gboolean quiet;
65 gboolean health;
66 gint timeout;
67 } options;
68
69 gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
70
71 static GOptionEntry command_options[] = {
72 { "status", 'S', 0, G_OPTION_ARG_CALLBACK, command_cb,
73 "Display the status of the specified node."
74 "\n Result is state of node's internal finite state"
75 "\n machine, which can be useful for debugging",
76 NULL
77 },
78 { "pacemakerd", 'P', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
79 "Display the status of local pacemakerd."
80 "\n Result is the state of the sub-daemons watched"
81 "\n by pacemakerd.",
82 NULL
83 },
84 { "dc_lookup", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
85 "Display the uname of the node co-ordinating the cluster."
86 "\n This is an internal detail rarely useful to"
87 "\n administrators except when deciding on which"
88 "\n node to examine the logs.",
89 NULL
90 },
91 { "nodes", 'N', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
92 "Display the uname of all member nodes",
93 NULL
94 },
95 { "election", 'E', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
96 "(Advanced) Start an election for the cluster co-ordinator",
97 NULL
98 },
99 { "kill", 'K', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb,
100 "(Advanced) Stop controller (not rest of cluster stack) on specified node",
101 NULL
102 },
103 { "health", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.health,
104 NULL,
105 NULL
106 },
107
108 { NULL }
109 };
110
111 static GOptionEntry additional_options[] = {
112 { "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
113 "Time (in milliseconds) to wait before declaring the"
114 "\n operation failed",
115 NULL
116 },
117 { "bash-export", 'B', 0, G_OPTION_ARG_NONE, &BASH_EXPORT,
118 "Display nodes as shell commands of the form 'export uname=uuid'"
119 "\n (valid with -N/--nodes)",
120 },
121 { "ipc-name", 'i', 0, G_OPTION_ARG_STRING, &ipc_name,
122 "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).",
123 NULL
124 },
125
126 { NULL }
127 };
128
129 gboolean
130 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error)
131 {
132 if (!strcmp(option_name, "--status") || !strcmp(option_name, "-S")) {
133 command = cmd_health;
134 crm_trace("Option %c => %s", 'S', optarg);
135 }
136
137 if (!strcmp(option_name, "--pacemakerd") || !strcmp(option_name, "-P")) {
138 command = cmd_pacemakerd_health;
139 need_pacemakerd_api = true;
140 need_controld_api = false;
141 }
142
143 if (!strcmp(option_name, "--dc_lookup") || !strcmp(option_name, "-D")) {
144 command = cmd_whois_dc;
145 }
146
147 if (!strcmp(option_name, "--nodes") || !strcmp(option_name, "-N")) {
148 command = cmd_list_nodes;
149 need_controld_api = false;
150 }
151
152 if (!strcmp(option_name, "--election") || !strcmp(option_name, "-E")) {
153 command = cmd_elect_dc;
154 }
155
156 if (!strcmp(option_name, "--kill") || !strcmp(option_name, "-K")) {
157 command = cmd_shutdown;
158 crm_trace("Option %c => %s", 'K', optarg);
159 }
160
161 if (optarg) {
162 if (dest_node != NULL) {
163 free(dest_node);
164 }
165 dest_node = strdup(optarg);
166 }
167
168 return TRUE;
169 }
170
171 static void
172 quit_main_loop(crm_exit_t ec)
173 {
174 exit_code = ec;
175 if (mainloop != NULL) {
176 GMainLoop *mloop = mainloop;
177
178 mainloop = NULL;
179 pcmk_quit_main_loop(mloop, 10);
180 g_main_loop_unref(mloop);
181 }
182 }
183
184 static void
185 controller_event_cb(pcmk_ipc_api_t *controld_api,
186 enum pcmk_ipc_event event_type, crm_exit_t status,
187 void *event_data, void *user_data)
188 {
189 pcmk_controld_api_reply_t *reply = event_data;
190
191 switch (event_type) {
192 case pcmk_ipc_event_disconnect:
193 if (exit_code == CRM_EX_DISCONNECT) {
194 fprintf(stderr, "error: Lost connection to controller\n");
195 }
196 goto done;
197 break;
198
199 case pcmk_ipc_event_reply:
200 break;
201
202 default:
203 return;
204 }
205
206 if (message_timer_id != 0) {
207 g_source_remove(message_timer_id);
208 message_timer_id = 0;
209 }
210
211 if (status != CRM_EX_OK) {
212 fprintf(stderr, "error: Bad reply from controller: %s",
213 crm_exit_str(status));
214 exit_code = status;
215 goto done;
216 }
217
218 if (reply->reply_type != pcmk_controld_reply_ping) {
219 fprintf(stderr, "error: Unknown reply type %d from controller\n",
220 reply->reply_type);
221 goto done;
222 }
223
224
225 switch (command) {
226 case cmd_health:
227 printf("Status of %s@%s: %s (%s)\n",
228 reply->data.ping.sys_from,
229 reply->host_from,
230 reply->data.ping.fsa_state,
231 reply->data.ping.result);
232 if (BE_SILENT && (reply->data.ping.fsa_state != NULL)) {
233 fprintf(stderr, "%s\n", reply->data.ping.fsa_state);
234 }
235 exit_code = CRM_EX_OK;
236 break;
237
238 case cmd_whois_dc:
239 printf("Designated Controller is: %s\n", reply->host_from);
240 if (BE_SILENT && (reply->host_from != NULL)) {
241 fprintf(stderr, "%s\n", reply->host_from);
242 }
243 exit_code = CRM_EX_OK;
244 break;
245
246 default:
247 exit_code = CRM_EX_SOFTWARE;
248 break;
249 }
250
251 done:
252 pcmk_disconnect_ipc(controld_api);
253 quit_main_loop(exit_code);
254 }
255
256 static void
257 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
258 enum pcmk_ipc_event event_type, crm_exit_t status,
259 void *event_data, void *user_data)
260 {
261 pcmk_pacemakerd_api_reply_t *reply = event_data;
262
263 switch (event_type) {
264 case pcmk_ipc_event_disconnect:
265 if (exit_code == CRM_EX_DISCONNECT) {
266 fprintf(stderr, "error: Lost connection to pacemakerd\n");
267 }
268 goto done;
269 break;
270
271 case pcmk_ipc_event_reply:
272 break;
273
274 default:
275 return;
276 }
277
278 if (message_timer_id != 0) {
279 g_source_remove(message_timer_id);
280 message_timer_id = 0;
281 }
282
283 if (status != CRM_EX_OK) {
284 fprintf(stderr, "error: Bad reply from pacemakerd: %s",
285 crm_exit_str(status));
286 exit_code = status;
287 goto done;
288 }
289
290 if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
291 fprintf(stderr, "error: Unknown reply type %d from pacemakerd\n",
292 reply->reply_type);
293 goto done;
294 }
295
296
297 switch (command) {
298 case cmd_pacemakerd_health:
299 {
300 crm_time_t *crm_when = crm_time_new(NULL);
301 char *pinged_buf = NULL;
302
303 crm_time_set_timet(crm_when, &reply->data.ping.last_good);
304 pinged_buf = crm_time_as_string(crm_when,
305 crm_time_log_date | crm_time_log_timeofday |
306 crm_time_log_with_timezone);
307
308 printf("Status of %s: '%s' %s %s\n",
309 reply->data.ping.sys_from,
310 (reply->data.ping.status == pcmk_rc_ok)?
311 pcmk_pacemakerd_api_daemon_state_enum2text(
312 reply->data.ping.state):"query failed",
313 (reply->data.ping.status == pcmk_rc_ok)?"last updated":"",
314 (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
315 if (BE_SILENT &&
316 (reply->data.ping.state != pcmk_pacemakerd_state_invalid)) {
317 fprintf(stderr, "%s\n",
318 (reply->data.ping.status == pcmk_rc_ok)?
319 pcmk_pacemakerd_api_daemon_state_enum2text(
320 reply->data.ping.state):
321 "query failed");
322 }
323 exit_code = CRM_EX_OK;
324 free(pinged_buf);
325 }
326 break;
327
328 default:
329 exit_code = CRM_EX_SOFTWARE;
330 break;
331 }
332
333 done:
334 pcmk_disconnect_ipc(pacemakerd_api);
335 quit_main_loop(exit_code);
336 }
337
338
339 static int
340 list_nodes()
341 {
342 cib_t *the_cib = cib_new();
343 xmlNode *output = NULL;
344 int rc;
345
346 if (the_cib == NULL) {
347 return ENOMEM;
348 }
349 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
350 if (rc != pcmk_ok) {
351 return pcmk_legacy2rc(rc);
352 }
353
354 rc = the_cib->cmds->query(the_cib, NULL, &output,
355 cib_scope_local | cib_sync_call);
356 if (rc == pcmk_ok) {
357 do_find_node_list(output);
358 free_xml(output);
359 }
360 the_cib->cmds->signoff(the_cib);
361 return pcmk_legacy2rc(rc);
362 }
363
364 static GOptionContext *
365 build_arg_context(pcmk__common_args_t *args) {
366 GOptionContext *context = NULL;
367
368 const char *description = "Report bugs to users@clusterlabs.org";
369
370 GOptionEntry extra_prog_entries[] = {
371 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &options.quiet,
372 "Display only the essential query information",
373 NULL },
374
375 { NULL }
376 };
377
378 context = pcmk__build_arg_context(args, NULL, NULL, NULL);
379 g_option_context_set_description(context, description);
380
381
382
383
384 pcmk__add_main_args(context, extra_prog_entries);
385
386 pcmk__add_arg_group(context, "command", "Commands:",
387 "Show command options", command_options);
388 pcmk__add_arg_group(context, "additional", "Additional Options:",
389 "Show additional options", additional_options);
390 return context;
391 }
392
393 int
394 main(int argc, char **argv)
395 {
396 int argerr = 0;
397 int rc;
398 pcmk_ipc_api_t *controld_api = NULL;
399 pcmk_ipc_api_t *pacemakerd_api = NULL;
400
401 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
402
403 GError *error = NULL;
404 GOptionContext *context = NULL;
405 gchar **processed_args = NULL;
406
407 context = build_arg_context(args);
408
409 crm_log_cli_init("crmadmin");
410
411 processed_args = pcmk__cmdline_preproc(argv, "itBDEHKNPS");
412
413 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
414 fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
415 exit_code = CRM_EX_USAGE;
416 goto done;
417 }
418
419 for (int i = 0; i < args->verbosity; i++) {
420 BE_VERBOSE = TRUE;
421 crm_bump_log_level(argc, argv);
422 }
423
424 if (args->version) {
425
426 pcmk__cli_help('v', CRM_EX_USAGE);
427 }
428
429 if (options.timeout) {
430 message_timeout_ms = (guint) options.timeout;
431 if (message_timeout_ms < 1) {
432 message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
433 }
434 }
435
436 if (options.quiet) {
437 BE_SILENT = TRUE;
438 }
439
440 if (options.health) {
441 fprintf(stderr, "Cluster-wide health option not supported\n");
442 ++argerr;
443 }
444
445 if (optind > argc) {
446 ++argerr;
447 }
448
449 if (command == cmd_none) {
450 fprintf(stderr, "error: Must specify a command option\n\n");
451 ++argerr;
452 }
453
454 if (argerr) {
455 char *help = g_option_context_get_help(context, TRUE, NULL);
456
457 fprintf(stderr, "%s", help);
458 g_free(help);
459 exit_code = CRM_EX_USAGE;
460 goto done;
461 }
462
463
464 if (need_controld_api) {
465 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
466 if (controld_api == NULL) {
467 fprintf(stderr, "error: Could not connect to controller: %s\n",
468 pcmk_rc_str(rc));
469 exit_code = pcmk_rc2exitc(rc);
470 goto done;
471 }
472 pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
473 rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_main);
474 if (rc != pcmk_rc_ok) {
475 fprintf(stderr, "error: Could not connect to controller: %s\n",
476 pcmk_rc_str(rc));
477 exit_code = pcmk_rc2exitc(rc);
478 goto done;
479 }
480 }
481
482
483 if (need_pacemakerd_api) {
484 rc = pcmk_new_ipc_api(&pacemakerd_api, pcmk_ipc_pacemakerd);
485 if (pacemakerd_api == NULL) {
486 fprintf(stderr, "error: Could not connect to pacemakerd: %s\n",
487 pcmk_rc_str(rc));
488 exit_code = pcmk_rc2exitc(rc);
489 goto done;
490 }
491 pcmk_register_ipc_callback(pacemakerd_api, pacemakerd_event_cb, NULL);
492 rc = pcmk_connect_ipc(pacemakerd_api, pcmk_ipc_dispatch_main);
493 if (rc != pcmk_rc_ok) {
494 fprintf(stderr, "error: Could not connect to pacemakerd: %s\n",
495 pcmk_rc_str(rc));
496 exit_code = pcmk_rc2exitc(rc);
497 goto done;
498 }
499 }
500
501 if (do_work(controld_api?controld_api:pacemakerd_api)) {
502
503 exit_code = CRM_EX_DISCONNECT;
504 mainloop = g_main_loop_new(NULL, FALSE);
505 message_timer_id = g_timeout_add(message_timeout_ms,
506 admin_message_timeout, NULL);
507 g_main_loop_run(mainloop);
508 }
509
510 done:
511
512 if (controld_api != NULL) {
513 pcmk_ipc_api_t *capi = controld_api;
514 controld_api = NULL;
515 pcmk_free_ipc_api(capi);
516 }
517
518 if (pacemakerd_api != NULL) {
519 pcmk_ipc_api_t *capi = pacemakerd_api;
520 pacemakerd_api = NULL;
521 pcmk_free_ipc_api(capi);
522 }
523
524 if (mainloop != NULL) {
525 g_main_loop_unref(mainloop);
526 mainloop = NULL;
527 }
528 g_strfreev(processed_args);
529 g_clear_error(&error);
530 pcmk__free_arg_context(context);
531 return crm_exit(exit_code);
532
533 }
534
535
536 bool
537 do_work(pcmk_ipc_api_t *api)
538 {
539 bool need_reply = false;
540 int rc = pcmk_rc_ok;
541
542 switch (command) {
543 case cmd_shutdown:
544 rc = pcmk_controld_api_shutdown(api, dest_node);
545 break;
546
547 case cmd_health:
548 case cmd_whois_dc:
549 rc = pcmk_controld_api_ping(api, dest_node);
550 need_reply = true;
551 break;
552
553 case cmd_elect_dc:
554 rc = pcmk_controld_api_start_election(api);
555 break;
556
557 case cmd_list_nodes:
558 rc = list_nodes();
559 break;
560
561 case cmd_pacemakerd_health:
562 rc = pcmk_pacemakerd_api_ping(api, ipc_name);
563 need_reply = true;
564 break;
565
566 case cmd_none:
567 break;
568 }
569 if (rc != pcmk_rc_ok) {
570 fprintf(stderr, "error: Command failed: %s", pcmk_rc_str(rc));
571 exit_code = pcmk_rc2exitc(rc);
572 }
573 return need_reply;
574 }
575
576 gboolean
577 admin_message_timeout(gpointer data)
578 {
579 fprintf(stderr,
580 "error: No reply received from controller before timeout (%dms)\n",
581 message_timeout_ms);
582 message_timer_id = 0;
583 quit_main_loop(CRM_EX_TIMEOUT);
584 return FALSE;
585 }
586
587 void
588 do_find_node_list(xmlNode * xml_node)
589 {
590 int found = 0;
591 xmlNode *node = NULL;
592 xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
593
594 for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL;
595 node = crm_next_same_xml(node)) {
596
597 if (BASH_EXPORT) {
598 printf("export %s=%s\n",
599 crm_element_value(node, XML_ATTR_UNAME),
600 crm_element_value(node, XML_ATTR_ID));
601 } else {
602 const char *node_type = crm_element_value(node, XML_ATTR_TYPE);
603
604 if (node_type == NULL) {
605 node_type = "member";
606 }
607 printf("%s node: %s (%s)\n", node_type,
608 crm_element_value(node, XML_ATTR_UNAME),
609 crm_element_value(node, XML_ATTR_ID));
610 }
611 found++;
612 }
613
614
615 if (found == 0) {
616 printf("No nodes configured\n");
617 }
618 }