This source file includes following definitions.
- blank_screen
- mon_timer_popped
- mon_cib_connection_destroy
- mon_shutdown
- mon_winresize
- cib_connect
- get_option_desc
- detect_user_input
- main
- count_resources
- print_simple_status
- print_nvpair
- print_node_start
- print_node_end
- print_resources_heading
- print_resources_closing
- print_resources
- print_rsc_history_start
- print_rsc_history_end
- print_op_history
- print_rsc_history
- print_node_history
- print_attr_msg
- compare_attribute
- create_attr_list
- print_node_attribute
- print_node_summary
- print_ticket
- print_cluster_tickets
- get_node_display_name
- print_ban
- print_neg_locations
- crm_mon_get_parameters
- print_node_attributes
- get_resource_display_options
- crm_now_string
- print_cluster_summary_header
- print_cluster_summary_footer
- print_cluster_times
- print_cluster_stack
- print_cluster_dc
- print_cluster_counts
- print_cluster_options
- get_cluster_stack
- print_cluster_summary
- print_failed_action
- print_failed_actions
- print_status
- print_xml_status
- print_html_status
- snmp_input
- crm_snmp_init
- send_snmp_trap
- print_recipient_status
- event_cb
- crm_smtp_debug
- send_custom_trap
- send_smtp_trap
- handle_rsc_op
- mon_trigger_refresh
- get_node_from_xpath
- crm_diff_update_v2
- crm_diff_update_v1
- crm_diff_update
- mon_refresh_display
- mon_st_callback
- clean_up
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include <crm_internal.h>
20
21 #include <sys/param.h>
22
23 #include <crm/crm.h>
24
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <libgen.h>
34 #include <sys/utsname.h>
35
36 #include <crm/msg_xml.h>
37 #include <crm/services.h>
38 #include <crm/lrmd.h>
39 #include <crm/common/internal.h>
40 #include <crm/common/ipc.h>
41 #include <crm/common/mainloop.h>
42 #include <crm/common/util.h>
43 #include <crm/common/xml.h>
44
45 #include <crm/cib/internal.h>
46 #include <crm/pengine/status.h>
47 #include <crm/pengine/internal.h>
48 #include <../lib/pengine/unpack.h>
49 #include <../pengine/pengine.h>
50 #include <crm/stonith-ng.h>
51
52 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
53
54 void clean_up(int rc);
55 void crm_diff_update(const char *event, xmlNode * msg);
56 gboolean mon_refresh_display(gpointer user_data);
57 int cib_connect(gboolean full);
58 void mon_st_callback(stonith_t * st, stonith_event_t * e);
59 static char *get_node_display_name(node_t *node);
60
61
62
63
64
65 #define mon_show_times (0x0001U)
66 #define mon_show_stack (0x0002U)
67 #define mon_show_dc (0x0004U)
68 #define mon_show_count (0x0008U)
69 #define mon_show_nodes (0x0010U)
70 #define mon_show_resources (0x0020U)
71 #define mon_show_attributes (0x0040U)
72 #define mon_show_failcounts (0x0080U)
73 #define mon_show_operations (0x0100U)
74 #define mon_show_tickets (0x0200U)
75 #define mon_show_bans (0x0400U)
76
77 #define mon_show_headers (mon_show_times | mon_show_stack | mon_show_dc | mon_show_count)
78 #define mon_show_default (mon_show_headers | mon_show_nodes | mon_show_resources)
79 #define mon_show_all (mon_show_default | mon_show_attributes | mon_show_failcounts \
80 | mon_show_operations | mon_show_tickets | mon_show_bans)
81
82 unsigned int show = mon_show_default;
83
84
85
86
87
88 enum mon_output_format_e {
89 mon_output_none,
90 mon_output_monitor,
91 mon_output_plain,
92 mon_output_console,
93 mon_output_xml,
94 mon_output_html,
95 mon_output_cgi
96 } output_format = mon_output_console;
97
98 char *output_filename = NULL;
99
100
101 char *xml_file = NULL;
102 char *pid_file = NULL;
103 char *snmp_target = NULL;
104 char *snmp_community = NULL;
105
106 gboolean group_by_node = FALSE;
107 gboolean inactive_resources = FALSE;
108 int reconnect_msec = 5000;
109 gboolean daemonize = FALSE;
110 GMainLoop *mainloop = NULL;
111 guint timer_id = 0;
112 GList *attr_list = NULL;
113
114 const char *crm_mail_host = NULL;
115 const char *crm_mail_prefix = NULL;
116 const char *crm_mail_from = NULL;
117 const char *crm_mail_to = NULL;
118 const char *external_agent = NULL;
119 const char *external_recipient = NULL;
120
121 cib_t *cib = NULL;
122 stonith_t *st = NULL;
123 xmlNode *current_cib = NULL;
124
125 gboolean one_shot = FALSE;
126 gboolean has_warnings = FALSE;
127 gboolean print_timing = FALSE;
128 gboolean watch_fencing = FALSE;
129 gboolean print_brief = FALSE;
130 gboolean print_pending = TRUE;
131 gboolean print_clone_detail = FALSE;
132
133
134 const char *print_neg_location_prefix = "";
135
136
137 #define FILTER_STR { CRM_FAIL_COUNT_PREFIX, CRM_LAST_FAILURE_PREFIX, \
138 "shutdown", "terminate", "standby", "probe_complete", \
139 "#", NULL }
140
141 long last_refresh = 0;
142 crm_trigger_t *refresh_trigger = NULL;
143
144
145
146
147
148 #define PACEMAKER_PREFIX "1.3.6.1.4.1.32723"
149 #define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1"
150
151 #define snmp_crm_trap_oid PACEMAKER_TRAP_PREFIX
152 #define snmp_crm_oid_node PACEMAKER_TRAP_PREFIX ".1"
153 #define snmp_crm_oid_rsc PACEMAKER_TRAP_PREFIX ".2"
154 #define snmp_crm_oid_task PACEMAKER_TRAP_PREFIX ".3"
155 #define snmp_crm_oid_desc PACEMAKER_TRAP_PREFIX ".4"
156 #define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5"
157 #define snmp_crm_oid_rc PACEMAKER_TRAP_PREFIX ".6"
158 #define snmp_crm_oid_trc PACEMAKER_TRAP_PREFIX ".7"
159
160
161 #define MON_STATUS_OK (0)
162 #define MON_STATUS_WARN (1)
163
164
165 #define s_if_plural(i) (((i) == 1)? "" : "s")
166
167 #if CURSES_ENABLED
168 # define print_dot() if (output_format == mon_output_console) { \
169 printw("."); \
170 clrtoeol(); \
171 refresh(); \
172 } else { \
173 fprintf(stdout, "."); \
174 }
175 #else
176 # define print_dot() fprintf(stdout, ".");
177 #endif
178
179 #if CURSES_ENABLED
180 # define print_as(fmt, args...) if (output_format == mon_output_console) { \
181 printw(fmt, ##args); \
182 clrtoeol(); \
183 refresh(); \
184 } else { \
185 fprintf(stdout, fmt, ##args); \
186 }
187 #else
188 # define print_as(fmt, args...) fprintf(stdout, fmt, ##args);
189 #endif
190
191 static void
192 blank_screen(void)
193 {
194 #if CURSES_ENABLED
195 int lpc = 0;
196
197 for (lpc = 0; lpc < LINES; lpc++) {
198 move(lpc, 0);
199 clrtoeol();
200 }
201 move(0, 0);
202 refresh();
203 #endif
204 }
205
206 static gboolean
207 mon_timer_popped(gpointer data)
208 {
209 int rc = pcmk_ok;
210
211 #if CURSES_ENABLED
212 if (output_format == mon_output_console) {
213 clear();
214 refresh();
215 }
216 #endif
217
218 if (timer_id > 0) {
219 g_source_remove(timer_id);
220 }
221
222 print_as("Reconnecting...\n");
223 rc = cib_connect(TRUE);
224
225 if (rc != pcmk_ok) {
226 timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
227 }
228 return FALSE;
229 }
230
231 static void
232 mon_cib_connection_destroy(gpointer user_data)
233 {
234 print_as("Connection to the CIB terminated\n");
235 if (cib) {
236 cib->cmds->signoff(cib);
237 timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
238 }
239 return;
240 }
241
242
243
244
245 static void
246 mon_shutdown(int nsig)
247 {
248 clean_up(EX_OK);
249 }
250
251 #if ON_DARWIN
252 # define sighandler_t sig_t
253 #endif
254
255 #if CURSES_ENABLED
256 # ifndef HAVE_SIGHANDLER_T
257 typedef void (*sighandler_t) (int);
258 # endif
259 static sighandler_t ncurses_winch_handler;
260 static void
261 mon_winresize(int nsig)
262 {
263 static int not_done;
264 int lines = 0, cols = 0;
265
266 if (!not_done++) {
267 if (ncurses_winch_handler)
268
269
270
271 (*ncurses_winch_handler) (SIGWINCH);
272 getmaxyx(stdscr, lines, cols);
273 resizeterm(lines, cols);
274 mainloop_set_trigger(refresh_trigger);
275 }
276 not_done--;
277 }
278 #endif
279
280 int
281 cib_connect(gboolean full)
282 {
283 int rc = pcmk_ok;
284 static gboolean need_pass = TRUE;
285
286 CRM_CHECK(cib != NULL, return -EINVAL);
287
288 if (getenv("CIB_passwd") != NULL) {
289 need_pass = FALSE;
290 }
291
292 if (watch_fencing && st == NULL) {
293 st = stonith_api_new();
294 }
295
296 if (watch_fencing && st->state == stonith_disconnected) {
297 crm_trace("Connecting to stonith");
298 rc = st->cmds->connect(st, crm_system_name, NULL);
299 if (rc == pcmk_ok) {
300 crm_trace("Setting up stonith callbacks");
301 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback);
302 }
303 }
304
305 if (cib->state != cib_connected_query && cib->state != cib_connected_command) {
306 crm_trace("Connecting to the CIB");
307 if ((output_format == mon_output_console) && need_pass && (cib->variant == cib_remote)) {
308 need_pass = FALSE;
309 print_as("Password:");
310 }
311
312 rc = cib->cmds->signon(cib, crm_system_name, cib_query);
313
314 if (rc != pcmk_ok) {
315 return rc;
316 }
317
318 rc = cib->cmds->query(cib, NULL, ¤t_cib, cib_scope_local | cib_sync_call);
319 if (rc == pcmk_ok) {
320 mon_refresh_display(NULL);
321 }
322
323 if (rc == pcmk_ok && full) {
324 if (rc == pcmk_ok) {
325 rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy);
326 if (rc == -EPROTONOSUPPORT) {
327 print_as
328 ("Notification setup not supported, won't be able to reconnect after failure");
329 if (output_format == mon_output_console) {
330 sleep(2);
331 }
332 rc = pcmk_ok;
333 }
334
335 }
336
337 if (rc == pcmk_ok) {
338 cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
339 rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
340 }
341
342 if (rc != pcmk_ok) {
343 print_as("Notification setup failed, could not monitor CIB actions");
344 if (output_format == mon_output_console) {
345 sleep(2);
346 }
347 clean_up(-rc);
348 }
349 }
350 }
351 return rc;
352 }
353
354
355 static struct crm_option long_options[] = {
356
357 {"help", 0, 0, '?', "\tThis text"},
358 {"version", 0, 0, '$', "\tVersion information" },
359 {"verbose", 0, 0, 'V', "\tIncrease debug output"},
360 {"quiet", 0, 0, 'Q', "\tDisplay only essential output" },
361
362 {"-spacer-", 1, 0, '-', "\nModes (mutually exclusive):"},
363 {"as-html", 1, 0, 'h', "\tWrite cluster status to the named html file"},
364 {"as-xml", 0, 0, 'X', "\t\tWrite cluster status as xml to stdout. This will enable one-shot mode."},
365 {"web-cgi", 0, 0, 'w', "\t\tWeb mode with output suitable for CGI (preselected when run as *.cgi)"},
366 {"simple-status", 0, 0, 's', "\tDisplay the cluster status once as a simple one line output (suitable for nagios)"},
367 {"snmp-traps", 1, 0, 'S', "\tSend SNMP traps to this station", !ENABLE_SNMP},
368 {"snmp-community", 1, 0, 'C', "Specify community for SNMP traps(default is NULL)", !ENABLE_SNMP},
369 {"mail-to", 1, 0, 'T', "\tSend Mail alerts to this user. See also --mail-from, --mail-host, --mail-prefix", !ENABLE_ESMTP},
370
371 {"-spacer-", 1, 0, '-', "\nDisplay Options:"},
372 {"group-by-node", 0, 0, 'n', "\tGroup resources by node" },
373 {"inactive", 0, 0, 'r', "\t\tDisplay inactive resources" },
374 {"failcounts", 0, 0, 'f', "\tDisplay resource fail counts"},
375 {"operations", 0, 0, 'o', "\tDisplay resource operation history" },
376 {"timing-details", 0, 0, 't', "\tDisplay resource operation history with timing details" },
377 {"tickets", 0, 0, 'c', "\t\tDisplay cluster tickets"},
378 {"watch-fencing", 0, 0, 'W', "\tListen for fencing events. For use with --external-agent, --mail-to and/or --snmp-traps where supported"},
379 {"neg-locations", 2, 0, 'L', "Display negative location constraints [optionally filtered by id prefix]"},
380 {"show-node-attributes", 0, 0, 'A', "Display node attributes" },
381 {"hide-headers", 0, 0, 'D', "\tHide all headers" },
382 {"show-detail", 0, 0, 'R', "\tShow more details (node IDs, individual clone instances)" },
383 {"brief", 0, 0, 'b', "\t\tBrief output" },
384 {"pending", 0, 0, 'j', "\t\tDisplay pending state if 'record-pending' is enabled", pcmk_option_hidden},
385
386 {"-spacer-", 1, 0, '-', "\nAdditional Options:"},
387 {"interval", 1, 0, 'i', "\tUpdate frequency in seconds" },
388 {"one-shot", 0, 0, '1', "\t\tDisplay the cluster status once on the console and exit"},
389 {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses", !CURSES_ENABLED},
390 {"daemonize", 0, 0, 'd', "\tRun in the background as a daemon"},
391 {"pid-file", 1, 0, 'p', "\t(Advanced) Daemon pid file location"},
392 {"mail-from", 1, 0, 'F', "\tMail alerts should come from the named user", !ENABLE_ESMTP},
393 {"mail-host", 1, 0, 'H', "\tMail alerts should be sent via the named host", !ENABLE_ESMTP},
394 {"mail-prefix", 1, 0, 'P', "Subjects for mail alerts should start with this string", !ENABLE_ESMTP},
395 {"external-agent", 1, 0, 'E', "A program to run when resource operations take place."},
396 {"external-recipient",1, 0, 'e', "A recipient for your program (assuming you want the program to send something to someone)."},
397
398
399 {"xml-file", 1, 0, 'x', NULL, pcmk_option_hidden},
400
401 {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph},
402 {"-spacer-", 1, 0, '-', "Display the cluster status on the console with updates as they occur:", pcmk_option_paragraph},
403 {"-spacer-", 1, 0, '-', " crm_mon", pcmk_option_example},
404 {"-spacer-", 1, 0, '-', "Display the cluster status on the console just once then exit:", pcmk_option_paragraph},
405 {"-spacer-", 1, 0, '-', " crm_mon -1", pcmk_option_example},
406 {"-spacer-", 1, 0, '-', "Display your cluster status, group resources by node, and include inactive resources in the list:", pcmk_option_paragraph},
407 {"-spacer-", 1, 0, '-', " crm_mon --group-by-node --inactive", pcmk_option_example},
408 {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it write the cluster status to an HTML file:", pcmk_option_paragraph},
409 {"-spacer-", 1, 0, '-', " crm_mon --daemonize --as-html /path/to/docroot/filename.html", pcmk_option_example},
410 {"-spacer-", 1, 0, '-', "Start crm_mon and export the current cluster status as xml to stdout, then exit.:", pcmk_option_paragraph},
411 {"-spacer-", 1, 0, '-', " crm_mon --as-xml", pcmk_option_example},
412 {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send email alerts:", pcmk_option_paragraph|!ENABLE_ESMTP},
413 {"-spacer-", 1, 0, '-', " crm_mon --daemonize --mail-to user@example.com --mail-host mail.example.com", pcmk_option_example|!ENABLE_ESMTP},
414 {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send SNMP alerts:", pcmk_option_paragraph|!ENABLE_SNMP},
415 {"-spacer-", 1, 0, '-', " crm_mon --daemonize --snmp-traps snmptrapd.example.com", pcmk_option_example|!ENABLE_SNMP},
416
417 {NULL, 0, 0, 0}
418 };
419
420
421 #if CURSES_ENABLED
422 static const char *
423 get_option_desc(char c)
424 {
425 int lpc;
426
427 for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
428
429 if (long_options[lpc].name[0] == '-')
430 continue;
431
432 if (long_options[lpc].val == c) {
433 const char * tab = NULL;
434 tab = strrchr(long_options[lpc].desc, '\t');
435 return tab ? ++tab : long_options[lpc].desc;
436 }
437 }
438
439 return NULL;
440 }
441
442 #define print_option_help(option, condition) \
443 print_as("%c %c: \t%s\n", ((condition)? '*': ' '), option, get_option_desc(option));
444
445 static gboolean
446 detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer unused)
447 {
448 int c;
449 gboolean config_mode = FALSE;
450
451 while (1) {
452
453
454 c = getchar();
455
456 switch (c) {
457 case 'c':
458 show ^= mon_show_tickets;
459 break;
460 case 'f':
461 show ^= mon_show_failcounts;
462 break;
463 case 'n':
464 group_by_node = ! group_by_node;
465 break;
466 case 'o':
467 show ^= mon_show_operations;
468 if ((show & mon_show_operations) == 0) {
469 print_timing = 0;
470 }
471 break;
472 case 'r':
473 inactive_resources = ! inactive_resources;
474 break;
475 case 'R':
476 print_clone_detail = ! print_clone_detail;
477 break;
478 case 't':
479 print_timing = ! print_timing;
480 if (print_timing) {
481 show |= mon_show_operations;
482 }
483 break;
484 case 'A':
485 show ^= mon_show_attributes;
486 break;
487 case 'L':
488 show ^= mon_show_bans;
489 break;
490 case 'D':
491
492 if (show & mon_show_headers) {
493 show &= ~mon_show_headers;
494 } else {
495 show |= mon_show_headers;
496 }
497 break;
498 case 'b':
499 print_brief = ! print_brief;
500 break;
501 case 'j':
502 print_pending = ! print_pending;
503 break;
504 case '?':
505 config_mode = TRUE;
506 break;
507 default:
508 goto refresh;
509 }
510
511 if (!config_mode)
512 goto refresh;
513
514 blank_screen();
515
516 print_as("Display option change mode\n");
517 print_as("\n");
518 print_option_help('c', show & mon_show_tickets);
519 print_option_help('f', show & mon_show_failcounts);
520 print_option_help('n', group_by_node);
521 print_option_help('o', show & mon_show_operations);
522 print_option_help('r', inactive_resources);
523 print_option_help('t', print_timing);
524 print_option_help('A', show & mon_show_attributes);
525 print_option_help('L', show & mon_show_bans);
526 print_option_help('D', (show & mon_show_headers) == 0);
527 print_option_help('R', print_clone_detail);
528 print_option_help('b', print_brief);
529 print_option_help('j', print_pending);
530 print_as("\n");
531 print_as("Toggle fields via field letter, type any other key to return");
532 }
533
534 refresh:
535 mon_refresh_display(NULL);
536 return TRUE;
537 }
538 #endif
539
540 int
541 main(int argc, char **argv)
542 {
543 int flag;
544 int argerr = 0;
545 int exit_code = 0;
546 int option_index = 0;
547
548 pid_file = strdup("/tmp/ClusterMon.pid");
549 crm_log_cli_init("crm_mon");
550 crm_set_options(NULL, "mode [options]", long_options,
551 "Provides a summary of cluster's current state."
552 "\n\nOutputs varying levels of detail in a number of different formats.\n");
553
554 #if !defined (ON_DARWIN) && !defined (ON_BSD)
555
556 signal(SIGCLD, SIG_IGN);
557 #endif
558
559 if (crm_ends_with_ext(argv[0], ".cgi") == TRUE) {
560 output_format = mon_output_cgi;
561 one_shot = TRUE;
562 }
563
564 while (1) {
565 flag = crm_get_option(argc, argv, &option_index);
566 if (flag == -1)
567 break;
568
569 switch (flag) {
570 case 'V':
571 crm_bump_log_level(argc, argv);
572 break;
573 case 'Q':
574 show &= ~mon_show_times;
575 break;
576 case 'i':
577 reconnect_msec = crm_get_msec(optarg);
578 break;
579 case 'n':
580 group_by_node = TRUE;
581 break;
582 case 'r':
583 inactive_resources = TRUE;
584 break;
585 case 'W':
586 watch_fencing = TRUE;
587 break;
588 case 'd':
589 daemonize = TRUE;
590 break;
591 case 't':
592 print_timing = TRUE;
593 show |= mon_show_operations;
594 break;
595 case 'o':
596 show |= mon_show_operations;
597 break;
598 case 'f':
599 show |= mon_show_failcounts;
600 break;
601 case 'A':
602 show |= mon_show_attributes;
603 break;
604 case 'L':
605 show |= mon_show_bans;
606 print_neg_location_prefix = optarg? optarg : "";
607 break;
608 case 'D':
609 show &= ~mon_show_headers;
610 break;
611 case 'b':
612 print_brief = TRUE;
613 break;
614 case 'j':
615 print_pending = TRUE;
616 break;
617 case 'R':
618 print_clone_detail = TRUE;
619 break;
620 case 'c':
621 show |= mon_show_tickets;
622 break;
623 case 'p':
624 free(pid_file);
625 if(optarg == NULL) {
626 return crm_help(flag, EX_USAGE);
627 }
628 pid_file = strdup(optarg);
629 break;
630 case 'x':
631 if(optarg == NULL) {
632 return crm_help(flag, EX_USAGE);
633 }
634 xml_file = strdup(optarg);
635 one_shot = TRUE;
636 break;
637 case 'h':
638 if(optarg == NULL) {
639 return crm_help(flag, EX_USAGE);
640 }
641 argerr += (output_format != mon_output_console);
642 output_format = mon_output_html;
643 output_filename = strdup(optarg);
644 umask(S_IWGRP | S_IWOTH);
645 break;
646 case 'X':
647 argerr += (output_format != mon_output_console);
648 output_format = mon_output_xml;
649 one_shot = TRUE;
650 break;
651 case 'w':
652
653 argerr += (output_format != mon_output_console);
654 output_format = mon_output_cgi;
655 one_shot = TRUE;
656 break;
657 case 's':
658 argerr += (output_format != mon_output_console);
659 output_format = mon_output_monitor;
660 one_shot = TRUE;
661 break;
662 case 'S':
663 snmp_target = optarg;
664 break;
665 case 'T':
666 crm_mail_to = optarg;
667 break;
668 case 'F':
669 crm_mail_from = optarg;
670 break;
671 case 'H':
672 crm_mail_host = optarg;
673 break;
674 case 'P':
675 crm_mail_prefix = optarg;
676 break;
677 case 'E':
678 external_agent = optarg;
679 break;
680 case 'e':
681 external_recipient = optarg;
682 break;
683 case '1':
684 one_shot = TRUE;
685 break;
686 case 'N':
687 if (output_format == mon_output_console) {
688 output_format = mon_output_plain;
689 }
690 break;
691 case 'C':
692 snmp_community = optarg;
693 break;
694 case '$':
695 case '?':
696 return crm_help(flag, EX_OK);
697 break;
698 default:
699 printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
700 ++argerr;
701 break;
702 }
703 }
704
705
706 if (output_format == mon_output_cgi) {
707 argerr += (optind < argc);
708 argerr += (output_filename != NULL);
709 argerr += (xml_file != NULL);
710 argerr += (snmp_target != NULL);
711 argerr += (crm_mail_to != NULL);
712 argerr += (external_agent != NULL);
713 argerr += (daemonize == TRUE);
714
715 } else if (optind < argc) {
716 printf("non-option ARGV-elements: ");
717 while (optind < argc)
718 printf("%s ", argv[optind++]);
719 printf("\n");
720 }
721
722 if (argerr) {
723 if (output_format == mon_output_cgi) {
724 fprintf(stdout, "Content-Type: text/plain\n"
725 "Status: 500\n\n");
726 return EX_USAGE;
727 }
728 return crm_help('?', EX_USAGE);
729 }
730
731
732 if (output_format == mon_output_xml) {
733 show = mon_show_all;
734 print_timing = TRUE;
735 }
736
737 if (one_shot) {
738 if (output_format == mon_output_console) {
739 output_format = mon_output_plain;
740 }
741
742 } else if (daemonize) {
743 if ((output_format == mon_output_console) || (output_format == mon_output_plain)) {
744 output_format = mon_output_none;
745 }
746 crm_enable_stderr(FALSE);
747
748 if ((output_format != mon_output_html) && (output_format != mon_output_xml)
749 && !snmp_target && !crm_mail_to && !external_agent) {
750 printf
751 ("Looks like you forgot to specify one or more of: --as-html, --as-xml, --mail-to, --snmp-target, --external-agent\n");
752 return crm_help('?', EX_USAGE);
753 }
754
755 crm_make_daemon(crm_system_name, TRUE, pid_file);
756
757 } else if (output_format == mon_output_console) {
758 #if CURSES_ENABLED
759 initscr();
760 cbreak();
761 noecho();
762 crm_enable_stderr(FALSE);
763 #else
764 one_shot = TRUE;
765 output_format = mon_output_plain;
766 printf("Defaulting to one-shot mode\n");
767 printf("You need to have curses available at compile time to enable console mode\n");
768 #endif
769 }
770
771 crm_info("Starting %s", crm_system_name);
772 if (xml_file != NULL) {
773 current_cib = filename2xml(xml_file);
774 mon_refresh_display(NULL);
775 return exit_code;
776 }
777
778 if (current_cib == NULL) {
779 cib = cib_new();
780
781 do {
782 if (!one_shot) {
783 print_as("Attempting connection to the cluster...\n");
784 }
785 exit_code = cib_connect(!one_shot);
786
787 if (one_shot) {
788 break;
789
790 } else if (exit_code != pcmk_ok) {
791 sleep(reconnect_msec / 1000);
792 #if CURSES_ENABLED
793 if (output_format == mon_output_console) {
794 clear();
795 refresh();
796 }
797 #endif
798 }
799
800 } while (exit_code == -ENOTCONN);
801
802 if (exit_code != pcmk_ok) {
803 if (output_format == mon_output_monitor) {
804 printf("CLUSTER WARN: Connection to cluster failed: %s\n", pcmk_strerror(exit_code));
805 clean_up(MON_STATUS_WARN);
806 } else {
807 print_as("\nConnection to cluster failed: %s\n", pcmk_strerror(exit_code));
808 }
809 if (output_format == mon_output_console) {
810 sleep(2);
811 }
812 clean_up(-exit_code);
813 }
814 }
815
816 if (one_shot) {
817 return exit_code;
818 }
819
820 mainloop = g_main_new(FALSE);
821
822 mainloop_add_signal(SIGTERM, mon_shutdown);
823 mainloop_add_signal(SIGINT, mon_shutdown);
824 #if CURSES_ENABLED
825 if (output_format == mon_output_console) {
826 ncurses_winch_handler = signal(SIGWINCH, mon_winresize);
827 if (ncurses_winch_handler == SIG_DFL ||
828 ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR)
829 ncurses_winch_handler = NULL;
830 g_io_add_watch(g_io_channel_unix_new(STDIN_FILENO), G_IO_IN, detect_user_input, NULL);
831 }
832 #endif
833 refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL);
834
835 g_main_run(mainloop);
836 g_main_destroy(mainloop);
837
838 crm_info("Exiting %s", crm_system_name);
839
840 clean_up(0);
841 return 0;
842 }
843
844 #define mon_warn(fmt...) do { \
845 if (!has_warnings) { \
846 print_as("CLUSTER WARN:"); \
847 } else { \
848 print_as(","); \
849 } \
850 print_as(fmt); \
851 has_warnings = TRUE; \
852 } while(0)
853
854 static int
855 count_resources(pe_working_set_t * data_set, resource_t * rsc)
856 {
857 int count = 0;
858 GListPtr gIter = NULL;
859
860 if (rsc == NULL) {
861 gIter = data_set->resources;
862 } else if (rsc->children) {
863 gIter = rsc->children;
864 } else {
865 return is_not_set(rsc->flags, pe_rsc_orphan);
866 }
867
868 for (; gIter != NULL; gIter = gIter->next) {
869 count += count_resources(data_set, gIter->data);
870 }
871 return count;
872 }
873
874
875
876
877
878
879
880
881
882
883 static void
884 print_simple_status(pe_working_set_t * data_set)
885 {
886 GListPtr gIter = NULL;
887 int nodes_online = 0;
888 int nodes_standby = 0;
889 int nodes_maintenance = 0;
890
891 if (data_set->dc_node == NULL) {
892 mon_warn(" No DC");
893 }
894
895 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
896 node_t *node = (node_t *) gIter->data;
897
898 if (node->details->standby && node->details->online) {
899 nodes_standby++;
900 } else if (node->details->maintenance && node->details->online) {
901 nodes_maintenance++;
902 } else if (node->details->online) {
903 nodes_online++;
904 } else {
905 mon_warn(" offline node: %s", node->details->uname);
906 }
907 }
908
909 if (!has_warnings) {
910 int nresources = count_resources(data_set, NULL);
911
912 print_as("CLUSTER OK: %d node%s online", nodes_online, s_if_plural(nodes_online));
913 if (nodes_standby > 0) {
914 print_as(", %d standby node%s", nodes_standby, s_if_plural(nodes_standby));
915 }
916 if (nodes_maintenance > 0) {
917 print_as(", %d maintenance node%s", nodes_maintenance, s_if_plural(nodes_maintenance));
918 }
919 print_as(", %d resource%s configured", nresources, s_if_plural(nresources));
920 }
921
922 print_as("\n");
923 }
924
925
926
927
928
929
930
931
932
933
934
935 static void
936 print_nvpair(FILE *stream, const char *name, const char *value,
937 const char *units, time_t epoch_time)
938 {
939
940 switch (output_format) {
941 case mon_output_plain:
942 case mon_output_console:
943 print_as(" %s=", name);
944 break;
945
946 case mon_output_html:
947 case mon_output_cgi:
948 case mon_output_xml:
949 fprintf(stream, " %s=", name);
950 break;
951
952 default:
953 break;
954 }
955
956
957 if (value) {
958 switch (output_format) {
959 case mon_output_plain:
960 case mon_output_console:
961 print_as("%s%s", value, (units? units : ""));
962 break;
963
964 case mon_output_html:
965 case mon_output_cgi:
966 fprintf(stream, "%s%s", value, (units? units : ""));
967 break;
968
969 case mon_output_xml:
970 fprintf(stream, "\"%s%s\"", value, (units? units : ""));
971 break;
972
973 default:
974 break;
975 }
976
977
978 } else {
979 static char empty_str[] = "";
980 char *c, *date_str = asctime(localtime(&epoch_time));
981
982 for (c = (date_str != NULL) ? date_str : empty_str; *c != '\0'; ++c) {
983 if (*c == '\n') {
984 *c = '\0';
985 break;
986 }
987 }
988 switch (output_format) {
989 case mon_output_plain:
990 case mon_output_console:
991 print_as("'%s'", date_str);
992 break;
993
994 case mon_output_html:
995 case mon_output_cgi:
996 case mon_output_xml:
997 fprintf(stream, "\"%s\"", date_str);
998 break;
999
1000 default:
1001 break;
1002 }
1003 }
1004 }
1005
1006
1007
1008
1009
1010
1011
1012
1013 static void
1014 print_node_start(FILE *stream, node_t *node)
1015 {
1016 char *node_name;
1017
1018 switch (output_format) {
1019 case mon_output_plain:
1020 case mon_output_console:
1021 node_name = get_node_display_name(node);
1022 print_as("* Node %s:\n", node_name);
1023 free(node_name);
1024 break;
1025
1026 case mon_output_html:
1027 case mon_output_cgi:
1028 node_name = get_node_display_name(node);
1029 fprintf(stream, " <h3>Node: %s</h3>\n <ul>\n", node_name);
1030 free(node_name);
1031 break;
1032
1033 case mon_output_xml:
1034 fprintf(stream, " <node name=\"%s\">\n", node->details->uname);
1035 break;
1036
1037 default:
1038 break;
1039 }
1040 }
1041
1042
1043
1044
1045
1046
1047
1048 static void
1049 print_node_end(FILE *stream)
1050 {
1051 switch (output_format) {
1052 case mon_output_html:
1053 case mon_output_cgi:
1054 fprintf(stream, " </ul>\n");
1055 break;
1056
1057 case mon_output_xml:
1058 fprintf(stream, " </node>\n");
1059 break;
1060
1061 default:
1062 break;
1063 }
1064 }
1065
1066
1067
1068
1069
1070
1071
1072 static void
1073 print_resources_heading(FILE *stream)
1074 {
1075 const char *heading;
1076
1077 if (group_by_node) {
1078
1079
1080 heading = (inactive_resources? "Inactive resources" : NULL);
1081
1082 } else if (inactive_resources) {
1083 heading = "Full list of resources";
1084
1085 } else {
1086 heading = "Active resources";
1087 }
1088
1089
1090 switch (output_format) {
1091 case mon_output_plain:
1092 case mon_output_console:
1093 print_as("\n%s:\n\n", heading);
1094 break;
1095
1096 case mon_output_html:
1097 case mon_output_cgi:
1098 fprintf(stream, " <hr />\n <h2>%s</h2>\n", heading);
1099 break;
1100
1101 case mon_output_xml:
1102 fprintf(stream, " <resources>\n");
1103 break;
1104
1105 default:
1106 break;
1107 }
1108
1109 }
1110
1111
1112
1113
1114
1115
1116
1117 static void
1118 print_resources_closing(FILE *stream, gboolean printed_heading)
1119 {
1120 const char *heading;
1121
1122
1123 if (group_by_node) {
1124 heading = "inactive ";
1125 } else if (inactive_resources) {
1126 heading = "";
1127 } else {
1128 heading = "active ";
1129 }
1130
1131 switch (output_format) {
1132 case mon_output_plain:
1133 case mon_output_console:
1134 if (!printed_heading) {
1135 print_as("\nNo %sresources\n\n", heading);
1136 }
1137 break;
1138
1139 case mon_output_html:
1140 case mon_output_cgi:
1141 if (!printed_heading) {
1142 fprintf(stream, " <hr />\n <h2>No %sresources</h2>\n", heading);
1143 }
1144 break;
1145
1146 case mon_output_xml:
1147 fprintf(stream, " %s\n",
1148 (printed_heading? "</resources>" : "<resources/>"));
1149 break;
1150
1151 default:
1152 break;
1153 }
1154 }
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164 static void
1165 print_resources(FILE *stream, pe_working_set_t *data_set, int print_opts)
1166 {
1167 GListPtr rsc_iter;
1168 const char *prefix = NULL;
1169 gboolean printed_heading = FALSE;
1170 gboolean brief_output = print_brief;
1171
1172
1173
1174
1175 if (group_by_node && !inactive_resources) {
1176 return;
1177 }
1178
1179
1180 if (output_format == mon_output_xml) {
1181 prefix = " ";
1182 brief_output = FALSE;
1183 }
1184
1185
1186
1187 if (brief_output && !group_by_node) {
1188 print_resources_heading(stream);
1189 printed_heading = TRUE;
1190 print_rscs_brief(data_set->resources, NULL, print_opts, stream,
1191 inactive_resources);
1192 }
1193
1194
1195 for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
1196 resource_t *rsc = (resource_t *) rsc_iter->data;
1197
1198
1199 gboolean is_active = rsc->fns->active(rsc, TRUE);
1200 gboolean partially_active = rsc->fns->active(rsc, FALSE);
1201
1202
1203 if (is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
1204 continue;
1205
1206
1207 } else if (group_by_node) {
1208 if (is_active) {
1209 continue;
1210 }
1211
1212
1213 } else if (brief_output && (rsc->variant == pe_native)) {
1214 continue;
1215
1216
1217
1218
1219 } else if (!partially_active && !inactive_resources) {
1220 continue;
1221 }
1222
1223
1224 if (printed_heading == FALSE) {
1225 print_resources_heading(stream);
1226 printed_heading = TRUE;
1227 }
1228 rsc->fns->print(rsc, prefix, print_opts, stream);
1229 }
1230
1231 print_resources_closing(stream, printed_heading);
1232 }
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245 static void
1246 print_rsc_history_start(FILE *stream, pe_working_set_t *data_set, node_t *node,
1247 resource_t *rsc, const char *rsc_id, gboolean all)
1248 {
1249 time_t last_failure = 0;
1250 int failcount = rsc?
1251 pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
1252 NULL, data_set)
1253 : 0;
1254
1255 if (!all && !failcount && (last_failure <= 0)) {
1256 return;
1257 }
1258
1259
1260 switch (output_format) {
1261 case mon_output_plain:
1262 case mon_output_console:
1263 print_as(" %s:", rsc_id);
1264 break;
1265
1266 case mon_output_html:
1267 case mon_output_cgi:
1268 fprintf(stream, " <li>%s:", rsc_id);
1269 break;
1270
1271 case mon_output_xml:
1272 fprintf(stream, " <resource_history id=\"%s\"", rsc_id);
1273 break;
1274
1275 default:
1276 break;
1277 }
1278
1279
1280 if (rsc == NULL) {
1281 switch (output_format) {
1282 case mon_output_plain:
1283 case mon_output_console:
1284 print_as(" orphan");
1285 break;
1286
1287 case mon_output_html:
1288 case mon_output_cgi:
1289 fprintf(stream, " orphan");
1290 break;
1291
1292 case mon_output_xml:
1293 fprintf(stream, " orphan=\"true\"");
1294 break;
1295
1296 default:
1297 break;
1298 }
1299
1300
1301 } else if (all || failcount || (last_failure > 0)) {
1302
1303
1304 switch (output_format) {
1305 case mon_output_plain:
1306 case mon_output_console:
1307 print_as(" migration-threshold=%d", rsc->migration_threshold);
1308 break;
1309
1310 case mon_output_html:
1311 case mon_output_cgi:
1312 fprintf(stream, " migration-threshold=%d", rsc->migration_threshold);
1313 break;
1314
1315 case mon_output_xml:
1316 fprintf(stream, " orphan=\"false\" migration-threshold=\"%d\"",
1317 rsc->migration_threshold);
1318 break;
1319
1320 default:
1321 break;
1322 }
1323
1324
1325 if (failcount > 0) {
1326 switch (output_format) {
1327 case mon_output_plain:
1328 case mon_output_console:
1329 print_as(" " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
1330 break;
1331
1332 case mon_output_html:
1333 case mon_output_cgi:
1334 fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
1335 break;
1336
1337 case mon_output_xml:
1338 fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=\"%d\"",
1339 failcount);
1340 break;
1341
1342 default:
1343 break;
1344 }
1345 }
1346
1347
1348 if (last_failure > 0) {
1349 print_nvpair(stream, CRM_LAST_FAILURE_PREFIX, NULL, NULL,
1350 last_failure);
1351 }
1352 }
1353
1354
1355 switch (output_format) {
1356 case mon_output_plain:
1357 case mon_output_console:
1358 print_as("\n");
1359 break;
1360
1361 case mon_output_html:
1362 case mon_output_cgi:
1363 fprintf(stream, "\n <ul>\n");
1364 break;
1365
1366 case mon_output_xml:
1367 fprintf(stream, ">\n");
1368 break;
1369
1370 default:
1371 break;
1372 }
1373 }
1374
1375
1376
1377
1378
1379
1380
1381 static void
1382 print_rsc_history_end(FILE *stream)
1383 {
1384 switch (output_format) {
1385 case mon_output_html:
1386 case mon_output_cgi:
1387 fprintf(stream, " </ul>\n </li>\n");
1388 break;
1389
1390 case mon_output_xml:
1391 fprintf(stream, " </resource_history>\n");
1392 break;
1393
1394 default:
1395 break;
1396 }
1397 }
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411 static void
1412 print_op_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
1413 xmlNode *xml_op, const char *task, const char *interval, int rc)
1414 {
1415 const char *value = NULL;
1416 const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
1417
1418
1419 switch (output_format) {
1420 case mon_output_plain:
1421 case mon_output_console:
1422 print_as(" + (%s) %s:", call, task);
1423 break;
1424
1425 case mon_output_html:
1426 case mon_output_cgi:
1427 fprintf(stream, " <li>(%s) %s:", call, task);
1428 break;
1429
1430 case mon_output_xml:
1431 fprintf(stream, " <operation_history call=\"%s\" task=\"%s\"",
1432 call, task);
1433 break;
1434
1435 default:
1436 break;
1437 }
1438
1439
1440 if (safe_str_neq(interval, "0")) {
1441 print_nvpair(stream, "interval", interval, "ms", 0);
1442 }
1443 if (print_timing) {
1444 int int_value;
1445 const char *attr;
1446
1447 attr = XML_RSC_OP_LAST_CHANGE;
1448 value = crm_element_value(xml_op, attr);
1449 if (value) {
1450 int_value = crm_parse_int(value, NULL);
1451 if (int_value > 0) {
1452 print_nvpair(stream, attr, NULL, NULL, int_value);
1453 }
1454 }
1455
1456 attr = XML_RSC_OP_LAST_RUN;
1457 value = crm_element_value(xml_op, attr);
1458 if (value) {
1459 int_value = crm_parse_int(value, NULL);
1460 if (int_value > 0) {
1461 print_nvpair(stream, attr, NULL, NULL, int_value);
1462 }
1463 }
1464
1465 attr = XML_RSC_OP_T_EXEC;
1466 value = crm_element_value(xml_op, attr);
1467 if (value) {
1468 print_nvpair(stream, attr, value, "ms", 0);
1469 }
1470
1471 attr = XML_RSC_OP_T_QUEUE;
1472 value = crm_element_value(xml_op, attr);
1473 if (value) {
1474 print_nvpair(stream, attr, value, "ms", 0);
1475 }
1476 }
1477
1478
1479 switch (output_format) {
1480 case mon_output_plain:
1481 case mon_output_console:
1482 print_as(" rc=%d (%s)\n", rc, services_ocf_exitcode_str(rc));
1483 break;
1484
1485 case mon_output_html:
1486 case mon_output_cgi:
1487 fprintf(stream, " rc=%d (%s)</li>\n", rc, services_ocf_exitcode_str(rc));
1488 break;
1489
1490 case mon_output_xml:
1491 fprintf(stream, " rc=\"%d\" rc_text=\"%s\" />\n", rc, services_ocf_exitcode_str(rc));
1492 break;
1493
1494 default:
1495 break;
1496 }
1497 }
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509 static void
1510 print_rsc_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
1511 xmlNode *rsc_entry, gboolean operations)
1512 {
1513 GListPtr gIter = NULL;
1514 GListPtr op_list = NULL;
1515 gboolean printed = FALSE;
1516 const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
1517 resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
1518 xmlNode *rsc_op = NULL;
1519
1520
1521 if (operations == FALSE) {
1522 print_rsc_history_start(stream, data_set, node, rsc, rsc_id, FALSE);
1523 print_rsc_history_end(stream);
1524 return;
1525 }
1526
1527
1528 for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
1529 if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
1530 op_list = g_list_append(op_list, rsc_op);
1531 }
1532 }
1533 op_list = g_list_sort(op_list, sort_op_by_callid);
1534
1535
1536 for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
1537 xmlNode *xml_op = (xmlNode *) gIter->data;
1538 const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
1539 const char *interval = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
1540 const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
1541 int rc = crm_parse_int(op_rc, "0");
1542
1543
1544 if (safe_str_eq(task, CRMD_ACTION_STATUS) && safe_str_eq(interval, "0")) {
1545 task = "probe";
1546 }
1547
1548
1549 if (safe_str_eq(task, CRMD_ACTION_NOTIFY) || (safe_str_eq(task, "probe") && (rc == 7))) {
1550 continue;
1551 }
1552
1553
1554 if (printed == FALSE) {
1555 printed = TRUE;
1556 print_rsc_history_start(stream, data_set, node, rsc, rsc_id, TRUE);
1557 }
1558
1559
1560 print_op_history(stream, data_set, node, xml_op, task, interval, rc);
1561 }
1562
1563
1564 g_list_free(op_list);
1565
1566
1567 if (printed) {
1568 print_rsc_history_end(stream);
1569 }
1570 }
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581 static void
1582 print_node_history(FILE *stream, pe_working_set_t *data_set,
1583 xmlNode *node_state, gboolean operations)
1584 {
1585 node_t *node = pe_find_node_id(data_set->nodes, ID(node_state));
1586 xmlNode *lrm_rsc = NULL;
1587 xmlNode *rsc_entry = NULL;
1588
1589 if (node && node->details && node->details->online) {
1590 print_node_start(stream, node);
1591
1592 lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
1593 lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
1594
1595
1596 for (rsc_entry = __xml_first_child(lrm_rsc); rsc_entry != NULL;
1597 rsc_entry = __xml_next(rsc_entry)) {
1598
1599 if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
1600 print_rsc_history(stream, data_set, node, rsc_entry, operations);
1601 }
1602 }
1603
1604 print_node_end(stream);
1605 }
1606 }
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619 static gboolean
1620 print_attr_msg(FILE *stream, node_t * node, GListPtr rsc_list, const char *attrname, const char *attrvalue)
1621 {
1622 GListPtr gIter = NULL;
1623
1624 for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
1625 resource_t *rsc = (resource_t *) gIter->data;
1626 const char *type = g_hash_table_lookup(rsc->meta, "type");
1627
1628 if (rsc->children != NULL) {
1629 if (print_attr_msg(stream, node, rsc->children, attrname, attrvalue)) {
1630 return TRUE;
1631 }
1632 }
1633
1634 if (safe_str_eq(type, "ping") || safe_str_eq(type, "pingd")) {
1635 const char *name = g_hash_table_lookup(rsc->parameters, "name");
1636
1637 if (name == NULL) {
1638 name = "pingd";
1639 }
1640
1641
1642 if (safe_str_eq(name, attrname)) {
1643 int host_list_num = 0;
1644 int expected_score = 0;
1645 int value = crm_parse_int(attrvalue, "0");
1646 const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list");
1647 const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier");
1648
1649 if(hosts) {
1650 char **host_list = g_strsplit(hosts, " ", 0);
1651 host_list_num = g_strv_length(host_list);
1652 g_strfreev(host_list);
1653 }
1654
1655
1656 expected_score = host_list_num * crm_parse_int(multiplier, "1");
1657
1658 switch (output_format) {
1659 case mon_output_plain:
1660 case mon_output_console:
1661 if (value <= 0) {
1662 print_as("\t: Connectivity is lost");
1663 } else if (value < expected_score) {
1664 print_as("\t: Connectivity is degraded (Expected=%d)", expected_score);
1665 }
1666 break;
1667
1668 case mon_output_html:
1669 case mon_output_cgi:
1670 if (value <= 0) {
1671 fprintf(stream, " <b>(connectivity is lost)</b>");
1672 } else if (value < expected_score) {
1673 fprintf(stream, " <b>(connectivity is degraded -- expected %d)</b>",
1674 expected_score);
1675 }
1676 break;
1677
1678 case mon_output_xml:
1679 fprintf(stream, " expected=\"%d\"", expected_score);
1680 break;
1681
1682 default:
1683 break;
1684 }
1685 return TRUE;
1686 }
1687 }
1688 }
1689 return FALSE;
1690 }
1691
1692 static int
1693 compare_attribute(gconstpointer a, gconstpointer b)
1694 {
1695 int rc;
1696
1697 rc = strcmp((const char *)a, (const char *)b);
1698
1699 return rc;
1700 }
1701
1702 static void
1703 create_attr_list(gpointer name, gpointer value, gpointer data)
1704 {
1705 int i;
1706 const char *filt_str[] = FILTER_STR;
1707
1708 CRM_CHECK(name != NULL, return);
1709
1710
1711 for (i = 0; filt_str[i] != NULL; i++) {
1712 if (g_str_has_prefix(name, filt_str[i])) {
1713 return;
1714 }
1715 }
1716
1717 attr_list = g_list_insert_sorted(attr_list, name, compare_attribute);
1718 }
1719
1720
1721 struct mon_attr_data {
1722 FILE *stream;
1723 node_t *node;
1724 };
1725
1726 static void
1727 print_node_attribute(gpointer name, gpointer user_data)
1728 {
1729 const char *value = NULL;
1730 struct mon_attr_data *data = (struct mon_attr_data *) user_data;
1731
1732 value = pe_node_attribute_raw(data->node, name);
1733
1734
1735 switch (output_format) {
1736 case mon_output_plain:
1737 case mon_output_console:
1738 print_as(" + %-32s\t: %-10s", (char *)name, value);
1739 break;
1740
1741 case mon_output_html:
1742 case mon_output_cgi:
1743 fprintf(data->stream, " <li>%s: %s",
1744 (char *)name, value);
1745 break;
1746
1747 case mon_output_xml:
1748 fprintf(data->stream,
1749 " <attribute name=\"%s\" value=\"%s\"",
1750 (char *)name, value);
1751 break;
1752
1753 default:
1754 break;
1755 }
1756
1757
1758 print_attr_msg(data->stream, data->node, data->node->details->running_rsc,
1759 name, value);
1760
1761
1762 switch (output_format) {
1763 case mon_output_plain:
1764 case mon_output_console:
1765 print_as("\n");
1766 break;
1767
1768 case mon_output_html:
1769 case mon_output_cgi:
1770 fprintf(data->stream, "</li>\n");
1771 break;
1772
1773 case mon_output_xml:
1774 fprintf(data->stream, " />\n");
1775 break;
1776
1777 default:
1778 break;
1779 }
1780 }
1781
1782 static void
1783 print_node_summary(FILE *stream, pe_working_set_t * data_set, gboolean operations)
1784 {
1785 xmlNode *node_state = NULL;
1786 xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
1787
1788
1789 switch (output_format) {
1790 case mon_output_plain:
1791 case mon_output_console:
1792 if (operations) {
1793 print_as("\nOperations:\n");
1794 } else {
1795 print_as("\nMigration Summary:\n");
1796 }
1797 break;
1798
1799 case mon_output_html:
1800 case mon_output_cgi:
1801 if (operations) {
1802 fprintf(stream, " <hr />\n <h2>Operations</h2>\n");
1803 } else {
1804 fprintf(stream, " <hr />\n <h2>Migration Summary</h2>\n");
1805 }
1806 break;
1807
1808 case mon_output_xml:
1809 fprintf(stream, " <node_history>\n");
1810 break;
1811
1812 default:
1813 break;
1814 }
1815
1816
1817 for (node_state = __xml_first_child(cib_status); node_state != NULL;
1818 node_state = __xml_next(node_state)) {
1819 if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
1820 print_node_history(stream, data_set, node_state, operations);
1821 }
1822 }
1823
1824
1825 switch (output_format) {
1826 case mon_output_xml:
1827 fprintf(stream, " </node_history>\n");
1828 break;
1829
1830 default:
1831 break;
1832 }
1833 }
1834
1835 static void
1836 print_ticket(gpointer name, gpointer value, gpointer data)
1837 {
1838 ticket_t *ticket = (ticket_t *) value;
1839 FILE *stream = (FILE *) data;
1840
1841 switch (output_format) {
1842 case mon_output_plain:
1843 case mon_output_console:
1844 print_as("* %s:\t%s%s", ticket->id,
1845 (ticket->granted? "granted" : "revoked"),
1846 (ticket->standby? " [standby]" : ""));
1847 break;
1848
1849 case mon_output_html:
1850 case mon_output_cgi:
1851 fprintf(stream, " <li>%s: %s%s", ticket->id,
1852 (ticket->granted? "granted" : "revoked"),
1853 (ticket->standby? " [standby]" : ""));
1854 break;
1855
1856 case mon_output_xml:
1857 fprintf(stream, " <ticket id=\"%s\" status=\"%s\" standby=\"%s\"",
1858 ticket->id, (ticket->granted? "granted" : "revoked"),
1859 (ticket->standby? "true" : "false"));
1860 break;
1861
1862 default:
1863 break;
1864 }
1865 if (ticket->last_granted > -1) {
1866 print_nvpair(stdout, "last-granted", NULL, NULL, ticket->last_granted);
1867 }
1868 switch (output_format) {
1869 case mon_output_plain:
1870 case mon_output_console:
1871 print_as("\n");
1872 break;
1873
1874 case mon_output_html:
1875 case mon_output_cgi:
1876 fprintf(stream, "</li>\n");
1877 break;
1878
1879 case mon_output_xml:
1880 fprintf(stream, " />\n");
1881 break;
1882
1883 default:
1884 break;
1885 }
1886 }
1887
1888 static void
1889 print_cluster_tickets(FILE *stream, pe_working_set_t * data_set)
1890 {
1891
1892 switch (output_format) {
1893 case mon_output_plain:
1894 case mon_output_console:
1895 print_as("\nTickets:\n");
1896 break;
1897
1898 case mon_output_html:
1899 case mon_output_cgi:
1900 fprintf(stream, " <hr />\n <h2>Tickets</h2>\n <ul>\n");
1901 break;
1902
1903 case mon_output_xml:
1904 fprintf(stream, " <tickets>\n");
1905 break;
1906
1907 default:
1908 break;
1909 }
1910
1911
1912 g_hash_table_foreach(data_set->tickets, print_ticket, stream);
1913
1914
1915 switch (output_format) {
1916 case mon_output_html:
1917 case mon_output_cgi:
1918 fprintf(stream, " </ul>\n");
1919 break;
1920
1921 case mon_output_xml:
1922 fprintf(stream, " </tickets>\n");
1923 break;
1924
1925 default:
1926 break;
1927 }
1928 }
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944 static char *
1945 get_node_display_name(node_t *node)
1946 {
1947 char *node_name;
1948 const char *node_host = NULL;
1949 const char *node_id = NULL;
1950 int name_len;
1951
1952 CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
1953
1954
1955 if (is_container_remote_node(node)) {
1956 if (node->details->remote_rsc->running_on) {
1957
1958
1959
1960
1961 node_t *host_node = (node_t *) node->details->remote_rsc->running_on->data;
1962
1963 if (host_node && host_node->details) {
1964 node_host = host_node->details->uname;
1965 }
1966 }
1967 if (node_host == NULL) {
1968 node_host = "";
1969 }
1970 }
1971
1972
1973 if (print_clone_detail && safe_str_neq(node->details->uname, node->details->id)) {
1974 node_id = node->details->id;
1975 }
1976
1977
1978 name_len = strlen(node->details->uname) + 1;
1979 if (node_host) {
1980 name_len += strlen(node_host) + 1;
1981 }
1982 if (node_id) {
1983 name_len += strlen(node_id) + 3;
1984 }
1985
1986
1987 node_name = malloc(name_len);
1988 CRM_ASSERT(node_name != NULL);
1989 strcpy(node_name, node->details->uname);
1990 if (node_host) {
1991 strcat(node_name, "@");
1992 strcat(node_name, node_host);
1993 }
1994 if (node_id) {
1995 strcat(node_name, " (");
1996 strcat(node_name, node_id);
1997 strcat(node_name, ")");
1998 }
1999 return node_name;
2000 }
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010 static void print_ban(FILE *stream, node_t *node, rsc_to_node_t *location)
2011 {
2012 char *node_name = NULL;
2013
2014 switch (output_format) {
2015 case mon_output_plain:
2016 case mon_output_console:
2017 node_name = get_node_display_name(node);
2018 print_as(" %s\tprevents %s from running %son %s\n",
2019 location->id, location->rsc_lh->id,
2020 ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
2021 node_name);
2022 break;
2023
2024 case mon_output_html:
2025 case mon_output_cgi:
2026 node_name = get_node_display_name(node);
2027 fprintf(stream, " <li>%s prevents %s from running %son %s</li>\n",
2028 location->id, location->rsc_lh->id,
2029 ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
2030 node_name);
2031 break;
2032
2033 case mon_output_xml:
2034 fprintf(stream,
2035 " <ban id=\"%s\" resource=\"%s\" node=\"%s\" weight=\"%d\" master_only=\"%s\" />\n",
2036 location->id, location->rsc_lh->id, node->details->uname, node->weight,
2037 ((location->role_filter == RSC_ROLE_MASTER)? "true" : "false"));
2038 break;
2039
2040 default:
2041 break;
2042 }
2043 free(node_name);
2044 }
2045
2046
2047
2048
2049
2050
2051
2052
2053 static void print_neg_locations(FILE *stream, pe_working_set_t *data_set)
2054 {
2055 GListPtr gIter, gIter2;
2056
2057
2058 switch (output_format) {
2059 case mon_output_plain:
2060 case mon_output_console:
2061 print_as("\nNegative Location Constraints:\n");
2062 break;
2063
2064 case mon_output_html:
2065 case mon_output_cgi:
2066 fprintf(stream, " <hr />\n <h2>Negative Location Constraints</h2>\n <ul>\n");
2067 break;
2068
2069 case mon_output_xml:
2070 fprintf(stream, " <bans>\n");
2071 break;
2072
2073 default:
2074 break;
2075 }
2076
2077
2078 for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
2079 rsc_to_node_t *location = (rsc_to_node_t *) gIter->data;
2080 if (!g_str_has_prefix(location->id, print_neg_location_prefix))
2081 continue;
2082 for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
2083 node_t *node = (node_t *) gIter2->data;
2084
2085 if (node->weight < 0) {
2086 print_ban(stream, node, location);
2087 }
2088 }
2089 }
2090
2091
2092 switch (output_format) {
2093 case mon_output_cgi:
2094 case mon_output_html:
2095 fprintf(stream, " </ul>\n");
2096 break;
2097
2098 case mon_output_xml:
2099 fprintf(stream, " </bans>\n");
2100 break;
2101
2102 default:
2103 break;
2104 }
2105 }
2106
2107 static void
2108 crm_mon_get_parameters(resource_t *rsc, pe_working_set_t * data_set)
2109 {
2110 get_rsc_attributes(rsc->parameters, rsc, NULL, data_set);
2111 crm_trace("Beekhof: unpacked params for %s (%d)", rsc->id, g_hash_table_size(rsc->parameters));
2112 if(rsc->children) {
2113 GListPtr gIter = NULL;
2114
2115 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
2116 crm_mon_get_parameters(gIter->data, data_set);
2117 }
2118 }
2119 }
2120
2121
2122
2123
2124
2125
2126
2127
2128 static void
2129 print_node_attributes(FILE *stream, pe_working_set_t *data_set)
2130 {
2131 GListPtr gIter = NULL;
2132
2133
2134 switch (output_format) {
2135 case mon_output_plain:
2136 case mon_output_console:
2137 print_as("\nNode Attributes:\n");
2138 break;
2139
2140 case mon_output_html:
2141 case mon_output_cgi:
2142 fprintf(stream, " <hr />\n <h2>Node Attributes</h2>\n");
2143 break;
2144
2145 case mon_output_xml:
2146 fprintf(stream, " <node_attributes>\n");
2147 break;
2148
2149 default:
2150 break;
2151 }
2152
2153
2154
2155
2156 for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
2157 crm_mon_get_parameters(gIter->data, data_set);
2158 }
2159
2160
2161 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
2162 struct mon_attr_data data;
2163
2164 data.stream = stream;
2165 data.node = (node_t *) gIter->data;
2166
2167 if (data.node && data.node->details && data.node->details->online) {
2168 print_node_start(stream, data.node);
2169 g_hash_table_foreach(data.node->details->attrs, create_attr_list, NULL);
2170 g_list_foreach(attr_list, print_node_attribute, &data);
2171 g_list_free(attr_list);
2172 attr_list = NULL;
2173 print_node_end(stream);
2174 }
2175 }
2176
2177
2178 switch (output_format) {
2179 case mon_output_xml:
2180 fprintf(stream, " </node_attributes>\n");
2181 break;
2182
2183 default:
2184 break;
2185 }
2186 }
2187
2188
2189
2190
2191
2192
2193
2194 static int
2195 get_resource_display_options(void)
2196 {
2197 int print_opts;
2198
2199
2200 switch (output_format) {
2201 case mon_output_console:
2202 print_opts = pe_print_ncurses;
2203 break;
2204 case mon_output_html:
2205 case mon_output_cgi:
2206 print_opts = pe_print_html;
2207 break;
2208 case mon_output_xml:
2209 print_opts = pe_print_xml;
2210 break;
2211 default:
2212 print_opts = pe_print_printf;
2213 break;
2214 }
2215
2216
2217 if (print_pending) {
2218 print_opts |= pe_print_pending;
2219 }
2220 if (print_clone_detail) {
2221 print_opts |= pe_print_clone_details|pe_print_implicit;
2222 }
2223 if (!inactive_resources) {
2224 print_opts |= pe_print_clone_active;
2225 }
2226 if (print_brief) {
2227 print_opts |= pe_print_brief;
2228 }
2229 return print_opts;
2230 }
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241 static const char *
2242 crm_now_string(void)
2243 {
2244 time_t a_time = time(NULL);
2245 char *since_epoch = ctime(&a_time);
2246
2247 if ((a_time == (time_t) -1) || (since_epoch == NULL)) {
2248 return "Could not determine current time";
2249 }
2250 since_epoch[strlen(since_epoch) - 1] = EOS;
2251 return (since_epoch);
2252 }
2253
2254
2255
2256
2257
2258
2259
2260 static void
2261 print_cluster_summary_header(FILE *stream)
2262 {
2263 switch (output_format) {
2264 case mon_output_html:
2265 case mon_output_cgi:
2266 fprintf(stream, " <h2>Cluster Summary</h2>\n <p>\n");
2267 break;
2268
2269 case mon_output_xml:
2270 fprintf(stream, " <summary>\n");
2271 break;
2272
2273 default:
2274 break;
2275 }
2276 }
2277
2278
2279
2280
2281
2282
2283
2284 static void
2285 print_cluster_summary_footer(FILE *stream)
2286 {
2287 switch (output_format) {
2288 case mon_output_cgi:
2289 case mon_output_html:
2290 fprintf(stream, " </p>\n");
2291 break;
2292
2293 case mon_output_xml:
2294 fprintf(stream, " </summary>\n");
2295 break;
2296
2297 default:
2298 break;
2299 }
2300 }
2301
2302
2303
2304
2305
2306
2307
2308
2309 static void
2310 print_cluster_times(FILE *stream, pe_working_set_t *data_set)
2311 {
2312 const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
2313 const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
2314 const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
2315 const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
2316
2317 switch (output_format) {
2318 case mon_output_plain:
2319 case mon_output_console:
2320 print_as("Last updated: %s", crm_now_string());
2321 print_as((user || client || origin)? "\n" : "\t\t");
2322 print_as("Last change: %s", last_written ? last_written : "");
2323 if (user) {
2324 print_as(" by %s", user);
2325 }
2326 if (client) {
2327 print_as(" via %s", client);
2328 }
2329 if (origin) {
2330 print_as(" on %s", origin);
2331 }
2332 print_as("\n");
2333 break;
2334
2335 case mon_output_html:
2336 case mon_output_cgi:
2337 fprintf(stream, " <b>Last updated:</b> %s<br/>\n", crm_now_string());
2338 fprintf(stream, " <b>Last change:</b> %s", last_written ? last_written : "");
2339 if (user) {
2340 fprintf(stream, " by %s", user);
2341 }
2342 if (client) {
2343 fprintf(stream, " via %s", client);
2344 }
2345 if (origin) {
2346 fprintf(stream, " on %s", origin);
2347 }
2348 fprintf(stream, "<br/>\n");
2349 break;
2350
2351 case mon_output_xml:
2352 fprintf(stream, " <last_update time=\"%s\" />\n", crm_now_string());
2353 fprintf(stream, " <last_change time=\"%s\" user=\"%s\" client=\"%s\" origin=\"%s\" />\n",
2354 last_written ? last_written : "", user ? user : "",
2355 client ? client : "", origin ? origin : "");
2356 break;
2357
2358 default:
2359 break;
2360 }
2361 }
2362
2363
2364
2365
2366
2367
2368
2369
2370 static void
2371 print_cluster_stack(FILE *stream, const char *stack_s)
2372 {
2373 switch (output_format) {
2374 case mon_output_plain:
2375 case mon_output_console:
2376 print_as("Stack: %s\n", stack_s);
2377 break;
2378
2379 case mon_output_html:
2380 case mon_output_cgi:
2381 fprintf(stream, " <b>Stack:</b> %s<br/>\n", stack_s);
2382 break;
2383
2384 case mon_output_xml:
2385 fprintf(stream, " <stack type=\"%s\" />\n", stack_s);
2386 break;
2387
2388 default:
2389 break;
2390 }
2391 }
2392
2393
2394
2395
2396
2397
2398
2399
2400 static void
2401 print_cluster_dc(FILE *stream, pe_working_set_t *data_set)
2402 {
2403 node_t *dc = data_set->dc_node;
2404 xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
2405 data_set->input, LOG_DEBUG);
2406 const char *dc_version_s = dc_version?
2407 crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
2408 : NULL;
2409 const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
2410 char *dc_name = dc? get_node_display_name(dc) : NULL;
2411
2412 switch (output_format) {
2413 case mon_output_plain:
2414 case mon_output_console:
2415 print_as("Current DC: ");
2416 if (dc) {
2417 print_as("%s (version %s) - partition %s quorum\n",
2418 dc_name, (dc_version_s? dc_version_s : "unknown"),
2419 (crm_is_true(quorum) ? "with" : "WITHOUT"));
2420 } else {
2421 print_as("NONE\n");
2422 }
2423 break;
2424
2425 case mon_output_html:
2426 case mon_output_cgi:
2427 fprintf(stream, " <b>Current DC:</b> ");
2428 if (dc) {
2429 fprintf(stream, "%s (version %s) - partition %s quorum",
2430 dc_name, (dc_version_s? dc_version_s : "unknown"),
2431 (crm_is_true(quorum)? "with" : "<font color=\"red\"><b>WITHOUT</b></font>"));
2432 } else {
2433 fprintf(stream, "<font color=\"red\"><b>NONE</b></font>");
2434 }
2435 fprintf(stream, "<br/>\n");
2436 break;
2437
2438 case mon_output_xml:
2439 fprintf(stream, " <current_dc ");
2440 if (dc) {
2441 fprintf(stream,
2442 "present=\"true\" version=\"%s\" name=\"%s\" id=\"%s\" with_quorum=\"%s\"",
2443 (dc_version_s? dc_version_s : ""), dc->details->uname, dc->details->id,
2444 (crm_is_true(quorum) ? "true" : "false"));
2445 } else {
2446 fprintf(stream, "present=\"false\"");
2447 }
2448 fprintf(stream, " />\n");
2449 break;
2450
2451 default:
2452 break;
2453 }
2454 free(dc_name);
2455 }
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465 static void
2466 print_cluster_counts(FILE *stream, pe_working_set_t *data_set, const char *stack_s)
2467 {
2468 int nnodes = g_list_length(data_set->nodes);
2469 int nresources = count_resources(data_set, NULL);
2470 xmlNode *quorum_node = get_xpath_object("//nvpair[@name='" XML_ATTR_EXPECTED_VOTES "']",
2471 data_set->input, LOG_DEBUG);
2472 const char *quorum_votes = quorum_node?
2473 crm_element_value(quorum_node, XML_NVPAIR_ATTR_VALUE)
2474 : "unknown";
2475
2476 switch (output_format) {
2477 case mon_output_plain:
2478 case mon_output_console:
2479
2480 print_as("\n%d node%s configured", nnodes, s_if_plural(nnodes));
2481 if (stack_s && strstr(stack_s, "classic openais") != NULL) {
2482 print_as(" (%s expected votes)", quorum_votes);
2483 }
2484 print_as("\n");
2485
2486 print_as("%d resource%s configured",
2487 nresources, s_if_plural(nresources));
2488 if(data_set->disabled_resources || data_set->blocked_resources) {
2489 print_as(" (");
2490 if (data_set->disabled_resources) {
2491 print_as("%d DISABLED", data_set->disabled_resources);
2492 }
2493 if (data_set->disabled_resources && data_set->blocked_resources) {
2494 print_as(", ");
2495 }
2496 if (data_set->blocked_resources) {
2497 print_as("%d BLOCKED from starting due to failure",
2498 data_set->blocked_resources);
2499 }
2500 print_as(")");
2501 }
2502 print_as("\n");
2503
2504 break;
2505
2506 case mon_output_html:
2507 case mon_output_cgi:
2508
2509 fprintf(stream, " %d node%s configured", nnodes, s_if_plural(nnodes));
2510 if (stack_s && strstr(stack_s, "classic openais") != NULL) {
2511 fprintf(stream, " (%s expected votes)", quorum_votes);
2512 }
2513 fprintf(stream, "<br/>\n");
2514
2515 fprintf(stream, " %d resource%s configured",
2516 nresources, s_if_plural(nresources));
2517 if (data_set->disabled_resources || data_set->blocked_resources) {
2518 fprintf(stream, " (");
2519 if (data_set->disabled_resources) {
2520 fprintf(stream, "%d <strong>DISABLED</strong>",
2521 data_set->disabled_resources);
2522 }
2523 if (data_set->disabled_resources && data_set->blocked_resources) {
2524 fprintf(stream, ", ");
2525 }
2526 if (data_set->blocked_resources) {
2527 fprintf(stream,
2528 "%d <strong>BLOCKED</strong> from starting due to failure",
2529 data_set->blocked_resources);
2530 }
2531 fprintf(stream, ")");
2532 }
2533 fprintf(stream, "<br/>\n");
2534 break;
2535
2536 case mon_output_xml:
2537 fprintf(stream,
2538 " <nodes_configured number=\"%d\" expected_votes=\"%s\" />\n",
2539 g_list_length(data_set->nodes), quorum_votes);
2540 fprintf(stream,
2541 " <resources_configured number=\"%d\" disabled=\"%d\" blocked=\"%d\" />\n",
2542 count_resources(data_set, NULL),
2543 data_set->disabled_resources, data_set->blocked_resources);
2544 break;
2545
2546 default:
2547 break;
2548 }
2549 }
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561 static void
2562 print_cluster_options(FILE *stream, pe_working_set_t *data_set)
2563 {
2564 switch (output_format) {
2565 case mon_output_plain:
2566 case mon_output_console:
2567 if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
2568 print_as("\n *** Resource management is DISABLED ***");
2569 print_as("\n The cluster will not attempt to start, stop or recover services");
2570 print_as("\n");
2571 }
2572 break;
2573
2574 case mon_output_html:
2575 fprintf(stream, " </p>\n <h3>Config Options</h3>\n");
2576 fprintf(stream, " <table>\n");
2577 fprintf(stream, " <tr><th>STONITH of failed nodes</th><td>%s</td></tr>\n",
2578 is_set(data_set->flags, pe_flag_stonith_enabled)? "enabled" : "disabled");
2579
2580 fprintf(stream, " <tr><th>Cluster is</th><td>%ssymmetric</td></tr>\n",
2581 is_set(data_set->flags, pe_flag_symmetric_cluster)? "" : "a");
2582
2583 fprintf(stream, " <tr><th>No Quorum Policy</th><td>");
2584 switch (data_set->no_quorum_policy) {
2585 case no_quorum_freeze:
2586 fprintf(stream, "Freeze resources");
2587 break;
2588 case no_quorum_stop:
2589 fprintf(stream, "Stop ALL resources");
2590 break;
2591 case no_quorum_ignore:
2592 fprintf(stream, "Ignore");
2593 break;
2594 case no_quorum_suicide:
2595 fprintf(stream, "Suicide");
2596 break;
2597 }
2598 fprintf(stream, "</td></tr>\n");
2599
2600 fprintf(stream, " <tr><th>Resource management</th><td>");
2601 if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
2602 fprintf(stream, "<strong>DISABLED</strong> (the cluster will "
2603 "not attempt to start, stop or recover services)");
2604 } else {
2605 fprintf(stream, "enabled");
2606 }
2607 fprintf(stream, "</td></tr>\n");
2608
2609 fprintf(stream, "</table>\n <p>\n");
2610 break;
2611
2612 case mon_output_xml:
2613 fprintf(stream, " <cluster_options");
2614 fprintf(stream, " stonith-enabled=\"%s\"",
2615 is_set(data_set->flags, pe_flag_stonith_enabled)?
2616 "true" : "false");
2617 fprintf(stream, " symmetric-cluster=\"%s\"",
2618 is_set(data_set->flags, pe_flag_symmetric_cluster)?
2619 "true" : "false");
2620 fprintf(stream, " no-quorum-policy=\"");
2621 switch (data_set->no_quorum_policy) {
2622 case no_quorum_freeze:
2623 fprintf(stream, "freeze");
2624 break;
2625 case no_quorum_stop:
2626 fprintf(stream, "stop");
2627 break;
2628 case no_quorum_ignore:
2629 fprintf(stream, "ignore");
2630 break;
2631 case no_quorum_suicide:
2632 fprintf(stream, "suicide");
2633 break;
2634 }
2635 fprintf(stream, "\"");
2636 fprintf(stream, " maintenance-mode=\"%s\"",
2637 is_set(data_set->flags, pe_flag_maintenance_mode)?
2638 "true" : "false");
2639 fprintf(stream, " />\n");
2640 break;
2641
2642 default:
2643 break;
2644 }
2645 }
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655 static const char *
2656 get_cluster_stack(pe_working_set_t *data_set)
2657 {
2658 xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
2659 data_set->input, LOG_DEBUG);
2660 return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
2661 }
2662
2663
2664
2665
2666
2667
2668
2669
2670 static void
2671 print_cluster_summary(FILE *stream, pe_working_set_t *data_set)
2672 {
2673 const char *stack_s = get_cluster_stack(data_set);
2674 gboolean header_printed = FALSE;
2675
2676 if (show & mon_show_stack) {
2677 if (header_printed == FALSE) {
2678 print_cluster_summary_header(stream);
2679 header_printed = TRUE;
2680 }
2681 print_cluster_stack(stream, stack_s);
2682 }
2683
2684
2685 if ((data_set->dc_node == NULL) || (show & mon_show_dc)) {
2686 if (header_printed == FALSE) {
2687 print_cluster_summary_header(stream);
2688 header_printed = TRUE;
2689 }
2690 print_cluster_dc(stream, data_set);
2691 }
2692
2693 if (show & mon_show_times) {
2694 if (header_printed == FALSE) {
2695 print_cluster_summary_header(stream);
2696 header_printed = TRUE;
2697 }
2698 print_cluster_times(stream, data_set);
2699 }
2700
2701 if (is_set(data_set->flags, pe_flag_maintenance_mode)
2702 || data_set->disabled_resources
2703 || data_set->blocked_resources
2704 || is_set(show, mon_show_count)) {
2705 if (header_printed == FALSE) {
2706 print_cluster_summary_header(stream);
2707 header_printed = TRUE;
2708 }
2709 print_cluster_counts(stream, data_set, stack_s);
2710 }
2711
2712
2713
2714
2715 if (show & mon_show_stack) {
2716 print_cluster_options(stream, data_set);
2717 }
2718
2719 if (header_printed) {
2720 print_cluster_summary_footer(stream);
2721 }
2722 }
2723
2724
2725
2726
2727
2728
2729
2730
2731 static void
2732 print_failed_action(FILE *stream, xmlNode *xml_op)
2733 {
2734 const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
2735 const char *op_key_attr = "op_key";
2736 const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE);
2737 const char *node = crm_element_value(xml_op, XML_ATTR_UNAME);
2738 const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
2739 const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
2740 int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0");
2741 int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0");
2742 char *exit_reason_cleaned;
2743
2744
2745 if (op_key == NULL) {
2746 op_key = ID(xml_op);
2747 op_key_attr = "id";
2748 }
2749
2750
2751 if (exit_reason == NULL) {
2752 exit_reason = "none";
2753 }
2754
2755
2756 switch (output_format) {
2757 case mon_output_plain:
2758 case mon_output_console:
2759 print_as("* %s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
2760 op_key, node, services_ocf_exitcode_str(rc), rc,
2761 call, services_lrm_status_str(status), exit_reason);
2762 break;
2763
2764 case mon_output_html:
2765 case mon_output_cgi:
2766 fprintf(stream, " <li>%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
2767 op_key, node, services_ocf_exitcode_str(rc), rc,
2768 call, services_lrm_status_str(status), exit_reason);
2769 break;
2770
2771 case mon_output_xml:
2772 exit_reason_cleaned = crm_xml_escape(exit_reason);
2773 fprintf(stream, " <failure %s=\"%s\" node=\"%s\"",
2774 op_key_attr, op_key, node);
2775 fprintf(stream, " exitstatus=\"%s\" exitreason=\"%s\" exitcode=\"%d\"",
2776 services_ocf_exitcode_str(rc), exit_reason_cleaned, rc);
2777 fprintf(stream, " call=\"%s\" status=\"%s\"",
2778 call, services_lrm_status_str(status));
2779 free(exit_reason_cleaned);
2780 break;
2781
2782 default:
2783 break;
2784 }
2785
2786
2787 if (last) {
2788 time_t run_at = crm_parse_int(last, "0");
2789 char *run_at_s = ctime(&run_at);
2790
2791 if (run_at_s) {
2792 run_at_s[24] = 0;
2793 }
2794
2795 switch (output_format) {
2796 case mon_output_plain:
2797 case mon_output_console:
2798 print_as(",\n last-rc-change='%s', queued=%sms, exec=%sms",
2799 run_at_s? run_at_s : "",
2800 crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2801 crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
2802 break;
2803
2804 case mon_output_html:
2805 case mon_output_cgi:
2806 fprintf(stream, " last-rc-change='%s', queued=%sms, exec=%sms",
2807 run_at_s? run_at_s : "",
2808 crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2809 crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
2810 break;
2811
2812 case mon_output_xml:
2813 fprintf(stream,
2814 " last-rc-change=\"%s\" queued=\"%s\" exec=\"%s\" interval=\"%d\" task=\"%s\"",
2815 run_at_s? run_at_s : "",
2816 crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2817 crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
2818 crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL), "0"),
2819 crm_element_value(xml_op, XML_LRM_ATTR_TASK));
2820 break;
2821
2822 default:
2823 break;
2824 }
2825 }
2826
2827
2828 switch (output_format) {
2829 case mon_output_plain:
2830 case mon_output_console:
2831 print_as("\n");
2832 break;
2833
2834 case mon_output_html:
2835 case mon_output_cgi:
2836 fprintf(stream, "</li>\n");
2837 break;
2838
2839 case mon_output_xml:
2840 fprintf(stream, " />\n");
2841 break;
2842
2843 default:
2844 break;
2845 }
2846 }
2847
2848
2849
2850
2851
2852
2853
2854
2855 static void
2856 print_failed_actions(FILE *stream, pe_working_set_t *data_set)
2857 {
2858 xmlNode *xml_op = NULL;
2859
2860
2861 switch (output_format) {
2862 case mon_output_plain:
2863 case mon_output_console:
2864 print_as("\nFailed Actions:\n");
2865 break;
2866
2867 case mon_output_html:
2868 case mon_output_cgi:
2869 fprintf(stream, " <hr />\n <h2>Failed Actions</h2>\n <ul>\n");
2870 break;
2871
2872 case mon_output_xml:
2873 fprintf(stream, " <failures>\n");
2874 break;
2875
2876 default:
2877 break;
2878 }
2879
2880
2881 for (xml_op = __xml_first_child(data_set->failed); xml_op != NULL;
2882 xml_op = __xml_next(xml_op)) {
2883 print_failed_action(stream, xml_op);
2884 }
2885
2886
2887 switch (output_format) {
2888 case mon_output_plain:
2889 case mon_output_console:
2890 print_as("\n");
2891 break;
2892
2893 case mon_output_html:
2894 case mon_output_cgi:
2895 fprintf(stream, " </ul>\n");
2896 break;
2897
2898 case mon_output_xml:
2899 fprintf(stream, " </failures>\n");
2900 break;
2901
2902 default:
2903 break;
2904 }
2905 }
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916 static void
2917 print_status(pe_working_set_t * data_set)
2918 {
2919 GListPtr gIter = NULL;
2920 int print_opts = get_resource_display_options();
2921
2922
2923 char *online_nodes = NULL;
2924 char *online_remote_nodes = NULL;
2925 char *online_guest_nodes = NULL;
2926 char *offline_nodes = NULL;
2927 char *offline_remote_nodes = NULL;
2928
2929 if (output_format == mon_output_console) {
2930 blank_screen();
2931 }
2932 print_cluster_summary(stdout, data_set);
2933 print_as("\n");
2934
2935
2936 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
2937 node_t *node = (node_t *) gIter->data;
2938 const char *node_mode = NULL;
2939 char *node_name = get_node_display_name(node);
2940
2941
2942 if (node->details->unclean) {
2943 if (node->details->online) {
2944 node_mode = "UNCLEAN (online)";
2945
2946 } else if (node->details->pending) {
2947 node_mode = "UNCLEAN (pending)";
2948
2949 } else {
2950 node_mode = "UNCLEAN (offline)";
2951 }
2952
2953 } else if (node->details->pending) {
2954 node_mode = "pending";
2955
2956 } else if (node->details->standby_onfail && node->details->online) {
2957 node_mode = "standby (on-fail)";
2958
2959 } else if (node->details->standby) {
2960 if (node->details->online) {
2961 node_mode = "standby";
2962 } else {
2963 node_mode = "OFFLINE (standby)";
2964 }
2965
2966 } else if (node->details->maintenance) {
2967 if (node->details->online) {
2968 node_mode = "maintenance";
2969 } else {
2970 node_mode = "OFFLINE (maintenance)";
2971 }
2972
2973 } else if (node->details->online) {
2974 node_mode = "online";
2975 if (group_by_node == FALSE) {
2976 if (is_container_remote_node(node)) {
2977 online_guest_nodes = add_list_element(online_guest_nodes, node_name);
2978 } else if (is_baremetal_remote_node(node)) {
2979 online_remote_nodes = add_list_element(online_remote_nodes, node_name);
2980 } else {
2981 online_nodes = add_list_element(online_nodes, node_name);
2982 }
2983 free(node_name);
2984 continue;
2985 }
2986 } else {
2987 node_mode = "OFFLINE";
2988 if (group_by_node == FALSE) {
2989 if (is_baremetal_remote_node(node)) {
2990 offline_remote_nodes = add_list_element(offline_remote_nodes, node_name);
2991 } else if (is_container_remote_node(node)) {
2992
2993 } else {
2994 offline_nodes = add_list_element(offline_nodes, node_name);
2995 }
2996 free(node_name);
2997 continue;
2998 }
2999 }
3000
3001
3002
3003
3004 if (is_container_remote_node(node)) {
3005 print_as("Guest");
3006 } else if (is_baremetal_remote_node(node)) {
3007 print_as("Remote");
3008 }
3009 print_as("Node %s: %s\n", node_name, node_mode);
3010
3011
3012 if (group_by_node) {
3013 if (print_brief) {
3014 print_rscs_brief(node->details->running_rsc, "\t", print_opts | pe_print_rsconly,
3015 stdout, FALSE);
3016 } else {
3017 GListPtr gIter2 = NULL;
3018
3019 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
3020 resource_t *rsc = (resource_t *) gIter2->data;
3021
3022 rsc->fns->print(rsc, "\t", print_opts | pe_print_rsconly, stdout);
3023 }
3024 }
3025 }
3026 free(node_name);
3027 }
3028
3029
3030 if (online_nodes) {
3031 print_as("Online: [%s ]\n", online_nodes);
3032 free(online_nodes);
3033 }
3034 if (offline_nodes) {
3035 print_as("OFFLINE: [%s ]\n", offline_nodes);
3036 free(offline_nodes);
3037 }
3038 if (online_remote_nodes) {
3039 print_as("RemoteOnline: [%s ]\n", online_remote_nodes);
3040 free(online_remote_nodes);
3041 }
3042 if (offline_remote_nodes) {
3043 print_as("RemoteOFFLINE: [%s ]\n", offline_remote_nodes);
3044 free(offline_remote_nodes);
3045 }
3046 if (online_guest_nodes) {
3047 print_as("GuestOnline: [%s ]\n", online_guest_nodes);
3048 free(online_guest_nodes);
3049 }
3050
3051
3052 print_resources(stdout, data_set, print_opts);
3053
3054
3055 if (show & mon_show_attributes) {
3056 print_node_attributes(stdout, data_set);
3057 }
3058
3059
3060
3061
3062 if (show & (mon_show_operations | mon_show_failcounts)) {
3063 print_node_summary(stdout, data_set,
3064 ((show & mon_show_operations)? TRUE : FALSE));
3065 }
3066
3067
3068 if (xml_has_children(data_set->failed)) {
3069 print_failed_actions(stdout, data_set);
3070 }
3071
3072
3073 if (show & mon_show_tickets) {
3074 print_cluster_tickets(stdout, data_set);
3075 }
3076
3077
3078 if (show & mon_show_bans) {
3079 print_neg_locations(stdout, data_set);
3080 }
3081
3082 #if CURSES_ENABLED
3083 if (output_format == mon_output_console) {
3084 refresh();
3085 }
3086 #endif
3087 }
3088
3089
3090
3091
3092
3093
3094
3095 static void
3096 print_xml_status(pe_working_set_t * data_set)
3097 {
3098 FILE *stream = stdout;
3099 GListPtr gIter = NULL;
3100 int print_opts = get_resource_display_options();
3101
3102 fprintf(stream, "<?xml version=\"1.0\"?>\n");
3103 fprintf(stream, "<crm_mon version=\"%s\">\n", VERSION);
3104
3105 print_cluster_summary(stream, data_set);
3106
3107
3108 fprintf(stream, " <nodes>\n");
3109 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
3110 node_t *node = (node_t *) gIter->data;
3111 const char *node_type = "unknown";
3112
3113 switch (node->details->type) {
3114 case node_member:
3115 node_type = "member";
3116 break;
3117 case node_remote:
3118 node_type = "remote";
3119 break;
3120 case node_ping:
3121 node_type = "ping";
3122 break;
3123 }
3124
3125 fprintf(stream, " <node name=\"%s\" ", node->details->uname);
3126 fprintf(stream, "id=\"%s\" ", node->details->id);
3127 fprintf(stream, "online=\"%s\" ", node->details->online ? "true" : "false");
3128 fprintf(stream, "standby=\"%s\" ", node->details->standby ? "true" : "false");
3129 fprintf(stream, "standby_onfail=\"%s\" ", node->details->standby_onfail ? "true" : "false");
3130 fprintf(stream, "maintenance=\"%s\" ", node->details->maintenance ? "true" : "false");
3131 fprintf(stream, "pending=\"%s\" ", node->details->pending ? "true" : "false");
3132 fprintf(stream, "unclean=\"%s\" ", node->details->unclean ? "true" : "false");
3133 fprintf(stream, "shutdown=\"%s\" ", node->details->shutdown ? "true" : "false");
3134 fprintf(stream, "expected_up=\"%s\" ", node->details->expected_up ? "true" : "false");
3135 fprintf(stream, "is_dc=\"%s\" ", node->details->is_dc ? "true" : "false");
3136 fprintf(stream, "resources_running=\"%d\" ", g_list_length(node->details->running_rsc));
3137 fprintf(stream, "type=\"%s\" ", node_type);
3138 if (is_container_remote_node(node)) {
3139 fprintf(stream, "id_as_resource=\"%s\" ", node->details->remote_rsc->container->id);
3140 }
3141
3142 if (group_by_node) {
3143 GListPtr lpc2 = NULL;
3144
3145 fprintf(stream, ">\n");
3146 for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
3147 resource_t *rsc = (resource_t *) lpc2->data;
3148
3149 rsc->fns->print(rsc, " ", print_opts | pe_print_rsconly, stream);
3150 }
3151 fprintf(stream, " </node>\n");
3152 } else {
3153 fprintf(stream, "/>\n");
3154 }
3155 }
3156 fprintf(stream, " </nodes>\n");
3157
3158
3159 print_resources(stream, data_set, print_opts);
3160
3161
3162 if (show & mon_show_attributes) {
3163 print_node_attributes(stream, data_set);
3164 }
3165
3166
3167
3168
3169 if (show & (mon_show_operations | mon_show_failcounts)) {
3170 print_node_summary(stream, data_set,
3171 ((show & mon_show_operations)? TRUE : FALSE));
3172 }
3173
3174
3175 if (xml_has_children(data_set->failed)) {
3176 print_failed_actions(stream, data_set);
3177 }
3178
3179
3180 if (show & mon_show_tickets) {
3181 print_cluster_tickets(stream, data_set);
3182 }
3183
3184
3185 if (show & mon_show_bans) {
3186 print_neg_locations(stream, data_set);
3187 }
3188
3189 fprintf(stream, "</crm_mon>\n");
3190 fflush(stream);
3191 fclose(stream);
3192 }
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203 static int
3204 print_html_status(pe_working_set_t * data_set, const char *filename)
3205 {
3206 FILE *stream;
3207 GListPtr gIter = NULL;
3208 char *filename_tmp = NULL;
3209 int print_opts = get_resource_display_options();
3210
3211 if (output_format == mon_output_cgi) {
3212 stream = stdout;
3213 fprintf(stream, "Content-Type: text/html\n\n");
3214
3215 } else {
3216 filename_tmp = crm_concat(filename, "tmp", '.');
3217 stream = fopen(filename_tmp, "w");
3218 if (stream == NULL) {
3219 crm_perror(LOG_ERR, "Cannot open %s for writing", filename_tmp);
3220 free(filename_tmp);
3221 return -1;
3222 }
3223 }
3224
3225 fprintf(stream, "<html>\n");
3226 fprintf(stream, " <head>\n");
3227 fprintf(stream, " <title>Cluster status</title>\n");
3228 fprintf(stream, " <meta http-equiv=\"refresh\" content=\"%d\">\n", reconnect_msec / 1000);
3229 fprintf(stream, " </head>\n");
3230 fprintf(stream, "<body>\n");
3231
3232 print_cluster_summary(stream, data_set);
3233
3234
3235
3236 fprintf(stream, " <hr />\n <h2>Node List</h2>\n");
3237 fprintf(stream, "<ul>\n");
3238 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
3239 node_t *node = (node_t *) gIter->data;
3240 char *node_name = get_node_display_name(node);
3241
3242 fprintf(stream, "<li>Node: %s: ", node_name);
3243 if (node->details->standby_onfail && node->details->online) {
3244 fprintf(stream, "<font color=\"orange\">standby (on-fail)</font>\n");
3245 } else if (node->details->standby && node->details->online) {
3246 fprintf(stream, "<font color=\"orange\">standby</font>\n");
3247 } else if (node->details->standby) {
3248 fprintf(stream, "<font color=\"red\">OFFLINE (standby)</font>\n");
3249 } else if (node->details->maintenance && node->details->online) {
3250 fprintf(stream, "<font color=\"blue\">maintenance</font>\n");
3251 } else if (node->details->maintenance) {
3252 fprintf(stream, "<font color=\"red\">OFFLINE (maintenance)</font>\n");
3253 } else if (node->details->online) {
3254 fprintf(stream, "<font color=\"green\">online</font>\n");
3255 } else {
3256 fprintf(stream, "<font color=\"red\">OFFLINE</font>\n");
3257 }
3258 if (print_brief && group_by_node) {
3259 fprintf(stream, "<ul>\n");
3260 print_rscs_brief(node->details->running_rsc, NULL, print_opts | pe_print_rsconly,
3261 stream, FALSE);
3262 fprintf(stream, "</ul>\n");
3263
3264 } else if (group_by_node) {
3265 GListPtr lpc2 = NULL;
3266
3267 fprintf(stream, "<ul>\n");
3268 for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
3269 resource_t *rsc = (resource_t *) lpc2->data;
3270
3271 fprintf(stream, "<li>");
3272 rsc->fns->print(rsc, NULL, print_opts | pe_print_rsconly, stream);
3273 fprintf(stream, "</li>\n");
3274 }
3275 fprintf(stream, "</ul>\n");
3276 }
3277 fprintf(stream, "</li>\n");
3278 free(node_name);
3279 }
3280 fprintf(stream, "</ul>\n");
3281
3282
3283 print_resources(stream, data_set, print_opts);
3284
3285
3286 if (show & mon_show_attributes) {
3287 print_node_attributes(stream, data_set);
3288 }
3289
3290
3291
3292
3293 if (show & (mon_show_operations | mon_show_failcounts)) {
3294 print_node_summary(stream, data_set,
3295 ((show & mon_show_operations)? TRUE : FALSE));
3296 }
3297
3298
3299 if (xml_has_children(data_set->failed)) {
3300 print_failed_actions(stream, data_set);
3301 }
3302
3303
3304 if (show & mon_show_tickets) {
3305 print_cluster_tickets(stream, data_set);
3306 }
3307
3308
3309 if (show & mon_show_bans) {
3310 print_neg_locations(stream, data_set);
3311 }
3312
3313 fprintf(stream, "</body>\n");
3314 fprintf(stream, "</html>\n");
3315 fflush(stream);
3316 fclose(stream);
3317
3318 if (output_format != mon_output_cgi) {
3319 if (rename(filename_tmp, filename) != 0) {
3320 crm_perror(LOG_ERR, "Unable to rename %s->%s", filename_tmp, filename);
3321 }
3322 free(filename_tmp);
3323 }
3324 return 0;
3325 }
3326
3327 #if ENABLE_SNMP
3328 # include <net-snmp/net-snmp-config.h>
3329 # include <net-snmp/snmpv3_api.h>
3330 # include <net-snmp/agent/agent_trap.h>
3331 # include <net-snmp/library/snmp_client.h>
3332 # include <net-snmp/library/mib.h>
3333 # include <net-snmp/library/snmp_debug.h>
3334
3335 # define add_snmp_field(list, oid_string, value) do { \
3336 oid name[MAX_OID_LEN]; \
3337 size_t name_length = MAX_OID_LEN; \
3338 if (snmp_parse_oid(oid_string, name, &name_length)) { \
3339 int s_rc = snmp_add_var(list, name, name_length, 's', (value)); \
3340 if(s_rc != 0) { \
3341 crm_err("Could not add %s=%s rc=%d", oid_string, value, s_rc); \
3342 } else { \
3343 crm_trace("Added %s=%s", oid_string, value); \
3344 } \
3345 } else { \
3346 crm_err("Could not parse OID: %s", oid_string); \
3347 } \
3348 } while(0) \
3349
3350 # define add_snmp_field_int(list, oid_string, value) do { \
3351 oid name[MAX_OID_LEN]; \
3352 size_t name_length = MAX_OID_LEN; \
3353 if (snmp_parse_oid(oid_string, name, &name_length)) { \
3354 if(NULL == snmp_pdu_add_variable( \
3355 list, name, name_length, ASN_INTEGER, \
3356 (u_char *) & value, sizeof(value))) { \
3357 crm_err("Could not add %s=%d", oid_string, value); \
3358 } else { \
3359 crm_trace("Added %s=%d", oid_string, value); \
3360 } \
3361 } else { \
3362 crm_err("Could not parse OID: %s", oid_string); \
3363 } \
3364 } while(0) \
3365
3366 static int
3367 snmp_input(int operation, netsnmp_session * session, int reqid, netsnmp_pdu * pdu, void *magic)
3368 {
3369 return 1;
3370 }
3371
3372 static netsnmp_session *
3373 crm_snmp_init(const char *target, char *community)
3374 {
3375 static netsnmp_session *session = NULL;
3376
3377 # ifdef NETSNMPV53
3378 char target53[128];
3379
3380 snprintf(target53, sizeof(target53), "%s:162", target);
3381 # endif
3382
3383 if (session) {
3384 return session;
3385 }
3386
3387 if (target == NULL) {
3388 return NULL;
3389 }
3390
3391 if (get_crm_log_level() > LOG_INFO) {
3392 char *debug_tokens = strdup("run:shell,snmptrap,tdomain");
3393
3394 debug_register_tokens(debug_tokens);
3395 snmp_set_do_debugging(1);
3396 }
3397
3398 session = calloc(1, sizeof(netsnmp_session));
3399 snmp_sess_init(session);
3400 session->version = SNMP_VERSION_2c;
3401 session->callback = snmp_input;
3402 session->callback_magic = NULL;
3403
3404 if (community) {
3405 session->community_len = strlen(community);
3406 session->community = (unsigned char *)community;
3407 }
3408
3409 session = snmp_add(session,
3410 # ifdef NETSNMPV53
3411 netsnmp_tdomain_transport(target53, 0, "udp"),
3412 # else
3413 netsnmp_transport_open_client("snmptrap", target),
3414 # endif
3415 NULL, NULL);
3416
3417 if (session == NULL) {
3418 snmp_sess_perror("Could not create snmp transport", session);
3419 }
3420 return session;
3421 }
3422
3423 #endif
3424
3425 static int
3426 send_snmp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
3427 int status, const char *desc)
3428 {
3429 int ret = 1;
3430
3431 #if ENABLE_SNMP
3432 static oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
3433 static oid sysuptime_oid[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
3434
3435 netsnmp_pdu *trap_pdu;
3436 netsnmp_session *session = crm_snmp_init(snmp_target, snmp_community);
3437
3438 trap_pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
3439 if (!trap_pdu) {
3440 crm_err("Failed to create SNMP notification");
3441 return SNMPERR_GENERR;
3442 }
3443
3444 if (1) {
3445
3446 char csysuptime[20];
3447 time_t now = time(NULL);
3448
3449 sprintf(csysuptime, "%lld", (long long) now);
3450 snmp_add_var(trap_pdu, sysuptime_oid, sizeof(sysuptime_oid) / sizeof(oid), 't', csysuptime);
3451 }
3452
3453
3454 ret =
3455 snmp_add_var(trap_pdu, snmptrap_oid, sizeof(snmptrap_oid) / sizeof(oid), 'o',
3456 snmp_crm_trap_oid);
3457 if (ret != 0) {
3458 crm_err("Failed set snmpTrapOid.0=%s", snmp_crm_trap_oid);
3459 return ret;
3460 }
3461
3462
3463 if (rsc) {
3464 add_snmp_field(trap_pdu, snmp_crm_oid_rsc, rsc);
3465 }
3466 add_snmp_field(trap_pdu, snmp_crm_oid_node, node);
3467 add_snmp_field(trap_pdu, snmp_crm_oid_task, task);
3468 add_snmp_field(trap_pdu, snmp_crm_oid_desc, desc);
3469
3470 add_snmp_field_int(trap_pdu, snmp_crm_oid_rc, rc);
3471 add_snmp_field_int(trap_pdu, snmp_crm_oid_trc, target_rc);
3472 add_snmp_field_int(trap_pdu, snmp_crm_oid_status, status);
3473
3474
3475 ret = snmp_send(session, trap_pdu);
3476 if (ret == 0) {
3477
3478 snmp_sess_perror("Could not send SNMP trap", session);
3479 snmp_free_pdu(trap_pdu);
3480 ret = SNMPERR_GENERR;
3481 } else {
3482 ret = SNMPERR_SUCCESS;
3483 }
3484 #else
3485 crm_err("Sending SNMP traps is not supported by this installation");
3486 #endif
3487 return ret;
3488 }
3489
3490 #if ENABLE_ESMTP
3491 # include <auth-client.h>
3492 # include <libesmtp.h>
3493
3494 static void
3495 print_recipient_status(smtp_recipient_t recipient, const char *mailbox, void *arg)
3496 {
3497 const smtp_status_t *status;
3498
3499 status = smtp_recipient_status(recipient);
3500 printf("%s: %d %s", mailbox, status->code, status->text);
3501 }
3502
3503 static void
3504 event_cb(smtp_session_t session, int event_no, void *arg, ...)
3505 {
3506 int *ok;
3507 va_list alist;
3508
3509 va_start(alist, arg);
3510 switch (event_no) {
3511 case SMTP_EV_CONNECT:
3512 case SMTP_EV_MAILSTATUS:
3513 case SMTP_EV_RCPTSTATUS:
3514 case SMTP_EV_MESSAGEDATA:
3515 case SMTP_EV_MESSAGESENT:
3516 case SMTP_EV_DISCONNECT:
3517 break;
3518
3519 case SMTP_EV_WEAK_CIPHER:{
3520 int bits = va_arg(alist, long);
3521 ok = va_arg(alist, int *);
3522
3523 crm_debug("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.", bits);
3524 *ok = 1;
3525 break;
3526 }
3527 case SMTP_EV_STARTTLS_OK:
3528 crm_debug("SMTP_EV_STARTTLS_OK - TLS started here.");
3529 break;
3530
3531 case SMTP_EV_INVALID_PEER_CERTIFICATE:{
3532 long vfy_result = va_arg(alist, long);
3533 ok = va_arg(alist, int *);
3534
3535
3536 crm_err("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld", vfy_result);
3537 *ok = 1;
3538 break;
3539 }
3540 case SMTP_EV_NO_PEER_CERTIFICATE:
3541 ok = va_arg(alist, int *);
3542
3543 crm_debug("SMTP_EV_NO_PEER_CERTIFICATE - accepted.");
3544 *ok = 1;
3545 break;
3546 case SMTP_EV_WRONG_PEER_CERTIFICATE:
3547 ok = va_arg(alist, int *);
3548
3549 crm_debug("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted.");
3550 *ok = 1;
3551 break;
3552 case SMTP_EV_NO_CLIENT_CERTIFICATE:
3553 ok = va_arg(alist, int *);
3554
3555 crm_debug("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted.");
3556 *ok = 1;
3557 break;
3558 default:
3559 crm_debug("Got event: %d - ignored.", event_no);
3560 }
3561 va_end(alist);
3562 }
3563 #endif
3564
3565 #define BODY_MAX 2048
3566
3567 #if ENABLE_ESMTP
3568 static void
3569 crm_smtp_debug(const char *buf, int buflen, int writing, void *arg)
3570 {
3571 char type = 0;
3572 int lpc = 0, last = 0, level = *(int *)arg;
3573
3574 if (writing == SMTP_CB_HEADERS) {
3575 type = 'H';
3576 } else if (writing) {
3577 type = 'C';
3578 } else {
3579 type = 'S';
3580 }
3581
3582 for (; lpc < buflen; lpc++) {
3583 switch (buf[lpc]) {
3584 case 0:
3585 case '\n':
3586 if (last > 0) {
3587 do_crm_log(level, " %.*s", lpc - last, buf + last);
3588 } else {
3589 do_crm_log(level, "%c: %.*s", type, lpc - last, buf + last);
3590 }
3591 last = lpc + 1;
3592 break;
3593 }
3594 }
3595 }
3596 #endif
3597
3598 static int
3599 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
3600 int status, const char *desc)
3601 {
3602 pid_t pid;
3603
3604
3605 char *rc_s = crm_itoa(rc);
3606 char *status_s = crm_itoa(status);
3607 char *target_rc_s = crm_itoa(target_rc);
3608
3609 crm_debug("Sending external notification to '%s' via '%s'", external_recipient, external_agent);
3610
3611 if(rsc) {
3612 setenv("CRM_notify_rsc", rsc, 1);
3613 }
3614 if (external_recipient) {
3615 setenv("CRM_notify_recipient", external_recipient, 1);
3616 }
3617 setenv("CRM_notify_node", node, 1);
3618 setenv("CRM_notify_task", task, 1);
3619 setenv("CRM_notify_desc", desc, 1);
3620 setenv("CRM_notify_rc", rc_s, 1);
3621 setenv("CRM_notify_target_rc", target_rc_s, 1);
3622 setenv("CRM_notify_status", status_s, 1);
3623
3624 pid = fork();
3625 if (pid == -1) {
3626 crm_perror(LOG_ERR, "notification fork() failed.");
3627 }
3628 if (pid == 0) {
3629
3630 execl(external_agent, external_agent, NULL);
3631 exit(EXIT_FAILURE);
3632 }
3633
3634 crm_trace("Finished running custom notification program '%s'.", external_agent);
3635 free(target_rc_s);
3636 free(status_s);
3637 free(rc_s);
3638 return 0;
3639 }
3640
3641 static int
3642 send_smtp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
3643 int status, const char *desc)
3644 {
3645 #if ENABLE_ESMTP
3646 smtp_session_t session;
3647 smtp_message_t message;
3648 auth_context_t authctx;
3649 struct sigaction sa;
3650
3651 int len = 25;
3652 int noauth = 1;
3653 int smtp_debug = LOG_DEBUG;
3654 char crm_mail_body[BODY_MAX];
3655 char *crm_mail_subject = NULL;
3656
3657 memset(&sa, 0, sizeof(struct sigaction));
3658
3659 if (node == NULL) {
3660 node = "-";
3661 }
3662 if (rsc == NULL) {
3663 rsc = "-";
3664 }
3665 if (desc == NULL) {
3666 desc = "-";
3667 }
3668
3669 if (crm_mail_to == NULL) {
3670 return 1;
3671 }
3672
3673 if (crm_mail_host == NULL) {
3674 crm_mail_host = "localhost:25";
3675 }
3676
3677 if (crm_mail_prefix == NULL) {
3678 crm_mail_prefix = "Cluster notification";
3679 }
3680
3681 crm_debug("Sending '%s' mail to %s via %s", crm_mail_prefix, crm_mail_to, crm_mail_host);
3682
3683 len += strlen(crm_mail_prefix);
3684 len += strlen(task);
3685 len += strlen(rsc);
3686 len += strlen(node);
3687 len += strlen(desc);
3688 len++;
3689
3690 crm_mail_subject = calloc(1, len);
3691
3692 snprintf(crm_mail_subject, len, "%s - %s event for %s on %s: %s\r\n", crm_mail_prefix, task,
3693 rsc, node, desc);
3694
3695 len = 0;
3696 len += snprintf(crm_mail_body + len, BODY_MAX - len, "\r\n%s\r\n", crm_mail_prefix);
3697 len += snprintf(crm_mail_body + len, BODY_MAX - len, "====\r\n\r\n");
3698 if (rc == target_rc) {
3699 len += snprintf(crm_mail_body + len, BODY_MAX - len,
3700 "Completed operation %s for resource %s on %s\r\n", task, rsc, node);
3701 } else {
3702 len += snprintf(crm_mail_body + len, BODY_MAX - len,
3703 "Operation %s for resource %s on %s failed: %s\r\n", task, rsc, node, desc);
3704 }
3705
3706 len += snprintf(crm_mail_body + len, BODY_MAX - len, "\r\nDetails:\r\n");
3707 len += snprintf(crm_mail_body + len, BODY_MAX - len,
3708 "\toperation status: (%d) %s\r\n", status, services_lrm_status_str(status));
3709 if (status == PCMK_LRM_OP_DONE) {
3710 len += snprintf(crm_mail_body + len, BODY_MAX - len,
3711 "\tscript returned: (%d) %s\r\n", rc, services_ocf_exitcode_str(rc));
3712 len += snprintf(crm_mail_body + len, BODY_MAX - len,
3713 "\texpected return value: (%d) %s\r\n", target_rc,
3714 services_ocf_exitcode_str(target_rc));
3715 }
3716
3717 auth_client_init();
3718 session = smtp_create_session();
3719 message = smtp_add_message(session);
3720
3721 smtp_starttls_enable(session, Starttls_ENABLED);
3722
3723 sa.sa_handler = SIG_IGN;
3724 sigemptyset(&sa.sa_mask);
3725 sa.sa_flags = 0;
3726 sigaction(SIGPIPE, &sa, NULL);
3727
3728 smtp_set_server(session, crm_mail_host);
3729
3730 authctx = auth_create_context();
3731 auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
3732
3733 smtp_set_eventcb(session, event_cb, NULL);
3734
3735
3736
3737 if (!noauth) {
3738 crm_debug("Adding authentication context");
3739 smtp_auth_set_context(session, authctx);
3740 }
3741
3742 if (crm_mail_from == NULL) {
3743 struct utsname us;
3744 char auto_from[BODY_MAX];
3745
3746 CRM_ASSERT(uname(&us) == 0);
3747 snprintf(auto_from, BODY_MAX, "crm_mon@%s", us.nodename);
3748 smtp_set_reverse_path(message, auto_from);
3749
3750 } else {
3751
3752 smtp_set_reverse_path(message, crm_mail_from);
3753 }
3754
3755 smtp_set_header(message, "To", NULL , NULL );
3756 smtp_add_recipient(message, crm_mail_to);
3757
3758
3759 smtp_set_header(message, "Subject", crm_mail_subject);
3760 smtp_set_header_option(message, "Subject", Hdr_OVERRIDE, 1);
3761
3762 smtp_set_message_str(message, crm_mail_body);
3763 smtp_set_monitorcb(session, crm_smtp_debug, &smtp_debug, 1);
3764
3765 if (smtp_start_session(session)) {
3766 char buf[128];
3767 int rc = smtp_errno();
3768
3769 crm_err("SMTP server problem: %s (%d)", smtp_strerror(rc, buf, sizeof buf), rc);
3770
3771 } else {
3772 char buf[128];
3773 int rc = smtp_errno();
3774 const smtp_status_t *smtp_status = smtp_message_transfer_status(message);
3775
3776 if (rc != 0) {
3777 crm_err("SMTP server problem: %s (%d)", smtp_strerror(rc, buf, sizeof buf), rc);
3778 }
3779 crm_info("Send status: %d %s", smtp_status->code, crm_str(smtp_status->text));
3780 smtp_enumerate_recipients(message, print_recipient_status, NULL);
3781 }
3782
3783 smtp_destroy_session(session);
3784 auth_destroy_context(authctx);
3785 auth_client_exit();
3786 #endif
3787 return 0;
3788 }
3789
3790 static void
3791 handle_rsc_op(xmlNode * xml, const char *node_id)
3792 {
3793 int rc = -1;
3794 int status = -1;
3795 int action = -1;
3796 int interval = 0;
3797 int target_rc = -1;
3798 int transition_num = -1;
3799 gboolean notify = TRUE;
3800
3801 char *rsc = NULL;
3802 char *task = NULL;
3803 const char *desc = NULL;
3804 const char *magic = NULL;
3805 const char *id = NULL;
3806 char *update_te_uuid = NULL;
3807 const char *node = NULL;
3808
3809 xmlNode *n = xml;
3810 xmlNode * rsc_op = xml;
3811
3812 if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
3813 xmlNode *cIter;
3814
3815 for(cIter = xml->children; cIter; cIter = cIter->next) {
3816 handle_rsc_op(cIter, node_id);
3817 }
3818
3819 return;
3820 }
3821
3822 id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
3823 if (id == NULL) {
3824
3825 id = ID(rsc_op);
3826 }
3827
3828 magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
3829 if (magic == NULL) {
3830
3831 return;
3832 }
3833
3834 if (FALSE == decode_transition_magic(magic, &update_te_uuid, &transition_num, &action,
3835 &status, &rc, &target_rc)) {
3836 crm_err("Invalid event %s detected for %s", magic, id);
3837 return;
3838 }
3839
3840 if (parse_op_key(id, &rsc, &task, &interval) == FALSE) {
3841 crm_err("Invalid event detected for %s", id);
3842 goto bail;
3843 }
3844
3845 node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
3846
3847 while (n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) {
3848 n = n->parent;
3849 }
3850
3851 if(node == NULL && n) {
3852 node = crm_element_value(n, XML_ATTR_UNAME);
3853 }
3854
3855 if (node == NULL && n) {
3856 node = ID(n);
3857 }
3858
3859 if (node == NULL) {
3860 node = node_id;
3861 }
3862
3863 if (node == NULL) {
3864 crm_err("No node detected for event %s (%s)", magic, id);
3865 goto bail;
3866 }
3867
3868
3869 desc = pcmk_strerror(pcmk_ok);
3870 if (status == PCMK_LRM_OP_DONE && target_rc == rc) {
3871 crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc);
3872 if (rc == PCMK_OCF_NOT_RUNNING) {
3873 notify = FALSE;
3874 }
3875
3876 } else if (status == PCMK_LRM_OP_DONE) {
3877 desc = services_ocf_exitcode_str(rc);
3878 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
3879
3880 } else {
3881 desc = services_lrm_status_str(status);
3882 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
3883 }
3884
3885 if (notify && snmp_target) {
3886 send_snmp_trap(node, rsc, task, target_rc, rc, status, desc);
3887 }
3888 if (notify && crm_mail_to) {
3889 send_smtp_trap(node, rsc, task, target_rc, rc, status, desc);
3890 }
3891 if (notify && external_agent) {
3892 send_custom_trap(node, rsc, task, target_rc, rc, status, desc);
3893 }
3894 bail:
3895 free(update_te_uuid);
3896 free(rsc);
3897 free(task);
3898 }
3899
3900 static gboolean
3901 mon_trigger_refresh(gpointer user_data)
3902 {
3903 mainloop_set_trigger(refresh_trigger);
3904 return FALSE;
3905 }
3906
3907 #define NODE_PATT "/lrm[@id="
3908 static char *get_node_from_xpath(const char *xpath)
3909 {
3910 char *nodeid = NULL;
3911 char *tmp = strstr(xpath, NODE_PATT);
3912
3913 if(tmp) {
3914 tmp += strlen(NODE_PATT);
3915 tmp += 1;
3916
3917 nodeid = strdup(tmp);
3918 tmp = strstr(nodeid, "\'");
3919 CRM_ASSERT(tmp);
3920 tmp[0] = 0;
3921 }
3922 return nodeid;
3923 }
3924
3925 static void crm_diff_update_v2(const char *event, xmlNode * msg)
3926 {
3927 xmlNode *change = NULL;
3928 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
3929
3930 for (change = __xml_first_child(diff); change != NULL; change = __xml_next(change)) {
3931 const char *name = NULL;
3932 const char *op = crm_element_value(change, XML_DIFF_OP);
3933 const char *xpath = crm_element_value(change, XML_DIFF_PATH);
3934 xmlNode *match = NULL;
3935 const char *node = NULL;
3936
3937 if(op == NULL) {
3938 continue;
3939
3940 } else if(strcmp(op, "create") == 0) {
3941 match = change->children;
3942
3943 } else if(strcmp(op, "move") == 0) {
3944 continue;
3945
3946 } else if(strcmp(op, "delete") == 0) {
3947 continue;
3948
3949 } else if(strcmp(op, "modify") == 0) {
3950 match = first_named_child(change, XML_DIFF_RESULT);
3951 if(match) {
3952 match = match->children;
3953 }
3954 }
3955
3956 if(match) {
3957 name = (const char *)match->name;
3958 }
3959
3960 crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
3961 if(xpath == NULL) {
3962
3963
3964 } else if(name == NULL) {
3965 crm_debug("No result for %s operation to %s", op, xpath);
3966 CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
3967
3968 } else if(strcmp(name, XML_TAG_CIB) == 0) {
3969 xmlNode *state = NULL;
3970 xmlNode *status = first_named_child(match, XML_CIB_TAG_STATUS);
3971
3972 for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
3973 node = crm_element_value(state, XML_ATTR_UNAME);
3974 if (node == NULL) {
3975 node = ID(state);
3976 }
3977 handle_rsc_op(state, node);
3978 }
3979
3980 } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
3981 xmlNode *state = NULL;
3982
3983 for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
3984 node = crm_element_value(state, XML_ATTR_UNAME);
3985 if (node == NULL) {
3986 node = ID(state);
3987 }
3988 handle_rsc_op(state, node);
3989 }
3990
3991 } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
3992 node = crm_element_value(match, XML_ATTR_UNAME);
3993 if (node == NULL) {
3994 node = ID(match);
3995 }
3996 handle_rsc_op(match, node);
3997
3998 } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
3999 node = ID(match);
4000 handle_rsc_op(match, node);
4001
4002 } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
4003 char *local_node = get_node_from_xpath(xpath);
4004
4005 handle_rsc_op(match, local_node);
4006 free(local_node);
4007
4008 } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
4009 char *local_node = get_node_from_xpath(xpath);
4010
4011 handle_rsc_op(match, local_node);
4012 free(local_node);
4013
4014 } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
4015 char *local_node = get_node_from_xpath(xpath);
4016
4017 handle_rsc_op(match, local_node);
4018 free(local_node);
4019
4020 } else {
4021 crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
4022 }
4023 }
4024 }
4025
4026 static void crm_diff_update_v1(const char *event, xmlNode * msg)
4027 {
4028
4029 xmlXPathObject *xpathObj = xpath_search(msg,
4030 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
4031 "//" XML_LRM_TAG_RSC_OP);
4032 int lpc = 0, max = numXpathResults(xpathObj);
4033
4034 for (lpc = 0; lpc < max; lpc++) {
4035 xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
4036
4037 handle_rsc_op(rsc_op, NULL);
4038 }
4039 freeXpathObject(xpathObj);
4040 }
4041
4042 void
4043 crm_diff_update(const char *event, xmlNode * msg)
4044 {
4045 int rc = -1;
4046 long now = time(NULL);
4047 static bool stale = FALSE;
4048 static int updates = 0;
4049 static mainloop_timer_t *refresh_timer = NULL;
4050 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
4051
4052 print_dot();
4053
4054 if(refresh_timer == NULL) {
4055 refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL);
4056 }
4057
4058 if (current_cib != NULL) {
4059 rc = xml_apply_patchset(current_cib, diff, TRUE);
4060
4061 switch (rc) {
4062 case -pcmk_err_diff_resync:
4063 case -pcmk_err_diff_failed:
4064 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
4065 free_xml(current_cib); current_cib = NULL;
4066 break;
4067 case pcmk_ok:
4068 updates++;
4069 break;
4070 default:
4071 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
4072 free_xml(current_cib); current_cib = NULL;
4073 }
4074 }
4075
4076 if (current_cib == NULL) {
4077 crm_trace("Re-requesting the full cib");
4078 cib->cmds->query(cib, NULL, ¤t_cib, cib_scope_local | cib_sync_call);
4079 }
4080
4081 if (crm_mail_to || snmp_target || external_agent) {
4082 int format = 0;
4083 crm_element_value_int(diff, "format", &format);
4084 switch(format) {
4085 case 1:
4086 crm_diff_update_v1(event, msg);
4087 break;
4088 case 2:
4089 crm_diff_update_v2(event, msg);
4090 break;
4091 default:
4092 crm_err("Unknown patch format: %d", format);
4093 }
4094 }
4095
4096 if (current_cib == NULL) {
4097 if(!stale) {
4098 print_as("--- Stale data ---");
4099 }
4100 stale = TRUE;
4101 return;
4102 }
4103
4104 stale = FALSE;
4105
4106
4107
4108
4109
4110 if ((now - last_refresh) > (reconnect_msec / 1000)) {
4111 mainloop_set_trigger(refresh_trigger);
4112 mainloop_timer_stop(refresh_timer);
4113 updates = 0;
4114
4115 } else if(updates > 10) {
4116 mainloop_set_trigger(refresh_trigger);
4117 mainloop_timer_stop(refresh_timer);
4118 updates = 0;
4119
4120 } else {
4121 mainloop_timer_start(refresh_timer);
4122 }
4123 }
4124
4125 gboolean
4126 mon_refresh_display(gpointer user_data)
4127 {
4128 xmlNode *cib_copy = copy_xml(current_cib);
4129 pe_working_set_t data_set;
4130
4131 last_refresh = time(NULL);
4132
4133 if (cli_config_update(&cib_copy, NULL, FALSE) == FALSE) {
4134 if (cib) {
4135 cib->cmds->signoff(cib);
4136 }
4137 print_as("Upgrade failed: %s", pcmk_strerror(-pcmk_err_schema_validation));
4138 if (output_format == mon_output_console) {
4139 sleep(2);
4140 }
4141 clean_up(EX_USAGE);
4142 return FALSE;
4143 }
4144
4145 set_working_set_defaults(&data_set);
4146 data_set.input = cib_copy;
4147 cluster_status(&data_set);
4148
4149
4150
4151
4152 if (show & (mon_show_bans | mon_show_tickets)) {
4153 xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input);
4154 unpack_constraints(cib_constraints, &data_set);
4155 }
4156
4157 switch (output_format) {
4158 case mon_output_html:
4159 case mon_output_cgi:
4160 if (print_html_status(&data_set, output_filename) != 0) {
4161 fprintf(stderr, "Critical: Unable to output html file\n");
4162 clean_up(EX_USAGE);
4163 }
4164 break;
4165
4166 case mon_output_xml:
4167 print_xml_status(&data_set);
4168 break;
4169
4170 case mon_output_monitor:
4171 print_simple_status(&data_set);
4172 if (has_warnings) {
4173 clean_up(MON_STATUS_WARN);
4174 }
4175 break;
4176
4177 case mon_output_plain:
4178 case mon_output_console:
4179 print_status(&data_set);
4180 break;
4181
4182 case mon_output_none:
4183 break;
4184 }
4185
4186 cleanup_alloc_calculations(&data_set);
4187 return TRUE;
4188 }
4189
4190 void
4191 mon_st_callback(stonith_t * st, stonith_event_t * e)
4192 {
4193 char *desc = crm_strdup_printf("Operation %s requested by %s for peer %s: %s (ref=%s)",
4194 e->operation, e->origin, e->target, pcmk_strerror(e->result),
4195 e->id);
4196
4197 if (snmp_target) {
4198 send_snmp_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4199 }
4200 if (crm_mail_to) {
4201 send_smtp_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4202 }
4203 if (external_agent) {
4204 send_custom_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4205 }
4206 free(desc);
4207 }
4208
4209
4210
4211
4212 void
4213 clean_up(int rc)
4214 {
4215 #if ENABLE_SNMP
4216 netsnmp_session *session = crm_snmp_init(NULL, NULL);
4217
4218 if (session) {
4219 snmp_close(session);
4220 snmp_shutdown("snmpapp");
4221 }
4222 #endif
4223
4224 #if CURSES_ENABLED
4225 if (output_format == mon_output_console) {
4226 output_format = mon_output_plain;
4227 echo();
4228 nocbreak();
4229 endwin();
4230 }
4231 #endif
4232
4233 if (cib != NULL) {
4234 cib->cmds->signoff(cib);
4235 cib_delete(cib);
4236 cib = NULL;
4237 }
4238
4239 free(output_filename);
4240 free(xml_file);
4241 free(pid_file);
4242
4243 if (rc >= 0) {
4244 crm_exit(rc);
4245 }
4246 return;
4247 }