This source file includes following definitions.
- quit_main_loop
- resource_ipc_timeout
- controller_event_callback
- start_mainloop
- build_constraint_list
- validate_opt_list
- attr_set_type_cb
- command_cb
- option_cb
- timeout_cb
- ban_or_move
- cleanup
- initialize_scheduler_data
- refresh
- refresh_resource
- has_cmdline_config
- validate_cmdline_config
- handle_ban
- handle_cleanup
- handle_clear
- handle_colocations
- handle_cts
- handle_delete
- handle_delete_param
- handle_digests
- handle_execute_agent
- handle_fail
- handle_get_param
- handle_list_active_ops
- handle_list_agents
- handle_list_all_ops
- handle_list_alternatives
- handle_list_instances
- handle_list_options
- handle_list_providers
- handle_list_resources
- handle_list_standards
- handle_locate
- handle_metadata
- handle_move
- handle_query_xml
- handle_query_xml_raw
- handle_refresh
- handle_restart
- handle_set_param
- handle_wait
- handle_why
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <crm_resource.h>
13 #include <crm/lrmd_internal.h>
14 #include <crm/common/cmdline_internal.h>
15 #include <crm/common/ipc_attrd_internal.h>
16 #include <crm/common/lists_internal.h>
17 #include <crm/common/output.h>
18 #include <crm/fencing/internal.h>
19 #include <pacemaker-internal.h>
20
21 #include <sys/param.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <libgen.h>
31 #include <time.h>
32
33 #include <libxml/xpath.h>
34
35 #include <crm/crm.h>
36 #include <crm/stonith-ng.h>
37 #include <crm/common/agents.h>
38 #include <crm/common/ipc_controld.h>
39 #include <crm/cib/internal.h>
40
41 #define SUMMARY "crm_resource - perform tasks related to Pacemaker cluster resources"
42
43 enum rsc_command {
44 cmd_ban,
45 cmd_cleanup,
46 cmd_clear,
47 cmd_colocations,
48 cmd_cts,
49 cmd_delete,
50 cmd_delete_param,
51 cmd_digests,
52 cmd_execute_agent,
53 cmd_fail,
54 cmd_get_param,
55 cmd_list_active_ops,
56 cmd_list_agents,
57 cmd_list_all_ops,
58 cmd_list_alternatives,
59 cmd_list_instances,
60 cmd_list_options,
61 cmd_list_providers,
62 cmd_list_resources,
63 cmd_list_standards,
64 cmd_locate,
65 cmd_metadata,
66 cmd_move,
67 cmd_query_xml,
68 cmd_query_xml_raw,
69 cmd_refresh,
70 cmd_restart,
71 cmd_set_param,
72 cmd_wait,
73 cmd_why,
74
75
76 cmd_max = cmd_why,
77 };
78
79
80
81
82
83 typedef crm_exit_t (*crm_resource_fn_t)(pcmk_resource_t *, pcmk_node_t *,
84 cib_t *, pcmk_scheduler_t *,
85 pcmk_ipc_api_t *, xmlNode *);
86
87
88
89
90
91
92
93
94
95 enum crm_rsc_flags {
96
97 crm_rsc_find_match_anon_basename = (UINT32_C(1) << 0),
98
99
100 crm_rsc_find_match_basename = (UINT32_C(1) << 1),
101
102
103 crm_rsc_find_match_history = (UINT32_C(1) << 2),
104
105
106 crm_rsc_rejects_clone_instance = (UINT32_C(1) << 3),
107
108
109 crm_rsc_requires_cib = (UINT32_C(1) << 4),
110
111
112 crm_rsc_requires_controller = (UINT32_C(1) << 5),
113
114
115 crm_rsc_requires_node = (UINT32_C(1) << 6),
116
117
118 crm_rsc_requires_resource = (UINT32_C(1) << 7),
119
120
121 crm_rsc_requires_scheduler = (UINT32_C(1) << 8),
122 };
123
124
125
126
127
128 typedef struct {
129 crm_resource_fn_t fn;
130 uint32_t flags;
131 } crm_resource_cmd_info_t;
132
133 struct {
134 enum rsc_command rsc_cmd;
135
136
137 gchar *rsc_id;
138 gchar *rsc_type;
139 gboolean all;
140 gboolean force;
141 gboolean clear_expired;
142 gboolean recursive;
143 gboolean promoted_role_only;
144 gchar *host_uname;
145 gchar *interval_spec;
146 gchar *move_lifetime;
147 gchar *operation;
148 enum pcmk__opt_flags opt_list;
149 const char *attr_set_type;
150 gchar *prop_id;
151 char *prop_name;
152 gchar *prop_set;
153 gchar *prop_value;
154 guint timeout_ms;
155 char *agent_spec;
156 int check_level;
157
158
159 gchar *agent;
160 gchar *class;
161 gchar *provider;
162 GHashTable *cmdline_params;
163
164
165 gchar **remainder;
166 GHashTable *override_params;
167 } options = {
168 .attr_set_type = PCMK_XE_INSTANCE_ATTRIBUTES,
169 .check_level = -1,
170 .rsc_cmd = cmd_list_resources,
171 };
172
173 static crm_exit_t exit_code = CRM_EX_OK;
174 static pcmk__output_t *out = NULL;
175 static pcmk__common_args_t *args = NULL;
176
177
178 static GError *error = NULL;
179 static GMainLoop *mainloop = NULL;
180
181 #define MESSAGE_TIMEOUT_S 60
182
183 #define INDENT " "
184
185 static pcmk__supported_format_t formats[] = {
186 PCMK__SUPPORTED_FORMAT_NONE,
187 PCMK__SUPPORTED_FORMAT_TEXT,
188 PCMK__SUPPORTED_FORMAT_XML,
189 { NULL, NULL, NULL }
190 };
191
192 static void
193 quit_main_loop(crm_exit_t ec)
194 {
195 exit_code = ec;
196 if (mainloop != NULL) {
197 GMainLoop *mloop = mainloop;
198
199 mainloop = NULL;
200 pcmk_quit_main_loop(mloop, 10);
201 g_main_loop_unref(mloop);
202 }
203 }
204
205 static gboolean
206 resource_ipc_timeout(gpointer data)
207 {
208
209 if (error != NULL) {
210 g_clear_error(&error);
211 }
212
213 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_TIMEOUT,
214 _("Aborting because no messages received in %d seconds"), MESSAGE_TIMEOUT_S);
215
216 quit_main_loop(CRM_EX_TIMEOUT);
217 return FALSE;
218 }
219
220 static void
221 controller_event_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type,
222 crm_exit_t status, void *event_data, void *user_data)
223 {
224 crm_exit_t *ec = user_data;
225
226 pcmk__assert(ec != NULL);
227
228 switch (event_type) {
229 case pcmk_ipc_event_disconnect:
230 if (exit_code == CRM_EX_DISCONNECT) {
231 crm_info("Connection to controller was terminated");
232 }
233
234 *ec = exit_code;
235 quit_main_loop(*ec);
236 break;
237
238 case pcmk_ipc_event_reply:
239 if (status != CRM_EX_OK) {
240 out->err(out, "Error: bad reply from controller: %s",
241 crm_exit_str(status));
242 pcmk_disconnect_ipc(api);
243
244 *ec = status;
245 quit_main_loop(*ec);
246
247 } else {
248 if ((pcmk_controld_api_replies_expected(api) == 0)
249 && (mainloop != NULL)
250 && g_main_loop_is_running(mainloop)) {
251
252 out->info(out, "... got reply (done)");
253 crm_debug("Got all the replies we expected");
254 pcmk_disconnect_ipc(api);
255
256 *ec = CRM_EX_OK;
257 quit_main_loop(*ec);
258
259 } else {
260 out->info(out, "... got reply");
261 }
262 }
263 break;
264
265 default:
266 break;
267 }
268 }
269
270 static void
271 start_mainloop(pcmk_ipc_api_t *capi)
272 {
273
274 unsigned int count = pcmk_controld_api_replies_expected(capi);
275
276 if (count > 0) {
277 out->info(out, "Waiting for %u %s from the controller",
278 count, pcmk__plural_alt(count, "reply", "replies"));
279 exit_code = CRM_EX_DISCONNECT;
280 mainloop = g_main_loop_new(NULL, FALSE);
281 pcmk__create_timer(MESSAGE_TIMEOUT_S * 1000, resource_ipc_timeout, NULL);
282 g_main_loop_run(mainloop);
283 }
284 }
285
286 static GList *
287 build_constraint_list(xmlNode *root)
288 {
289 GList *retval = NULL;
290 xmlNode *cib_constraints = NULL;
291 xmlXPathObject *xpathObj = NULL;
292 int ndx = 0;
293 int num_results = 0;
294
295 cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
296 xpathObj = pcmk__xpath_search(cib_constraints->doc,
297 "//" PCMK_XE_RSC_LOCATION);
298 num_results = pcmk__xpath_num_results(xpathObj);
299
300 for (ndx = 0; ndx < num_results; ndx++) {
301 xmlNode *match = pcmk__xpath_result(xpathObj, ndx);
302
303 if (match != NULL) {
304 retval = g_list_insert_sorted(retval, (gpointer) pcmk__xe_id(match),
305 (GCompareFunc) g_strcmp0);
306 }
307 }
308
309 xmlXPathFreeObject(xpathObj);
310 return retval;
311 }
312
313 static gboolean
314 validate_opt_list(const gchar *optarg)
315 {
316 if (pcmk__str_eq(optarg, PCMK_VALUE_FENCING, pcmk__str_none)) {
317 options.opt_list = pcmk__opt_fencing;
318
319 } else if (pcmk__str_eq(optarg, PCMK__VALUE_PRIMITIVE, pcmk__str_none)) {
320 options.opt_list = pcmk__opt_primitive;
321
322 } else {
323 return FALSE;
324 }
325
326 return TRUE;
327 }
328
329
330
331 static gboolean
332 attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data,
333 GError **error) {
334 if (pcmk__str_any_of(option_name, "-m", "--meta", NULL)) {
335 options.attr_set_type = PCMK_XE_META_ATTRIBUTES;
336 } else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) {
337 options.attr_set_type = PCMK_XE_UTILIZATION;
338 } else if (pcmk__str_eq(option_name, "--element", pcmk__str_none)) {
339 options.attr_set_type = ATTR_SET_ELEMENT;
340 }
341 return TRUE;
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358 static gboolean
359 command_cb(const gchar *option_name, const gchar *optarg, gpointer data,
360 GError **error)
361 {
362
363 if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) {
364 options.rsc_cmd = cmd_ban;
365
366 } else if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) {
367 options.rsc_cmd = cmd_cleanup;
368
369 } else if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) {
370 options.rsc_cmd = cmd_clear;
371
372 } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) {
373 options.rsc_cmd = cmd_colocations;
374
375 } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) {
376 options.rsc_cmd = cmd_colocations;
377 options.recursive = TRUE;
378
379 } else if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) {
380 options.rsc_cmd = cmd_cts;
381
382 } else if (pcmk__str_any_of(option_name, "-D", "--delete", NULL)) {
383 options.rsc_cmd = cmd_delete;
384
385 } else if (pcmk__str_any_of(option_name, "-d", "--delete-parameter",
386 NULL)) {
387 options.rsc_cmd = cmd_delete_param;
388 pcmk__str_update(&options.prop_name, optarg);
389
390 } else if (pcmk__str_eq(option_name, "--digests", pcmk__str_none)) {
391 options.rsc_cmd = cmd_digests;
392
393 if (options.override_params == NULL) {
394 options.override_params = pcmk__strkey_table(g_free, g_free);
395 }
396
397 } else if (pcmk__str_any_of(option_name,
398 "--force-demote", "--force-promote",
399 "--force-start", "--force-stop",
400 "--force-check", "--validate", NULL)) {
401 options.rsc_cmd = cmd_execute_agent;
402
403 g_free(options.operation);
404 options.operation = g_strdup(option_name + 2);
405
406 if (options.override_params == NULL) {
407 options.override_params = pcmk__strkey_table(g_free, g_free);
408 }
409
410 if (optarg != NULL) {
411 if (pcmk__scan_min_int(optarg, &options.check_level,
412 0) != pcmk_rc_ok) {
413 g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
414 _("Invalid check level setting: %s"), optarg);
415 return FALSE;
416 }
417 }
418
419 } else if (pcmk__str_any_of(option_name, "-F", "--fail", NULL)) {
420 options.rsc_cmd = cmd_fail;
421
422 } else if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) {
423 options.rsc_cmd = cmd_get_param;
424 pcmk__str_update(&options.prop_name, optarg);
425
426 } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) {
427 options.rsc_cmd = cmd_list_active_ops;
428
429 } else if (pcmk__str_eq(option_name, "--list-agents", pcmk__str_none)) {
430 options.rsc_cmd = cmd_list_agents;
431 pcmk__str_update(&options.agent_spec, optarg);
432
433 } else if (pcmk__str_any_of(option_name, "-o", "--list-all-operations",
434 NULL)) {
435 options.rsc_cmd = cmd_list_all_ops;
436
437 } else if (pcmk__str_eq(option_name, "--list-ocf-alternatives",
438 pcmk__str_none)) {
439 options.rsc_cmd = cmd_list_alternatives;
440 pcmk__str_update(&options.agent_spec, optarg);
441
442 } else if (pcmk__str_eq(option_name, "--list-options", pcmk__str_none)) {
443 options.rsc_cmd = cmd_list_options;
444 return validate_opt_list(optarg);
445
446 } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) {
447 options.rsc_cmd = cmd_list_instances;
448
449 } else if (pcmk__str_eq(option_name, "--list-ocf-providers",
450 pcmk__str_none)) {
451 options.rsc_cmd = cmd_list_providers;
452 pcmk__str_update(&options.agent_spec, optarg);
453
454 } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) {
455 options.rsc_cmd = cmd_list_resources;
456
457 } else if (pcmk__str_eq(option_name, "--list-standards", pcmk__str_none)) {
458 options.rsc_cmd = cmd_list_standards;
459
460 } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) {
461 options.rsc_cmd = cmd_locate;
462
463 } else if (pcmk__str_eq(option_name, "--show-metadata", pcmk__str_none)) {
464 options.rsc_cmd = cmd_metadata;
465 pcmk__str_update(&options.agent_spec, optarg);
466
467 } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) {
468 options.rsc_cmd = cmd_move;
469
470 } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) {
471 options.rsc_cmd = cmd_query_xml;
472
473 } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) {
474 options.rsc_cmd = cmd_query_xml_raw;
475
476 } else if (pcmk__str_any_of(option_name, "-R", "--refresh", NULL)) {
477 options.rsc_cmd = cmd_refresh;
478
479 } else if (pcmk__str_eq(option_name, "--restart", pcmk__str_none)) {
480 options.rsc_cmd = cmd_restart;
481
482 } else if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) {
483 options.rsc_cmd = cmd_set_param;
484 pcmk__str_update(&options.prop_name, optarg);
485
486 } else if (pcmk__str_eq(option_name, "--wait", pcmk__str_none)) {
487 options.rsc_cmd = cmd_wait;
488
489 } else if (pcmk__str_any_of(option_name, "-Y", "--why", NULL)) {
490 options.rsc_cmd = cmd_why;
491 }
492
493 return TRUE;
494 }
495
496 static gboolean
497 option_cb(const gchar *option_name, const gchar *optarg, gpointer data,
498 GError **error)
499 {
500 gchar *name = NULL;
501 gchar *value = NULL;
502
503 if (pcmk__scan_nvpair(optarg, &name, &value) != pcmk_rc_ok) {
504 return FALSE;
505 }
506
507
508
509
510
511
512 if (options.cmdline_params == NULL) {
513 options.cmdline_params = pcmk__strkey_table(free, free);
514 }
515 pcmk__insert_dup(options.cmdline_params, name, value);
516 g_free(name);
517 g_free(value);
518 return TRUE;
519 }
520
521 static gboolean
522 timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data,
523 GError **error)
524 {
525 long long timeout_ms = crm_get_msec(optarg);
526
527 if (timeout_ms < 0) {
528 return FALSE;
529 }
530 options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX);
531 return TRUE;
532 }
533
534
535
536
537
538 static GOptionEntry query_entries[] = {
539 { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
540 "List all cluster resources with status",
541 NULL },
542 { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
543 "List IDs of all instantiated resources (individual members\n"
544 INDENT "rather than groups etc.)",
545 NULL },
546 { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG,
547 G_OPTION_ARG_CALLBACK, command_cb,
548 NULL,
549 NULL },
550 { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
551 command_cb,
552 "List active resource operations, optionally filtered by\n"
553 INDENT "--resource and/or --node",
554 NULL },
555 { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
556 command_cb,
557 "List all resource operations, optionally filtered by\n"
558 INDENT "--resource and/or --node",
559 NULL },
560 { "list-options", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
561 "List all available options of the given type.\n"
562 INDENT "Allowed values:\n"
563 INDENT PCMK__VALUE_PRIMITIVE " (primitive resource meta-attributes),\n"
564 INDENT PCMK_VALUE_FENCING " (parameters common to all fencing resources)",
565 "TYPE" },
566 { "list-standards", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
567 command_cb,
568 "List supported standards",
569 NULL },
570 { "list-ocf-providers", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
571 command_cb,
572 "List all available OCF providers",
573 NULL },
574 { "list-agents", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
575 command_cb,
576 "List all agents available for the named standard and/or provider",
577 "STD:PROV" },
578 { "list-ocf-alternatives", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
579 command_cb,
580 "List all available providers for the named OCF agent",
581 "AGENT" },
582 { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
583 "Show the metadata for the named class:provider:agent",
584 "SPEC" },
585 { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
586 "Show XML configuration of resource (after any template expansion)",
587 NULL },
588 { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
589 command_cb,
590 "Show XML configuration of resource (before any template expansion)",
591 NULL },
592 { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
593 command_cb,
594 "Display named parameter for resource (use instance attribute\n"
595 INDENT "unless --element, --meta, or --utilization is specified)",
596 "PARAM" },
597 { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
598 "Show node(s) currently running resource",
599 NULL },
600 { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
601 command_cb,
602 "Display the location and colocation constraints that apply to a\n"
603 INDENT "resource, and if --recursive is specified, to the resources\n"
604 INDENT "directly or indirectly involved in those colocations.\n"
605 INDENT "If the named resource is part of a group, or a clone or\n"
606 INDENT "bundle instance, constraints for the collective resource\n"
607 INDENT "will be shown unless --force is given.",
608 NULL },
609 { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
610 "Equivalent to --constraints --recursive",
611 NULL },
612 { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
613 "Show why resources are not running, optionally filtered by\n"
614 INDENT "--resource and/or --node",
615 NULL },
616
617 { NULL }
618 };
619
620 static GOptionEntry command_entries[] = {
621 { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
622 command_cb,
623 "Validate resource configuration by calling agent's validate-all\n"
624 INDENT "action. The configuration may be specified either by giving an\n"
625 INDENT "existing resource name with -r, or by specifying --class,\n"
626 INDENT "--agent, and --provider arguments, along with any number of\n"
627 INDENT "--option arguments. An optional LEVEL argument can be given\n"
628 INDENT "to control the level of checking performed.",
629 "LEVEL" },
630 { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
631 "If resource has any past failures, clear its history and fail\n"
632 INDENT "count. Optionally filtered by --resource, --node, --operation\n"
633 INDENT "and --interval (otherwise all). --operation and --interval\n"
634 INDENT "apply to fail counts, but entire history is always clear, to\n"
635 INDENT "allow current state to be rechecked. If the named resource is\n"
636 INDENT "part of a group, or one numbered instance of a clone or bundled\n"
637 INDENT "resource, the clean-up applies to the whole collective resource\n"
638 INDENT "unless --force is given.",
639 NULL },
640 { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
641 "Delete resource's history (including failures) so its current state\n"
642 INDENT "is rechecked. Optionally filtered by --resource and --node\n"
643 INDENT "(otherwise all). If the named resource is part of a group, or one\n"
644 INDENT "numbered instance of a clone or bundled resource, the refresh\n"
645 INDENT "applies to the whole collective resource unless --force is given.",
646 NULL },
647 { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
648 command_cb,
649 "Set named parameter for resource (requires -v). Use instance\n"
650 INDENT "attribute unless --element, --meta, or --utilization is "
651 "specified.",
652 "PARAM" },
653 { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
654 command_cb,
655 "Delete named parameter for resource. Use instance attribute\n"
656 INDENT "unless --element, --meta or, --utilization is specified.",
657 "PARAM" },
658
659 { NULL }
660 };
661
662 static GOptionEntry location_entries[] = {
663 { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
664 "Create a constraint to move resource. If --node is specified,\n"
665 INDENT "the constraint will be to move to that node, otherwise it\n"
666 INDENT "will be to ban the current node. Unless --force is specified\n"
667 INDENT "this will return an error if the resource is already running\n"
668 INDENT "on the specified node. If --force is specified, this will\n"
669 INDENT "always ban the current node.\n"
670 INDENT "Optional: --lifetime, --promoted. NOTE: This may prevent the\n"
671 INDENT "resource from running on its previous location until the\n"
672 INDENT "implicit constraint expires or is removed with --clear.",
673 NULL },
674 { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
675 "Create a constraint to keep resource off a node.\n"
676 INDENT "Optional: --node, --lifetime, --promoted.\n"
677 INDENT "NOTE: This will prevent the resource from running on the\n"
678 INDENT "affected node until the implicit constraint expires or is\n"
679 INDENT "removed with --clear. If --node is not specified, it defaults\n"
680 INDENT "to the node currently running the resource for primitives\n"
681 INDENT "and groups, or the promoted instance of promotable clones with\n"
682 INDENT PCMK_META_PROMOTED_MAX "=1 (all other situations result in an\n"
683 INDENT "error as there is no sane default).",
684 NULL },
685 { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
686 "Remove all constraints created by the --ban and/or --move\n"
687 INDENT "commands. Requires: --resource. Optional: --node, --promoted,\n"
688 INDENT "--expired. If --node is not specified, all constraints created\n"
689 INDENT "by --ban and --move will be removed for the named resource. If\n"
690 INDENT "--node and --force are specified, any constraint created by\n"
691 INDENT "--move will be cleared, even if it is not for the specified\n"
692 INDENT "node. If --expired is specified, only those constraints whose\n"
693 INDENT "lifetimes have expired will be removed.",
694 NULL },
695 { "expired", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
696 &options.clear_expired,
697 "Modifies the --clear argument to remove constraints with\n"
698 INDENT "expired lifetimes.",
699 NULL },
700 { "lifetime", 'u', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.move_lifetime,
701 "Lifespan (as ISO 8601 duration) of created constraints (with\n"
702 INDENT "-B, -M) see https://en.wikipedia.org/wiki/ISO_8601#Durations)",
703 "TIMESPEC" },
704 { "promoted", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
705 &options.promoted_role_only,
706 "Limit scope of command to promoted role (with -B, -M, -U). For\n"
707 INDENT "-B and -M, previously promoted instances may remain\n"
708 INDENT "active in the unpromoted role.",
709 NULL },
710
711
712 { "master", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
713 &options.promoted_role_only,
714 "Deprecated: Use --promoted instead", NULL },
715
716 { NULL }
717 };
718
719 static GOptionEntry advanced_entries[] = {
720 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
721 "(Advanced) Delete a resource from the CIB. Required: -t",
722 NULL },
723 { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
724 "(Advanced) Tell the cluster this resource has failed",
725 NULL },
726 { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
727 "(Advanced) Tell the cluster to restart this resource and\n"
728 INDENT "anything that depends on it. This temporarily modifies\n"
729 INDENT "the CIB, and other CIB modifications should be avoided\n"
730 INDENT "while this is in progress. If a node is fenced because\n"
731 INDENT "the stop portion of the restart fails, CIB modifications\n"
732 INDENT "such as target-role may remain.",
733 NULL },
734 { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
735 "(Advanced) Wait until the cluster settles into a stable state",
736 NULL },
737 { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
738 "(Advanced) Show parameter hashes that Pacemaker uses to detect\n"
739 INDENT "configuration changes (only accurate if there is resource\n"
740 INDENT "history on the specified node). Required: --resource, --node.\n"
741 INDENT "Optional: any NAME=VALUE parameters will be used to override\n"
742 INDENT "the configuration (to see what the hash would be with those\n"
743 INDENT "changes).",
744 NULL },
745 { "force-demote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
746 command_cb,
747 "(Advanced) Bypass the cluster and demote a resource on the local\n"
748 INDENT "node. Unless --force is specified, this will refuse to do so if\n"
749 INDENT "the cluster believes the resource is a clone instance already\n"
750 INDENT "running on the local node.",
751 NULL },
752 { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
753 "(Advanced) Bypass the cluster and stop a resource on the local node",
754 NULL },
755 { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
756 "(Advanced) Bypass the cluster and start a resource on the local\n"
757 INDENT "node. Unless --force is specified, this will refuse to do so if\n"
758 INDENT "the cluster believes the resource is a clone instance already\n"
759 INDENT "running on the local node.",
760 NULL },
761 { "force-promote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
762 command_cb,
763 "(Advanced) Bypass the cluster and promote a resource on the local\n"
764 INDENT "node. Unless --force is specified, this will refuse to do so if\n"
765 INDENT "the cluster believes the resource is a clone instance already\n"
766 INDENT "running on the local node.",
767 NULL },
768 { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
769 command_cb,
770 "(Advanced) Bypass the cluster and check the state of a resource on\n"
771 INDENT "the local node. An optional LEVEL argument can be given\n"
772 INDENT "to control the level of checking performed.",
773 "LEVEL" },
774
775 { NULL }
776 };
777
778 static GOptionEntry addl_entries[] = {
779 { "node", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.host_uname,
780 "Node name",
781 "NAME" },
782 { "recursive", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.recursive,
783 "Follow colocation chains when using --set-parameter or --constraints",
784 NULL },
785 { "resource-type", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.rsc_type,
786 "Resource XML element (primitive, group, etc.) (with -D)",
787 "ELEMENT" },
788 { "parameter-value", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_value,
789 "Value to use with -p",
790 "PARAM" },
791 { "meta", 'm', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
792 "Use resource meta-attribute instead of instance attribute\n"
793 INDENT "(with -p, -g, -d)",
794 NULL },
795 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
796 "Use resource utilization attribute instead of instance attribute\n"
797 INDENT "(with -p, -g, -d)",
798 NULL },
799 { "element", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
800 "Use resource element attribute instead of instance attribute\n"
801 INDENT "(with -p, -g, -d)",
802 NULL },
803 { "operation", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.operation,
804 "Operation to clear instead of all (with -C -r)",
805 "OPERATION" },
806 { "interval", 'I', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.interval_spec,
807 "Interval of operation to clear (default 0s) (with -C -r -n)",
808 "N" },
809 { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.class,
810 "The standard the resource agent conforms to (for example, ocf).\n"
811 INDENT "Use with --agent, --provider, --option, and --validate.",
812 "CLASS" },
813 { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.agent,
814 "The agent to use (for example, IPaddr). Use with --class,\n"
815 INDENT "--provider, --option, and --validate.",
816 "AGENT" },
817 { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.provider,
818 "The vendor that supplies the resource agent (for example,\n"
819 INDENT "heartbeat). Use with --class, --agent, --option, and --validate.",
820 "PROVIDER" },
821 { "option", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, option_cb,
822 "Specify a device configuration parameter as NAME=VALUE (may be\n"
823 INDENT "specified multiple times). Use with --validate and without the\n"
824 INDENT "-r option.",
825 "PARAM" },
826 { "set-name", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_set,
827 "(Advanced) XML ID of attributes element to use (with -p, -d)",
828 "ID" },
829 { "nvpair", 'i', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_id,
830 "(Advanced) XML ID of nvpair element to use (with -p, -d)",
831 "ID" },
832 { "timeout", 'T', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, timeout_cb,
833 "(Advanced) Abort if command does not finish in this time (with\n"
834 INDENT "--restart, --wait, --force-*)",
835 "N" },
836 { "all", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all,
837 "List all options, including advanced and deprecated (with\n"
838 INDENT "--list-options)",
839 NULL },
840 { "force", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.force,
841 "Force the action to be performed. See help for individual commands for\n"
842 INDENT "additional behavior.",
843 NULL },
844
845
846 { "host-uname", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.host_uname,
847 NULL,
848 "HOST" },
849
850 { NULL }
851 };
852
853 static int
854 ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc, cib_t *cib_conn,
855 const char *move_lifetime)
856 {
857 int rc = pcmk_rc_ok;
858 pcmk_node_t *current = NULL;
859 unsigned int nactive = 0;
860
861 CRM_CHECK(rsc != NULL, return EINVAL);
862
863 current = pe__find_active_requires(rsc, &nactive);
864
865 if (nactive == 1) {
866 rc = cli_resource_ban(out, options.rsc_id, current->priv->name,
867 move_lifetime, cib_conn,
868 options.promoted_role_only, PCMK_ROLE_PROMOTED);
869
870 } else if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
871 int count = 0;
872 GList *iter = NULL;
873
874 current = NULL;
875 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
876 pcmk_resource_t *child = (pcmk_resource_t *)iter->data;
877 enum rsc_role_e child_role = child->priv->fns->state(child, true);
878
879 if (child_role == pcmk_role_promoted) {
880 count++;
881 current = pcmk__current_node(child);
882 }
883 }
884
885 if(count == 1 && current) {
886 rc = cli_resource_ban(out, options.rsc_id, current->priv->name,
887 move_lifetime, cib_conn,
888 options.promoted_role_only,
889 PCMK_ROLE_PROMOTED);
890
891 } else {
892 rc = EINVAL;
893 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
894 _("Resource '%s' not moved: active in %d locations (promoted in %d).\n"
895 "To prevent '%s' from running on a specific location, "
896 "specify a node."
897 "To prevent '%s' from being promoted at a specific "
898 "location, specify a node and the --promoted option."),
899 options.rsc_id, nactive, count, options.rsc_id, options.rsc_id);
900 }
901
902 } else {
903 rc = EINVAL;
904 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
905 _("Resource '%s' not moved: active in %d locations.\n"
906 "To prevent '%s' from running on a specific location, "
907 "specify a node."),
908 options.rsc_id, nactive, options.rsc_id);
909 }
910
911 return rc;
912 }
913
914 static void
915 cleanup(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node,
916 pcmk_ipc_api_t *controld_api)
917 {
918 int rc = pcmk_rc_ok;
919
920 if (options.force == FALSE) {
921 rsc = uber_parent(rsc);
922 }
923
924 crm_debug("Erasing failures of %s (%s requested) on %s",
925 rsc->id, options.rsc_id,
926 ((node != NULL)? pcmk__node_name(node) : "all nodes"));
927 rc = cli_resource_delete(controld_api, rsc, node, options.operation,
928 options.interval_spec, true, options.force);
929
930 if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
931
932 cli_resource_check(out, rsc, node);
933 }
934
935
936
937
938 if (rc == pcmk_rc_ok) {
939 start_mainloop(controld_api);
940 }
941 }
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963 static int
964 initialize_scheduler_data(pcmk_scheduler_t **scheduler, cib_t *cib_conn,
965 pcmk__output_t *out, xmlNode **cib_xml_orig)
966 {
967 int rc = pcmk_rc_ok;
968
969 pcmk__assert((scheduler != NULL) && (*scheduler == NULL)
970 && (cib_conn != NULL) && (out != NULL)
971 && (cib_xml_orig != NULL) && (*cib_xml_orig == NULL));
972
973 *scheduler = pcmk_new_scheduler();
974 if (*scheduler == NULL) {
975 return ENOMEM;
976 }
977
978 pcmk__set_scheduler_flags(*scheduler, pcmk__sched_no_counts);
979 (*scheduler)->priv->out = out;
980
981 rc = update_scheduler_input(out, *scheduler, cib_conn, cib_xml_orig);
982 if (rc != pcmk_rc_ok) {
983 pcmk_free_scheduler(*scheduler);
984 *scheduler = NULL;
985 return rc;
986 }
987
988 cluster_status(*scheduler);
989 return pcmk_rc_ok;
990 }
991
992 static crm_exit_t
993 refresh(pcmk__output_t *out, const pcmk_node_t *node,
994 pcmk_ipc_api_t *controld_api)
995 {
996 const char *node_name = NULL;
997 const char *log_node_name = "all nodes";
998 const char *router_node = NULL;
999 int attr_options = pcmk__node_attr_none;
1000 int rc = pcmk_rc_ok;
1001
1002 if (node != NULL) {
1003 node_name = node->priv->name;
1004 log_node_name = pcmk__node_name(node);
1005 router_node = node->priv->name;
1006 }
1007
1008 if (pcmk__is_pacemaker_remote_node(node)) {
1009 const pcmk_node_t *conn_host = pcmk__current_node(node->priv->remote);
1010
1011 if (conn_host == NULL) {
1012 rc = ENXIO;
1013 g_set_error(&error, PCMK__RC_ERROR, rc,
1014 _("No cluster connection to Pacemaker Remote node %s "
1015 "detected"),
1016 log_node_name);
1017 return pcmk_rc2exitc(rc);
1018 }
1019 router_node = conn_host->priv->name;
1020 pcmk__set_node_attr_flags(attr_options, pcmk__node_attr_remote);
1021 }
1022
1023 if (controld_api == NULL) {
1024 out->info(out, "Dry run: skipping clean-up of %s due to CIB_file",
1025 log_node_name);
1026 return CRM_EX_OK;
1027 }
1028
1029 crm_debug("Re-checking the state of all resources on %s", log_node_name);
1030
1031
1032 rc = pcmk__attrd_api_clear_failures(NULL, node_name, NULL, NULL, NULL, NULL,
1033 attr_options);
1034
1035
1036
1037
1038 if (pcmk_controld_api_reprobe(controld_api, node_name,
1039 router_node) == pcmk_rc_ok) {
1040 start_mainloop(controld_api);
1041 return exit_code;
1042 }
1043
1044 return pcmk_rc2exitc(rc);
1045 }
1046
1047 static void
1048 refresh_resource(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node,
1049 pcmk_ipc_api_t *controld_api)
1050 {
1051 int rc = pcmk_rc_ok;
1052
1053 if (options.force == FALSE) {
1054 rsc = uber_parent(rsc);
1055 }
1056
1057 crm_debug("Re-checking the state of %s (%s requested) on %s",
1058 rsc->id, options.rsc_id,
1059 ((node != NULL)? pcmk__node_name(node) : "all nodes"));
1060 rc = cli_resource_delete(controld_api, rsc, node, NULL, 0, false,
1061 options.force);
1062
1063 if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
1064
1065 cli_resource_check(out, rsc, node);
1066 }
1067
1068
1069
1070
1071 if (rc == pcmk_rc_ok) {
1072 start_mainloop(controld_api);
1073 }
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083 static inline bool
1084 has_cmdline_config(void)
1085 {
1086 return ((options.class != NULL) || (options.provider != NULL)
1087 || (options.agent != NULL));
1088 }
1089
1090 static void
1091 validate_cmdline_config(void)
1092 {
1093 bool is_ocf = pcmk__str_eq(options.class, PCMK_RESOURCE_CLASS_OCF,
1094 pcmk__str_none);
1095
1096
1097 if (!has_cmdline_config()) {
1098 return;
1099 }
1100
1101
1102 if (options.rsc_id != NULL) {
1103 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1104 _("--class, --agent, and --provider cannot be used with "
1105 "-r/--resource"));
1106 return;
1107 }
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126 if (options.rsc_cmd != cmd_execute_agent) {
1127 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1128 _("--class, --agent, and --provider can only be used with "
1129 "--validate and --force-*"));
1130 return;
1131 }
1132
1133
1134 if (is_ocf) {
1135 if ((options.provider == NULL) || (options.agent == NULL)) {
1136 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1137 _("--provider and --agent are required with "
1138 "--class=ocf"));
1139 return;
1140 }
1141
1142 } else {
1143 if (options.provider != NULL) {
1144 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1145 _("--provider is supported only with --class=ocf"));
1146 return;
1147 }
1148
1149
1150 if (options.agent == NULL) {
1151 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1152 _("--agent is required with --class"));
1153 return;
1154 }
1155 if (options.class == NULL) {
1156 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1157 _("--class is required with --agent"));
1158 return;
1159 }
1160 }
1161
1162
1163 if (pcmk__str_eq(options.class, PCMK_RESOURCE_CLASS_STONITH,
1164 pcmk__str_none)) {
1165 if (!stonith__agent_exists(options.agent)) {
1166 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1167 _("%s is not a known stonith agent"), options.agent);
1168 return;
1169 }
1170
1171 } else if (!resources_agent_exists(options.class, options.provider,
1172 options.agent)) {
1173 if (is_ocf) {
1174 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1175 _("%s:%s:%s is not a known resource agent"),
1176 options.class, options.provider, options.agent);
1177 } else {
1178 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1179 _("%s:%s is not a known resource agent"),
1180 options.class, options.agent);
1181 }
1182 return;
1183 }
1184
1185 if (options.cmdline_params == NULL) {
1186 options.cmdline_params = pcmk__strkey_table(free, free);
1187 }
1188 }
1189
1190 static crm_exit_t
1191 handle_ban(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1192 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1193 xmlNode *cib_xml_orig)
1194 {
1195 int rc = pcmk_rc_ok;
1196
1197 if (node == NULL) {
1198 rc = ban_or_move(out, rsc, cib_conn, options.move_lifetime);
1199 } else {
1200 rc = cli_resource_ban(out, options.rsc_id, node->priv->name,
1201 options.move_lifetime, cib_conn,
1202 options.promoted_role_only, PCMK_ROLE_PROMOTED);
1203 }
1204
1205 if (rc == EINVAL) {
1206 return CRM_EX_USAGE;
1207 }
1208 return pcmk_rc2exitc(rc);
1209 }
1210
1211 static crm_exit_t
1212 handle_cleanup(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1213 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1214 xmlNode *cib_xml_orig)
1215 {
1216 if (rsc == NULL) {
1217 int rc = cli_cleanup_all(controld_api, node, options.operation,
1218 options.interval_spec, scheduler);
1219
1220 if (rc == pcmk_rc_ok) {
1221 start_mainloop(controld_api);
1222 }
1223
1224 } else {
1225 cleanup(out, rsc, node, controld_api);
1226 }
1227
1228
1229
1230
1231
1232
1233 return exit_code;
1234 }
1235
1236 static crm_exit_t
1237 handle_clear(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1238 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1239 xmlNode *cib_xml_orig)
1240 {
1241 const char *node_name = (node != NULL)? node->priv->name : NULL;
1242 GList *before = NULL;
1243 GList *after = NULL;
1244 GList *remaining = NULL;
1245 int rc = pcmk_rc_ok;
1246
1247 if (!out->is_quiet(out)) {
1248 before = build_constraint_list(scheduler->input);
1249 }
1250
1251 if (options.clear_expired) {
1252 rc = cli_resource_clear_all_expired(scheduler->input, cib_conn,
1253 options.rsc_id, node_name,
1254 options.promoted_role_only);
1255
1256 } else if (node != NULL) {
1257 rc = cli_resource_clear(options.rsc_id, node_name, NULL, cib_conn, true,
1258 options.force);
1259
1260 } else {
1261 rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes,
1262 cib_conn, true, options.force);
1263 }
1264
1265 if (!out->is_quiet(out)) {
1266 xmlNode *cib_xml = NULL;
1267
1268 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml, cib_sync_call);
1269 rc = pcmk_legacy2rc(rc);
1270
1271 if (rc != pcmk_rc_ok) {
1272 g_set_error(&error, PCMK__RC_ERROR, rc,
1273 _("Could not get modified CIB: %s"), pcmk_rc_str(rc));
1274 g_list_free(before);
1275 pcmk__xml_free(cib_xml);
1276 return pcmk_rc2exitc(rc);
1277 }
1278
1279 scheduler->input = cib_xml;
1280 cluster_status(scheduler);
1281
1282 after = build_constraint_list(scheduler->input);
1283 remaining = pcmk__subtract_lists(before, after, (GCompareFunc) strcmp);
1284
1285 for (const GList *iter = remaining; iter != NULL; iter = iter->next) {
1286 const char *constraint = iter->data;
1287
1288 out->info(out, "Removing constraint: %s", constraint);
1289 }
1290
1291 g_list_free(before);
1292 g_list_free(after);
1293 g_list_free(remaining);
1294 }
1295
1296 return pcmk_rc2exitc(rc);
1297 }
1298
1299 static crm_exit_t
1300 handle_colocations(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1301 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1302 xmlNode *cib_xml_orig)
1303 {
1304 int rc = out->message(out, "locations-and-colocations", rsc,
1305 options.recursive, options.force);
1306
1307 return pcmk_rc2exitc(rc);
1308 }
1309
1310 static crm_exit_t
1311 handle_cts(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1312 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1313 xmlNode *cib_xml_orig)
1314 {
1315 g_list_foreach(scheduler->priv->resources, (GFunc) cli_resource_print_cts,
1316 out);
1317 cli_resource_print_cts_constraints(scheduler);
1318 return CRM_EX_OK;
1319 }
1320
1321 static crm_exit_t
1322 handle_delete(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1323 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1324 xmlNode *cib_xml_orig)
1325 {
1326
1327
1328
1329 int rc = pcmk_rc_ok;
1330
1331 if (options.rsc_type == NULL) {
1332 crm_exit_t ec = CRM_EX_USAGE;
1333
1334 g_set_error(&error, PCMK__EXITC_ERROR, ec,
1335 _("You need to specify a resource type with -t"));
1336 return ec;
1337 }
1338
1339 rc = pcmk__resource_delete(cib_conn, cib_sync_call, options.rsc_id,
1340 options.rsc_type);
1341 if (rc != pcmk_rc_ok) {
1342 g_set_error(&error, PCMK__RC_ERROR, rc,
1343 _("Could not delete resource %s: %s"),
1344 options.rsc_id, pcmk_rc_str(rc));
1345 }
1346 return pcmk_rc2exitc(rc);
1347 }
1348
1349 static crm_exit_t
1350 handle_delete_param(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1351 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1352 xmlNode *cib_xml_orig)
1353 {
1354 int rc = cli_resource_delete_attribute(rsc, options.rsc_id,
1355 options.prop_set,
1356 options.attr_set_type,
1357 options.prop_id,
1358 options.prop_name, cib_conn,
1359 cib_xml_orig, options.force);
1360
1361 return pcmk_rc2exitc(rc);
1362 }
1363
1364 static crm_exit_t
1365 handle_digests(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1366 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1367 xmlNode *cib_xml_orig)
1368 {
1369 int rc = pcmk__resource_digests(out, rsc, node, options.override_params);
1370
1371 return pcmk_rc2exitc(rc);
1372 }
1373
1374 static crm_exit_t
1375 handle_execute_agent(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1376 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1377 xmlNode *cib_xml_orig)
1378 {
1379 if (has_cmdline_config()) {
1380 return cli_resource_execute_from_params(out, NULL, options.class,
1381 options.provider, options.agent,
1382 options.operation,
1383 options.cmdline_params,
1384 options.override_params,
1385 options.timeout_ms,
1386 args->verbosity, options.force,
1387 options.check_level);
1388 }
1389 return cli_resource_execute(rsc, options.rsc_id, options.operation,
1390 options.override_params, options.timeout_ms,
1391 cib_conn, args->verbosity, options.force,
1392 options.check_level);
1393 }
1394
1395 static crm_exit_t
1396 handle_fail(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1397 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1398 xmlNode *cib_xml_orig)
1399 {
1400 int rc = cli_resource_fail(controld_api, rsc, options.rsc_id, node);
1401
1402 if (rc == pcmk_rc_ok) {
1403
1404 start_mainloop(controld_api);
1405 return exit_code;
1406 }
1407 return pcmk_rc2exitc(rc);;
1408 }
1409
1410 static crm_exit_t
1411 handle_get_param(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1412 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1413 xmlNode *cib_xml_orig)
1414 {
1415 unsigned int count = 0;
1416 GHashTable *params = NULL;
1417 pcmk_node_t *current = rsc->priv->fns->active_node(rsc, &count, NULL);
1418 bool free_params = true;
1419 const char *value = NULL;
1420 int rc = pcmk_rc_ok;
1421
1422 if (count > 1) {
1423 out->err(out,
1424 "%s is active on more than one node, returning the default "
1425 "value for %s",
1426 rsc->id, pcmk__s(options.prop_name, "unspecified property"));
1427 current = NULL;
1428 }
1429
1430 crm_debug("Looking up %s in %s", options.prop_name, rsc->id);
1431
1432 if (pcmk__str_eq(options.attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES,
1433 pcmk__str_none)) {
1434 params = pe_rsc_params(rsc, current, scheduler);
1435 free_params = false;
1436
1437 value = g_hash_table_lookup(params, options.prop_name);
1438
1439 } else if (pcmk__str_eq(options.attr_set_type, PCMK_XE_META_ATTRIBUTES,
1440 pcmk__str_none)) {
1441 params = pcmk__strkey_table(free, free);
1442 get_meta_attributes(params, rsc, NULL, scheduler);
1443
1444 value = g_hash_table_lookup(params, options.prop_name);
1445
1446 } else if (pcmk__str_eq(options.attr_set_type, ATTR_SET_ELEMENT,
1447 pcmk__str_none)) {
1448 value = crm_element_value(rsc->priv->xml, options.prop_name);
1449 free_params = false;
1450
1451 } else {
1452 const pcmk_rule_input_t rule_input = {
1453 .now = scheduler->priv->now,
1454 };
1455
1456 params = pcmk__strkey_table(free, free);
1457 pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_UTILIZATION,
1458 &rule_input, params, NULL, scheduler);
1459
1460 value = g_hash_table_lookup(params, options.prop_name);
1461 }
1462
1463 rc = out->message(out, "attribute-list", rsc, options.prop_name, value);
1464 if (free_params) {
1465 g_hash_table_destroy(params);
1466 }
1467
1468 return pcmk_rc2exitc(rc);
1469 }
1470
1471 static crm_exit_t
1472 handle_list_active_ops(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1473 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1474 xmlNode *cib_xml_orig)
1475 {
1476 const char *node_name = (node != NULL)? node->priv->name : NULL;
1477 int rc = cli_resource_print_operations(options.rsc_id, node_name, true,
1478 scheduler);
1479
1480 return pcmk_rc2exitc(rc);
1481 }
1482
1483 static crm_exit_t
1484 handle_list_agents(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1485 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1486 xmlNode *cib_xml_orig)
1487 {
1488 int rc = pcmk__list_agents(out, options.agent_spec);
1489
1490 return pcmk_rc2exitc(rc);
1491 }
1492
1493 static crm_exit_t
1494 handle_list_all_ops(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1495 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1496 xmlNode *cib_xml_orig)
1497 {
1498 const char *node_name = (node != NULL)? node->priv->name : NULL;
1499 int rc = cli_resource_print_operations(options.rsc_id, node_name, false,
1500 scheduler);
1501
1502 return pcmk_rc2exitc(rc);
1503 }
1504
1505 static crm_exit_t
1506 handle_list_alternatives(pcmk_resource_t *rsc, pcmk_node_t *node,
1507 cib_t *cib_conn, pcmk_scheduler_t *scheduler,
1508 pcmk_ipc_api_t *controld_api, xmlNode *cib_xml_orig)
1509 {
1510 int rc = pcmk__list_alternatives(out, options.agent_spec);
1511
1512 return pcmk_rc2exitc(rc);
1513 }
1514
1515 static crm_exit_t
1516 handle_list_instances(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1517 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1518 xmlNode *cib_xml_orig)
1519 {
1520 int rc = out->message(out, "resource-names-list",
1521 scheduler->priv->resources);
1522
1523 if (rc == pcmk_rc_no_output) {
1524
1525 return CRM_EX_NOSUCH;
1526 }
1527 return pcmk_rc2exitc(rc);
1528 }
1529
1530 static crm_exit_t
1531 handle_list_options(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1532 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1533 xmlNode *cib_xml_orig)
1534 {
1535 crm_exit_t ec = CRM_EX_OK;
1536 int rc = pcmk_rc_ok;
1537
1538 switch (options.opt_list) {
1539 case pcmk__opt_fencing:
1540 rc = pcmk__list_fencing_params(out, options.all);
1541 return pcmk_rc2exitc(rc);
1542
1543 case pcmk__opt_primitive:
1544 rc = pcmk__list_primitive_meta(out, options.all);
1545 return pcmk_rc2exitc(rc);
1546
1547 default:
1548 ec = CRM_EX_SOFTWARE;
1549 g_set_error(&error, PCMK__EXITC_ERROR, ec,
1550 "Bug: Invalid option list type");
1551 return ec;
1552 }
1553 }
1554
1555 static crm_exit_t
1556 handle_list_providers(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1557 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1558 xmlNode *cib_xml_orig)
1559 {
1560 int rc = pcmk__list_providers(out, options.agent_spec);
1561
1562 return pcmk_rc2exitc(rc);
1563 }
1564
1565 static crm_exit_t
1566 handle_list_resources(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1567 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1568 xmlNode *cib_xml_orig)
1569 {
1570 GList *all = g_list_prepend(NULL, (gpointer) "*");
1571 int rc = out->message(out, "resource-list", scheduler,
1572 pcmk_show_inactive_rscs
1573 |pcmk_show_rsc_only
1574 |pcmk_show_pending,
1575 true, all, all, false);
1576
1577 g_list_free(all);
1578
1579 if (rc == pcmk_rc_no_output) {
1580
1581 return CRM_EX_NOSUCH;
1582 }
1583 return pcmk_rc2exitc(rc);
1584 }
1585
1586 static crm_exit_t
1587 handle_list_standards(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1588 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1589 xmlNode *cib_xml_orig)
1590 {
1591 int rc = pcmk__list_standards(out);
1592
1593 return pcmk_rc2exitc(rc);
1594 }
1595
1596 static crm_exit_t
1597 handle_locate(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1598 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1599 xmlNode *cib_xml_orig)
1600 {
1601 GList *nodes = cli_resource_search(rsc, options.rsc_id);
1602 int rc = out->message(out, "resource-search-list", nodes, options.rsc_id);
1603
1604 g_list_free_full(nodes, free);
1605 return pcmk_rc2exitc(rc);
1606 }
1607
1608 static crm_exit_t
1609 handle_metadata(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1610 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1611 xmlNode *cib_xml_orig)
1612 {
1613 int rc = pcmk_rc_ok;
1614 char *standard = NULL;
1615 char *provider = NULL;
1616 char *type = NULL;
1617 char *metadata = NULL;
1618 lrmd_t *lrmd_conn = NULL;
1619
1620 rc = lrmd__new(&lrmd_conn, NULL, NULL, 0);
1621 if (rc != pcmk_rc_ok) {
1622 g_set_error(&error, PCMK__RC_ERROR, rc,
1623 _("Could not create executor connection"));
1624 lrmd_api_delete(lrmd_conn);
1625 return pcmk_rc2exitc(rc);
1626 }
1627
1628 rc = crm_parse_agent_spec(options.agent_spec, &standard, &provider, &type);
1629 rc = pcmk_legacy2rc(rc);
1630
1631 if (rc == pcmk_rc_ok) {
1632 rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard,
1633 provider, type,
1634 &metadata, 0);
1635 rc = pcmk_legacy2rc(rc);
1636
1637 if (metadata != NULL) {
1638 out->output_xml(out, PCMK_XE_METADATA, metadata);
1639 free(metadata);
1640 } else {
1641
1642
1643
1644
1645
1646 rc = ENXIO;
1647 g_set_error(&error, PCMK__RC_ERROR, rc,
1648 _("Metadata query for %s failed: %s"),
1649 options.agent_spec, pcmk_rc_str(rc));
1650 }
1651 } else {
1652 rc = ENXIO;
1653 g_set_error(&error, PCMK__RC_ERROR, rc,
1654 _("'%s' is not a valid agent specification"),
1655 options.agent_spec);
1656 }
1657
1658 lrmd_api_delete(lrmd_conn);
1659 return pcmk_rc2exitc(rc);
1660 }
1661
1662 static crm_exit_t
1663 handle_move(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1664 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1665 xmlNode *cib_xml_orig)
1666 {
1667 int rc = pcmk_rc_ok;
1668
1669 if (node == NULL) {
1670 rc = ban_or_move(out, rsc, cib_conn, options.move_lifetime);
1671 } else {
1672 rc = cli_resource_move(rsc, options.rsc_id, node, options.move_lifetime,
1673 cib_conn, options.promoted_role_only,
1674 options.force);
1675 }
1676
1677 if (rc == EINVAL) {
1678 return CRM_EX_USAGE;
1679 }
1680 return pcmk_rc2exitc(rc);
1681 }
1682
1683 static crm_exit_t
1684 handle_query_xml(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1685 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1686 xmlNode *cib_xml_orig)
1687 {
1688 int rc = cli_resource_print(rsc, true);
1689
1690 return pcmk_rc2exitc(rc);
1691 }
1692
1693 static crm_exit_t
1694 handle_query_xml_raw(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1695 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1696 xmlNode *cib_xml_orig)
1697 {
1698 int rc = cli_resource_print(rsc, false);
1699
1700 return pcmk_rc2exitc(rc);
1701 }
1702
1703 static crm_exit_t
1704 handle_refresh(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1705 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1706 xmlNode *cib_xml_orig)
1707 {
1708 if (rsc == NULL) {
1709 return refresh(out, node, controld_api);
1710 }
1711 refresh_resource(out, rsc, node, controld_api);
1712
1713
1714
1715
1716
1717
1718
1719 return exit_code;
1720 }
1721
1722 static crm_exit_t
1723 handle_restart(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1724 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1725 xmlNode *cib_xml_orig)
1726 {
1727
1728
1729
1730
1731 int rc = cli_resource_restart(out, rsc, node, options.move_lifetime,
1732 options.timeout_ms, cib_conn,
1733 options.promoted_role_only, options.force);
1734
1735 return pcmk_rc2exitc(rc);
1736 }
1737
1738 static crm_exit_t
1739 handle_set_param(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1740 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1741 xmlNode *cib_xml_orig)
1742 {
1743 int rc = pcmk_rc_ok;
1744
1745 if (pcmk__str_empty(options.prop_value)) {
1746 crm_exit_t ec = CRM_EX_USAGE;
1747
1748 g_set_error(&error, PCMK__EXITC_ERROR, ec,
1749 _("You need to supply a value with the -v option"));
1750 return ec;
1751 }
1752
1753 rc = cli_resource_update_attribute(rsc, options.rsc_id, options.prop_set,
1754 options.attr_set_type, options.prop_id,
1755 options.prop_name, options.prop_value,
1756 options.recursive, cib_conn,
1757 cib_xml_orig, options.force);
1758 return pcmk_rc2exitc(rc);
1759 }
1760
1761 static crm_exit_t
1762 handle_wait(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1763 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1764 xmlNode *cib_xml_orig)
1765 {
1766 int rc = wait_till_stable(out, options.timeout_ms, cib_conn);
1767
1768 return pcmk_rc2exitc(rc);
1769 }
1770
1771 static crm_exit_t
1772 handle_why(pcmk_resource_t *rsc, pcmk_node_t *node, cib_t *cib_conn,
1773 pcmk_scheduler_t *scheduler, pcmk_ipc_api_t *controld_api,
1774 xmlNode *cib_xml_orig)
1775 {
1776 int rc = out->message(out, "resource-reasons-list",
1777 scheduler->priv->resources, rsc, node);
1778
1779 return pcmk_rc2exitc(rc);
1780 }
1781
1782 static const crm_resource_cmd_info_t crm_resource_command_info[] = {
1783 [cmd_ban] = {
1784 handle_ban,
1785 crm_rsc_find_match_anon_basename
1786 |crm_rsc_find_match_history
1787 |crm_rsc_rejects_clone_instance
1788 |crm_rsc_requires_cib
1789 |crm_rsc_requires_resource
1790 |crm_rsc_requires_scheduler,
1791 },
1792 [cmd_cleanup] = {
1793 handle_cleanup,
1794 crm_rsc_find_match_anon_basename
1795 |crm_rsc_find_match_history
1796 |crm_rsc_requires_cib
1797 |crm_rsc_requires_controller
1798 |crm_rsc_requires_scheduler,
1799 },
1800 [cmd_clear] = {
1801 handle_clear,
1802 crm_rsc_find_match_anon_basename
1803 |crm_rsc_find_match_history
1804 |crm_rsc_rejects_clone_instance
1805 |crm_rsc_requires_cib
1806 |crm_rsc_requires_resource
1807 |crm_rsc_requires_scheduler,
1808 },
1809 [cmd_colocations] = {
1810 handle_colocations,
1811 crm_rsc_find_match_anon_basename
1812 |crm_rsc_find_match_history
1813 |crm_rsc_requires_cib
1814 |crm_rsc_requires_resource
1815 |crm_rsc_requires_scheduler,
1816 },
1817 [cmd_cts] = {
1818 handle_cts,
1819 crm_rsc_requires_cib
1820 |crm_rsc_requires_scheduler,
1821 },
1822 [cmd_delete] = {
1823 handle_delete,
1824 crm_rsc_rejects_clone_instance
1825 |crm_rsc_requires_cib
1826 |crm_rsc_requires_resource,
1827 },
1828 [cmd_delete_param] = {
1829 handle_delete_param,
1830 crm_rsc_find_match_basename
1831 |crm_rsc_find_match_history
1832 |crm_rsc_requires_cib
1833 |crm_rsc_requires_resource
1834 |crm_rsc_requires_scheduler,
1835 },
1836 [cmd_digests] = {
1837 handle_digests,
1838 crm_rsc_find_match_anon_basename
1839 |crm_rsc_find_match_history
1840 |crm_rsc_requires_cib
1841 |crm_rsc_requires_node
1842 |crm_rsc_requires_resource
1843 |crm_rsc_requires_scheduler,
1844 },
1845 [cmd_execute_agent] = {
1846 handle_execute_agent,
1847 crm_rsc_find_match_anon_basename
1848 |crm_rsc_find_match_history
1849 |crm_rsc_requires_cib
1850 |crm_rsc_requires_resource
1851 |crm_rsc_requires_scheduler,
1852 },
1853 [cmd_fail] = {
1854 handle_fail,
1855 crm_rsc_find_match_history
1856 |crm_rsc_requires_cib
1857 |crm_rsc_requires_controller
1858 |crm_rsc_requires_node
1859 |crm_rsc_requires_resource
1860 |crm_rsc_requires_scheduler,
1861 },
1862 [cmd_get_param] = {
1863 handle_get_param,
1864 crm_rsc_find_match_basename
1865 |crm_rsc_find_match_history
1866 |crm_rsc_requires_cib
1867 |crm_rsc_requires_resource
1868 |crm_rsc_requires_scheduler,
1869 },
1870 [cmd_list_active_ops] = {
1871 handle_list_active_ops,
1872 crm_rsc_requires_cib
1873 |crm_rsc_requires_scheduler,
1874 },
1875 [cmd_list_agents] = {
1876 handle_list_agents,
1877 0,
1878 },
1879 [cmd_list_all_ops] = {
1880 handle_list_all_ops,
1881 crm_rsc_requires_cib
1882 |crm_rsc_requires_scheduler,
1883 },
1884 [cmd_list_alternatives] = {
1885 handle_list_alternatives,
1886 0,
1887 },
1888 [cmd_list_instances] = {
1889 handle_list_instances,
1890 crm_rsc_requires_cib
1891 |crm_rsc_requires_scheduler,
1892 },
1893 [cmd_list_options] = {
1894 handle_list_options,
1895 0,
1896 },
1897 [cmd_list_providers] = {
1898 handle_list_providers,
1899 0,
1900 },
1901 [cmd_list_resources] = {
1902 handle_list_resources,
1903 crm_rsc_requires_cib
1904 |crm_rsc_requires_scheduler,
1905 },
1906 [cmd_list_standards] = {
1907 handle_list_standards,
1908 0,
1909 },
1910 [cmd_locate] = {
1911 handle_locate,
1912 crm_rsc_find_match_anon_basename
1913 |crm_rsc_find_match_history
1914 |crm_rsc_requires_cib
1915 |crm_rsc_requires_resource
1916 |crm_rsc_requires_scheduler,
1917 },
1918 [cmd_metadata] = {
1919 handle_metadata,
1920 0,
1921 },
1922 [cmd_move] = {
1923 handle_move,
1924 crm_rsc_find_match_anon_basename
1925 |crm_rsc_find_match_history
1926 |crm_rsc_rejects_clone_instance
1927 |crm_rsc_requires_cib
1928 |crm_rsc_requires_resource
1929 |crm_rsc_requires_scheduler,
1930 },
1931 [cmd_query_xml] = {
1932 handle_query_xml,
1933 crm_rsc_find_match_basename
1934 |crm_rsc_find_match_history
1935 |crm_rsc_requires_cib
1936 |crm_rsc_requires_resource
1937 |crm_rsc_requires_scheduler,
1938 },
1939 [cmd_query_xml_raw] = {
1940 handle_query_xml_raw,
1941 crm_rsc_find_match_basename
1942 |crm_rsc_find_match_history
1943 |crm_rsc_requires_cib
1944 |crm_rsc_requires_resource
1945 |crm_rsc_requires_scheduler,
1946 },
1947 [cmd_refresh] = {
1948 handle_refresh,
1949 crm_rsc_find_match_anon_basename
1950 |crm_rsc_find_match_history
1951 |crm_rsc_requires_cib
1952 |crm_rsc_requires_controller
1953 |crm_rsc_requires_scheduler,
1954 },
1955 [cmd_restart] = {
1956 handle_restart,
1957 crm_rsc_find_match_anon_basename
1958 |crm_rsc_find_match_history
1959 |crm_rsc_rejects_clone_instance
1960 |crm_rsc_requires_cib
1961 |crm_rsc_requires_resource
1962 |crm_rsc_requires_scheduler,
1963 },
1964 [cmd_set_param] = {
1965 handle_set_param,
1966 crm_rsc_find_match_basename
1967 |crm_rsc_find_match_history
1968 |crm_rsc_requires_cib
1969 |crm_rsc_requires_resource
1970 |crm_rsc_requires_scheduler,
1971 },
1972 [cmd_wait] = {
1973 handle_wait,
1974 crm_rsc_requires_cib,
1975 },
1976 [cmd_why] = {
1977 handle_why,
1978 crm_rsc_find_match_anon_basename
1979 |crm_rsc_find_match_history
1980 |crm_rsc_requires_cib
1981 |crm_rsc_requires_scheduler,
1982 },
1983 };
1984
1985 static GOptionContext *
1986 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
1987 GOptionContext *context = NULL;
1988
1989 GOptionEntry extra_prog_entries[] = {
1990 { "quiet", 'Q', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &(args->quiet),
1991 "Be less descriptive in output.",
1992 NULL },
1993 { "resource", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.rsc_id,
1994 "Resource ID",
1995 "ID" },
1996 { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY, &options.remainder,
1997 NULL,
1998 NULL },
1999
2000 { NULL }
2001 };
2002
2003 const char *description = "Examples:\n\n"
2004 "List the available OCF agents:\n\n"
2005 "\t# crm_resource --list-agents ocf\n\n"
2006 "List the available OCF agents from the linux-ha project:\n\n"
2007 "\t# crm_resource --list-agents ocf:heartbeat\n\n"
2008 "Move 'myResource' to a specific node:\n\n"
2009 "\t# crm_resource --resource myResource --move --node altNode\n\n"
2010 "Allow (but not force) 'myResource' to move back to its original "
2011 "location:\n\n"
2012 "\t# crm_resource --resource myResource --clear\n\n"
2013 "Stop 'myResource' (and anything that depends on it):\n\n"
2014 "\t# crm_resource --resource myResource --set-parameter "
2015 PCMK_META_TARGET_ROLE "--meta --parameter-value Stopped\n\n"
2016 "Tell the cluster not to manage 'myResource' (the cluster will not "
2017 "attempt to start or stop the\n"
2018 "resource under any circumstances; useful when performing maintenance "
2019 "tasks on a resource):\n\n"
2020 "\t# crm_resource --resource myResource --set-parameter "
2021 PCMK_META_IS_MANAGED "--meta --parameter-value false\n\n"
2022 "Erase the operation history of 'myResource' on 'aNode' (the cluster "
2023 "will 'forget' the existing\n"
2024 "resource state, including any errors, and attempt to recover the"
2025 "resource; useful when a resource\n"
2026 "had failed permanently and has been repaired by an administrator):\n\n"
2027 "\t# crm_resource --resource myResource --cleanup --node aNode\n\n";
2028
2029 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
2030 g_option_context_set_description(context, description);
2031
2032
2033
2034
2035 pcmk__add_main_args(context, extra_prog_entries);
2036
2037 pcmk__add_arg_group(context, "queries", "Queries:",
2038 "Show query help", query_entries);
2039 pcmk__add_arg_group(context, "commands", "Commands:",
2040 "Show command help", command_entries);
2041 pcmk__add_arg_group(context, "locations", "Locations:",
2042 "Show location help", location_entries);
2043 pcmk__add_arg_group(context, "advanced", "Advanced:",
2044 "Show advanced option help", advanced_entries);
2045 pcmk__add_arg_group(context, "additional", "Additional Options:",
2046 "Show additional options", addl_entries);
2047 return context;
2048 }
2049
2050 int
2051 main(int argc, char **argv)
2052 {
2053 const crm_resource_cmd_info_t *command_info = NULL;
2054 pcmk_resource_t *rsc = NULL;
2055 pcmk_node_t *node = NULL;
2056 cib_t *cib_conn = NULL;
2057 pcmk_scheduler_t *scheduler = NULL;
2058 pcmk_ipc_api_t *controld_api = NULL;
2059 xmlNode *cib_xml_orig = NULL;
2060 uint32_t find_flags = 0;
2061 int rc = pcmk_rc_ok;
2062
2063 GOptionGroup *output_group = NULL;
2064 gchar **processed_args = NULL;
2065 GOptionContext *context = NULL;
2066
2067
2068
2069
2070
2071 args = pcmk__new_common_args(SUMMARY);
2072 processed_args = pcmk__cmdline_preproc(argv, "GHINSTdginpstuvx");
2073 context = build_arg_context(args, &output_group);
2074
2075 pcmk__register_formats(output_group, formats);
2076 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
2077 exit_code = CRM_EX_USAGE;
2078 goto done;
2079 }
2080
2081 pcmk__cli_init_logging("crm_resource", args->verbosity);
2082
2083 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
2084 if (rc != pcmk_rc_ok) {
2085 exit_code = CRM_EX_ERROR;
2086 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("Error creating output format %s: %s"),
2087 args->output_ty, pcmk_rc_str(rc));
2088 goto done;
2089 }
2090
2091 pe__register_messages(out);
2092 crm_resource_register_messages(out);
2093 lrmd__register_messages(out);
2094 pcmk__register_lib_messages(out);
2095
2096 out->quiet = args->quiet;
2097
2098 crm_log_args(argc, argv);
2099
2100
2101
2102
2103
2104
2105 if (options.clear_expired && (options.rsc_cmd != cmd_clear)) {
2106 exit_code = CRM_EX_USAGE;
2107 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("--expired requires --clear or -U"));
2108 goto done;
2109 }
2110
2111 if (options.remainder != NULL) {
2112
2113 if (options.override_params == NULL) {
2114 GString *msg = g_string_sized_new(128);
2115 guint len = g_strv_length(options.remainder);
2116
2117 g_string_append(msg, "non-option ARGV-elements:");
2118
2119 for (int i = 0; i < len; i++) {
2120 g_string_append_printf(msg, "\n[%d of %u] %s",
2121 i + 1, len, options.remainder[i]);
2122 }
2123 exit_code = CRM_EX_USAGE;
2124 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg->str);
2125 g_string_free(msg, TRUE);
2126 goto done;
2127 }
2128
2129 for (gchar **arg = options.remainder; *arg != NULL; arg++) {
2130 gchar *name = NULL;
2131 gchar *value = NULL;
2132 int rc = pcmk__scan_nvpair(*arg, &name, &value);
2133
2134 if (rc != pcmk_rc_ok) {
2135 exit_code = CRM_EX_USAGE;
2136 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2137 _("Error parsing '%s' as a name=value pair"), *arg);
2138 goto done;
2139 }
2140
2141 g_hash_table_insert(options.override_params, name, value);
2142 }
2143 }
2144
2145 if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
2146 switch (options.rsc_cmd) {
2147
2148
2149
2150
2151 case cmd_get_param:
2152 case cmd_list_instances:
2153 case cmd_list_standards:
2154 pcmk__output_enable_list_element(out);
2155 break;
2156
2157 default:
2158 break;
2159 }
2160
2161 } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) {
2162 switch (options.rsc_cmd) {
2163 case cmd_colocations:
2164 case cmd_list_resources:
2165 pcmk__output_text_set_fancy(out, true);
2166 break;
2167 default:
2168 break;
2169 }
2170 }
2171
2172 if (args->version) {
2173 out->version(out, false);
2174 goto done;
2175 }
2176
2177
2178 if ((options.rsc_cmd >= 0) && (options.rsc_cmd <= cmd_max)) {
2179 command_info = &crm_resource_command_info[options.rsc_cmd];
2180 }
2181 if ((command_info == NULL) || (command_info->fn == NULL)) {
2182 exit_code = CRM_EX_SOFTWARE;
2183 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2184 _("Bug: Unimplemented command: %d"), (int) options.rsc_cmd);
2185 goto done;
2186 }
2187
2188
2189
2190
2191 if (has_cmdline_config()) {
2192 validate_cmdline_config();
2193 if (error != NULL) {
2194 exit_code = CRM_EX_USAGE;
2195 goto done;
2196 }
2197
2198 } else if (options.cmdline_params != NULL) {
2199 exit_code = CRM_EX_USAGE;
2200 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2201 _("--option must be used with --validate and without -r"));
2202 g_hash_table_destroy(options.cmdline_params);
2203 goto done;
2204 }
2205
2206
2207 if (pcmk_is_set(command_info->flags, crm_rsc_requires_resource)
2208 && !has_cmdline_config()
2209 && !options.clear_expired
2210 && (options.rsc_id == NULL)) {
2211
2212 exit_code = CRM_EX_USAGE;
2213 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2214 _("Must supply a resource ID with -r/--resource"));
2215 goto done;
2216 }
2217
2218
2219 if (pcmk_is_set(command_info->flags, crm_rsc_requires_node)
2220 && (options.host_uname == NULL)) {
2221
2222 exit_code = CRM_EX_USAGE;
2223 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2224 _("Must supply a node name with -N/--node"));
2225 goto done;
2226 }
2227
2228
2229 if (pcmk_is_set(command_info->flags, crm_rsc_requires_cib)
2230 && !has_cmdline_config()) {
2231
2232 cib_conn = cib_new();
2233 if ((cib_conn == NULL) || (cib_conn->cmds == NULL)) {
2234 exit_code = CRM_EX_DISCONNECT;
2235 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2236 _("Could not create CIB connection"));
2237 goto done;
2238 }
2239 rc = cib__signon_attempts(cib_conn, cib_command, 5);
2240 rc = pcmk_legacy2rc(rc);
2241 if (rc != pcmk_rc_ok) {
2242 exit_code = pcmk_rc2exitc(rc);
2243 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2244 _("Could not connect to the CIB: %s"), pcmk_rc_str(rc));
2245 goto done;
2246 }
2247 }
2248
2249
2250 if (pcmk_is_set(command_info->flags, crm_rsc_requires_scheduler)
2251 && !has_cmdline_config()) {
2252
2253 rc = initialize_scheduler_data(&scheduler, cib_conn, out,
2254 &cib_xml_orig);
2255 if (rc != pcmk_rc_ok) {
2256 exit_code = pcmk_rc2exitc(rc);
2257 goto done;
2258 }
2259 }
2260
2261
2262 if (pcmk_is_set(command_info->flags, crm_rsc_requires_controller)
2263 && (getenv("CIB_file") == NULL)) {
2264
2265 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
2266 if (rc != pcmk_rc_ok) {
2267 exit_code = pcmk_rc2exitc(rc);
2268 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2269 _("Error connecting to the controller: %s"), pcmk_rc_str(rc));
2270 goto done;
2271 }
2272
2273 pcmk_register_ipc_callback(controld_api, controller_event_callback,
2274 &exit_code);
2275
2276 rc = pcmk__connect_ipc(controld_api, pcmk_ipc_dispatch_main, 5);
2277 if (rc != pcmk_rc_ok) {
2278 exit_code = pcmk_rc2exitc(rc);
2279 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2280 _("Error connecting to %s: %s"),
2281 pcmk_ipc_name(controld_api, true), pcmk_rc_str(rc));
2282 goto done;
2283 }
2284 }
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298 if (options.host_uname != NULL) {
2299 node = pcmk_find_node(scheduler, options.host_uname);
2300
2301 if (node == NULL) {
2302 exit_code = CRM_EX_NOSUCH;
2303 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2304 _("Node '%s' not found"), options.host_uname);
2305 goto done;
2306 }
2307 }
2308
2309
2310
2311
2312
2313
2314 if (pcmk_is_set(command_info->flags, crm_rsc_find_match_anon_basename)) {
2315 find_flags |= pcmk_rsc_match_anon_basename;
2316 }
2317 if (pcmk_is_set(command_info->flags, crm_rsc_find_match_basename)) {
2318 find_flags |= pcmk_rsc_match_basename;
2319 }
2320 if (pcmk_is_set(command_info->flags, crm_rsc_find_match_history)) {
2321 find_flags |= pcmk_rsc_match_history;
2322 }
2323 if ((find_flags != 0) && (options.rsc_id != NULL)) {
2324 pcmk__assert(scheduler != NULL);
2325
2326 rsc = pe_find_resource_with_flags(scheduler->priv->resources,
2327 options.rsc_id, find_flags);
2328 if (rsc == NULL) {
2329 exit_code = CRM_EX_NOSUCH;
2330 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2331 _("Resource '%s' not found"), options.rsc_id);
2332 goto done;
2333 }
2334
2335 if (pcmk_is_set(command_info->flags, crm_rsc_rejects_clone_instance)
2336 && pcmk__is_clone(rsc->priv->parent)
2337 && (strchr(options.rsc_id, ':') != NULL)) {
2338
2339 exit_code = CRM_EX_INVALID_PARAM;
2340 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2341 _("Cannot operate on clone resource instance '%s'"),
2342 options.rsc_id);
2343 goto done;
2344 }
2345 }
2346
2347 exit_code = command_info->fn(rsc, node, cib_conn, scheduler, controld_api,
2348 cib_xml_orig);
2349
2350 done:
2351
2352 if ((exit_code != CRM_EX_OK) && (exit_code != CRM_EX_USAGE)) {
2353 if (error != NULL) {
2354 char *msg = crm_strdup_printf("%s\nError performing operation: %s",
2355 error->message, crm_exit_str(exit_code));
2356 g_clear_error(&error);
2357 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg);
2358 free(msg);
2359 } else {
2360 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2361 _("Error performing operation: %s"), crm_exit_str(exit_code));
2362 }
2363 }
2364
2365 g_free(options.host_uname);
2366 g_free(options.interval_spec);
2367 g_free(options.move_lifetime);
2368 g_free(options.operation);
2369 g_free(options.prop_id);
2370 free(options.prop_name);
2371 g_free(options.prop_set);
2372 g_free(options.prop_value);
2373 g_free(options.rsc_id);
2374 g_free(options.rsc_type);
2375 free(options.agent_spec);
2376 g_free(options.agent);
2377 g_free(options.class);
2378 g_free(options.provider);
2379 if (options.override_params != NULL) {
2380 g_hash_table_destroy(options.override_params);
2381 }
2382 g_strfreev(options.remainder);
2383
2384
2385
2386 g_strfreev(processed_args);
2387 g_option_context_free(context);
2388
2389 pcmk__xml_free(cib_xml_orig);
2390 cib__clean_up_connection(&cib_conn);
2391 pcmk_free_ipc_api(controld_api);
2392 pcmk_free_scheduler(scheduler);
2393 if (mainloop != NULL) {
2394 g_main_loop_unref(mainloop);
2395 }
2396
2397 pcmk__output_and_clear_error(&error, out);
2398
2399 if (out != NULL) {
2400 out->finish(out, exit_code, true, NULL);
2401 pcmk__output_free(out);
2402 }
2403
2404 pcmk__unregister_formats();
2405 return crm_exit(exit_code);
2406 }