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