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