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