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