This source file includes following definitions.
- add_env_params
- add_stonith_device
- add_tolerance
- add_stonith_params
- set_tag
- build_arg_context
- request_fencing
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <sys/utsname.h>
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <glib.h>
26
27 #include <crm/crm.h>
28 #include <crm/common/ipc.h>
29 #include <crm/cluster/internal.h>
30 #include <crm/common/cmdline_internal.h>
31 #include <crm/common/output_internal.h>
32
33 #include <crm/stonith-ng.h>
34 #include <crm/fencing/internal.h>
35 #include <crm/cib.h>
36 #include <crm/pengine/status.h>
37
38 #include <crm/common/xml.h>
39 #include <pacemaker-internal.h>
40
41 #define SUMMARY "stonith_admin - Access the Pacemaker fencing API"
42
43 char action = 0;
44
45 struct {
46 gboolean as_nodeid;
47 gboolean broadcast;
48 gboolean cleanup;
49 gboolean installed;
50 gboolean metadata;
51 gboolean registered;
52 gboolean validate_cfg;
53 GList *devices;
54 GHashTable *params;
55 int fence_level;
56 int timeout ;
57 long long tolerance_ms;
58 int delay;
59 char *agent;
60 char *confirm_host;
61 char *fence_host;
62 char *history;
63 char *last_fenced;
64 char *query;
65 char *reboot_host;
66 char *register_dev;
67 char *register_level;
68 char *targets;
69 char *terminate;
70 char *unfence_host;
71 char *unregister_dev;
72 char *unregister_level;
73 } options = {
74 .timeout = 120,
75 .delay = 0
76 };
77
78 gboolean add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
79 gboolean add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
80 gboolean add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
81 gboolean add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
82 gboolean set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
83
84 #define INDENT " "
85
86
87 static GOptionEntry defn_entries[] = {
88 { "register", 'R', 0, G_OPTION_ARG_STRING, &options.register_dev,
89 "Register the named stonith device. Requires: --agent.\n"
90 INDENT "Optional: --option, --env-option.",
91 "DEVICE" },
92 { "deregister", 'D', 0, G_OPTION_ARG_STRING, &options.unregister_dev,
93 "De-register the named stonith device.",
94 "DEVICE" },
95 { "register-level", 'r', 0, G_OPTION_ARG_STRING, &options.register_level,
96 "Register a stonith level for the named target,\n"
97 INDENT "specified as one of NAME, @PATTERN, or ATTR=VALUE.\n"
98 INDENT "Requires: --index and one or more --device entries.",
99 "TARGET" },
100 { "deregister-level", 'd', 0, G_OPTION_ARG_STRING, &options.unregister_level,
101 "Unregister a stonith level for the named target,\n"
102 INDENT "specified as for --register-level. Requires: --index",
103 "TARGET" },
104
105 { NULL }
106 };
107
108 static GOptionEntry query_entries[] = {
109 { "list", 'l', 0, G_OPTION_ARG_STRING, &options.terminate,
110 "List devices that can terminate the specified host.\n"
111 INDENT "Optional: --timeout",
112 "HOST" },
113 { "list-registered", 'L', 0, G_OPTION_ARG_NONE, &options.registered,
114 "List all registered devices. Optional: --timeout.",
115 NULL },
116 { "list-installed", 'I', 0, G_OPTION_ARG_NONE, &options.installed,
117 "List all installed devices. Optional: --timeout.",
118 NULL },
119 { "list-targets", 's', 0, G_OPTION_ARG_STRING, &options.targets,
120 "List the targets that can be fenced by the\n"
121 INDENT "named device. Optional: --timeout.",
122 "DEVICE" },
123 { "metadata", 'M', 0, G_OPTION_ARG_NONE, &options.metadata,
124 "Show agent metadata. Requires: --agent.\n"
125 INDENT "Optional: --timeout.",
126 NULL },
127 { "query", 'Q', 0, G_OPTION_ARG_STRING, &options.query,
128 "Check the named device's status. Optional: --timeout.",
129 "DEVICE" },
130 { "history", 'H', 0, G_OPTION_ARG_STRING, &options.history,
131 "Show last successful fencing operation for named node\n"
132 INDENT "(or '*' for all nodes). Optional: --timeout, --cleanup,\n"
133 INDENT "--quiet (show only the operation's epoch timestamp),\n"
134 INDENT "--verbose (show all recorded and pending operations),\n"
135 INDENT "--broadcast (update history from all nodes available).",
136 "NODE" },
137 { "last", 'h', 0, G_OPTION_ARG_STRING, &options.last_fenced,
138 "Indicate when the named node was last fenced.\n"
139 INDENT "Optional: --as-node-id.",
140 "NODE" },
141 { "validate", 'K', 0, G_OPTION_ARG_NONE, &options.validate_cfg,
142 "Validate a fence device configuration.\n"
143 INDENT "Requires: --agent. Optional: --option, --env-option,\n"
144 INDENT "--quiet (print no output, only return status).",
145 NULL },
146
147 { NULL }
148 };
149
150 static GOptionEntry fence_entries[] = {
151 { "fence", 'F', 0, G_OPTION_ARG_STRING, &options.fence_host,
152 "Fence named host. Optional: --timeout, --tolerance, --delay.",
153 "HOST" },
154 { "unfence", 'U', 0, G_OPTION_ARG_STRING, &options.unfence_host,
155 "Unfence named host. Optional: --timeout, --tolerance, --delay.",
156 "HOST" },
157 { "reboot", 'B', 0, G_OPTION_ARG_STRING, &options.reboot_host,
158 "Reboot named host. Optional: --timeout, --tolerance, --delay.",
159 "HOST" },
160 { "confirm", 'C', 0, G_OPTION_ARG_STRING, &options.confirm_host,
161 "Tell cluster that named host is now safely down.",
162 "HOST", },
163
164 { NULL }
165 };
166
167 static GOptionEntry addl_entries[] = {
168 { "cleanup", 'c', 0, G_OPTION_ARG_NONE, &options.cleanup,
169 "Cleanup wherever appropriate. Requires --history.",
170 NULL },
171 { "broadcast", 'b', 0, G_OPTION_ARG_NONE, &options.broadcast,
172 "Broadcast wherever appropriate.",
173 NULL },
174 { "agent", 'a', 0, G_OPTION_ARG_STRING, &options.agent,
175 "The agent to use (for example, fence_xvm;\n"
176 INDENT "with --register, --metadata, --validate).",
177 "AGENT" },
178 { "option", 'o', 0, G_OPTION_ARG_CALLBACK, add_stonith_params,
179 "Specify a device configuration parameter as NAME=VALUE\n"
180 INDENT "(may be specified multiple times; with --register,\n"
181 INDENT "--validate).",
182 "PARAM" },
183 { "env-option", 'e', 0, G_OPTION_ARG_CALLBACK, add_env_params,
184 "Specify a device configuration parameter with the\n"
185 INDENT "specified name, using the value of the\n"
186 INDENT "environment variable of the same name prefixed with\n"
187 INDENT "OCF_RESKEY_ (may be specified multiple times;\n"
188 INDENT "with --register, --validate).",
189 "PARAM" },
190 { "tag", 'T', 0, G_OPTION_ARG_CALLBACK, set_tag,
191 "Identify fencing operations in logs with the specified\n"
192 INDENT "tag; useful when multiple entities might invoke\n"
193 INDENT "stonith_admin (used with most commands).",
194 "TAG" },
195 { "device", 'v', 0, G_OPTION_ARG_CALLBACK, add_stonith_device,
196 "Device ID (with --register-level, device to associate with\n"
197 INDENT "a given host and level; may be specified multiple times)"
198 #if PCMK__ENABLE_CIBSECRETS
199 "\n" INDENT "(with --validate, name to use to load CIB secrets)"
200 #endif
201 ".",
202 "DEVICE" },
203 { "index", 'i', 0, G_OPTION_ARG_INT, &options.fence_level,
204 "The stonith level (1-9) (with --register-level,\n"
205 INDENT "--deregister-level).",
206 "LEVEL" },
207 { "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
208 "Operation timeout in seconds (default 120;\n"
209 INDENT "used with most commands).",
210 "SECONDS" },
211 { "delay", 'y', 0, G_OPTION_ARG_INT, &options.delay,
212 "Apply a fencing delay in seconds. Any static/random delays from\n"
213 INDENT "pcmk_delay_base/max will be added, otherwise all\n"
214 INDENT "disabled with the value -1\n"
215 INDENT "(default 0; with --fence, --reboot, --unfence).",
216 "SECONDS" },
217 { "as-node-id", 'n', 0, G_OPTION_ARG_NONE, &options.as_nodeid,
218 "(Advanced) The supplied node is the corosync node ID\n"
219 INDENT "(with --last).",
220 NULL },
221 { "tolerance", 0, 0, G_OPTION_ARG_CALLBACK, add_tolerance,
222 "(Advanced) Do nothing if an equivalent --fence request\n"
223 INDENT "succeeded less than this many seconds earlier\n"
224 INDENT "(with --fence, --unfence, --reboot).",
225 "SECONDS" },
226
227 { NULL }
228 };
229
230
231 static pcmk__supported_format_t formats[] = {
232 PCMK__SUPPORTED_FORMAT_HTML,
233 PCMK__SUPPORTED_FORMAT_NONE,
234 PCMK__SUPPORTED_FORMAT_TEXT,
235 PCMK__SUPPORTED_FORMAT_XML,
236 { NULL, NULL, NULL }
237 };
238
239 static const int st_opts = st_opt_sync_call|st_opt_allow_self_fencing;
240
241 static char *name = NULL;
242
243 gboolean
244 add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
245 char *key = crm_strdup_printf("OCF_RESKEY_%s", optarg);
246 const char *env = getenv(key);
247 gboolean retval = TRUE;
248
249 if (env == NULL) {
250 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid option: -e %s", optarg);
251 retval = FALSE;
252 } else {
253 crm_info("Got: '%s'='%s'", optarg, env);
254
255 if (options.params != NULL) {
256 options.params = pcmk__strkey_table(free, free);
257 }
258
259 pcmk__insert_dup(options.params, optarg, env);
260 }
261
262 free(key);
263 return retval;
264 }
265
266 gboolean
267 add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
268 options.devices = g_list_append(options.devices, pcmk__str_copy(optarg));
269 return TRUE;
270 }
271
272 gboolean
273 add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
274
275 options.tolerance_ms = crm_get_msec(optarg);
276
277 if (options.tolerance_ms < 0) {
278 crm_warn("Ignoring invalid tolerance '%s'", optarg);
279 options.tolerance_ms = 0;
280 } else {
281 options.tolerance_ms = QB_MIN(options.tolerance_ms, UINT_MAX);
282 }
283 return TRUE;
284 }
285
286 gboolean
287 add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
288 gchar *name = NULL;
289 gchar *value = NULL;
290 int rc = 0;
291 gboolean retval = TRUE;
292
293 crm_info("Scanning: -o %s", optarg);
294
295 rc = pcmk__scan_nvpair(optarg, &name, &value);
296
297 if (rc != pcmk_rc_ok) {
298 g_set_error(error, PCMK__RC_ERROR, rc, "Invalid option: -o %s: %s", optarg, pcmk_rc_str(rc));
299 retval = FALSE;
300 } else {
301 crm_info("Got: '%s'='%s'", name, value);
302
303 if (options.params == NULL) {
304 options.params = pcmk__strkey_table(free, free);
305 }
306
307 pcmk__insert_dup(options.params, name, value);
308 }
309
310 g_free(name);
311 g_free(value);
312 return retval;
313 }
314
315 gboolean
316 set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
317 free(name);
318 name = crm_strdup_printf("%s.%s", crm_system_name, optarg);
319 return TRUE;
320 }
321
322 static GOptionContext *
323 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
324 GOptionContext *context = NULL;
325
326 GOptionEntry extra_prog_entries[] = {
327 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
328 "Be less descriptive in output.",
329 NULL },
330
331 { NULL }
332 };
333
334 context = pcmk__build_arg_context(args, "text (default), html, xml", group, NULL);
335
336
337
338
339 pcmk__add_main_args(context, extra_prog_entries);
340
341 pcmk__add_arg_group(context, "definition", "Device Definition Commands:",
342 "Show device definition help", defn_entries);
343 pcmk__add_arg_group(context, "queries", "Queries:",
344 "Show query help", query_entries);
345 pcmk__add_arg_group(context, "fence", "Fencing Commands:",
346 "Show fence help", fence_entries);
347 pcmk__add_arg_group(context, "additional", "Additional Options:",
348 "Show additional options", addl_entries);
349 return context;
350 }
351
352
353 static int
354 request_fencing(stonith_t *st, const char *target, const char *command,
355 GError **error)
356 {
357 char *reason = NULL;
358 int rc = pcmk__request_fencing(st, target, command, name,
359 options.timeout * 1000,
360 options.tolerance_ms, options.delay,
361 &reason);
362
363 if (rc != pcmk_rc_ok) {
364 const char *rc_str = pcmk_rc_str(rc);
365 const char *what = "fence";
366
367 if (strcmp(command, PCMK_ACTION_ON) == 0) {
368 what = "unfence";
369 }
370
371
372 if (pcmk__str_eq(rc_str, reason, pcmk__str_none)) {
373 free(reason);
374 reason = NULL;
375 }
376
377 g_set_error(error, PCMK__RC_ERROR, rc,
378 "Couldn't %s %s: %s%s%s%s",
379 what, target, rc_str,
380 ((reason == NULL)? "" : " ("),
381 ((reason == NULL)? "" : reason),
382 ((reason == NULL)? "" : ")"));
383 }
384 free(reason);
385 return rc;
386 }
387
388 int
389 main(int argc, char **argv)
390 {
391 int rc = 0;
392 crm_exit_t exit_code = CRM_EX_OK;
393 bool no_connect = false;
394 bool required_agent = false;
395
396 char *target = NULL;
397 const char *device = NULL;
398 stonith_t *st = NULL;
399
400 GError *error = NULL;
401
402 pcmk__output_t *out = NULL;
403
404 GOptionGroup *output_group = NULL;
405 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
406 gchar **processed_args = pcmk__cmdline_preproc(argv, "adehilorstvyBCDFHQRTU");
407 GOptionContext *context = build_arg_context(args, &output_group);
408
409 pcmk__register_formats(output_group, formats);
410 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
411 exit_code = CRM_EX_USAGE;
412 goto done;
413 }
414
415 pcmk__cli_init_logging("stonith_admin", args->verbosity);
416
417 if (name == NULL) {
418 name = strdup(crm_system_name);
419 }
420
421 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
422 if (rc != pcmk_rc_ok) {
423 exit_code = CRM_EX_ERROR;
424 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
425 args->output_ty, pcmk_rc_str(rc));
426 goto done;
427 }
428
429 pcmk__output_enable_list_element(out);
430
431 stonith__register_messages(out);
432
433 if (args->version) {
434 out->version(out, false);
435 goto done;
436 }
437
438 if (options.validate_cfg) {
439 required_agent = true;
440 no_connect = true;
441 action = 'K';
442 }
443
444 if (options.installed) {
445 no_connect = true;
446 action = 'I';
447 }
448
449 if (options.registered) {
450 action = 'L';
451 }
452
453 if (options.register_dev != NULL) {
454 required_agent = true;
455 action = 'R';
456 device = options.register_dev;
457 }
458
459 if (options.query != NULL) {
460 action = 'Q';
461 device = options.query;
462 }
463
464 if (options.unregister_dev != NULL) {
465 action = 'D';
466 device = options.unregister_dev;
467 }
468
469 if (options.targets != NULL) {
470 action = 's';
471 device = options.targets;
472 }
473
474 if (options.terminate != NULL) {
475 action = 'L';
476 target = options.terminate;
477 }
478
479 if (options.metadata) {
480 no_connect = true;
481 required_agent = true;
482 action = 'M';
483 }
484
485 if (options.reboot_host != NULL) {
486 no_connect = true;
487 action = 'B';
488 target = options.reboot_host;
489 crm_log_args(argc, argv);
490 }
491
492 if (options.fence_host != NULL) {
493 no_connect = true;
494 action = 'F';
495 target = options.fence_host;
496 crm_log_args(argc, argv);
497 }
498
499 if (options.unfence_host != NULL) {
500 no_connect = true;
501 action = 'U';
502 target = options.unfence_host;
503 crm_log_args(argc, argv);
504 }
505
506 if (options.confirm_host != NULL) {
507 action = 'C';
508 target = options.confirm_host;
509 crm_log_args(argc, argv);
510 }
511
512 if (options.last_fenced != NULL) {
513 action = 'h';
514 target = options.last_fenced;
515 }
516
517 if (options.history != NULL) {
518 action = 'H';
519 target = options.history;
520 }
521
522 if (options.register_level != NULL) {
523 action = 'r';
524 target = options.register_level;
525 }
526
527 if (options.unregister_level != NULL) {
528 action = 'd';
529 target = options.unregister_level;
530 }
531
532 if ((options.timeout > (UINT_MAX / 1000)) || (options.timeout < 0)) {
533 out->err(out, "Integer value \"%d\" for -t out of range", options.timeout);
534 exit_code = CRM_EX_USAGE;
535 goto done;
536 }
537
538 if (action == 0) {
539 char *help = g_option_context_get_help(context, TRUE, NULL);
540
541 out->err(out, "%s", help);
542 g_free(help);
543 exit_code = CRM_EX_USAGE;
544 goto done;
545 }
546
547 if (required_agent && options.agent == NULL) {
548 char *help = g_option_context_get_help(context, TRUE, NULL);
549
550 out->err(out, "Please specify an agent to query using -a,--agent [value]");
551 out->err(out, "%s", help);
552 g_free(help);
553 exit_code = CRM_EX_USAGE;
554 goto done;
555 }
556
557 out->quiet = args->quiet;
558
559 st = stonith__api_new();
560 if (st == NULL) {
561 rc = -ENOMEM;
562 } else if (!no_connect) {
563 rc = st->cmds->connect(st, name, NULL);
564 }
565 if (rc < 0) {
566 out->err(out, "Could not connect to fencer: %s", pcmk_strerror(rc));
567 exit_code = CRM_EX_DISCONNECT;
568 goto done;
569 }
570
571 switch (action) {
572 case 'I':
573 rc = pcmk__fence_installed(out, st);
574 if (rc != pcmk_rc_ok) {
575 out->err(out, "Failed to list installed devices: %s", pcmk_rc_str(rc));
576 }
577
578 break;
579
580 case 'L':
581 rc = pcmk__fence_registered(out, st, target, options.timeout*1000);
582 if (rc != pcmk_rc_ok) {
583 out->err(out, "Failed to list registered devices: %s", pcmk_rc_str(rc));
584 }
585
586 break;
587
588 case 'Q':
589 rc = st->cmds->monitor(st, st_opts, device, options.timeout);
590 if (rc != pcmk_rc_ok) {
591 rc = st->cmds->list(st, st_opts, device, NULL, options.timeout);
592 }
593 rc = pcmk_legacy2rc(rc);
594 break;
595
596 case 's':
597 rc = pcmk__fence_list_targets(out, st, device, options.timeout*1000);
598 if (rc != pcmk_rc_ok) {
599 out->err(out, "Couldn't list targets: %s", pcmk_rc_str(rc));
600 }
601
602 break;
603
604 case 'R': {
605
606 stonith_key_value_t *params = NULL;
607 GHashTableIter iter;
608 gpointer key, val;
609
610 if (options.params != NULL) {
611 g_hash_table_iter_init(&iter, options.params);
612 while (g_hash_table_iter_next(&iter, &key, &val)) {
613 params = stonith__key_value_add(params, key, val);
614 }
615 }
616 rc = st->cmds->register_device(st, st_opts, device, NULL, options.agent,
617 params);
618 stonith__key_value_freeall(params, true, true);
619
620 rc = pcmk_legacy2rc(rc);
621 if (rc != pcmk_rc_ok) {
622 out->err(out, "Can't register device %s using agent %s: %s",
623 device, options.agent, pcmk_rc_str(rc));
624 }
625 break;
626 }
627
628 case 'D':
629 rc = st->cmds->remove_device(st, st_opts, device);
630 rc = pcmk_legacy2rc(rc);
631 if (rc != pcmk_rc_ok) {
632 out->err(out, "Can't unregister device %s: %s",
633 device, pcmk_rc_str(rc));
634 }
635 break;
636
637 case 'd':
638 rc = pcmk__fence_unregister_level(st, target, options.fence_level);
639 if (rc != pcmk_rc_ok) {
640 out->err(out, "Can't unregister topology level %d for %s: %s",
641 options.fence_level, target, pcmk_rc_str(rc));
642 }
643 break;
644
645 case 'r':
646 rc = pcmk__fence_register_level(st, target, options.fence_level, options.devices);
647 if (rc != pcmk_rc_ok) {
648 out->err(out, "Can't register topology level %d for %s: %s",
649 options.fence_level, target, pcmk_rc_str(rc));
650 }
651 break;
652
653 case 'M':
654 rc = pcmk__fence_metadata(out, st, options.agent, options.timeout*1000);
655 if (rc != pcmk_rc_ok) {
656 out->err(out, "Can't get fence agent meta-data: %s",
657 pcmk_rc_str(rc));
658 }
659
660 break;
661
662 case 'C':
663 rc = st->cmds->confirm(st, st_opts, target);
664 rc = pcmk_legacy2rc(rc);
665 break;
666
667 case 'B':
668 rc = request_fencing(st, target, PCMK_ACTION_REBOOT, &error);
669 break;
670
671 case 'F':
672 rc = request_fencing(st, target, PCMK_ACTION_OFF, &error);
673 break;
674
675 case 'U':
676 rc = request_fencing(st, target, PCMK_ACTION_ON, &error);
677 break;
678
679 case 'h':
680 rc = pcmk__fence_last(out, target, options.as_nodeid);
681 break;
682
683 case 'H':
684 rc = pcmk__fence_history(out, st, target, options.timeout*1000, args->verbosity,
685 options.broadcast, options.cleanup);
686 break;
687
688 case 'K':
689 device = NULL;
690 if (options.devices != NULL) {
691 device = g_list_nth_data(options.devices, 0);
692 }
693
694 rc = pcmk__fence_validate(out, st, options.agent, device, options.params,
695 options.timeout*1000);
696 break;
697 }
698
699 crm_info("Command returned: %s (%d)", pcmk_rc_str(rc), rc);
700 exit_code = pcmk_rc2exitc(rc);
701
702 done:
703 g_strfreev(processed_args);
704 pcmk__free_arg_context(context);
705
706 pcmk__output_and_clear_error(&error, out);
707
708 if (out != NULL) {
709 out->finish(out, exit_code, true, NULL);
710 pcmk__output_free(out);
711 }
712 pcmk__unregister_formats();
713 free(name);
714 g_list_free_full(options.devices, free);
715
716 if (options.params != NULL) {
717 g_hash_table_destroy(options.params);
718 }
719
720 if (st != NULL) {
721 st->cmds->disconnect(st);
722 stonith__api_free(st);
723 }
724
725 return exit_code;
726 }