This source file includes following definitions.
- try_mainloop_connect
- notify_callback
- fence_callback
- async_fence_helper
- mainloop_fencing
- handle_level
- fence_action_str
- print_fence_event
- show_history
- main
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 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <sys/utsname.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <fcntl.h>
32
33 #include <crm/crm.h>
34 #include <crm/msg_xml.h>
35 #include <crm/common/ipc.h>
36 #include <crm/cluster/internal.h>
37 #include <crm/common/mainloop.h>
38
39 #include <crm/stonith-ng.h>
40 #include <crm/cib.h>
41 #include <crm/pengine/status.h>
42
43 #include <crm/common/xml.h>
44
45
46
47 static struct crm_option long_options[] = {
48 { "help", no_argument, NULL, '?',
49 "\tDisplay this text and exit."
50 },
51 { "version", no_argument, NULL, '$',
52 "\tDisplay version information and exit."
53 },
54 { "verbose", no_argument, NULL, 'V',
55 "\tIncrease debug output (may be specified multiple times)."
56 },
57 { "quiet", no_argument, NULL, 'q',
58 "\tBe less descriptive in output."
59 },
60
61 { "-spacer-", no_argument, NULL, '-', "\nDevice definition commands:" },
62
63 { "register", required_argument, NULL, 'R',
64 "Register the named stonith device. Requires: --agent.\n"
65 "\t\t\tOptional: any number of --option and/or --env entries."
66 },
67 { "deregister", required_argument, NULL, 'D',
68 "De-register the named stonith device."
69 },
70 { "register-level", required_argument, NULL, 'r',
71 "Register a stonith level for the named target,\n"
72 "\t\t\tspecified as one of NAME, @PATTERN, or ATTR=VALUE.\n"
73 "\t\t\tRequires: --index and one or more --device entries."
74 },
75 { "deregister-level", required_argument, NULL, 'd',
76 "Unregister a stonith level for the named target,\n"
77 "\t\t\tspecified as for --register-level. Requires: --index."
78 },
79
80 { "-spacer-", no_argument, NULL, '-', "\nQueries:" },
81
82 { "list", required_argument, NULL, 'l',
83 "List devices that can terminate the specified host.\n"
84 "\t\t\tOptional: --timeout."
85 },
86 { "list-registered", no_argument, NULL, 'L',
87 "List all registered devices. Optional: --timeout."
88 },
89 { "list-installed", no_argument, NULL, 'I',
90 "List all installed devices. Optional: --timeout."
91 },
92 { "list-targets", required_argument, NULL, 's',
93 "List the targets that can be fenced by the\n"
94 "\t\t\tnamed device. Optional: --timeout."
95 },
96 { "metadata", no_argument, NULL, 'M',
97 "\tShow agent metadata. Requires: --agent.\n"
98 "\t\t\tOptional: --timeout."
99 },
100 { "query", required_argument, NULL, 'Q',
101 "Check the named device's status. Optional: --timeout."
102 },
103
104 { "-spacer-", no_argument, NULL, '-', "\nFencing Commands:" },
105
106 { "fence", required_argument, NULL, 'F',
107 "Fence named host. Optional: --timeout, --tolerance."
108 },
109 { "unfence", required_argument, NULL, 'U',
110 "Unfence named host. Optional: --timeout, --tolerance."
111 },
112 { "reboot", required_argument, NULL, 'B',
113 "Reboot named host. Optional: --timeout, --tolerance."
114 },
115 { "confirm", required_argument, NULL, 'C',
116 "Tell cluster that named host is now safely down."
117 },
118 { "history", required_argument, NULL, 'H',
119 "Show last successful fencing operation for named node\n"
120 "\t\t\t(or '*' for all nodes). Optional: --timeout, --quiet\n"
121 "\t\t\t(show only the operation's epoch timestamp),\n"
122 "\t\t\t--verbose (show all recorded and pending operations)."
123 },
124 { "last", required_argument, NULL, 'h',
125 "Indicate when the named node was last fenced.\n"
126 "\t\t\tOptional: --as-node-id."
127 },
128
129 { "-spacer-", no_argument, NULL, '-', "\nAdditional Options:" },
130
131 { "agent", required_argument, NULL, 'a',
132 "The agent to use (for example, fence_xvm;\n"
133 "\t\t\twith --register, --metadata)."
134 },
135 { "option", required_argument, NULL, 'o',
136 "Specify a device configuration parameter as NAME=VALUE\n"
137 "\t\t\t(with --register)."
138 },
139 { "env-option", required_argument, NULL, 'e',
140 "Specify a device configuration parameter with the\n"
141 "\t\t\tspecified name, using the value of the\n"
142 "\t\t\tenvironment variable of the same name prefixed with\n"
143 "\t\t\tOCF_RESKEY_ (with --register)."
144 },
145 { "tag", required_argument, NULL, 'T',
146 "Identify fencing operations in logs with the specified\n"
147 "\t\t\ttag; useful when multiple entities might invoke\n"
148 "\t\t\tstonith_admin (used with most commands)."
149 },
150 { "device", required_argument, NULL, 'v',
151 "A device to associate with a given host and\n"
152 "\t\t\tstonith level (with --register-level)."
153 },
154 { "index", required_argument, NULL, 'i',
155 "The stonith level (1-9) (with --register-level,\n"
156 "\t\t\t--deregister-level)."
157 },
158 { "timeout", required_argument, NULL, 't',
159 "Operation timeout in seconds (default 120;\n"
160 "\t\t\tused with most commands)."
161 },
162 { "as-node-id", no_argument, NULL, 'n',
163 "(Advanced) The supplied node is the corosync node ID\n"
164 "\t\t\t(with --last)."
165 },
166 { "tolerance", required_argument, NULL, 0,
167 "(Advanced) Do nothing if an equivalent --fence request\n"
168 "\t\t\tsucceeded less than this many seconds earlier\n"
169 "\t\t\t(with --fence, --unfence, --reboot)."
170 },
171
172 { "list-all", no_argument, NULL, 'L', NULL, pcmk_option_hidden },
173 { 0, 0, 0, 0 }
174 };
175
176
177 int st_opts = st_opt_sync_call | st_opt_allow_suicide;
178
179 GMainLoop *mainloop = NULL;
180 struct {
181 stonith_t *st;
182 const char *target;
183 const char *action;
184 char *name;
185 int timeout;
186 int tolerance;
187 int rc;
188 } async_fence_data;
189
190 static int
191 try_mainloop_connect(void)
192 {
193 stonith_t *st = async_fence_data.st;
194 int tries = 10;
195 int i = 0;
196 int rc = 0;
197
198 for (i = 0; i < tries; i++) {
199 crm_debug("Connecting as %s", async_fence_data.name);
200 rc = st->cmds->connect(st, async_fence_data.name, NULL);
201
202 if (!rc) {
203 crm_debug("stonith client connection established");
204 return 0;
205 } else {
206 crm_debug("stonith client connection failed");
207 }
208 sleep(1);
209 }
210
211 crm_err("Could not connect to stonithd.");
212 return -1;
213 }
214
215 static void
216 notify_callback(stonith_t * st, stonith_event_t * e)
217 {
218 if (e->result != pcmk_ok) {
219 return;
220 }
221
222 if (safe_str_eq(async_fence_data.target, e->target) &&
223 safe_str_eq(async_fence_data.action, e->action)) {
224
225 async_fence_data.rc = e->result;
226 g_main_loop_quit(mainloop);
227 }
228 }
229
230 static void
231 fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
232 {
233 async_fence_data.rc = data->rc;
234
235 g_main_loop_quit(mainloop);
236 }
237
238 static gboolean
239 async_fence_helper(gpointer user_data)
240 {
241 stonith_t *st = async_fence_data.st;
242 int call_id = 0;
243
244 if (try_mainloop_connect()) {
245 g_main_loop_quit(mainloop);
246 return TRUE;
247 }
248
249 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, notify_callback);
250
251 call_id = st->cmds->fence(st,
252 st_opt_allow_suicide,
253 async_fence_data.target,
254 async_fence_data.action,
255 async_fence_data.timeout, async_fence_data.tolerance);
256
257 if (call_id < 0) {
258 g_main_loop_quit(mainloop);
259 return TRUE;
260 }
261
262 st->cmds->register_callback(st,
263 call_id,
264 async_fence_data.timeout,
265 st_opt_timeout_updates, NULL, "callback", fence_callback);
266
267 return TRUE;
268 }
269
270 static int
271 mainloop_fencing(stonith_t * st, const char *target, const char *action, int timeout, int tolerance)
272 {
273 crm_trigger_t *trig;
274
275 async_fence_data.st = st;
276 async_fence_data.target = target;
277 async_fence_data.action = action;
278 async_fence_data.timeout = timeout;
279 async_fence_data.tolerance = tolerance;
280 async_fence_data.rc = -1;
281
282 trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
283 mainloop_set_trigger(trig);
284
285 mainloop = g_main_new(FALSE);
286 g_main_run(mainloop);
287
288 return async_fence_data.rc;
289 }
290
291 static int
292 handle_level(stonith_t *st, char *target, int fence_level,
293 stonith_key_value_t *devices, bool added)
294 {
295 char *node = NULL;
296 char *pattern = NULL;
297 char *name = NULL;
298 char *value = strchr(target, '=');
299
300
301 if (value != NULL) {
302 name = target;
303 *value++ = '\0';
304 } else if (*target == '@') {
305 pattern = target + 1;
306 } else {
307 node = target;
308 }
309
310
311 if (added) {
312 return st->cmds->register_level_full(st, st_opts, node, pattern,
313 name, value, fence_level,
314 devices);
315 }
316 return st->cmds->remove_level_full(st, st_opts, node, pattern,
317 name, value, fence_level);
318 }
319
320 static char *
321 fence_action_str(const char *action)
322 {
323 char *str = NULL;
324
325 if (action == NULL) {
326 str = strdup("unknown");
327 } else if (action[0] == 'o') {
328 str = crm_concat("turn", action, ' ');
329 } else {
330 str = strdup(action);
331 }
332 return str;
333 }
334
335 static void
336 print_fence_event(stonith_history_t *event)
337 {
338 char *action_s = fence_action_str(event->action);
339 time_t complete = event->completed;
340
341 printf("%s was able to %s node %s on behalf of %s from %s at %s\n",
342 (event->delegate? event->delegate : "This node"), action_s,
343 event->target, event->client, event->origin, ctime(&complete));
344 free(action_s);
345 }
346
347 static int
348 show_history(stonith_t *st, const char *target, int timeout, int quiet,
349 int verbose)
350 {
351 stonith_history_t *history, *hp, *latest = NULL;
352 int rc = 0;
353
354 rc = st->cmds->history(st, st_opts,
355 (safe_str_eq(target, "*")? NULL : target),
356 &history, timeout);
357 for (hp = history; hp; hp = hp->next) {
358 char *action_s = NULL;
359 time_t complete = hp->completed;
360
361 if (hp->state == st_done) {
362 latest = hp;
363 }
364
365 if (quiet || !verbose) {
366 continue;
367 }
368
369 if (hp->state == st_failed) {
370 action_s = fence_action_str(hp->action);
371 printf("%s failed to %s node %s on behalf of %s from %s at %s\n",
372 hp->delegate ? hp->delegate : "We", action_s, hp->target,
373 hp->client, hp->origin, ctime(&complete));
374
375 } else if (hp->state == st_done) {
376 print_fence_event(latest);
377
378 } else {
379
380
381
382
383 action_s = fence_action_str(hp->action);
384 printf("%s at %s wishes to %s node %s - %d %d\n",
385 hp->client, hp->origin, action_s, hp->target, hp->state, hp->completed);
386 }
387
388 free(action_s);
389 }
390
391 if (latest) {
392 if (quiet) {
393 printf("%d\n", latest->completed);
394 } else if (!verbose) {
395 print_fence_event(latest);
396 }
397 }
398 return rc;
399 }
400
401 int
402 main(int argc, char **argv)
403 {
404 int flag;
405 int rc = 0;
406 int quiet = 0;
407 int verbose = 0;
408 int argerr = 0;
409 int timeout = 120;
410 int option_index = 0;
411 int fence_level = 0;
412 int no_connect = 0;
413 int tolerance = 0;
414 int as_nodeid = FALSE;
415
416 char *name = NULL;
417 char *value = NULL;
418 char *target = NULL;
419 char *lists = NULL;
420 const char *agent = NULL;
421 const char *device = NULL;
422 const char *longname = NULL;
423
424 char action = 0;
425 stonith_t *st = NULL;
426 stonith_key_value_t *params = NULL;
427 stonith_key_value_t *devices = NULL;
428 stonith_key_value_t *dIter = NULL;
429
430 crm_log_cli_init("stonith_admin");
431 crm_set_options(NULL, "<command> [<options>]", long_options,
432 "access the Pacemaker fencing API");
433
434 async_fence_data.name = strdup(crm_system_name);
435
436 while (1) {
437 flag = crm_get_option_long(argc, argv, &option_index, &longname);
438 if (flag == -1)
439 break;
440
441 switch (flag) {
442 case 'V':
443 verbose = 1;
444 crm_bump_log_level(argc, argv);
445 break;
446 case '$':
447 case '?':
448 crm_help(flag, EX_OK);
449 break;
450 case 'I':
451 no_connect = 1;
452
453 case 'L':
454 action = flag;
455 break;
456 case 'q':
457 quiet = 1;
458 break;
459 case 'Q':
460 case 'R':
461 case 'D':
462 case 's':
463 action = flag;
464 device = optarg;
465 break;
466 case 'T':
467 free(async_fence_data.name);
468 async_fence_data.name = crm_strdup_printf("%s.%s", crm_system_name, optarg);
469 break;
470 case 'a':
471 agent = optarg;
472 break;
473 case 'l':
474 target = optarg;
475 action = 'L';
476 break;
477 case 'M':
478 no_connect = 1;
479 action = flag;
480 break;
481 case 't':
482 timeout = crm_atoi(optarg, NULL);
483 break;
484 case 'B':
485 case 'F':
486 case 'U':
487
488 no_connect = 1;
489
490 case 'C':
491
492 crm_log_args(argc, argv);
493 target = optarg;
494 action = flag;
495 break;
496 case 'n':
497 as_nodeid = TRUE;
498 break;
499 case 'h':
500 case 'H':
501 case 'r':
502 case 'd':
503 target = optarg;
504 action = flag;
505 break;
506 case 'i':
507 fence_level = crm_atoi(optarg, NULL);
508 break;
509 case 'v':
510 devices = stonith_key_value_add(devices, NULL, optarg);
511 break;
512 case 'o':
513 crm_info("Scanning: -o %s", optarg);
514 rc = sscanf(optarg, "%m[^=]=%m[^=]", &name, &value);
515 if (rc != 2) {
516 crm_err("Invalid option: -o %s", optarg);
517 ++argerr;
518 } else {
519 crm_info("Got: '%s'='%s'", name, value);
520 params = stonith_key_value_add(params, name, value);
521 }
522 free(value); value = NULL;
523 free(name); name = NULL;
524 break;
525 case 'e':
526 {
527 char *key = crm_concat("OCF_RESKEY", optarg, '_');
528 const char *env = getenv(key);
529
530 if (env == NULL) {
531 crm_err("Invalid option: -e %s", optarg);
532 ++argerr;
533 } else {
534 crm_info("Got: '%s'='%s'", optarg, env);
535 params = stonith_key_value_add(params, optarg, env);
536 }
537 free(key);
538 }
539 break;
540 case 0:
541 if (safe_str_eq("tolerance", longname)) {
542 tolerance = crm_get_msec(optarg) / 1000;
543 }
544 break;
545 default:
546 ++argerr;
547 break;
548 }
549 }
550
551 if (optind > argc) {
552 ++argerr;
553 }
554
555 if (argerr) {
556 crm_help('?', EX_USAGE);
557 }
558
559 st = stonith_api_new();
560
561 if (!no_connect) {
562 rc = st->cmds->connect(st, async_fence_data.name, NULL);
563 if (rc < 0) {
564 goto done;
565 }
566 }
567
568 switch (action) {
569 case 'I':
570 rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout);
571 for (dIter = devices; dIter; dIter = dIter->next) {
572 fprintf(stdout, " %s\n", dIter->value);
573 }
574 if (rc == 0) {
575 fprintf(stderr, "No devices found\n");
576
577 } else if (rc > 0) {
578 fprintf(stderr, "%d devices found\n", rc);
579 rc = 0;
580 }
581 stonith_key_value_freeall(devices, 1, 1);
582 break;
583 case 'L':
584 rc = st->cmds->query(st, st_opts, target, &devices, timeout);
585 for (dIter = devices; dIter; dIter = dIter->next) {
586 fprintf(stdout, " %s\n", dIter->value);
587 }
588 if (rc == 0) {
589 fprintf(stderr, "No devices found\n");
590 } else if (rc > 0) {
591 fprintf(stderr, "%d devices found\n", rc);
592 rc = 0;
593 }
594 stonith_key_value_freeall(devices, 1, 1);
595 break;
596 case 'Q':
597 rc = st->cmds->monitor(st, st_opts, device, timeout);
598 if (rc < 0) {
599 rc = st->cmds->list(st, st_opts, device, NULL, timeout);
600 }
601 break;
602 case 's':
603 rc = st->cmds->list(st, st_opts, device, &lists, timeout);
604 if (rc == 0) {
605 if (lists) {
606 char *source = lists, *dest = lists;
607
608 while (*dest) {
609 if ((*dest == '\\') && (*(dest+1) == 'n')) {
610 *source = '\n';
611 dest++;
612 dest++;
613 source++;
614 } else if ((*dest == ',') || (*dest == ';')) {
615 dest++;
616 } else {
617 *source = *dest;
618 dest++;
619 source++;
620 }
621
622 if (!(*dest)) {
623 *source = 0;
624 }
625 }
626 fprintf(stdout, "%s", lists);
627 free(lists);
628 }
629 } else {
630 fprintf(stderr, "List command returned error. rc : %d\n", rc);
631 }
632 break;
633 case 'R':
634 rc = st->cmds->register_device(st, st_opts, device, "stonith-ng", agent, params);
635 break;
636 case 'D':
637 rc = st->cmds->remove_device(st, st_opts, device);
638 break;
639 case 'd':
640 case 'r':
641 rc = handle_level(st, target, fence_level, devices, action == 'r');
642 break;
643 case 'M':
644 if (agent == NULL) {
645 printf("Please specify an agent to query using -a,--agent [value]\n");
646 return -1;
647 } else {
648 char *buffer = NULL;
649
650 rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer, timeout);
651 if (rc == pcmk_ok) {
652 printf("%s\n", buffer);
653 }
654 free(buffer);
655 }
656 break;
657 case 'C':
658 rc = st->cmds->confirm(st, st_opts, target);
659 break;
660 case 'B':
661 rc = mainloop_fencing(st, target, "reboot", timeout, tolerance);
662 break;
663 case 'F':
664 rc = mainloop_fencing(st, target, "off", timeout, tolerance);
665 break;
666 case 'U':
667 rc = mainloop_fencing(st, target, "on", timeout, tolerance);
668 break;
669 case 'h':
670 {
671 time_t when = 0;
672
673 if(as_nodeid) {
674 uint32_t nodeid = atol(target);
675 when = stonith_api_time(nodeid, NULL, FALSE);
676 } else {
677 when = stonith_api_time(0, target, FALSE);
678 }
679 if(when) {
680 printf("Node %s last kicked at: %s\n", target, ctime(&when));
681 } else {
682 printf("Node %s has never been kicked\n", target);
683 }
684 }
685 break;
686 case 'H':
687 rc = show_history(st, target, timeout, quiet, verbose);
688 break;
689 }
690
691 done:
692 free(async_fence_data.name);
693 crm_info("Command returned: %s (%d)", pcmk_strerror(rc), rc);
694
695 stonith_key_value_freeall(params, 1, 1);
696 st->cmds->disconnect(st);
697 stonith_api_delete(st);
698
699 return rc;
700 }