diff --git a/cts/cli/regression.crm_resource.exp b/cts/cli/regression.crm_resource.exp index 9859fe316dd..065dada5e22 100644 --- a/cts/cli/regression.crm_resource.exp +++ b/cts/cli/regression.crm_resource.exp @@ -2,7 +2,6 @@ crm_resource: non-option ARGV-elements: [1 of 2] foo [2 of 2] bar - =#=#=#= End test: crm_resource run with extra arguments - Incorrect usage (64) =#=#=#= * Passed: crm_resource - crm_resource run with extra arguments =#=#=#= Begin test: List all available resource options (invalid type) =#=#=#= diff --git a/cts/cts-cli.in b/cts/cts-cli.in index d2816d0410b..fe35dbd0af9 100644 --- a/cts/cts-cli.in +++ b/cts/cts-cli.in @@ -1804,7 +1804,7 @@ class CrmResourceRegressionTest(RegressionTest): "crm_resource --locate -r promotable-rsc:1 {fmt}", [Test, ValidatingTest]), Test("Try to move an instance of a cloned resource", - "crm_resource -r promotable-rsc:0 --move --node node1", + "crm_resource -r promotable-rsc:0 --move --node cluster01", expected_rc=ExitStatus.INVALID_PARAM), ] diff --git a/lib/services/services.c b/lib/services/services.c index ebd9c83c48c..1141c14b64e 100644 --- a/lib/services/services.c +++ b/lib/services/services.c @@ -250,6 +250,7 @@ copy_action_arguments(svc_action_t *op, uint32_t ra_caps, const char *name, return pcmk_rc_ok; } +// Takes ownership of params svc_action_t * services__create_resource_action(const char *name, const char *standard, const char *provider, const char *agent, diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 0cff5d08827..ddeb1a76bf2 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -111,13 +111,6 @@ struct { .rsc_cmd = cmd_list_resources, // List all resources if no command given }; -gboolean attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean cmdline_config_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean option_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); - static crm_exit_t exit_code = CRM_EX_OK; static pcmk__output_t *out = NULL; static pcmk__common_args_t *args = NULL; @@ -140,43 +133,6 @@ static pcmk__supported_format_t formats[] = { { NULL, NULL, NULL } }; -// Clean up and exit -static crm_exit_t -bye(crm_exit_t ec) -{ - pcmk__output_and_clear_error(&error, out); - - if (out != NULL) { - out->finish(out, ec, true, NULL); - pcmk__output_free(out); - } - pcmk__unregister_formats(); - - if (cib_conn != NULL) { - cib_t *save_cib_conn = cib_conn; - - cib_conn = NULL; // Ensure we can't free this twice - cib__clean_up_connection(&save_cib_conn); - } - - if (controld_api != NULL) { - pcmk_ipc_api_t *save_controld_api = controld_api; - - controld_api = NULL; // Ensure we can't free this twice - pcmk_free_ipc_api(save_controld_api); - } - - if (mainloop != NULL) { - g_main_loop_unref(mainloop); - mainloop = NULL; - } - - pcmk_free_scheduler(scheduler); - scheduler = NULL; - crm_exit(ec); - return ec; -} - static void quit_main_loop(crm_exit_t ec) { @@ -299,6 +255,39 @@ validate_opt_list(const gchar *optarg) return TRUE; } +// GOptionArgFunc callback functions + +static gboolean +attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) { + if (pcmk__str_any_of(option_name, "-m", "--meta", NULL)) { + options.attr_set_type = PCMK_XE_META_ATTRIBUTES; + } else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) { + options.attr_set_type = PCMK_XE_UTILIZATION; + } else if (pcmk__str_eq(option_name, "--element", pcmk__str_none)) { + options.attr_set_type = ATTR_SET_ELEMENT; + } + return TRUE; +} + +static gboolean +cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + options.cmdline_config = true; + + if (pcmk__str_eq(option_name, "--class", pcmk__str_none)) { + pcmk__str_update(&options.v_class, optarg); + + } else if (pcmk__str_eq(option_name, "--provider", pcmk__str_none)) { + pcmk__str_update(&options.v_provider, optarg); + + } else { // --agent + pcmk__str_update(&options.v_agent, optarg); + } + return TRUE; +} + /*! * \internal * \brief Process options that set the command @@ -349,7 +338,7 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, options.rsc_cmd = cmd_digests; if (options.override_params == NULL) { - options.override_params = pcmk__strkey_table(free, free); + options.override_params = pcmk__strkey_table(g_free, g_free); } } else if (pcmk__str_any_of(option_name, @@ -362,7 +351,7 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, options.operation = g_strdup(option_name + 2); // skip "--" if (options.override_params == NULL) { - options.override_params = pcmk__strkey_table(free, free); + options.override_params = pcmk__strkey_table(g_free, g_free); } if (optarg != NULL) { @@ -451,6 +440,46 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, return TRUE; } +static gboolean +option_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + gchar *name = NULL; + gchar *value = NULL; + + if (pcmk__scan_nvpair(optarg, &name, &value) != pcmk_rc_ok) { + return FALSE; + } + + /* services__create_resource_action() ultimately takes ownership of + * options.cmdline_params. It's not worth trying to ensure that the entire + * call path uses (gchar *) strings and g_free(). So create the table for + * (char *) strings, and duplicate the (gchar *) strings when inserting. + */ + if (options.cmdline_params == NULL) { + options.cmdline_params = pcmk__strkey_table(free, free); + } + pcmk__insert_dup(options.cmdline_params, name, value); + g_free(name); + g_free(value); + return TRUE; +} + +static gboolean +timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + long long timeout_ms = crm_get_msec(optarg); + + if (timeout_ms < 0) { + return FALSE; + } + options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX); + return TRUE; +} + +// Command line option specification + /* short option letters still available: eEJkKXyYZ */ static GOptionEntry query_entries[] = { @@ -769,66 +798,6 @@ static GOptionEntry addl_entries[] = { { NULL } }; -gboolean -attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-m", "--meta", NULL)) { - options.attr_set_type = PCMK_XE_META_ATTRIBUTES; - } else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) { - options.attr_set_type = PCMK_XE_UTILIZATION; - } else if (pcmk__str_eq(option_name, "--element", pcmk__str_none)) { - options.attr_set_type = ATTR_SET_ELEMENT; - } - return TRUE; -} - -gboolean -cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - options.cmdline_config = true; - - if (pcmk__str_eq(option_name, "--class", pcmk__str_none)) { - pcmk__str_update(&options.v_class, optarg); - - } else if (pcmk__str_eq(option_name, "--provider", pcmk__str_none)) { - pcmk__str_update(&options.v_provider, optarg); - - } else { // --agent - pcmk__str_update(&options.v_agent, optarg); - } - return TRUE; -} - -gboolean -option_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - gchar *name = NULL; - gchar *value = NULL; - - if (pcmk__scan_nvpair(optarg, &name, &value) != pcmk_rc_ok) { - return FALSE; - } - if (options.cmdline_params == NULL) { - options.cmdline_params = pcmk__strkey_table(free, free); - } - pcmk__insert_dup(options.cmdline_params, name, value); - g_free(name); - g_free(value); - return TRUE; -} - -gboolean -timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - long long timeout_ms = crm_get_msec(optarg); - - if (timeout_ms < 0) { - return FALSE; - } - options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX); - return TRUE; -} - static int ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc, const char *move_lifetime) @@ -900,10 +869,11 @@ cleanup(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node) } crm_debug("Erasing failures of %s (%s requested) on %s", - rsc->id, options.rsc_id, (options.host_uname? options.host_uname: "all nodes")); - rc = cli_resource_delete(controld_api, options.host_uname, rsc, - options.operation, options.interval_spec, TRUE, - scheduler, options.force); + rsc->id, options.rsc_id, + ((node != NULL)? pcmk__node_name(node) : "all nodes")); + rc = cli_resource_delete(controld_api, node, rsc, options.operation, + options.interval_spec, true, scheduler, + options.force); if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) { // Show any reasons why resource might stay stopped @@ -915,79 +885,13 @@ cleanup(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node) } } -static int -clear_constraints(pcmk__output_t *out) -{ - GList *before = NULL; - GList *after = NULL; - GList *remaining = NULL; - GList *ele = NULL; - pcmk_node_t *dest = NULL; - int rc = pcmk_rc_ok; - - if (!out->is_quiet(out)) { - before = build_constraint_list(scheduler->input); - } - - if (options.clear_expired) { - rc = cli_resource_clear_all_expired(scheduler->input, cib_conn, - options.rsc_id, options.host_uname, - options.promoted_role_only); - - } else if (options.host_uname) { - dest = pcmk_find_node(scheduler, options.host_uname); - if (dest == NULL) { - rc = pcmk_rc_node_unknown; - if (!out->is_quiet(out)) { - g_list_free(before); - } - return rc; - } - rc = cli_resource_clear(options.rsc_id, dest->priv->name, NULL, - cib_conn, true, options.force); - - } else { - rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes, - cib_conn, true, options.force); - } - - if (!out->is_quiet(out)) { - xmlNode *cib_xml = NULL; - - rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml, cib_sync_call); - rc = pcmk_legacy2rc(rc); - - if (rc != pcmk_rc_ok) { - g_set_error(&error, PCMK__RC_ERROR, rc, - _("Could not get modified CIB: %s\n"), pcmk_rc_str(rc)); - g_list_free(before); - pcmk__xml_free(cib_xml); - return rc; - } - - scheduler->input = cib_xml; - cluster_status(scheduler); - - after = build_constraint_list(scheduler->input); - remaining = pcmk__subtract_lists(before, after, (GCompareFunc) strcmp); - - for (ele = remaining; ele != NULL; ele = ele->next) { - out->info(out, "Removing constraint: %s", (char *) ele->data); - } - - g_list_free(before); - g_list_free(after); - g_list_free(remaining); - } - - return rc; -} - static int initialize_scheduler_data(xmlNode **cib_xml_orig) { int rc = pcmk_rc_ok; + pcmk__assert(cib_conn != NULL); + scheduler = pcmk_new_scheduler(); if (scheduler == NULL) { return ENOMEM; @@ -1004,63 +908,48 @@ initialize_scheduler_data(xmlNode **cib_xml_orig) return pcmk_rc_ok; } -static void -list_options(void) -{ - switch (options.opt_list) { - case pcmk__opt_fencing: - exit_code = pcmk_rc2exitc(pcmk__list_fencing_params(out, - options.all)); - break; - case pcmk__opt_primitive: - exit_code = pcmk_rc2exitc(pcmk__list_primitive_meta(out, - options.all)); - break; - default: - exit_code = CRM_EX_SOFTWARE; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "BUG: Invalid option list type"); - break; - } -} - static int -refresh(pcmk__output_t *out) +refresh(pcmk__output_t *out, const pcmk_node_t *node) { - int rc = pcmk_rc_ok; - const char *router_node = options.host_uname; + const char *node_name = NULL; + const char *log_node_name = "all nodes"; + const char *router_node = NULL; int attr_options = pcmk__node_attr_none; + int rc = pcmk_rc_ok; - if (options.host_uname) { - pcmk_node_t *node = pcmk_find_node(scheduler, options.host_uname); + if (node != NULL) { + node_name = node->priv->name; + log_node_name = pcmk__node_name(node); + router_node = node->priv->name; + } - if (pcmk__is_pacemaker_remote_node(node)) { - node = pcmk__current_node(node->priv->remote); - if (node == NULL) { - rc = ENXIO; - g_set_error(&error, PCMK__RC_ERROR, rc, - _("No cluster connection to Pacemaker Remote node %s detected"), - options.host_uname); - return rc; - } - router_node = node->priv->name; - attr_options |= pcmk__node_attr_remote; + if (pcmk__is_pacemaker_remote_node(node)) { + const pcmk_node_t *conn_host = pcmk__current_node(node->priv->remote); + + if (conn_host == NULL) { + rc = ENXIO; + g_set_error(&error, PCMK__RC_ERROR, rc, + _("No cluster connection to Pacemaker Remote node %s " + "detected"), + log_node_name); + return rc; } + router_node = conn_host->priv->name; + pcmk__set_node_attr_flags(attr_options, pcmk__node_attr_remote); } if (controld_api == NULL) { out->info(out, "Dry run: skipping clean-up of %s due to CIB_file", - options.host_uname? options.host_uname : "all nodes"); - rc = pcmk_rc_ok; - return rc; + log_node_name); + return pcmk_rc_ok; } - crm_debug("Re-checking the state of all resources on %s", options.host_uname?options.host_uname:"all nodes"); + crm_debug("Re-checking the state of all resources on %s", log_node_name); - rc = pcmk__attrd_api_clear_failures(NULL, options.host_uname, NULL, - NULL, NULL, NULL, attr_options); + rc = pcmk__attrd_api_clear_failures(NULL, node_name, NULL, NULL, NULL, NULL, + attr_options); - if (pcmk_controld_api_reprobe(controld_api, options.host_uname, + if (pcmk_controld_api_reprobe(controld_api, node_name, router_node) == pcmk_rc_ok) { start_mainloop(controld_api); } @@ -1078,9 +967,10 @@ refresh_resource(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node) } crm_debug("Re-checking the state of %s (%s requested) on %s", - rsc->id, options.rsc_id, (options.host_uname? options.host_uname: "all nodes")); - rc = cli_resource_delete(controld_api, options.host_uname, rsc, NULL, 0, - FALSE, scheduler, options.force); + rsc->id, options.rsc_id, + ((node != NULL)? pcmk__node_name(node) : "all nodes")); + rc = cli_resource_delete(controld_api, node, rsc, NULL, 0, false, scheduler, + options.force); if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) { // Show any reasons why resource might stay stopped @@ -1092,78 +982,27 @@ refresh_resource(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node) } } -static int -show_metadata(pcmk__output_t *out, const char *agent_spec) +static void +validate_cmdline_config(void) { - int rc = pcmk_rc_ok; - char *standard = NULL; - char *provider = NULL; - char *type = NULL; - char *metadata = NULL; - lrmd_t *lrmd_conn = NULL; + // Cannot use both --resource and command-line resource configuration + if (options.rsc_id != NULL) { + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, + _("--resource cannot be used with --class, --agent, and --provider")); - rc = lrmd__new(&lrmd_conn, NULL, NULL, 0); - if (rc != pcmk_rc_ok) { - g_set_error(&error, PCMK__RC_ERROR, rc, - _("Could not create executor connection")); - lrmd_api_delete(lrmd_conn); - return rc; - } + // Not all commands support command-line resource configuration + } else if (options.rsc_cmd != cmd_execute_agent) { + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, + _("--class, --agent, and --provider can only be used with " + "--validate and --force-*")); - rc = crm_parse_agent_spec(agent_spec, &standard, &provider, &type); - rc = pcmk_legacy2rc(rc); - - if (rc == pcmk_rc_ok) { - rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, - provider, type, - &metadata, 0); - rc = pcmk_legacy2rc(rc); - - if (metadata) { - out->output_xml(out, PCMK_XE_METADATA, metadata); - free(metadata); - } else { - /* We were given a validly formatted spec, but it doesn't necessarily - * match up with anything that exists. Use ENXIO as the return code - * here because that maps to an exit code of CRM_EX_NOSUCH, which - * probably is the most common reason to get here. - */ - rc = ENXIO; - g_set_error(&error, PCMK__RC_ERROR, rc, - _("Metadata query for %s failed: %s"), - agent_spec, pcmk_rc_str(rc)); - } - } else { - rc = ENXIO; - g_set_error(&error, PCMK__RC_ERROR, rc, - _("'%s' is not a valid agent specification"), agent_spec); - } - - lrmd_api_delete(lrmd_conn); - return rc; -} - -static void -validate_cmdline_config(void) -{ - // Cannot use both --resource and command-line resource configuration - if (options.rsc_id != NULL) { - g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, - _("--resource cannot be used with --class, --agent, and --provider")); - - // Not all commands support command-line resource configuration - } else if (options.rsc_cmd != cmd_execute_agent) { - g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, - _("--class, --agent, and --provider can only be used with " - "--validate and --force-*")); - - // Not all of --class, --agent, and --provider need to be given. Not all - // classes support the concept of a provider. Check that what we were given - // is valid. - } else if (pcmk__str_eq(options.v_class, "stonith", pcmk__str_none)) { - if (options.v_provider != NULL) { - g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, - _("stonith does not support providers")); + // Not all of --class, --agent, and --provider need to be given. Not all + // classes support the concept of a provider. Check that what we were given + // is valid. + } else if (pcmk__str_eq(options.v_class, "stonith", pcmk__str_none)) { + if (options.v_provider != NULL) { + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, + _("stonith does not support providers")); } else if (stonith_agent_exists(options.v_agent, 0) == FALSE) { g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, @@ -1278,30 +1117,63 @@ is_resource_required(void) /*! * \internal - * \brief Check whether a CIB connection is required + * \brief Check whether a scheduler data object is required * - * \return \c true if a CIB connection is required, or \c false otherwise + * If true, the caller will populate the scheduler data from the CIB connection. + * + * \return \c true if scheduler data is required, or \c false otherwise */ static bool -is_cib_required(void) +is_scheduler_required(void) { if (options.cmdline_config) { + // cmd_execute_agent using CLI parameters instead of CIB connection return false; } switch (options.rsc_cmd) { + case cmd_delete: case cmd_list_agents: case cmd_list_alternatives: case cmd_list_options: case cmd_list_providers: case cmd_list_standards: case cmd_metadata: + case cmd_wait: return false; default: return true; } } +/*! + * \internal + * \brief Check whether a CIB connection is required + * + * \return \c true if a CIB connection is required, or \c false otherwise + */ +static bool +is_cib_required(void) +{ + if (options.cmdline_config) { + // cmd_execute_agent using CLI parameters instead of CIB connection + return false; + } + + if (is_scheduler_required()) { + return true; + } + + // Commands that requires CIB connection but not scheduler data + switch (options.rsc_cmd) { + case cmd_delete: + case cmd_wait: + return true; + default: + return false; + } +} + /*! * \internal * \brief Check whether a controller IPC connection is required @@ -1326,52 +1198,511 @@ is_controller_required(void) /*! * \internal - * \brief Check whether a scheduler IPC connection is required + * \brief Check whether the chosen command accepts clone instances * - * \return \c true if a scheduler connection is required, or \c false otherwise + * \return \c true if \p options.rsc_cmd accepts or ignores clone instances, or + * \c false otherwise */ static bool -is_scheduler_required(void) +accept_clone_instance(void) +{ + switch (options.rsc_cmd) { + case cmd_ban: + case cmd_clear: + case cmd_delete: + case cmd_move: + case cmd_restart: + return false; + default: + return true; + } +} + +static int +handle_ban(pcmk_resource_t *rsc, const pcmk_node_t *node) +{ + int rc = pcmk_rc_ok; + + if (node == NULL) { + rc = ban_or_move(out, rsc, options.move_lifetime); + } else { + rc = cli_resource_ban(out, options.rsc_id, node->priv->name, + options.move_lifetime, cib_conn, + options.promoted_role_only, PCMK_ROLE_PROMOTED); + } + + if (rc == EINVAL) { + exit_code = CRM_EX_USAGE; + return pcmk_rc_ok; + } + return rc; +} + +static int +handle_cleanup(pcmk_resource_t *rsc, pcmk_node_t *node) +{ + if (rsc == NULL) { + int rc = cli_cleanup_all(controld_api, node, options.operation, + options.interval_spec, scheduler); + + if (rc == pcmk_rc_ok) { + start_mainloop(controld_api); + } + + } else { + cleanup(out, rsc, node); + } + + return pcmk_rc_ok; +} + +static int +handle_clear(const pcmk_node_t *node) +{ + const char *node_name = (node != NULL)? node->priv->name : NULL; + GList *before = NULL; + GList *after = NULL; + GList *remaining = NULL; + int rc = pcmk_rc_ok; + + if (!out->is_quiet(out)) { + before = build_constraint_list(scheduler->input); + } + + if (options.clear_expired) { + rc = cli_resource_clear_all_expired(scheduler->input, cib_conn, + options.rsc_id, node_name, + options.promoted_role_only); + + } else if (node != NULL) { + rc = cli_resource_clear(options.rsc_id, node_name, NULL, cib_conn, true, + options.force); + + } else { + rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes, + cib_conn, true, options.force); + } + + if (!out->is_quiet(out)) { + xmlNode *cib_xml = NULL; + + rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml, cib_sync_call); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, + _("Could not get modified CIB: %s"), pcmk_rc_str(rc)); + g_list_free(before); + pcmk__xml_free(cib_xml); + return rc; + } + + scheduler->input = cib_xml; + cluster_status(scheduler); + + after = build_constraint_list(scheduler->input); + remaining = pcmk__subtract_lists(before, after, (GCompareFunc) strcmp); + + for (const GList *iter = remaining; iter != NULL; iter = iter->next) { + const char *constraint = iter->data; + + out->info(out, "Removing constraint: %s", constraint); + } + + g_list_free(before); + g_list_free(after); + g_list_free(remaining); + } + + return rc; +} + +static int +handle_colocations(pcmk_resource_t *rsc) +{ + return out->message(out, "locations-and-colocations", rsc, + options.recursive, options.force); +} + +static int +handle_cts(void) +{ + // coverity[var_deref_op] False positive + g_list_foreach(scheduler->priv->resources, (GFunc) cli_resource_print_cts, + out); + cli_resource_print_cts_constraints(scheduler); + return pcmk_rc_ok; +} + +static int +handle_delete(void) +{ + /* rsc_id was already checked for NULL much earlier when validating command + * line arguments + */ + int rc = pcmk_rc_ok; + + if (options.rsc_type == NULL) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, + _("You need to specify a resource type with -t")); + + } else { + rc = pcmk__resource_delete(cib_conn, cib_sync_call, options.rsc_id, + options.rsc_type); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, + _("Could not delete resource %s: %s"), + options.rsc_id, pcmk_rc_str(rc)); + } + } + return rc; +} + +static int +handle_delete_param(pcmk_resource_t *rsc, xmlNode *cib_xml_orig) +{ + /* coverity[var_deref_model] False positive */ + return cli_resource_delete_attribute(rsc, options.rsc_id, options.prop_set, + options.attr_set_type, options.prop_id, + options.prop_name, cib_conn, + cib_xml_orig, options.force); +} + +static int +handle_digests(pcmk_resource_t *rsc, const pcmk_node_t *node) +{ + return pcmk__resource_digests(out, rsc, node, options.override_params); +} + +static int +handle_execute_agent(pcmk_resource_t *rsc) +{ + if (options.cmdline_config) { + exit_code = cli_resource_execute_from_params(out, NULL, options.v_class, + options.v_provider, + options.v_agent, + options.operation, + options.cmdline_params, + options.override_params, + options.timeout_ms, + args->verbosity, + options.force, + options.check_level); + } else { + exit_code = cli_resource_execute(rsc, options.rsc_id, options.operation, + options.override_params, + options.timeout_ms, cib_conn, + scheduler, args->verbosity, + options.force, options.check_level); + } + return pcmk_rc_ok; +} + +static int +handle_fail(const pcmk_node_t *node) +{ + int rc = cli_resource_fail(controld_api, node, options.rsc_id, + scheduler); + + if (rc == pcmk_rc_ok) { + start_mainloop(controld_api); + } + return rc; +} + +static int +handle_get_param(pcmk_resource_t *rsc) +{ + unsigned int count = 0; + GHashTable *params = NULL; + pcmk_node_t *current = rsc->priv->fns->active_node(rsc, &count, NULL); + bool free_params = true; + const char *value = NULL; + int rc = pcmk_rc_ok; + + if (count > 1) { + out->err(out, + "%s is active on more than one node, returning the default " + "value for %s", + rsc->id, pcmk__s(options.prop_name, "unspecified property")); + current = NULL; + } + + crm_debug("Looking up %s in %s", options.prop_name, rsc->id); + + if (pcmk__str_eq(options.attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES, + pcmk__str_none)) { + params = pe_rsc_params(rsc, current, scheduler); + free_params = false; + + value = g_hash_table_lookup(params, options.prop_name); + + } else if (pcmk__str_eq(options.attr_set_type, PCMK_XE_META_ATTRIBUTES, + pcmk__str_none)) { + params = pcmk__strkey_table(free, free); + get_meta_attributes(params, rsc, NULL, scheduler); + + value = g_hash_table_lookup(params, options.prop_name); + + } else if (pcmk__str_eq(options.attr_set_type, ATTR_SET_ELEMENT, + pcmk__str_none)) { + value = crm_element_value(rsc->priv->xml, options.prop_name); + free_params = false; + + } else { + const pcmk_rule_input_t rule_input = { + .now = scheduler->priv->now, + }; + + params = pcmk__strkey_table(free, free); + pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_UTILIZATION, + &rule_input, params, NULL, scheduler); + + value = g_hash_table_lookup(params, options.prop_name); + } + + rc = out->message(out, "attribute-list", rsc, options.prop_name, value); + if (free_params) { + g_hash_table_destroy(params); + } + + return rc; +} + +static int +handle_list_active_ops(const pcmk_node_t *node) +{ + const char *node_name = (node != NULL)? node->priv->name : NULL; + + return cli_resource_print_operations(options.rsc_id, node_name, true, + scheduler); +} + +static int +handle_list_agents(void) +{ + return pcmk__list_agents(out, options.agent_spec); +} + +static int +handle_list_all_ops(const pcmk_node_t *node) +{ + const char *node_name = (node != NULL)? node->priv->name : NULL; + + return cli_resource_print_operations(options.rsc_id, node_name, false, + scheduler); +} + +static int +handle_list_alternatives(void) +{ + return pcmk__list_alternatives(out, options.agent_spec); +} + +static int +handle_list_instances(void) +{ + // coverity[var_deref_op] False positive + if (out->message(out, "resource-names-list", + scheduler->priv->resources) != pcmk_rc_ok) { + return ENXIO; + } + return pcmk_rc_ok; +} + +static int +handle_list_options(void) +{ + switch (options.opt_list) { + case pcmk__opt_fencing: + return pcmk__list_fencing_params(out, options.all); + case pcmk__opt_primitive: + return pcmk__list_primitive_meta(out, options.all); + default: + exit_code = CRM_EX_SOFTWARE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "BUG: Invalid option list type"); + return pcmk_rc_ok; + } +} + +static int +handle_list_providers(void) +{ + return pcmk__list_providers(out, options.agent_spec); +} + +static int +handle_list_resources(void) +{ + GList *all = g_list_prepend(NULL, (gpointer) "*"); + int rc = out->message(out, "resource-list", scheduler, + pcmk_show_inactive_rscs + |pcmk_show_rsc_only + |pcmk_show_pending, + true, all, all, false); + + g_list_free(all); + + if (rc == pcmk_rc_no_output) { + rc = ENXIO; + } + return rc; +} + +static int +handle_list_standards(void) +{ + return pcmk__list_standards(out); +} + +static int +handle_locate(pcmk_resource_t *rsc) +{ + GList *nodes = cli_resource_search(rsc, options.rsc_id, scheduler); + int rc = out->message(out, "resource-search-list", nodes, options.rsc_id); + + g_list_free_full(nodes, free); + return rc; +} + +static int +handle_metadata(void) +{ + int rc = pcmk_rc_ok; + char *standard = NULL; + char *provider = NULL; + char *type = NULL; + char *metadata = NULL; + lrmd_t *lrmd_conn = NULL; + + rc = lrmd__new(&lrmd_conn, NULL, NULL, 0); + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, + _("Could not create executor connection")); + lrmd_api_delete(lrmd_conn); + return rc; + } + + rc = crm_parse_agent_spec(options.agent_spec, &standard, &provider, &type); + rc = pcmk_legacy2rc(rc); + + if (rc == pcmk_rc_ok) { + rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, + provider, type, + &metadata, 0); + rc = pcmk_legacy2rc(rc); + + if (metadata != NULL) { + out->output_xml(out, PCMK_XE_METADATA, metadata); + free(metadata); + } else { + /* We were given a validly formatted spec, but it doesn't necessarily + * match up with anything that exists. Use ENXIO as the return code + * here because that maps to an exit code of CRM_EX_NOSUCH, which + * probably is the most common reason to get here. + */ + rc = ENXIO; + g_set_error(&error, PCMK__RC_ERROR, rc, + _("Metadata query for %s failed: %s"), + options.agent_spec, pcmk_rc_str(rc)); + } + } else { + rc = ENXIO; + g_set_error(&error, PCMK__RC_ERROR, rc, + _("'%s' is not a valid agent specification"), + options.agent_spec); + } + + lrmd_api_delete(lrmd_conn); + return rc; +} + +static int +handle_move(pcmk_resource_t *rsc, const pcmk_node_t *node) +{ + int rc = pcmk_rc_ok; + + if (node == NULL) { + rc = ban_or_move(out, rsc, options.move_lifetime); + } else { + rc = cli_resource_move(rsc, options.rsc_id, node, options.move_lifetime, + cib_conn, scheduler, options.promoted_role_only, + options.force); + } + + if (rc == EINVAL) { + exit_code = CRM_EX_USAGE; + return pcmk_rc_ok; + } + return rc; +} + +static int +handle_query_xml(pcmk_resource_t *rsc) +{ + return cli_resource_print(rsc, scheduler, true); +} + +static int +handle_query_xml_raw(pcmk_resource_t *rsc) +{ + return cli_resource_print(rsc, scheduler, false); +} + +static int +handle_refresh(pcmk_resource_t *rsc, pcmk_node_t *node) +{ + if (rsc == NULL) { + return refresh(out, node); + } + refresh_resource(out, rsc, node); + return pcmk_rc_ok; +} + +static int +handle_restart(pcmk_resource_t *rsc, const pcmk_node_t *node) +{ + /* We don't pass scheduler because rsc needs to stay valid for the entire + * lifetime of cli_resource_restart(), but it will reset and update the + * scheduler data multiple times, so it needs to use its own copy. + */ + return cli_resource_restart(out, rsc, node, options.move_lifetime, + options.timeout_ms, cib_conn, + options.promoted_role_only, options.force); +} + +static int +handle_set_param(pcmk_resource_t *rsc, xmlNode *cib_xml_orig) { - if (options.cmdline_config) { - return false; + if (pcmk__str_empty(options.prop_value)) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + _("You need to supply a value with the -v option")); + return pcmk_rc_ok; } - switch (options.rsc_cmd) { - case cmd_delete: - case cmd_list_agents: - case cmd_list_alternatives: - case cmd_list_options: - case cmd_list_providers: - case cmd_list_standards: - case cmd_metadata: - case cmd_wait: - return false; - default: - return true; - } + // coverity[var_deref_model] False positive + return cli_resource_update_attribute(rsc, options.rsc_id, options.prop_set, + options.attr_set_type, options.prop_id, + options.prop_name, options.prop_value, + options.recursive, cib_conn, + cib_xml_orig, options.force); } -/*! - * \internal - * \brief Check whether the chosen command accepts clone instances - * - * \return \c true if \p options.rsc_cmd accepts or ignores clone instances, or - * \c false otherwise - */ -static bool -accept_clone_instance(void) +static int +handle_wait(void) { - switch (options.rsc_cmd) { - case cmd_ban: - case cmd_clear: - case cmd_delete: - case cmd_move: - case cmd_restart: - return false; - default: - return true; - } + return wait_till_stable(out, options.timeout_ms, cib_conn); +} + +static int +handle_why(pcmk_resource_t *rsc, pcmk_node_t *node) +{ + return out->message(out, "resource-reasons-list", + scheduler->priv->resources, rsc, node); } static GOptionContext * @@ -1496,64 +1827,38 @@ main(int argc, char **argv) goto done; } - if ((options.remainder != NULL) && (options.override_params != NULL)) { + if (options.remainder != NULL) { // Commands that use positional arguments will create override_params - for (gchar **s = options.remainder; *s; s++) { - char *name = pcmk__assert_alloc(1, strlen(*s)); - char *value = pcmk__assert_alloc(1, strlen(*s)); - int rc = sscanf(*s, "%[^=]=%s", name, value); + if (options.override_params == NULL) { + GString *msg = g_string_sized_new(128); + guint len = g_strv_length(options.remainder); - if (rc == 2) { - g_hash_table_replace(options.override_params, name, value); + g_string_append(msg, "non-option ARGV-elements:"); - } else { - exit_code = CRM_EX_USAGE; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - _("Error parsing '%s' as a name=value pair"), - argv[optind]); - free(value); - free(name); - goto done; + for (int i = 0; i < len; i++) { + g_string_append_printf(msg, "\n[%d of %u] %s", + i + 1, len, options.remainder[i]); } + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg->str); + g_string_free(msg, TRUE); + goto done; } - } else if (options.remainder != NULL) { - gchar **strv = NULL; - gchar *msg = NULL; - int i = 1; - int len = 0; - - for (gchar **s = options.remainder; *s; s++) { - len++; - } - - pcmk__assert(len > 0); - - /* Add 1 for the strv[0] string below, and add another 1 for the NULL - * at the end of the array so g_strjoinv knows when to stop. - */ - strv = pcmk__assert_alloc(len+2, sizeof(char *)); - strv[0] = strdup("non-option ARGV-elements:\n"); - - for (gchar **s = options.remainder; *s; s++) { - strv[i] = crm_strdup_printf("[%d of %d] %s\n", i, len, *s); - i++; - } - - strv[i] = NULL; + for (gchar **arg = options.remainder; *arg != NULL; arg++) { + gchar *name = NULL; + gchar *value = NULL; + int rc = pcmk__scan_nvpair(*arg, &name, &value); - exit_code = CRM_EX_USAGE; - msg = g_strjoinv("", strv); - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg); - g_free(msg); + if (rc != pcmk_rc_ok) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + _("Error parsing '%s' as a name=value pair"), *arg); + goto done; + } - /* Don't try to free the last element, which is just NULL. */ - for(i = 0; i < len+1; i++) { - free(strv[i]); + g_hash_table_insert(options.override_params, name, value); } - free(strv); - - goto done; } if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { @@ -1603,7 +1908,6 @@ main(int argc, char **argv) g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("--option must be used with --validate and without -r")); g_hash_table_destroy(options.cmdline_params); - options.cmdline_params = NULL; goto done; } @@ -1643,13 +1947,27 @@ main(int argc, char **argv) } } - // Populate scheduler data from XML file if specified or CIB query otherwise + // Populate scheduler data from CIB query if (is_scheduler_required()) { rc = initialize_scheduler_data(&cib_xml_orig); if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); goto done; } + + /* If user supplied a node name, check whether it exists. + * Commands that don't require scheduler data ignore the node argument. + */ + if (options.host_uname != NULL) { + node = pcmk_find_node(scheduler, options.host_uname); + + if (node == NULL) { + exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + _("Node '%s' not found"), options.host_uname); + goto done; + } + } } find_flags = get_find_flags(); @@ -1679,18 +1997,6 @@ main(int argc, char **argv) } } - // If user supplied a node name, check whether it exists - if ((options.host_uname != NULL) && (scheduler != NULL)) { - node = pcmk_find_node(scheduler, options.host_uname); - - if (node == NULL) { - exit_code = CRM_EX_NOSUCH; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - _("Node '%s' not found"), options.host_uname); - goto done; - } - } - // Establish a connection to the controller if needed if (is_controller_required()) { rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); @@ -1716,324 +2022,101 @@ main(int argc, char **argv) * Handle requested command */ + /* Some of these set exit_code explicitly and return pcmk_rc_ok to skip + * setting exit_code based on rc after the switch. + */ switch (options.rsc_cmd) { - case cmd_list_resources: { - GList *all = NULL; - uint32_t show_opts = pcmk_show_inactive_rscs | pcmk_show_rsc_only | pcmk_show_pending; - - all = g_list_prepend(all, (gpointer) "*"); - rc = out->message(out, "resource-list", scheduler, - show_opts, true, all, all, false); - g_list_free(all); - - if (rc == pcmk_rc_no_output) { - rc = ENXIO; - } - break; - } - - case cmd_list_instances: - // coverity[var_deref_op] False positive - rc = out->message(out, "resource-names-list", - scheduler->priv->resources); - - if (rc != pcmk_rc_ok) { - rc = ENXIO; - } - - break; - - case cmd_list_options: - list_options(); - break; - - case cmd_list_alternatives: - rc = pcmk__list_alternatives(out, options.agent_spec); + case cmd_ban: + rc = handle_ban(rsc, node); break; - - case cmd_list_agents: - rc = pcmk__list_agents(out, options.agent_spec); + case cmd_cleanup: + rc = handle_cleanup(rsc, node); break; - - case cmd_list_standards: - rc = pcmk__list_standards(out); + case cmd_clear: + rc = handle_clear(node); break; - - case cmd_list_providers: - rc = pcmk__list_providers(out, options.agent_spec); + case cmd_colocations: + rc = handle_colocations(rsc); break; - - case cmd_metadata: - rc = show_metadata(out, options.agent_spec); + case cmd_cts: + rc = handle_cts(); break; - - case cmd_restart: - /* We don't pass scheduler because rsc needs to stay valid for the - * entire lifetime of cli_resource_restart(), but it will reset and - * update the scheduler data multiple times, so it needs to use its - * own copy. - */ - rc = cli_resource_restart(out, rsc, node, options.move_lifetime, - options.timeout_ms, cib_conn, - options.promoted_role_only, - options.force); + case cmd_delete: + rc = handle_delete(); break; - - case cmd_wait: - rc = wait_till_stable(out, options.timeout_ms, cib_conn); + case cmd_delete_param: + rc = handle_delete_param(rsc, cib_xml_orig); break; - - case cmd_execute_agent: - if (options.cmdline_config) { - exit_code = cli_resource_execute_from_params(out, NULL, - options.v_class, options.v_provider, options.v_agent, - options.operation, options.cmdline_params, - options.override_params, options.timeout_ms, - args->verbosity, options.force, options.check_level); - } else { - exit_code = cli_resource_execute(rsc, options.rsc_id, - options.operation, options.override_params, - options.timeout_ms, cib_conn, scheduler, - args->verbosity, options.force, options.check_level); - } - goto done; - case cmd_digests: - node = pcmk_find_node(scheduler, options.host_uname); - if (node == NULL) { - rc = pcmk_rc_node_unknown; - } else { - rc = pcmk__resource_digests(out, rsc, node, - options.override_params); - } - break; - - case cmd_colocations: - rc = out->message(out, "locations-and-colocations", rsc, - options.recursive, (bool) options.force); + rc = handle_digests(rsc, node); break; - - case cmd_cts: - rc = pcmk_rc_ok; - // coverity[var_deref_op] False positive - g_list_foreach(scheduler->priv->resources, - (GFunc) cli_resource_print_cts, out); - cli_resource_print_cts_constraints(scheduler); + case cmd_execute_agent: + rc = handle_execute_agent(rsc); break; - case cmd_fail: - rc = cli_resource_fail(controld_api, options.host_uname, - options.rsc_id, scheduler); - if (rc == pcmk_rc_ok) { - start_mainloop(controld_api); - } + rc = handle_fail(node); + break; + case cmd_get_param: + // coverity[var_deref_model] False positive + rc = handle_get_param(rsc); break; - case cmd_list_active_ops: - rc = cli_resource_print_operations(options.rsc_id, - options.host_uname, TRUE, - scheduler); + rc = handle_list_active_ops(node); + break; + case cmd_list_agents: + rc = handle_list_agents(); break; - case cmd_list_all_ops: - rc = cli_resource_print_operations(options.rsc_id, - options.host_uname, FALSE, - scheduler); + rc = handle_list_all_ops(node); break; - - case cmd_locate: { - GList *nodes = cli_resource_search(rsc, options.rsc_id, scheduler); - rc = out->message(out, "resource-search-list", nodes, options.rsc_id); - g_list_free_full(nodes, free); + case cmd_list_alternatives: + rc = handle_list_alternatives(); break; - } - - case cmd_query_xml: - rc = cli_resource_print(rsc, scheduler, true); + case cmd_list_instances: + rc = handle_list_instances(); break; - - case cmd_query_xml_raw: - rc = cli_resource_print(rsc, scheduler, false); + case cmd_list_providers: + rc = handle_list_providers(); break; - - case cmd_why: - if ((options.host_uname != NULL) && (node == NULL)) { - rc = pcmk_rc_node_unknown; - } else { - rc = out->message(out, "resource-reasons-list", - scheduler->priv->resources, rsc, node); - } + case cmd_list_options: + rc = handle_list_options(); break; - - case cmd_clear: - rc = clear_constraints(out); + case cmd_list_resources: + rc = handle_list_resources(); break; - - case cmd_move: - if (options.host_uname == NULL) { - rc = ban_or_move(out, rsc, options.move_lifetime); - } else { - rc = cli_resource_move(rsc, options.rsc_id, options.host_uname, - options.move_lifetime, cib_conn, - scheduler, options.promoted_role_only, - options.force); - } - - if (rc == EINVAL) { - exit_code = CRM_EX_USAGE; - goto done; - } - + case cmd_list_standards: + rc = handle_list_standards(); break; - - case cmd_ban: - if (options.host_uname == NULL) { - rc = ban_or_move(out, rsc, options.move_lifetime); - } else if (node == NULL) { - rc = pcmk_rc_node_unknown; - } else { - rc = cli_resource_ban(out, options.rsc_id, node->priv->name, - options.move_lifetime, cib_conn, - options.promoted_role_only, - PCMK_ROLE_PROMOTED); - } - - if (rc == EINVAL) { - exit_code = CRM_EX_USAGE; - goto done; - } - + case cmd_locate: + rc = handle_locate(rsc); break; - - case cmd_get_param: { - unsigned int count = 0; - GHashTable *params = NULL; - // coverity[var_deref_op] False positive - pcmk_node_t *current = rsc->priv->fns->active_node(rsc, &count, - NULL); - bool free_params = true; - const char* value = NULL; - - if (count > 1) { - out->err(out, "%s is active on more than one node," - " returning the default value for %s", rsc->id, - pcmk__s(options.prop_name, "unspecified property")); - current = NULL; - } - - crm_debug("Looking up %s in %s", options.prop_name, rsc->id); - - if (pcmk__str_eq(options.attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES, - pcmk__str_none)) { - params = pe_rsc_params(rsc, current, scheduler); - free_params = false; - - value = g_hash_table_lookup(params, options.prop_name); - - } else if (pcmk__str_eq(options.attr_set_type, - PCMK_XE_META_ATTRIBUTES, pcmk__str_none)) { - params = pcmk__strkey_table(free, free); - get_meta_attributes(params, rsc, NULL, scheduler); - - value = g_hash_table_lookup(params, options.prop_name); - - } else if (pcmk__str_eq(options.attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) { - - value = crm_element_value(rsc->priv->xml, options.prop_name); - free_params = false; - - } else { - const pcmk_rule_input_t rule_input = { - .now = scheduler->priv->now, - }; - - params = pcmk__strkey_table(free, free); - pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_UTILIZATION, - &rule_input, params, NULL, - scheduler); - - value = g_hash_table_lookup(params, options.prop_name); - } - - rc = out->message(out, "attribute-list", rsc, options.prop_name, value); - if (free_params) { - g_hash_table_destroy(params); - } - + case cmd_metadata: + rc = handle_metadata(); break; - } - - case cmd_set_param: - if (pcmk__str_empty(options.prop_value)) { - exit_code = CRM_EX_USAGE; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - _("You need to supply a value with the -v option")); - goto done; - } - - /* coverity[var_deref_model] False positive */ - rc = cli_resource_update_attribute(rsc, options.rsc_id, - options.prop_set, - options.attr_set_type, - options.prop_id, - options.prop_name, - options.prop_value, - options.recursive, cib_conn, - cib_xml_orig, options.force); + case cmd_move: + rc = handle_move(rsc, node); break; - - case cmd_delete_param: - /* coverity[var_deref_model] False positive */ - rc = cli_resource_delete_attribute(rsc, options.rsc_id, - options.prop_set, - options.attr_set_type, - options.prop_id, - options.prop_name, cib_conn, - cib_xml_orig, options.force); + case cmd_query_xml: + rc = handle_query_xml(rsc); break; - - case cmd_cleanup: - if (rsc == NULL) { - rc = cli_cleanup_all(controld_api, options.host_uname, - options.operation, options.interval_spec, - scheduler); - if (rc == pcmk_rc_ok) { - start_mainloop(controld_api); - } - } else { - cleanup(out, rsc, node); - } + case cmd_query_xml_raw: + rc = handle_query_xml_raw(rsc); break; - case cmd_refresh: - if (rsc == NULL) { - rc = refresh(out); - } else { - refresh_resource(out, rsc, node); - } + rc = handle_refresh(rsc, node); break; - - case cmd_delete: - /* rsc_id was already checked for NULL much earlier when validating - * command line arguments. - */ - if (options.rsc_type == NULL) { - exit_code = CRM_EX_USAGE; - g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, - _("You need to specify a resource type with -t")); - } else { - rc = pcmk__resource_delete(cib_conn, cib_sync_call, - options.rsc_id, options.rsc_type); - - if (rc != pcmk_rc_ok) { - g_set_error(&error, PCMK__RC_ERROR, rc, - _("Could not delete resource %s: %s"), - options.rsc_id, pcmk_rc_str(rc)); - } - } - + case cmd_restart: + rc = handle_restart(rsc, node); + break; + case cmd_set_param: + rc = handle_set_param(rsc, cib_xml_orig); + break; + case cmd_wait: + rc = handle_wait(); + break; + case cmd_why: + rc = handle_why(rsc, node); break; - default: exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, @@ -2071,8 +2154,6 @@ main(int argc, char **argv) } } - pcmk__xml_free(cib_xml_orig); - g_free(options.host_uname); g_free(options.interval_spec); g_free(options.move_lifetime); @@ -2087,11 +2168,10 @@ main(int argc, char **argv) free(options.v_agent); free(options.v_class); free(options.v_provider); - g_strfreev(options.remainder); - if (options.override_params != NULL) { g_hash_table_destroy(options.override_params); } + g_strfreev(options.remainder); /* options.cmdline_params does not need to be destroyed here. See the * comments in cli_resource_execute_from_params. @@ -2100,5 +2180,21 @@ main(int argc, char **argv) g_strfreev(processed_args); g_option_context_free(context); - return bye(exit_code); + pcmk__xml_free(cib_xml_orig); + cib__clean_up_connection(&cib_conn); + pcmk_free_ipc_api(controld_api); + pcmk_free_scheduler(scheduler); + if (mainloop != NULL) { + g_main_loop_unref(mainloop); + } + + pcmk__output_and_clear_error(&error, out); + + if (out != NULL) { + out->finish(out, exit_code, true, NULL); + pcmk__output_free(out); + } + + pcmk__unregister_formats(); + return crm_exit(exit_code); } diff --git a/tools/crm_resource.h b/tools/crm_resource.h index 8f1e67584d4..d8a27547e43 100644 --- a/tools/crm_resource.h +++ b/tools/crm_resource.h @@ -85,15 +85,15 @@ int cli_resource_print_operations(const char *rsc_id, const char *host_uname, /* runtime */ int cli_resource_check(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node); -int cli_resource_fail(pcmk_ipc_api_t *controld_api, const char *host_uname, +int cli_resource_fail(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const char *rsc_id, pcmk_scheduler_t *scheduler); GList *cli_resource_search(pcmk_resource_t *rsc, const char *requested_name, pcmk_scheduler_t *scheduler); -int cli_resource_delete(pcmk_ipc_api_t *controld_api, const char *host_uname, +int cli_resource_delete(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const pcmk_resource_t *rsc, const char *operation, const char *interval_spec, bool just_failures, - pcmk_scheduler_t *scheduler, gboolean force); -int cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name, + pcmk_scheduler_t *scheduler, bool force); +int cli_cleanup_all(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const char *operation, const char *interval_spec, pcmk_scheduler_t *scheduler); int cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, @@ -101,9 +101,9 @@ int cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, guint timeout_ms, cib_t *cib, gboolean promoted_role_only, gboolean force); int cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, - const char *host_name, const char *move_lifetime, + const pcmk_node_t *node, const char *move_lifetime, cib_t *cib, pcmk_scheduler_t *scheduler, - gboolean promoted_role_only, gboolean force); + bool promoted_role_only, bool force); crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, const char *rsc_class, const char *rsc_prov, const char *rsc_type, const char *rsc_action, diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c index 483b4e18323..a6d27af511e 100644 --- a/tools/crm_resource_print.c +++ b/tools/crm_resource_print.c @@ -362,6 +362,7 @@ override_xml(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } +// Does not modify overrides or its contents PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "GHashTable *", "crm_exit_t", "int", "const char *", "const char *", "const char *") @@ -422,6 +423,7 @@ resource_agent_action_default(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } +// Does not modify overrides or its contents PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "GHashTable *", "crm_exit_t", "int", "const char *", "const char *", "const char *") diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c index 7e6c6831dda..a1f638cc916 100644 --- a/tools/crm_resource_runtime.c +++ b/tools/crm_resource_runtime.c @@ -9,6 +9,7 @@ #include +#include // bool, true, false #include #include #include // bool, true, false @@ -708,30 +709,31 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, // \return Standard Pacemaker return code static int send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource, - const char *host_uname, const char *rsc_id, + const pcmk_node_t *node, const char *rsc_id, pcmk_scheduler_t *scheduler) { pcmk__output_t *out = NULL; - const char *router_node = host_uname; const char *rsc_api_id = NULL; const char *rsc_long_id = NULL; const char *rsc_class = NULL; const char *rsc_provider = NULL; const char *rsc_type = NULL; + const char *router_node = NULL; bool cib_only = false; pcmk_resource_t *rsc = NULL; - pcmk__assert(scheduler != NULL); + pcmk__assert((node != NULL) && (scheduler != NULL)); - rsc = pe_find_resource(scheduler->priv->resources, rsc_id); out = scheduler->priv->out; + rsc = pe_find_resource(scheduler->priv->resources, rsc_id); if (rsc == NULL) { out->err(out, "Resource %s not found", rsc_id); return ENXIO; - - } else if (!pcmk__is_primitive(rsc)) { - out->err(out, "We can only process primitive resources, not %s", rsc_id); + } + if (!pcmk__is_primitive(rsc)) { + out->err(out, "We can only process primitive resources, not %s", + rsc_id); return EINVAL; } @@ -743,31 +745,26 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource, return EINVAL; } - { - pcmk_node_t *node = pcmk_find_node(scheduler, host_uname); + router_node = node->priv->name; - if (node == NULL) { - out->err(out, "Node %s not found", host_uname); - return pcmk_rc_node_unknown; + if (!node->details->online) { + if (do_fail_resource) { + out->err(out, "Node %s is not online", pcmk__node_name(node)); + return ENOTCONN; } + cib_only = true; - if (!(node->details->online)) { - if (do_fail_resource) { - out->err(out, "Node %s is not online", host_uname); - return ENOTCONN; - } else { - cib_only = true; - } - } - if (!cib_only && pcmk__is_pacemaker_remote_node(node)) { - node = pcmk__current_node(node->priv->remote); - if (node == NULL) { - out->err(out, "No cluster connection to Pacemaker Remote node %s detected", - host_uname); - return ENOTCONN; - } - router_node = node->priv->name; + } else if (pcmk__is_pacemaker_remote_node(node)) { + const pcmk_node_t *conn_host = pcmk__current_node(node->priv->remote); + + if (conn_host == NULL) { + out->err(out, + "No cluster connection to Pacemaker Remote node %s " + "detected", + pcmk__node_name(node)); + return ENOTCONN; } + router_node = conn_host->priv->name; } if (rsc->priv->history_id != NULL) { @@ -776,15 +773,16 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource, } else { rsc_api_id = rsc->id; } + if (do_fail_resource) { - return pcmk_controld_api_fail(controld_api, host_uname, router_node, - rsc_api_id, rsc_long_id, + return pcmk_controld_api_fail(controld_api, node->priv->name, + router_node, rsc_api_id, rsc_long_id, rsc_class, rsc_provider, rsc_type); - } else { - return pcmk_controld_api_refresh(controld_api, host_uname, router_node, - rsc_api_id, rsc_long_id, rsc_class, - rsc_provider, rsc_type, cib_only); } + return pcmk_controld_api_refresh(controld_api, node->priv->name, + router_node, rsc_api_id, rsc_long_id, + rsc_class, rsc_provider, rsc_type, + cib_only); } /*! @@ -809,17 +807,19 @@ rsc_fail_name(const pcmk_resource_t *rsc) // \return Standard Pacemaker return code static int -clear_rsc_history(pcmk_ipc_api_t *controld_api, const char *host_uname, +clear_rsc_history(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const char *rsc_id, pcmk_scheduler_t *scheduler) { int rc = pcmk_rc_ok; + pcmk__assert(node != NULL); + /* Erase the resource's entire LRM history in the CIB, even if we're only * clearing a single operation's fail count. If we erased only entries for a * single operation, we might wind up with a wrong idea of the current * resource state, and we might not re-probe the resource. */ - rc = send_lrm_rsc_op(controld_api, false, host_uname, rsc_id, scheduler); + rc = send_lrm_rsc_op(controld_api, false, node, rsc_id, scheduler); if (rc != pcmk_rc_ok) { return rc; } @@ -836,8 +836,9 @@ clear_rsc_history(pcmk_ipc_api_t *controld_api, const char *host_uname, // \return Standard Pacemaker return code static int clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, - const char *node_name, const char *rsc_id, const char *operation, - const char *interval_spec, pcmk_scheduler_t *scheduler) + const pcmk_node_t *node, const char *rsc_id, + const char *operation, const char *interval_spec, + pcmk_scheduler_t *scheduler) { int rc = pcmk_rc_ok; const char *failed_value = NULL; @@ -846,6 +847,8 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, GHashTable *rscs = NULL; GHashTableIter iter; + pcmk__assert(node != NULL); + /* Create a hash table to use as a set of resources to clean. This lets us * clean each resource only once (per node) regardless of how many failed * operations it has. @@ -886,7 +889,7 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, // Host name should always have been provided by this point failed_value = crm_element_value(xml_op, PCMK_XA_UNAME); - if (!pcmk__str_eq(node_name, failed_value, pcmk__str_casei)) { + if (!pcmk__str_eq(node->priv->name, failed_value, pcmk__str_casei)) { continue; } @@ -911,8 +914,9 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, g_hash_table_iter_init(&iter, rscs); while (g_hash_table_iter_next(&iter, (gpointer *) &failed_id, NULL)) { - crm_debug("Erasing failures of %s on %s", failed_id, node_name); - rc = clear_rsc_history(controld_api, node_name, failed_id, scheduler); + crm_debug("Erasing failures of %s on %s", + failed_id, pcmk__node_name(node)); + rc = clear_rsc_history(controld_api, node, failed_id, scheduler); if (rc != pcmk_rc_ok) { return rc; } @@ -943,26 +947,25 @@ clear_rsc_fail_attrs(const pcmk_resource_t *rsc, const char *operation, // \return Standard Pacemaker return code int -cli_resource_delete(pcmk_ipc_api_t *controld_api, const char *host_uname, +cli_resource_delete(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const pcmk_resource_t *rsc, const char *operation, const char *interval_spec, bool just_failures, - pcmk_scheduler_t *scheduler, gboolean force) + pcmk_scheduler_t *scheduler, bool force) { pcmk__output_t *out = scheduler->priv->out; int rc = pcmk_rc_ok; - pcmk_node_t *node = NULL; if (rsc == NULL) { return ENXIO; + } - } else if (rsc->priv->children != NULL) { - - for (const GList *lpc = rsc->priv->children; - lpc != NULL; lpc = lpc->next) { + if (rsc->priv->children != NULL) { + for (const GList *iter = rsc->priv->children; iter != NULL; + iter = iter->next) { - const pcmk_resource_t *child = (const pcmk_resource_t *) lpc->data; + const pcmk_resource_t *child = iter->data; - rc = cli_resource_delete(controld_api, host_uname, child, operation, + rc = cli_resource_delete(controld_api, node, child, operation, interval_spec, just_failures, scheduler, force); if (rc != pcmk_rc_ok) { @@ -970,154 +973,152 @@ cli_resource_delete(pcmk_ipc_api_t *controld_api, const char *host_uname, } } return pcmk_rc_ok; + } - } else if (host_uname == NULL) { - GList *lpc = NULL; + if (node == NULL) { GList *nodes = g_hash_table_get_values(rsc->priv->probed_nodes); - if(nodes == NULL && force) { - nodes = pcmk__copy_node_list(scheduler->nodes, false); + if (nodes == NULL) { + if (force) { + nodes = g_list_copy(scheduler->nodes); - } else if ((nodes == NULL) - && pcmk_is_set(rsc->flags, pcmk__rsc_exclusive_probes)) { - GHashTableIter iter; - pcmk_node_t *node = NULL; + } else if (pcmk_is_set(rsc->flags, pcmk__rsc_exclusive_probes)) { + GHashTableIter iter; - g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes); - while (g_hash_table_iter_next(&iter, NULL, (void**)&node)) { - if (node->assign->score >= 0) { - nodes = g_list_prepend(nodes, node); + g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes); + while (g_hash_table_iter_next(&iter, NULL, + (gpointer *) &node)) { + if ((node != NULL) && (node->assign->score >= 0)) { + nodes = g_list_prepend(nodes, (gpointer *) node); + } } - } - } else if(nodes == NULL) { - nodes = g_hash_table_get_values(rsc->priv->allowed_nodes); + } else { + nodes = g_hash_table_get_values(rsc->priv->allowed_nodes); + } } - for (lpc = nodes; lpc != NULL; lpc = lpc->next) { - node = (pcmk_node_t *) lpc->data; + for (const GList *iter = nodes; iter != NULL; iter = iter->next) { + node = (const pcmk_node_t *) iter->data; - if (node->details->online) { - rc = cli_resource_delete(controld_api, node->priv->name, rsc, - operation, interval_spec, just_failures, - scheduler, force); + if (!node->details->online) { + continue; } + + rc = cli_resource_delete(controld_api, node, rsc, operation, + interval_spec, just_failures, scheduler, + force); if (rc != pcmk_rc_ok) { - g_list_free(nodes); - return rc; + break; } } g_list_free(nodes); - return pcmk_rc_ok; - } - - node = pcmk_find_node(scheduler, host_uname); - - if (node == NULL) { - out->err(out, "Unable to clean up %s because node %s not found", - rsc->id, host_uname); - return ENODEV; + return rc; } if (!pcmk_is_set(node->priv->flags, pcmk__node_probes_allowed)) { - out->err(out, "Unable to clean up %s because resource discovery disabled on %s", - rsc->id, host_uname); + out->err(out, + "Unable to clean up %s because resource discovery disabled on " + "%s", + rsc->id, pcmk__node_name(node)); return EOPNOTSUPP; } if (controld_api == NULL) { out->err(out, "Dry run: skipping clean-up of %s on %s due to CIB_file", - rsc->id, host_uname); + rsc->id, pcmk__node_name(node)); return pcmk_rc_ok; } rc = clear_rsc_fail_attrs(rsc, operation, interval_spec, node); if (rc != pcmk_rc_ok) { out->err(out, "Unable to clean up %s failures on %s: %s", - rsc->id, host_uname, pcmk_rc_str(rc)); + rsc->id, pcmk__node_name(node), pcmk_rc_str(rc)); return rc; } if (just_failures) { - rc = clear_rsc_failures(out, controld_api, host_uname, rsc->id, operation, + rc = clear_rsc_failures(out, controld_api, node, rsc->id, operation, interval_spec, scheduler); } else { - rc = clear_rsc_history(controld_api, host_uname, rsc->id, scheduler); + rc = clear_rsc_history(controld_api, node, rsc->id, scheduler); } + if (rc != pcmk_rc_ok) { - out->err(out, "Cleaned %s failures on %s, but unable to clean history: %s", - rsc->id, host_uname, pcmk_rc_str(rc)); + out->err(out, + "Cleaned %s failures on %s, but unable to clean history: %s", + rsc->id, pcmk__node_name(node), pcmk_rc_str(rc)); } else { - out->info(out, "Cleaned up %s on %s", rsc->id, host_uname); + out->info(out, "Cleaned up %s on %s", rsc->id, pcmk__node_name(node)); } return rc; } // \return Standard Pacemaker return code int -cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name, +cli_cleanup_all(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const char *operation, const char *interval_spec, pcmk_scheduler_t *scheduler) { pcmk__output_t *out = NULL; int rc = pcmk_rc_ok; int attr_options = pcmk__node_attr_none; - const char *display_name = node_name? node_name : "all nodes"; + const char *node_name = NULL; + const char *log_node_name = "all nodes"; pcmk__assert(scheduler != NULL); out = scheduler->priv->out; + if (node != NULL) { + node_name = node->priv->name; + log_node_name = pcmk__node_name(node); + } + if (controld_api == NULL) { out->info(out, "Dry run: skipping clean-up of %s due to CIB_file", - display_name); + log_node_name); return rc; } - if (node_name) { - pcmk_node_t *node = pcmk_find_node(scheduler, node_name); - - if (node == NULL) { - out->err(out, "Unknown node: %s", node_name); - return ENXIO; - } - if (pcmk__is_pacemaker_remote_node(node)) { - attr_options |= pcmk__node_attr_remote; - } + if (pcmk__is_pacemaker_remote_node(node)) { + pcmk__set_node_attr_flags(attr_options, pcmk__node_attr_remote); } rc = pcmk__attrd_api_clear_failures(NULL, node_name, NULL, operation, interval_spec, NULL, attr_options); if (rc != pcmk_rc_ok) { out->err(out, "Unable to clean up all failures on %s: %s", - display_name, pcmk_rc_str(rc)); + log_node_name, pcmk_rc_str(rc)); return rc; } - if (node_name) { - rc = clear_rsc_failures(out, controld_api, node_name, NULL, - operation, interval_spec, scheduler); - if (rc != pcmk_rc_ok) { - out->err(out, "Cleaned all resource failures on %s, but unable to clean history: %s", - node_name, pcmk_rc_str(rc)); - return rc; - } + if (node != NULL) { + rc = clear_rsc_failures(out, controld_api, node, NULL, operation, + interval_spec, scheduler); + } else { - for (GList *iter = scheduler->nodes; iter; iter = iter->next) { - pcmk_node_t *node = (pcmk_node_t *) iter->data; + for (const GList *iter = scheduler->nodes; iter; iter = iter->next) { + const pcmk_node_t *sched_node = iter->data; - rc = clear_rsc_failures(out, controld_api, node->priv->name, - NULL, operation, interval_spec, scheduler); + rc = clear_rsc_failures(out, controld_api, sched_node, NULL, + operation, interval_spec, scheduler); if (rc != pcmk_rc_ok) { - out->err(out, "Cleaned all resource failures on all nodes, but unable to clean history: %s", - pcmk_rc_str(rc)); - return rc; + break; } } } - out->info(out, "Cleaned up all resources on %s", display_name); + if (rc == pcmk_rc_ok) { + out->info(out, "Cleaned up all resources on %s", log_node_name); + } else { + // @TODO But didn't clear_rsc_failures() fail? + out->err(out, + "Cleaned all resource failures on %s, but unable to clean " + "history: %s", + log_node_name, pcmk_rc_str(rc)); + } return rc; } @@ -1242,11 +1243,13 @@ cli_resource_check(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node) // \return Standard Pacemaker return code int -cli_resource_fail(pcmk_ipc_api_t *controld_api, const char *host_uname, +cli_resource_fail(pcmk_ipc_api_t *controld_api, const pcmk_node_t *node, const char *rsc_id, pcmk_scheduler_t *scheduler) { - crm_notice("Failing %s on %s", rsc_id, host_uname); - return send_lrm_rsc_op(controld_api, true, host_uname, rsc_id, scheduler); + pcmk__assert(node != NULL); + + crm_notice("Failing %s on %s", rsc_id, pcmk__node_name(node)); + return send_lrm_rsc_op(controld_api, true, node, rsc_id, scheduler); } static GHashTable * @@ -2235,6 +2238,9 @@ apply_overrides(GHashTable *params, GHashTable *overrides) } } +/* Takes ownership of params. + * Does not modify override_hash or its contents. + */ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, const char *rsc_class, const char *rsc_prov, @@ -2256,6 +2262,7 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, set_agent_environment(params, timeout_ms, check_level, resource_verbose); apply_overrides(params, override_hash); + // services__create_resource_action() takes ownership of params on success op = services__create_resource_action(rsc_name? rsc_name : "test", rsc_class, rsc_prov, rsc_type, action, 0, QB_MIN(timeout_ms, INT_MAX), @@ -2327,6 +2334,7 @@ get_action_timeout(pcmk_resource_t *rsc, const char *action) return (guint) QB_MIN(timeout_ms, UINT_MAX); } +// Does not modify override_hash or its contents crm_exit_t cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, const char *rsc_action, GHashTable *override_hash, @@ -2396,24 +2404,20 @@ cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, // \return Standard Pacemaker return code int cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, - const char *host_name, const char *move_lifetime, cib_t *cib, - pcmk_scheduler_t *scheduler, gboolean promoted_role_only, - gboolean force) + const pcmk_node_t *dest, const char *move_lifetime, + cib_t *cib, pcmk_scheduler_t *scheduler, + bool promoted_role_only, bool force) { pcmk__output_t *out = NULL; int rc = pcmk_rc_ok; unsigned int count = 0; pcmk_node_t *current = NULL; - pcmk_node_t *dest = NULL; bool cur_is_dest = false; + const char *active_s = promoted_role_only? "promoted" : "active"; - pcmk__assert(scheduler != NULL); + pcmk__assert((dest != NULL) && (scheduler != NULL)); out = scheduler->priv->out; - dest = pcmk_find_node(scheduler, host_name); - if (dest == NULL) { - return pcmk_rc_node_unknown; - } if (promoted_role_only && !pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) { @@ -2421,14 +2425,32 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, const pcmk_resource_t *p = pe__const_top_resource(rsc, false); if (pcmk_is_set(p->flags, pcmk__rsc_promotable)) { - out->info(out, "Using parent '%s' for move instead of '%s'.", rsc->id, rsc_id); + /* @TODO This is dead code. If rsc is part of a promotable clone, + * then it has the pcmk__rsc_promotable flag set. + * + * This was added by 36e4b490. Prior to that commit, we were + * checking whether rsc itself is the promotable clone, and if not, + * trying to get a promotable clone ancestor. + * + * As of that commit, we check whether rsc has the promotable flag + * set. But if it has a promotable clone ancestor, that flag is set. + * + * Question: Should we drop this block and use rsc for the move, or + * should we check whether rsc is a clone instead of only checking + * whether the promotable flag is set (as we did prior to 36e4b490)? + * The latter seems appropriate, especially considering the block + * below with promoted_count and promoted_node; but we need to trace + * and test. + */ + out->info(out, "Using parent '%s' for move instead of '%s'", + rsc->id, rsc_id); rsc_id = p->id; rsc = p; } else { out->info(out, "Ignoring --promoted option: %s is not promotable", rsc_id); - promoted_role_only = FALSE; + promoted_role_only = false; } } @@ -2438,10 +2460,10 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, unsigned int promoted_count = 0; pcmk_node_t *promoted_node = NULL; - for (const GList *iter = rsc->priv->children; - iter != NULL; iter = iter->next) { + for (const GList *iter = rsc->priv->children; iter != NULL; + iter = iter->next) { - const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data; + const pcmk_resource_t *child = iter->data; enum rsc_role_e child_role = child->priv->fns->state(child, true); if (child_role == pcmk_role_promoted) { @@ -2454,26 +2476,24 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, count = promoted_count; current = promoted_node; } - } if (count > 1) { - if (pcmk__is_clone(rsc)) { - current = NULL; - } else { + if (!pcmk__is_clone(rsc)) { return pcmk_rc_multiple; } + current = NULL; } if (pcmk__same_node(current, dest)) { - cur_is_dest = true; - if (force) { - crm_info("%s is already %s on %s, reinforcing placement with location constraint.", - rsc_id, promoted_role_only?"promoted":"active", - pcmk__node_name(dest)); - } else { + if (!force) { return pcmk_rc_already; } + + cur_is_dest = true; + crm_info("%s is already %s on %s, reinforcing placement with location " + "constraint", + rsc_id, active_s, pcmk__node_name(dest)); } /* @TODO The constraint changes in the following commands should done @@ -2494,28 +2514,31 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, crm_trace("%s%s now prefers %s%s", rsc->id, (promoted_role_only? " (promoted)" : ""), - pcmk__node_name(dest), force?"(forced)":""); + pcmk__node_name(dest), (force? " (forced)" : "")); - /* only ban the previous location if current location != destination location. - * it is possible to use -M to enforce a location without regard of where the - * resource is currently located */ + /* Ban the current location if force is set and the current location is not + * the destination. It is possible to use move to enforce a location without + * regard for where the resource is currently located. + */ if (force && !cur_is_dest) { /* Ban the original location if possible */ - if(current) { - (void)cli_resource_ban(out, rsc_id, current->priv->name, - move_lifetime, cib, promoted_role_only, - PCMK_ROLE_PROMOTED); - } else if(count > 1) { - out->info(out, "Resource '%s' is currently %s in %d locations. " + if (current != NULL) { + cli_resource_ban(out, rsc_id, current->priv->name, move_lifetime, + cib, promoted_role_only, PCMK_ROLE_PROMOTED); + + } else if (count > 1) { + out->info(out, + "Resource '%s' is currently %s in %u locations. " "One may now move to %s", - rsc_id, (promoted_role_only? "promoted" : "active"), - count, pcmk__node_name(dest)); - out->info(out, "To prevent '%s' from being %s at a specific location, " - "specify a node.", - rsc_id, (promoted_role_only? "promoted" : "active")); + rsc_id, active_s, count, pcmk__node_name(dest)); + out->info(out, + "To prevent '%s' from being %s at a specific location, " + "specify a node", + rsc_id, active_s); } else { - crm_trace("Not banning %s from its current location: not active", rsc_id); + crm_trace("Not banning %s from its current location: not active", + rsc_id); } }