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