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