root/tools/crm_resource.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bye
  2. quit_main_loop
  3. resource_ipc_timeout
  4. controller_event_callback
  5. start_mainloop
  6. compare_id
  7. build_constraint_list
  8. validate_opt_list
  9. command_cb
  10. attr_set_type_cb
  11. cmdline_config_cb
  12. option_cb
  13. timeout_cb
  14. ban_or_move
  15. cleanup
  16. clear_constraints
  17. initialize_scheduler_data
  18. list_options
  19. refresh
  20. refresh_resource
  21. set_property
  22. show_metadata
  23. validate_cmdline_config
  24. get_find_flags
  25. is_node_required
  26. is_resource_required
  27. is_cib_required
  28. is_controller_required
  29. is_scheduler_required
  30. accept_clone_instance
  31. build_arg_context
  32. main

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

/* [previous][next][first][last][top][bottom][index][help] */