diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:28 +0000 |
commit | 924f5ea83e48277e014ebf0d19a27187cb93e2f7 (patch) | |
tree | 75920a275bba045f6d108204562c218a9a26ea15 /tools | |
parent | Adding upstream version 2.1.7. (diff) | |
download | pacemaker-upstream.tar.xz pacemaker-upstream.zip |
Adding upstream version 2.1.8~rc1.upstream/2.1.8_rc1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/attrd_updater.c | 12 | ||||
-rw-r--r-- | tools/cibadmin.c | 209 | ||||
-rw-r--r-- | tools/cibsecret.in | 2 | ||||
-rw-r--r-- | tools/crm_attribute.c | 316 | ||||
-rw-r--r-- | tools/crm_diff.c | 50 | ||||
-rw-r--r-- | tools/crm_error.c | 4 | ||||
-rw-r--r-- | tools/crm_mon.c | 324 | ||||
-rw-r--r-- | tools/crm_mon.h | 3 | ||||
-rw-r--r-- | tools/crm_mon_curses.c | 28 | ||||
-rw-r--r-- | tools/crm_node.c | 87 | ||||
-rw-r--r-- | tools/crm_resource.c | 1101 | ||||
-rw-r--r-- | tools/crm_resource.h | 26 | ||||
-rw-r--r-- | tools/crm_resource_ban.c | 188 | ||||
-rw-r--r-- | tools/crm_resource_print.c | 271 | ||||
-rw-r--r-- | tools/crm_resource_runtime.c | 779 | ||||
-rw-r--r-- | tools/crm_rule.c | 15 | ||||
-rw-r--r-- | tools/crm_shadow.c | 94 | ||||
-rw-r--r-- | tools/crm_simulate.c | 52 | ||||
-rw-r--r-- | tools/crm_ticket.c | 614 | ||||
-rw-r--r-- | tools/crm_verify.c | 174 | ||||
-rw-r--r-- | tools/crmadmin.c | 14 | ||||
-rw-r--r-- | tools/stonith_admin.c | 23 |
22 files changed, 2300 insertions, 2086 deletions
diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c index 5f91356..5316101 100644 --- a/tools/attrd_updater.c +++ b/tools/attrd_updater.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,13 +18,13 @@ #include <sys/types.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/ipc_attrd_internal.h> #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/common/xml_internal.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/attrs_internal.h> #include <pcmki/pcmki_output.h> @@ -85,9 +85,9 @@ private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError static gboolean section_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { - if (pcmk__str_any_of(optarg, "nodes", "forever", NULL)) { + if (pcmk__str_any_of(optarg, PCMK_XE_NODES, "forever", NULL)) { pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_perm); - } else if (pcmk__str_any_of(optarg, "status", "reboot", NULL)) { + } else if (pcmk__str_any_of(optarg, PCMK_XE_STATUS, "reboot", NULL)) { pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_perm); } else { g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE, "Unknown value for --lifetime: %s", @@ -398,7 +398,7 @@ print_attrd_values(pcmk__output_t *out, const GList *reply) const pcmk__attrd_query_pair_t *pair = iter->data; out->message(out, "attribute", NULL, NULL, pair->name, pair->value, - pair->node); + pair->node, false, false); printed_values = true; } } diff --git a/tools/cibadmin.c b/tools/cibadmin.c index 44488b5..9ac252d 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,7 +10,6 @@ #include <crm_internal.h> #include <stdio.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/cmdline_internal.h> #include <crm/common/ipc.h> #include <crm/common/xml.h> @@ -53,6 +52,7 @@ static struct { gboolean get_node_path; gboolean local; gboolean no_children; + gboolean score_update; gboolean sync_call; /* @COMPAT: For "-!" version option. Not advertised nor marked as @@ -79,9 +79,9 @@ print_xml_output(xmlNode * xml) } if (pcmk_is_set(options.cmd_options, cib_xpath_address)) { - const char *id = crm_element_value(xml, XML_ATTR_ID); + const char *id = crm_element_value(xml, PCMK_XA_ID); - if (pcmk__str_eq((const char *)xml->name, "xpath-query", pcmk__str_casei)) { + if (pcmk__xe_is(xml, PCMK__XE_XPATH_QUERY)) { xmlNode *child = NULL; for (child = xml->children; child; child = child->next) { @@ -93,9 +93,12 @@ print_xml_output(xmlNode * xml) } } else { - char *buffer = dump_xml_formatted(xml); - fprintf(stdout, "%s", buffer); - free(buffer); + GString *buf = g_string_sized_new(1024); + + pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buf, 0); + + fprintf(stdout, "%s", buf->str); + g_string_free(buf, TRUE); } } @@ -138,18 +141,18 @@ static inline bool scope_is_valid(const char *scope) { return pcmk__str_any_of(scope, - XML_CIB_TAG_CONFIGURATION, - XML_CIB_TAG_NODES, - XML_CIB_TAG_RESOURCES, - XML_CIB_TAG_CONSTRAINTS, - XML_CIB_TAG_CRMCONFIG, - XML_CIB_TAG_RSCCONFIG, - XML_CIB_TAG_OPCONFIG, - XML_CIB_TAG_ACLS, - XML_TAG_FENCING_TOPOLOGY, - XML_CIB_TAG_TAGS, - XML_CIB_TAG_ALERTS, - XML_CIB_TAG_STATUS, + PCMK_XE_CONFIGURATION, + PCMK_XE_NODES, + PCMK_XE_RESOURCES, + PCMK_XE_CONSTRAINTS, + PCMK_XE_CRM_CONFIG, + PCMK_XE_RSC_DEFAULTS, + PCMK_XE_OP_DEFAULTS, + PCMK_XE_ACLS, + PCMK_XE_FENCING_TOPOLOGY, + PCMK_XE_TAGS, + PCMK_XE_ALERTS, + PCMK_XE_STATUS, NULL); } @@ -283,8 +286,8 @@ static GOptionEntry command_entries[] = { { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Delete first object matching supplied criteria (for example, " - "<" XML_ATTR_OP " " XML_ATTR_ID "=\"rsc1_op1\" " - XML_ATTR_NAME "=\"monitor\"/>).\n" + "<" PCMK_XE_OP " " PCMK_XA_ID "=\"rsc1_op1\" " + PCMK_XA_NAME "=\"monitor\"/>).\n" INDENT "The XML element name and all attributes must match in order for " "the element to be deleted.", NULL }, @@ -298,7 +301,7 @@ static GOptionEntry command_entries[] = { { "empty", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Output an empty CIB. Accepts an optional schema name argument to use as " - "the " XML_ATTR_VALIDATION " value.\n" + "the " PCMK_XA_VALIDATE_WITH " value.\n" INDENT "If no schema is given, the latest will be used.", "[schema]" }, @@ -350,12 +353,12 @@ static GOptionEntry addl_entries[] = { { "scope", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb, "Limit scope of operation to specific section of CIB\n" - INDENT "Valid values: " XML_CIB_TAG_CONFIGURATION ", " XML_CIB_TAG_NODES - ", " XML_CIB_TAG_RESOURCES ", " XML_CIB_TAG_CONSTRAINTS - ", " XML_CIB_TAG_CRMCONFIG ", " XML_CIB_TAG_RSCCONFIG ",\n" - INDENT " " XML_CIB_TAG_OPCONFIG ", " XML_CIB_TAG_ACLS - ", " XML_TAG_FENCING_TOPOLOGY ", " XML_CIB_TAG_TAGS - ", " XML_CIB_TAG_ALERTS ", " XML_CIB_TAG_STATUS "\n" + INDENT "Valid values: " PCMK_XE_CONFIGURATION ", " PCMK_XE_NODES + ", " PCMK_XE_RESOURCES ", " PCMK_XE_CONSTRAINTS + ", " PCMK_XE_CRM_CONFIG ", " PCMK_XE_RSC_DEFAULTS ",\n" + INDENT " " PCMK_XE_OP_DEFAULTS ", " PCMK_XE_ACLS + ", " PCMK_XE_FENCING_TOPOLOGY ", " PCMK_XE_TAGS ", " PCMK_XE_ALERTS + ", " PCMK_XE_STATUS "\n" INDENT "If both --scope/-o and --xpath/-a are specified, the last one to " "appear takes effect", "value" }, @@ -370,10 +373,10 @@ static GOptionEntry addl_entries[] = { &options.get_node_path, "When performing XPath queries, return paths of any matches found\n" INDENT "(for example, " - "\"/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION - "/" XML_CIB_TAG_RESOURCES "/" XML_CIB_TAG_INCARNATION - "[@" XML_ATTR_ID "='dummy-clone']" - "/" XML_CIB_TAG_RESOURCE "[@" XML_ATTR_ID "='dummy']\")", + "\"/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION + "/" PCMK_XE_RESOURCES "/" PCMK_XE_CLONE + "[@" PCMK_XA_ID "='dummy-clone']" + "/" PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "='dummy']\")", NULL }, { "show-access", 'S', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, @@ -386,6 +389,32 @@ static GOptionEntry addl_entries[] = { INDENT "Default value: 'auto'", "[value]" }, + { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update, + "Treat new attribute values as atomic score updates where possible " + "(with --modify/-M)\n\n" + + INDENT "This currently happens by default and cannot be disabled, but\n" + INDENT "this default behavior is deprecated and will be removed in a\n" + INDENT "future release. Set this flag if this behavior is desired.\n\n" + + INDENT "This option takes effect when updating XML attributes. For an\n" + INDENT "attribute named \"name\", if the new value is \"name++\" or\n" + INDENT "\"name+=X\" for some score X, the new value is set as follows:\n" + INDENT " * If attribute \"name\" is not already set to some value in\n" + INDENT " the element being updated, the new value is set as a literal\n" + INDENT " string.\n" + INDENT " * If the new value is \"name++\", then the attribute is set to\n" + INDENT " its existing value (parsed as a score) plus 1.\n" + INDENT " * If the new value is \"name+=X\" for some score X, then the\n" + INDENT " attribute is set to its existing value plus X, where the\n" + INDENT " existing value and X are parsed and added as scores.\n\n" + + INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n" + INDENT "Refer to Pacemaker Explained and to the char2score() function\n" + INDENT "for more details on scores, including how they're parsed and\n" + INDENT "added.", + NULL }, + { "allow-create", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.allow_create, "(Advanced) Allow target of --modify/-M to be created if it does not " @@ -430,34 +459,33 @@ build_arg_context(pcmk__common_args_t *args) "Query the configuration from the local node:\n\n" "\t# cibadmin --query --local\n\n" "Query just the cluster options configuration:\n\n" - "\t# cibadmin --query --scope " XML_CIB_TAG_CRMCONFIG "\n\n" - "Query all '" XML_RSC_ATTR_TARGET_ROLE "' settings:\n\n" + "\t# cibadmin --query --scope " PCMK_XE_CRM_CONFIG "\n\n" + "Query all '" PCMK_META_TARGET_ROLE "' settings:\n\n" "\t# cibadmin --query --xpath " - "\"//" XML_CIB_TAG_NVPAIR - "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_TARGET_ROLE"']\"" - "\n\n" - "Remove all '" XML_RSC_ATTR_MANAGED "' settings:\n\n" + "\"//" PCMK_XE_NVPAIR + "[@" PCMK_XA_NAME "='" PCMK_META_TARGET_ROLE"']\"\n\n" + "Remove all '" PCMK_META_IS_MANAGED "' settings:\n\n" "\t# cibadmin --delete-all --xpath " - "\"//" XML_CIB_TAG_NVPAIR - "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_MANAGED "']\"\n\n" + "\"//" PCMK_XE_NVPAIR + "[@" PCMK_XA_NAME "='" PCMK_META_IS_MANAGED "']\"\n\n" "Remove the resource named 'old':\n\n" "\t# cibadmin --delete --xml-text " - "'<" XML_CIB_TAG_RESOURCE " " XML_ATTR_ID "=\"old\"/>'\n\n" + "'<" PCMK_XE_PRIMITIVE " " PCMK_XA_ID "=\"old\"/>'\n\n" "Remove all resources from the configuration:\n\n" - "\t# cibadmin --replace --scope " XML_CIB_TAG_RESOURCES - " --xml-text '<" XML_CIB_TAG_RESOURCES "/>'\n\n" + "\t# cibadmin --replace --scope " PCMK_XE_RESOURCES + " --xml-text '<" PCMK_XE_RESOURCES "/>'\n\n" "Replace complete configuration with contents of " "$HOME/pacemaker.xml:\n\n" "\t# cibadmin --replace --xml-file $HOME/pacemaker.xml\n\n" - "Replace " XML_CIB_TAG_CONSTRAINTS " section of configuration with " + "Replace " PCMK_XE_CONSTRAINTS " section of configuration with " "contents of $HOME/constraints.xml:\n\n" - "\t# cibadmin --replace --scope " XML_CIB_TAG_CONSTRAINTS + "\t# cibadmin --replace --scope " PCMK_XE_CONSTRAINTS " --xml-file $HOME/constraints.xml\n\n" "Increase configuration version to prevent old configurations from " "being loaded accidentally:\n\n" "\t# cibadmin --modify --xml-text " - "'<" XML_TAG_CIB " " XML_ATTR_GENERATION_ADMIN - "=\"" XML_ATTR_GENERATION_ADMIN "++\"/>'\n\n" + "'<" PCMK_XE_CIB " " PCMK_XA_ADMIN_EPOCH + "=\"" PCMK_XA_ADMIN_EPOCH "++\"/>'\n\n" "Edit the configuration with your favorite $EDITOR:\n\n" "\t# cibadmin --query > $HOME/local.xml\n\n" "\t# $EDITOR $HOME/local.xml\n\n" @@ -567,13 +595,14 @@ main(int argc, char **argv) if (strcmp(options.cib_action, "empty") == 0) { // Output an empty CIB - char *buf = NULL; + GString *buf = g_string_sized_new(1024); output = createEmptyCib(1); - crm_xml_add(output, XML_ATTR_VALIDATION, options.validate_with); - buf = dump_xml_formatted(output); - fprintf(stdout, "%s", buf); - free(buf); + crm_xml_add(output, PCMK_XA_VALIDATE_WITH, options.validate_with); + + pcmk__xml_string(output, pcmk__xml_fmt_pretty, buf, 0); + fprintf(stdout, "%s", buf->str); + g_string_free(buf, TRUE); goto done; } @@ -658,16 +687,16 @@ main(int argc, char **argv) } if (options.input_file != NULL) { - input = filename2xml(options.input_file); + input = pcmk__xml_read(options.input_file); source = options.input_file; } else if (options.input_xml != NULL) { - input = string2xml(options.input_xml); + input = pcmk__xml_parse(options.input_xml); source = "input string"; } else if (options.input_stdin) { + input = pcmk__xml_read(NULL); source = "STDIN"; - input = stdin2xml(); } else if (options.acl_render_mode != pcmk__acl_render_none) { char *username = pcmk__uid2username(geteuid()); @@ -751,12 +780,20 @@ main(int argc, char **argv) goto done; } - version = crm_element_value(input, XML_ATTR_CRM_VERSION); + version = crm_element_value(input, PCMK_XA_CRM_FEATURE_SET); digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version); fprintf(stderr, "Versioned (%s) digest: ", version); fprintf(stdout, "%s\n", pcmk__s(digest, "<null>")); free(digest); goto done; + + } else if (pcmk__str_eq(options.cib_action, PCMK__CIB_REQUEST_MODIFY, + pcmk__str_none)) { + /* @COMPAT When we drop default support for expansion in cibadmin, guard + * with `if (options.score_update)` + */ + cib__set_call_options(options.cmd_options, crm_system_name, + cib_score_update); } rc = do_init(); @@ -773,9 +810,13 @@ main(int argc, char **argv) } rc = do_work(input, &output); - if (rc > 0) { - /* wait for the reply by creating a mainloop and running it until - * the callbacks are invoked... + if (!pcmk_is_set(options.cmd_options, cib_sync_call) + && (the_cib->variant != cib_file) + && (rc >= 0)) { + /* For async call, positive rc is the call ID (file always synchronous). + * + * Wait for the reply by creating a mainloop and running it until the + * callbacks are invoked. */ request_id = rc; @@ -791,32 +832,36 @@ main(int argc, char **argv) crm_info("Starting mainloop"); g_main_loop_run(mainloop); - } else if ((rc == -pcmk_err_schema_unchanged) - && (strcmp(options.cib_action, - PCMK__CIB_REQUEST_UPGRADE) == 0)) { - report_schema_unchanged(); - - } else if (rc < 0) { + } else { rc = pcmk_legacy2rc(rc); - crm_err("Call failed: %s", pcmk_rc_str(rc)); - fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc)); - if (rc == pcmk_rc_schema_validation) { - if (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0) { - xmlNode *obj = NULL; - int version = 0; + if ((rc == pcmk_rc_schema_unchanged) + && (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0)) { - if (the_cib->cmds->query(the_cib, NULL, &obj, - options.cmd_options) == pcmk_ok) { - update_validation(&obj, &version, 0, TRUE, FALSE); - } - free_xml(obj); + report_schema_unchanged(); + + } else if (rc != pcmk_rc_ok) { + crm_err("Call failed: %s", pcmk_rc_str(rc)); + fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); - } else if (output) { - validate_xml_verbose(output); + if (rc == pcmk_rc_schema_validation) { + if (strcmp(options.cib_action, + PCMK__CIB_REQUEST_UPGRADE) == 0) { + xmlNode *obj = NULL; + + if (the_cib->cmds->query(the_cib, NULL, &obj, + options.cmd_options) == pcmk_ok) { + pcmk__update_schema(&obj, NULL, true, false); + } + free_xml(obj); + + } else if (output != NULL) { + // Show validation errors to stderr + pcmk__validate_xml(output, NULL, NULL, NULL); + } } } - exit_code = pcmk_rc2exitc(rc); } if ((output != NULL) @@ -883,11 +928,11 @@ do_work(xmlNode *input, xmlNode **output) /* construct the request */ the_cib->call_timeout = options.message_timeout_sec; if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_REPLACE) == 0) - && pcmk__xe_is(input, XML_TAG_CIB)) { - xmlNode *status = pcmk_find_cib_element(input, XML_CIB_TAG_STATUS); + && pcmk__xe_is(input, PCMK_XE_CIB)) { + xmlNode *status = pcmk_find_cib_element(input, PCMK_XE_STATUS); if (status == NULL) { - create_xml_node(input, XML_CIB_TAG_STATUS); + pcmk__xe_create(input, PCMK_XE_STATUS); } } diff --git a/tools/cibsecret.in b/tools/cibsecret.in index 4569863..9df4201 100644 --- a/tools/cibsecret.in +++ b/tools/cibsecret.in @@ -171,7 +171,7 @@ check_env() { else fatal $CRM_EX_NOT_INSTALLED "please install pssh, pdsh, or ssh to run $PROG" fi - ps -ef | grep '[p]acemaker-controld' >/dev/null || + ps axww | grep '[p]acemaker-controld' >/dev/null || fatal $CRM_EX_UNAVAILABLE "pacemaker not running? $PROG needs pacemaker" } diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index defe294..f9b1434 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,7 +22,6 @@ #include <sys/types.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/util.h> @@ -30,7 +29,7 @@ #include <crm/cib.h> #include <crm/cib/internal.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/attrs_internal.h> #include <crm/common/cmdline_internal.h> #include <crm/common/ipc_attrd_internal.h> #include <crm/common/ipc_controld.h> @@ -41,36 +40,18 @@ #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes" +enum attr_cmd { + attr_cmd_none, + attr_cmd_delete, + attr_cmd_list, + attr_cmd_query, + attr_cmd_update, +}; + GError *error = NULL; crm_exit_t exit_code = CRM_EX_OK; uint64_t cib_opts = cib_sync_call; -PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *", - "const char *", "const char *") -static int -attribute_text(pcmk__output_t *out, va_list args) -{ - const char *scope = va_arg(args, const char *); - const char *instance = va_arg(args, const char *); - const char *name = va_arg(args, const char *); - const char *value = va_arg(args, const char *); - const char *host G_GNUC_UNUSED = va_arg(args, const char *); - - if (out->quiet) { - if (value != NULL) { - pcmk__formatted_printf(out, "%s\n", value); - } - } else { - out->info(out, "%s%s %s%s %s%s value=%s", - scope ? "scope=" : "", scope ? scope : "", - instance ? "id=" : "", instance ? instance : "", - name ? "name=" : "", name ? name : "", - value ? value : "(null)"); - } - - return pcmk_rc_ok; -} - static pcmk__supported_format_t formats[] = { PCMK__SUPPORTED_FORMAT_NONE, PCMK__SUPPORTED_FORMAT_TEXT, @@ -78,14 +59,8 @@ static pcmk__supported_format_t formats[] = { { NULL, NULL, NULL } }; -static pcmk__message_entry_t fmt_functions[] = { - { "attribute", "text", attribute_text }, - - { NULL, NULL, NULL } -}; - struct { - char command; + enum attr_cmd command; gchar *attr_default; gchar *attr_id; gchar *attr_name; @@ -98,26 +73,49 @@ struct { gchar *set_name; char *set_type; gchar *type; - gboolean promotion_score; + char *opt_list; + gboolean all; + bool promotion_score; + gboolean score_update; } options = { - .command = 'G', - .promotion_score = FALSE + .command = attr_cmd_query, }; #define INDENT " " static gboolean +list_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) { + options.command = attr_cmd_list; + pcmk__str_update(&options.opt_list, optarg); + return TRUE; +} + +static gboolean delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.command = 'D'; + options.command = attr_cmd_delete; pcmk__str_update(&options.attr_value, NULL); return TRUE; } static gboolean +attr_name_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + options.promotion_score = false; + + if (options.attr_name != NULL) { + g_free(options.attr_name); + } + options.attr_name = g_strdup(optarg); + return TRUE; +} + +static gboolean promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { char *score_name = NULL; - options.promotion_score = TRUE; + options.promotion_score = true; if (options.attr_name) { g_free(options.attr_name); @@ -136,7 +134,7 @@ promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErro static gboolean update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.command = 'u'; + options.command = attr_cmd_update; pcmk__str_update(&options.attr_value, optarg); return TRUE; } @@ -147,14 +145,14 @@ utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GEr g_free(options.type); } - options.type = g_strdup(XML_CIB_TAG_NODES); - pcmk__str_update(&options.set_type, XML_TAG_UTILIZATION); + options.type = g_strdup(PCMK_XE_NODES); + pcmk__str_update(&options.set_type, PCMK_XE_UTILIZATION); return TRUE; } static gboolean value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.command = 'G'; + options.command = attr_cmd_query; pcmk__str_update(&options.attr_value, NULL); return TRUE; } @@ -180,13 +178,20 @@ wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError ** } static GOptionEntry selecting_entries[] = { + { "all", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all, + "With -L/--list-options, include advanced and deprecated options in the\n" + INDENT "output. This is always treated as true when --output-as=xml is\n" + INDENT "specified.", + NULL, + }, + { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id, "(Advanced) Operate on instance of specified attribute with this\n" INDENT "XML ID", "XML_ID" }, - { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name, + { "name", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, attr_name_cb, "Operate on attribute or option with this name. For queries, this\n" INDENT "is optional, in which case all matching attributes will be\n" INDENT "returned.", @@ -217,8 +222,14 @@ static GOptionEntry selecting_entries[] = { }; static GOptionEntry command_entries[] = { + { "list-options", 'L', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, list_cb, + "List all available options of the given type.\n" + INDENT "Allowed values: " PCMK__VALUE_CLUSTER, + "TYPE" + }, + { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb, - "Delete the attribute/option", + "Delete the attribute/option (with -n or -P)", NULL }, @@ -229,7 +240,7 @@ static GOptionEntry command_entries[] = { }, { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb, - "Update the value of the attribute/option", + "Update the value of the attribute/option (with -n or -P)", "VALUE" }, @@ -260,6 +271,35 @@ static GOptionEntry addl_entries[] = { "SECTION" }, + { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update, + "Treat new attribute values as atomic score updates where possible\n" + INDENT "(with --update/-v, when running against a CIB file or updating\n" + INDENT "an attribute outside the " PCMK_XE_STATUS " section; enabled\n" + INDENT "by default if --promotion/-p is specified)\n\n" + + INDENT "This currently happens by default and cannot be disabled, but\n" + INDENT "this default behavior is deprecated and will be removed in a\n" + INDENT "future release (exception: this will remain the default with\n" + INDENT "--promotion/-p). Set this flag if this behavior is desired.\n\n" + + INDENT "This option takes effect when updating XML attributes. For an\n" + INDENT "attribute named \"name\", if the new value is \"name++\" or\n" + INDENT "\"name+=X\" for some score X, the new value is set as follows:\n" + INDENT " * If attribute \"name\" is not already set to some value in\n" + INDENT " the element being updated, the new value is set as a literal\n" + INDENT " string.\n" + INDENT " * If the new value is \"name++\", then the attribute is set to\n" + INDENT " its existing value (parsed as a score) plus 1.\n" + INDENT " * If the new value is \"name+=X\" for some score X, then the\n" + INDENT " attribute is set to its existing value plus X, where the\n" + INDENT " existing value and X are parsed and added as scores.\n\n" + + INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n" + INDENT "Refer to Pacemaker Explained and to the char2score() function\n" + INDENT "for more details on scores, including how they're parsed and\n" + INDENT "added.", + NULL }, + { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb, "Wait for some event to occur before returning. Values are 'no' (wait\n" INDENT "only for the attribute daemon to acknowledge the request),\n" @@ -288,7 +328,7 @@ static GOptionEntry deprecated_entries[] = { NULL, NULL }, - { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name, + { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, attr_name_cb, NULL, NULL }, @@ -314,36 +354,39 @@ static GOptionEntry deprecated_entries[] = { static void get_node_name_from_local(void) { - char *hostname = pcmk_hostname(); + struct utsname hostinfo; g_free(options.dest_uname); - /* This silliness is so that dest_uname is always a glib-managed - * string so we know how to free it later. pcmk_hostname returns - * a newly allocated string via strdup. - */ - options.dest_uname = g_strdup(hostname); - free(hostname); + if (uname(&hostinfo) == 0) { + options.dest_uname = g_strdup(hostinfo.nodename); + } else { + options.dest_uname = NULL; + } } static int -send_attrd_update(char command, const char *attr_node, const char *attr_name, - const char *attr_value, const char *attr_set, - const char *attr_dampen, uint32_t attr_options) +send_attrd_update(enum attr_cmd command, const char *attr_node, + const char *attr_name, const char *attr_value, + const char *attr_set, const char *attr_dampen, + uint32_t attr_options) { int rc = pcmk_rc_ok; uint32_t opts = attr_options; switch (command) { - case 'D': + case attr_cmd_delete: rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts); break; - case 'u': + case attr_cmd_update: rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value, NULL, attr_set, NULL, opts | pcmk__node_attr_value); break; + + default: + break; } if (rc != pcmk_rc_ok) { @@ -364,7 +407,7 @@ delete_attr_on_node(xmlNode *child, void *userdata) { struct delete_data_s *dd = (struct delete_data_s *) userdata; - const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME); + const char *attr_name = crm_element_value(child, PCMK_XA_NAME); int rc = pcmk_rc_ok; if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) { @@ -383,6 +426,22 @@ delete_attr_on_node(xmlNode *child, void *userdata) return rc; } +static void +command_list(pcmk__output_t *out) +{ + if (pcmk__str_eq(options.opt_list, PCMK__VALUE_CLUSTER, pcmk__str_none)) { + exit_code = pcmk_rc2exitc(pcmk__list_cluster_options(out, options.all)); + + } else { + // @TODO Improve usage messages to reduce duplication + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, + "Invalid --list-options value '%s'. Allowed values: " + PCMK__VALUE_CLUSTER, + pcmk__s(options.opt_list, "(BUG: none)")); + } +} + static int command_delete(pcmk__output_t *out, cib_t *cib) { @@ -405,10 +464,6 @@ command_delete(pcmk__output_t *out, cib_t *cib) rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd); - if (rc != pcmk_rc_ok) { - goto done_deleting; - } - } else { rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name, options.attr_id, @@ -440,7 +495,7 @@ update_attr_on_node(xmlNode *child, void *userdata) { struct update_data_s *ud = (struct update_data_s *) userdata; - const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME); + const char *attr_name = crm_element_value(child, PCMK_XA_NAME); if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) { return pcmk_rc_ok; @@ -450,7 +505,7 @@ update_attr_on_node(xmlNode *child, void *userdata) options.dest_node, options.set_type, options.set_name, options.attr_id, attr_name, options.attr_value, NULL, - ud->is_remote_node ? "remote" : NULL); + ud->is_remote_node? PCMK_VALUE_REMOTE : NULL); } static int @@ -461,9 +516,10 @@ command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node) xmlNode *result = NULL; bool use_pattern = options.attr_pattern != NULL; - CRM_LOG_ASSERT(options.type != NULL); - CRM_LOG_ASSERT(options.attr_name != NULL); - CRM_LOG_ASSERT(options.attr_value != NULL); + /* @COMPAT When we drop default support for expansion in crm_attribute, + * guard with `if (options.score_update)` + */ + cib__set_call_options(cib_opts, crm_system_name, cib_score_update); /* See the comment in command_query regarding xpath and regular expressions. */ if (use_pattern) { @@ -479,16 +535,12 @@ command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node) rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud); - if (rc != pcmk_rc_ok) { - goto done_updating; - } - } else { rc = cib__update_node_attr(out, cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name, options.attr_id, - options.attr_name, options.attr_value, - NULL, is_remote_node ? "remote" : NULL); + options.attr_name, options.attr_value, NULL, + is_remote_node? PCMK_VALUE_REMOTE : NULL); } done_updating: @@ -507,9 +559,8 @@ output_one_attribute(xmlNode *node, void *userdata) { struct output_data_s *od = (struct output_data_s *) userdata; - const char *name = crm_element_value(node, XML_NVPAIR_ATTR_NAME); - const char *value = crm_element_value(node, XML_NVPAIR_ATTR_VALUE); - const char *host = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME); + const char *name = crm_element_value(node, PCMK_XA_NAME); + const char *value = crm_element_value(node, PCMK_XA_VALUE); const char *type = options.type; const char *attr_id = options.attr_id; @@ -518,7 +569,8 @@ output_one_attribute(xmlNode *node, void *userdata) return pcmk_rc_ok; } - od->out->message(od->out, "attribute", type, attr_id, name, value, host); + od->out->message(od->out, "attribute", type, attr_id, name, value, NULL, + od->out->quiet, true); od->did_output = true; crm_info("Read %s='%s' %s%s", pcmk__s(name, "<null>"), pcmk__s(value, ""), @@ -556,10 +608,9 @@ command_query(pcmk__output_t *out, cib_t *cib) const char *attr_id = options.attr_id; const char *attr_name = options.attr_name; const char *attr_default = options.attr_default; - const char *dest_uname = options.dest_uname; out->message(out, "attribute", type, attr_id, attr_name, attr_default, - dest_uname); + NULL, out->quiet, true); rc = pcmk_rc_ok; } else if (rc != pcmk_rc_ok) { @@ -589,22 +640,22 @@ set_type(void) if (options.type == NULL) { if (options.promotion_score) { // Updating a promotion score node attribute - options.type = g_strdup(XML_CIB_TAG_STATUS); + options.type = g_strdup(PCMK_XE_STATUS); } else if (options.dest_uname != NULL) { // Updating some other node attribute - options.type = g_strdup(XML_CIB_TAG_NODES); + options.type = g_strdup(PCMK_XE_NODES); } else { // Updating cluster options - options.type = g_strdup(XML_CIB_TAG_CRMCONFIG); + options.type = g_strdup(PCMK_XE_CRM_CONFIG); } } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) { - options.type = g_strdup(XML_CIB_TAG_STATUS); + options.type = g_strdup(PCMK_XE_STATUS); } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) { - options.type = g_strdup(XML_CIB_TAG_NODES); + options.type = g_strdup(PCMK_XE_NODES); } } @@ -614,14 +665,16 @@ use_attrd(void) /* Only go through the attribute manager for transient attributes, and * then only if we're not using a file as the CIB. */ - return pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei) && + return pcmk__str_eq(options.type, PCMK_XE_STATUS, pcmk__str_casei) && getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL; } static bool try_ipc_update(void) { - return use_attrd() && (options.command == 'D' || options.command == 'u'); + return use_attrd() + && ((options.command == attr_cmd_delete) + || (options.command == attr_cmd_update)); } static bool @@ -630,13 +683,30 @@ pattern_used_correctly(void) /* --pattern can only be used with: * -G (query), -v (update), or -D (delete) */ - return options.command == 'G' || options.command == 'u' || options.command == 'D'; + switch (options.command) { + case attr_cmd_delete: + case attr_cmd_query: + case attr_cmd_update: + return true; + default: + return false; + } } static bool delete_used_correctly(void) { - return options.command != 'D' || options.attr_name != NULL || options.attr_pattern != NULL; + return (options.command != attr_cmd_delete) + || (options.attr_name != NULL) + || (options.attr_pattern != NULL); +} + +static bool +update_used_correctly(void) +{ + return (options.command != attr_cmd_update) + || (options.attr_name != NULL) + || (options.attr_pattern != NULL); } static GOptionContext * @@ -664,10 +734,14 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { "\tcrm_attribute --node myhost --name location --update backoffice\n\n" "Delete the 'location' node attribute for host 'myhost':\n\n" "\tcrm_attribute --node myhost --name location --delete\n\n" - "Query the value of the 'cluster-delay' cluster option:\n\n" - "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n" - "Query value of the 'cluster-delay' cluster option and print only the value:\n\n" - "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n"; + "Query the value of the '" PCMK_OPT_CLUSTER_DELAY + "' cluster option:\n\n" + "\tcrm_attribute --type crm_config --name " + PCMK_OPT_CLUSTER_DELAY " --query\n\n" + "Query value of the '" PCMK_OPT_CLUSTER_DELAY + "' cluster option and print only the value:\n\n" + "\tcrm_attribute --type crm_config --name " + PCMK_OPT_CLUSTER_DELAY " --query --quiet\n\n"; context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); pcmk__add_main_args(context, extra_prog_entries); @@ -717,7 +791,6 @@ main(int argc, char **argv) } pcmk__register_lib_messages(out); - pcmk__register_messages(out, fmt_functions); if (args->version) { out->version(out, false); @@ -726,6 +799,11 @@ main(int argc, char **argv) out->quiet = args->quiet; + if (options.command == attr_cmd_list) { + command_list(out); + goto done; + } + if (options.promotion_score && options.attr_name == NULL) { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, @@ -753,8 +831,8 @@ main(int argc, char **argv) set_type(); // Use default node if not given (except for cluster options and tickets) - if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS, - NULL)) { + if (!pcmk__strcase_any_of(options.type, + PCMK_XE_CRM_CONFIG, PCMK_XE_TICKETS, NULL)) { /* If we are being called from a resource agent via the cluster, * the correct local node name will be passed as an environment * variable. Otherwise, we have to ask the cluster. @@ -762,8 +840,23 @@ main(int argc, char **argv) const char *target = pcmk__node_attr_target(options.dest_uname); if (target != NULL) { - g_free(options.dest_uname); - options.dest_uname = g_strdup(target); + /* If options.dest_uname is "auto" or "localhost", then + * pcmk__node_attr_target() may return it, depending on environment + * variables. In that case, attribute lookups will fail for "auto" + * (unless there's a node named "auto"). attrd maps "localhost" to + * the true local node name for queries. + * + * @TODO + * * Investigate whether "localhost" is mapped to a real node name + * for non-query commands. If not, possibly modify it so that it + * is. + * * Map "auto" to "localhost" (probably). + */ + if (target != (const char *) options.dest_uname) { + g_free(options.dest_uname); + options.dest_uname = g_strdup(target); + } + } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) { get_node_name_from_local(); } @@ -800,6 +893,13 @@ main(int argc, char **argv) goto done; } + if (!update_used_correctly()) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Error: must specify attribute name or pattern to update"); + goto done; + } + if (options.attr_pattern) { if (options.attr_name) { exit_code = CRM_EX_USAGE; @@ -824,20 +924,26 @@ main(int argc, char **argv) options.attr_options |= pcmk__node_attr_remote; } - if (pcmk__str_eq(options.set_type, XML_TAG_UTILIZATION, pcmk__str_none)) { + if (pcmk__str_eq(options.set_type, PCMK_XE_UTILIZATION, pcmk__str_none)) { options.attr_options |= pcmk__node_attr_utilization; } if (try_ipc_update() && (send_attrd_update(options.command, options.dest_uname, options.attr_name, options.attr_value, options.set_name, NULL, options.attr_options) == pcmk_rc_ok)) { + + const char *update = options.attr_value; + + if (options.command == attr_cmd_delete) { + update = "<none>"; + } crm_info("Update %s=%s sent via pacemaker-attrd", - options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value)); + options.attr_name, update); - } else if (options.command == 'D') { + } else if (options.command == attr_cmd_delete) { rc = command_delete(out, the_cib); - } else if (options.command == 'u') { + } else if (options.command == attr_cmd_update) { rc = command_update(out, the_cib, is_remote_node); } else { diff --git a/tools/crm_diff.c b/tools/crm_diff.c index 9925ea7..09e24c1 100644 --- a/tools/crm_diff.c +++ b/tools/crm_diff.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2023 the Pacemaker project contributors + * Copyright 2005-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,7 +18,6 @@ #include <sys/types.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/common/xml.h> @@ -106,10 +105,12 @@ patch_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError ** static void print_patch(xmlNode *patch) { - char *buffer = dump_xml_formatted(patch); + GString *buffer = g_string_sized_new(1024); - printf("%s", buffer); - free(buffer); + pcmk__xml_string(patch, pcmk__xml_fmt_pretty, buffer, 0); + + printf("%s", buffer->str); + g_string_free(buffer, TRUE); fflush(stdout); } @@ -117,7 +118,7 @@ print_patch(xmlNode *patch) static int apply_patch(xmlNode *input, xmlNode *patch, gboolean as_cib) { - xmlNode *output = copy_xml(input); + xmlNode *output = pcmk__xml_copy(NULL, input); int rc = xml_apply_patchset(output, patch, as_cib); rc = pcmk_legacy2rc(rc); @@ -133,7 +134,7 @@ apply_patch(xmlNode *input, xmlNode *patch, gboolean as_cib) print_patch(output); - version = crm_element_value(output, XML_ATTR_CRM_VERSION); + version = crm_element_value(output, PCMK_XA_CRM_FEATURE_SET); buffer = calculate_xml_versioned_digest(output, FALSE, TRUE, version); crm_trace("Digest: %s", pcmk__s(buffer, "<null>\n")); free(buffer); @@ -153,7 +154,7 @@ log_patch_cib_versions(xmlNode *patch) xml_patch_versions(patch, add, del); fmt = crm_element_value(patch, PCMK_XA_FORMAT); - digest = crm_element_value(patch, XML_ATTR_DIGEST); + digest = crm_element_value(patch, PCMK__XA_DIGEST); if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) { crm_info("Patch: --- %d.%d.%d %s", del[0], del[1], del[2], fmt); @@ -168,7 +169,8 @@ strip_patch_cib_version(xmlNode *patch, const char **vfields, size_t nvfields) crm_element_value_int(patch, PCMK_XA_FORMAT, &format); if (format == 2) { - xmlNode *version_xml = find_xml_node(patch, "version", FALSE); + xmlNode *version_xml = pcmk__xe_first_child(patch, PCMK_XE_VERSION, + NULL, NULL); if (version_xml) { free_xml(version_xml); @@ -178,24 +180,24 @@ strip_patch_cib_version(xmlNode *patch, const char **vfields, size_t nvfields) int i = 0; const char *tags[] = { - XML_TAG_DIFF_REMOVED, - XML_TAG_DIFF_ADDED, + PCMK__XE_DIFF_REMOVED, + PCMK__XE_DIFF_ADDED, }; for (i = 0; i < PCMK__NELEM(tags); i++) { xmlNode *tmp = NULL; int lpc; - tmp = find_xml_node(patch, tags[i], FALSE); + tmp = pcmk__xe_first_child(patch, tags[i], NULL, NULL); if (tmp) { for (lpc = 0; lpc < nvfields; lpc++) { - xml_remove_prop(tmp, vfields[lpc]); + pcmk__xe_remove_attr(tmp, vfields[lpc]); } - tmp = find_xml_node(tmp, XML_TAG_CIB, FALSE); + tmp = pcmk__xe_first_child(tmp, PCMK_XE_CIB, NULL, NULL); if (tmp) { for (lpc = 0; lpc < nvfields; lpc++) { - xml_remove_prop(tmp, vfields[lpc]); + pcmk__xe_remove_attr(tmp, vfields[lpc]); } } } @@ -209,9 +211,9 @@ generate_patch(xmlNode *object_1, xmlNode *object_2, const char *xml_file_2, gboolean as_cib, gboolean no_version) { const char *vfields[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, }; xmlNode *output = NULL; @@ -328,25 +330,25 @@ main(int argc, char **argv) } if (options.raw_1) { - object_1 = string2xml(options.xml_file_1); + object_1 = pcmk__xml_parse(options.xml_file_1); } else if (options.use_stdin) { fprintf(stderr, "Input first XML fragment:"); - object_1 = stdin2xml(); + object_1 = pcmk__xml_read(NULL); } else if (options.xml_file_1 != NULL) { - object_1 = filename2xml(options.xml_file_1); + object_1 = pcmk__xml_read(options.xml_file_1); } if (options.raw_2) { - object_2 = string2xml(options.xml_file_2); + object_2 = pcmk__xml_parse(options.xml_file_2); } else if (options.use_stdin) { fprintf(stderr, "Input second XML fragment:"); - object_2 = stdin2xml(); + object_2 = pcmk__xml_read(NULL); } else if (options.xml_file_2 != NULL) { - object_2 = filename2xml(options.xml_file_2); + object_2 = pcmk__xml_read(options.xml_file_2); } if (object_1 == NULL) { diff --git a/tools/crm_error.c b/tools/crm_error.c index 8911eae..51a0051 100644 --- a/tools/crm_error.c +++ b/tools/crm_error.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the Pacemaker project contributors + * Copyright 2012-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,7 +8,7 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/common/strings_internal.h> diff --git a/tools/crm_mon.c b/tools/crm_mon.c index dbe76fc..85be8dc 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -26,7 +26,6 @@ #include <signal.h> #include <sys/utsname.h> -#include <crm/msg_xml.h> #include <crm/services.h> #include <crm/lrmd.h> #include <crm/common/cmdline_internal.h> @@ -45,7 +44,7 @@ #include <crm/pengine/internal.h> #include <pacemaker-internal.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__* #include "crm_mon.h" @@ -121,19 +120,20 @@ crm_mon_disconnected_html(pcmk__output_t *out, va_list args) out->reset(out); } - pcmk__output_create_xml_text_node(out, "span", "Not connected to CIB"); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, + "Not connected to CIB"); if (desc != NULL) { - pcmk__output_create_xml_text_node(out, "span", ": "); - pcmk__output_create_xml_text_node(out, "span", desc); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, ": "); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, desc); } if (state != pcmk_pacemakerd_state_invalid) { const char *state_s = pcmk__pcmkd_state_enum2friendly(state); - pcmk__output_create_xml_text_node(out, "span", " ("); - pcmk__output_create_xml_text_node(out, "span", state_s); - pcmk__output_create_xml_text_node(out, "span", ")"); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, " ("); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, state_s); + pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, ")"); } out->finish(out, CRM_EX_DISCONNECT, true, NULL); @@ -185,9 +185,9 @@ crm_mon_disconnected_xml(pcmk__output_t *out, va_list args) state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state); } - pcmk__output_create_xml_node(out, "crm-mon-disconnected", - XML_ATTR_DESC, desc, - "pacemakerd-state", state_s, + pcmk__output_create_xml_node(out, PCMK_XE_CRM_MON_DISCONNECTED, + PCMK_XA_DESCRIPTION, desc, + PCMK_XA_PACEMAKERD_STATE, state_s, NULL); out->finish(out, CRM_EX_DISCONNECT, true, NULL); @@ -284,7 +284,7 @@ struct { { "dc", pcmk_section_dc }, { "failcounts", pcmk_section_failcounts }, { "failures", pcmk_section_failures }, - { PCMK__VALUE_FENCING, pcmk_section_fencing_all }, + { PCMK_VALUE_FENCING, pcmk_section_fencing_all }, { "fencing-failed", pcmk_section_fence_failed }, { "fencing-pending", pcmk_section_fence_pending }, { "fencing-succeeded", pcmk_section_fence_worked }, @@ -322,7 +322,7 @@ apply_exclude(const gchar *excludes, GError **error) { if (pcmk__str_eq(*s, "all", pcmk__str_none)) { show = 0; - } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) { + } else if (pcmk__str_eq(*s, PCMK_VALUE_NONE, pcmk__str_none)) { show = all_includes(output_format); } else if (bit != 0) { show &= ~bit; @@ -331,7 +331,7 @@ apply_exclude(const gchar *excludes, GError **error) { "--exclude options: all, attributes, bans, counts, dc, " "failcounts, failures, fencing, fencing-failed, " "fencing-pending, fencing-succeeded, maint-mode, nodes, " - PCMK__VALUE_NONE ", operations, options, resources, " + PCMK_VALUE_NONE ", operations, options, resources, " "stack, summary, tickets, times"); result = FALSE; break; @@ -362,19 +362,19 @@ apply_include(const gchar *includes, GError **error) { if (strlen(*s) > 4 && (*s)[4] == ':') { options.neg_location_prefix = strdup(*s+5); } - } else if (pcmk__str_any_of(*s, "default", "defaults", NULL)) { + } else if (pcmk__str_any_of(*s, PCMK_VALUE_DEFAULT, "defaults", NULL)) { show |= default_includes(output_format); - } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) { + } else if (pcmk__str_eq(*s, PCMK_VALUE_NONE, pcmk__str_none)) { show = 0; } else if (bit != 0) { show |= bit; } else { g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "--include options: all, attributes, bans[:PREFIX], counts, dc, " - "default, failcounts, failures, fencing, fencing-failed, " - "fencing-pending, fencing-succeeded, maint-mode, nodes, " - PCMK__VALUE_NONE ", operations, options, resources, " - "stack, summary, tickets, times"); + PCMK_VALUE_DEFAULT ", failcounts, failures, fencing, " + "fencing-failed, fencing-pending, fencing-succeeded, " + "maint-mode, nodes, " PCMK_VALUE_NONE ", operations, " + "options, resources, stack, summary, tickets, times"); result = FALSE; break; } @@ -471,13 +471,13 @@ fence_history_cb(const gchar *option_name, const gchar *optarg, gpointer data, G case 3: options.fence_connect = TRUE; fence_history = pcmk__fence_history_full; - return include_exclude_cb("--include", PCMK__VALUE_FENCING, data, + return include_exclude_cb("--include", PCMK_VALUE_FENCING, data, err); case 2: options.fence_connect = TRUE; fence_history = pcmk__fence_history_full; - return include_exclude_cb("--include", PCMK__VALUE_FENCING, data, + return include_exclude_cb("--include", PCMK_VALUE_FENCING, data, err); case 1: @@ -488,7 +488,7 @@ fence_history_cb(const gchar *option_name, const gchar *optarg, gpointer data, G case 0: options.fence_connect = FALSE; fence_history = pcmk__fence_history_none; - return include_exclude_cb("--exclude", PCMK__VALUE_FENCING, data, + return include_exclude_cb("--exclude", PCMK_VALUE_FENCING, data, err); default: @@ -553,7 +553,7 @@ reconnect_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErro g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid value for -i: %s", optarg); return FALSE; } else { - options.reconnect_ms = crm_parse_interval_spec(optarg); + pcmk_parse_interval_spec(optarg, &options.reconnect_ms); if (options.exec_mode != mon_exec_daemonized) { // Reconnect interval applies to daemonized too, so don't override @@ -756,7 +756,7 @@ static GOptionEntry display_entries[] = { NULL }, { "pending", 'j', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.print_pending, - "Display pending state if 'record-pending' is enabled", + "Display pending state if '" PCMK_META_RECORD_PENDING "' is enabled", NULL }, { NULL } @@ -854,8 +854,12 @@ mon_cib_connection_destroy(gpointer user_data) /* the client API won't properly reconnect notifications if they are still * in the table - so remove them */ - stonith_api_delete(st); - st = NULL; + if (st != NULL) { + if (st->state != stonith_disconnected) { + st->cmds->disconnect(st); + } + st->cmds->remove_notification(st, NULL); + } if (cib) { cib->cmds->signoff(cib); @@ -918,13 +922,17 @@ setup_fencer_connection(void) if (rc == pcmk_ok) { crm_trace("Setting up stonith callbacks"); if (options.watch_fencing) { - st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, + st->cmds->register_notification(st, + PCMK__VALUE_ST_NOTIFY_DISCONNECT, + mon_st_callback_event); + st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE, mon_st_callback_event); - st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback_event); } else { - st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, + st->cmds->register_notification(st, + PCMK__VALUE_ST_NOTIFY_DISCONNECT, + mon_st_callback_display); + st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_HISTORY, mon_st_callback_display); - st->cmds->register_notification(st, T_STONITH_NOTIFY_HISTORY, mon_st_callback_display); } } else { stonith_api_delete(st); @@ -960,10 +968,11 @@ setup_cib_connection(void) } if (rc == pcmk_rc_ok) { - cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, + cib->cmds->del_notify_callback(cib, PCMK__VALUE_CIB_DIFF_NOTIFY, crm_diff_update); - rc = pcmk_legacy2rc(cib->cmds->add_notify_callback(cib, - T_CIB_DIFF_NOTIFY, crm_diff_update)); + rc = cib->cmds->add_notify_callback(cib, PCMK__VALUE_CIB_DIFF_NOTIFY, + crm_diff_update); + rc = pcmk_legacy2rc(rc); } if (rc != pcmk_rc_ok) { @@ -1088,6 +1097,28 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_dat { int c; gboolean config_mode = FALSE; + gboolean rc = G_SOURCE_CONTINUE; + + /* If the attached pty device (pseudo-terminal) has been closed/deleted, + * the condition (G_IO_IN | G_IO_ERR | G_IO_HUP) occurs. + * Exit with an error, otherwise the process would persist in the + * background and significantly raise the CPU usage. + */ + if ((condition & G_IO_ERR) && (condition & G_IO_HUP)) { + rc = G_SOURCE_REMOVE; + clean_up(CRM_EX_IOERR); + } + + /* The connection/fd has been closed. Refresh the screen and remove this + * event source hence ignore stdin. + */ + if (condition & (G_IO_HUP | G_IO_NVAL)) { + rc = G_SOURCE_REMOVE; + } + + if ((condition & G_IO_IN) == 0) { + return rc; + } while (1) { @@ -1194,7 +1225,7 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_dat refresh: refresh_after_event(FALSE, TRUE); - return TRUE; + return rc; } #endif // CURSES_ENABLED @@ -1313,27 +1344,11 @@ static void add_output_args(void) { GError *err = NULL; - if (output_format == mon_output_plain) { - if (!pcmk__force_args(context, &err, "%s --text-fancy", g_get_prgname())) { - g_propagate_error(&error, err); - clean_up(CRM_EX_USAGE); - } - } else if (output_format == mon_output_cgi) { + if (output_format == mon_output_cgi) { if (!pcmk__force_args(context, &err, "%s --html-cgi", g_get_prgname())) { g_propagate_error(&error, err); clean_up(CRM_EX_USAGE); } - } else if (output_format == mon_output_xml) { - if (!pcmk__force_args(context, &err, "%s --xml-simple-list --xml-substitute", g_get_prgname())) { - g_propagate_error(&error, err); - clean_up(CRM_EX_USAGE); - } - } else if (output_format == mon_output_legacy_xml) { - output_format = mon_output_xml; - if (!pcmk__force_args(context, &err, "%s --xml-legacy --xml-substitute", g_get_prgname())) { - g_propagate_error(&error, err); - clean_up(CRM_EX_USAGE); - } } } @@ -1359,7 +1374,7 @@ reconcile_output_format(pcmk__common_args_t *args) return; } - if (pcmk__str_eq(args->output_ty, "none", pcmk__str_none)) { + if (pcmk__str_eq(args->output_ty, PCMK_VALUE_NONE, pcmk__str_none)) { output_format = mon_output_none; } else if (pcmk__str_eq(args->output_ty, "html", pcmk__str_none)) { @@ -1582,8 +1597,6 @@ main(int argc, char **argv) set_default_exec_mode(args); add_output_args(); - /* output_format MUST NOT BE CHANGED AFTER THIS POINT. */ - rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); if (rc != pcmk_rc_ok) { g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error creating output format %s: %s", @@ -1591,11 +1604,22 @@ main(int argc, char **argv) return clean_up(CRM_EX_ERROR); } + if (output_format == mon_output_legacy_xml) { + output_format = mon_output_xml; + pcmk__output_set_legacy_xml(out); + } + + /* output_format MUST NOT BE CHANGED AFTER THIS POINT. */ + /* If we had a valid format for pcmk__output_new(), output_format should be * set by now. */ CRM_ASSERT(output_format != mon_output_unset); + if (output_format == mon_output_plain) { + pcmk__output_text_set_fancy(out, true); + } + if (options.exec_mode == mon_exec_daemonized) { if (!options.external_agent && (output_format == mon_output_none)) { g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, @@ -1670,10 +1694,16 @@ main(int argc, char **argv) show_opts |= pcmk_show_inactive_rscs | pcmk_show_timing; } - if ((output_format == mon_output_html || output_format == mon_output_cgi) && - out->dest != stdout) { - pcmk__html_add_header("meta", "http-equiv", "refresh", "content", - pcmk__itoa(options.reconnect_ms / 1000), NULL); + if ((output_format == mon_output_html || output_format == mon_output_cgi) + && (out->dest != stdout)) { + + char *content = pcmk__itoa(options.reconnect_ms / 1000); + + pcmk__html_add_header(PCMK__XE_META, + PCMK__XA_HTTP_EQUIV, PCMK__VALUE_REFRESH, + PCMK__XA_CONTENT, content, + NULL); + free(content); } #ifdef PCMK__COMPAT_2_0 @@ -1731,7 +1761,8 @@ main(int argc, char **argv) ncurses_winch_handler = NULL; io_channel = g_io_channel_unix_new(STDIN_FILENO); - g_io_add_watch(io_channel, G_IO_IN, detect_user_input, NULL); + g_io_add_watch(io_channel, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL), + detect_user_input, NULL); } #endif @@ -1743,10 +1774,6 @@ main(int argc, char **argv) g_main_loop_run(mainloop); g_main_loop_unref(mainloop); - if (io_channel != NULL) { - g_io_channel_shutdown(io_channel, TRUE, NULL); - } - crm_info("Exiting %s", crm_system_name); return clean_up(CRM_EX_OK); @@ -1785,7 +1812,7 @@ send_custom_trap(const char *node, const char *rsc, const char *task, int target if (pid == 0) { /* crm_debug("notification: I am the child. Executing the nofitication program."); */ execl(options.external_agent, options.external_agent, NULL); - exit(CRM_EX_ERROR); + crm_exit(CRM_EX_ERROR); } crm_trace("Finished running custom notification program '%s'.", options.external_agent); @@ -1814,14 +1841,14 @@ handle_rsc_op(xmlNode *xml, void *userdata) xmlNode *n = xml; xmlNode * rsc_op = xml; - if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) { + if(strcmp((const char*)xml->name, PCMK__XE_LRM_RSC_OP) != 0) { pcmk__xe_foreach_child(xml, NULL, handle_rsc_op, (void *) node_id); return pcmk_rc_ok; } - id = pe__xe_history_key(rsc_op); + id = pcmk__xe_history_key(rsc_op); - magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC); + magic = crm_element_value(rsc_op, PCMK__XA_TRANSITION_MAGIC); if (magic == NULL) { /* non-change */ return pcmk_rc_ok; @@ -1838,18 +1865,18 @@ handle_rsc_op(xmlNode *xml, void *userdata) goto bail; } - node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET); + node = crm_element_value(rsc_op, PCMK__META_ON_NODE); - while ((n != NULL) && !pcmk__xe_is(n, XML_CIB_TAG_STATE)) { + while ((n != NULL) && !pcmk__xe_is(n, PCMK__XE_NODE_STATE)) { n = n->parent; } if(node == NULL && n) { - node = crm_element_value(n, XML_ATTR_UNAME); + node = crm_element_value(n, PCMK_XA_UNAME); } if (node == NULL && n) { - node = ID(n); + node = pcmk__xe_id(n); } if (node == NULL) { @@ -1902,101 +1929,107 @@ mon_trigger_refresh(gpointer user_data) static int handle_op_for_node(xmlNode *xml, void *userdata) { - const char *node = crm_element_value(xml, XML_ATTR_UNAME); + const char *node = crm_element_value(xml, PCMK_XA_UNAME); if (node == NULL) { - node = ID(xml); + node = pcmk__xe_id(xml); } handle_rsc_op(xml, (void *) node); return pcmk_rc_ok; } -static void -crm_diff_update_v2(const char *event, xmlNode * msg) +static int +crm_diff_update_element_v2(xmlNode *change, void *userdata) { - xmlNode *change = NULL; - xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT); - - for (change = pcmk__xml_first_child(diff); change != NULL; - change = pcmk__xml_next(change)) { - const char *name = NULL; - const char *op = crm_element_value(change, XML_DIFF_OP); - const char *xpath = crm_element_value(change, XML_DIFF_PATH); - xmlNode *match = NULL; - const char *node = NULL; - - if(op == NULL) { - continue; - - } else if(strcmp(op, "create") == 0) { - match = change->children; + const char *name = NULL; + const char *op = crm_element_value(change, PCMK_XA_OPERATION); + const char *xpath = crm_element_value(change, PCMK_XA_PATH); + xmlNode *match = NULL; + const char *node = NULL; - } else if(strcmp(op, "move") == 0) { - continue; + if (op == NULL) { + return pcmk_rc_ok; - } else if(strcmp(op, "delete") == 0) { - continue; + } else if (strcmp(op, PCMK_VALUE_CREATE) == 0) { + match = change->children; - } else if(strcmp(op, "modify") == 0) { - match = first_named_child(change, XML_DIFF_RESULT); - if(match) { - match = match->children; - } - } + } else if (pcmk__str_any_of(op, PCMK_VALUE_MOVE, PCMK_VALUE_DELETE, + NULL)) { + return pcmk_rc_ok; + } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) { + match = pcmk__xe_first_child(change, PCMK_XE_CHANGE_RESULT, NULL, NULL); if(match) { - name = (const char *)match->name; + match = match->children; } + } - crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name); - if(xpath == NULL) { - /* Version field, ignore */ + if(match) { + name = (const char *)match->name; + } - } else if(name == NULL) { - crm_debug("No result for %s operation to %s", op, xpath); - CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0); + crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name); + if(xpath == NULL) { + /* Version field, ignore */ - } else if(strcmp(name, XML_TAG_CIB) == 0) { - pcmk__xe_foreach_child(first_named_child(match, XML_CIB_TAG_STATUS), - NULL, handle_op_for_node, NULL); + } else if(name == NULL) { + crm_debug("No result for %s operation to %s", op, xpath); + CRM_ASSERT(pcmk__str_any_of(op, PCMK_VALUE_MOVE, PCMK_VALUE_DELETE, + NULL)); - } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) { - pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL); + } else if (strcmp(name, PCMK_XE_CIB) == 0) { + pcmk__xe_foreach_child(pcmk__xe_first_child(match, PCMK_XE_STATUS, NULL, + NULL), + NULL, handle_op_for_node, NULL); - } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) { - node = crm_element_value(match, XML_ATTR_UNAME); - if (node == NULL) { - node = ID(match); - } - handle_rsc_op(match, (void *) node); + } else if (strcmp(name, PCMK_XE_STATUS) == 0) { + pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL); - } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) { - node = ID(match); - handle_rsc_op(match, (void *) node); + } else if (strcmp(name, PCMK__XE_NODE_STATE) == 0) { + node = crm_element_value(match, PCMK_XA_UNAME); + if (node == NULL) { + node = pcmk__xe_id(match); + } + handle_rsc_op(match, (void *) node); - } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) { - char *local_node = pcmk__xpath_node_id(xpath, "lrm"); + } else if (strcmp(name, PCMK__XE_LRM) == 0) { + node = pcmk__xe_id(match); + handle_rsc_op(match, (void *) node); - handle_rsc_op(match, local_node); - free(local_node); + } else if (strcmp(name, PCMK__XE_LRM_RESOURCES) == 0) { + char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); - } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) { - char *local_node = pcmk__xpath_node_id(xpath, "lrm"); + handle_rsc_op(match, local_node); + free(local_node); - handle_rsc_op(match, local_node); - free(local_node); + } else if (strcmp(name, PCMK__XE_LRM_RESOURCE) == 0) { + char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); - } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) { - char *local_node = pcmk__xpath_node_id(xpath, "lrm"); + handle_rsc_op(match, local_node); + free(local_node); - handle_rsc_op(match, local_node); - free(local_node); + } else if (strcmp(name, PCMK__XE_LRM_RSC_OP) == 0) { + char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); - } else { - crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name); - } + handle_rsc_op(match, local_node); + free(local_node); + + } else { + crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name); } + + return pcmk_rc_ok; +} + +static void +crm_diff_update_v2(const char *event, xmlNode * msg) +{ + xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, + NULL, NULL); + xmlNode *diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + + pcmk__xe_foreach_child(diff, NULL, crm_diff_update_element_v2, NULL); } static void @@ -2004,8 +2037,9 @@ crm_diff_update_v1(const char *event, xmlNode * msg) { /* Process operation updates */ xmlXPathObject *xpathObj = xpath_search(msg, - "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED - "//" XML_LRM_TAG_RSC_OP); + "//" PCMK__XE_CIB_UPDATE_RESULT + "//" PCMK__XE_DIFF_ADDED + "//" PCMK__XE_LRM_RSC_OP); int lpc = 0, max = numXpathResults(xpathObj); for (lpc = 0; lpc < max; lpc++) { @@ -2022,7 +2056,9 @@ crm_diff_update(const char *event, xmlNode * msg) int rc = -1; static bool stale = FALSE; gboolean cib_updated = FALSE; - xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT); + xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, + NULL, NULL); + xmlNode *diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); out->progress(out, false); @@ -2218,6 +2254,10 @@ clean_up(crm_exit_t exit_code) /* Quitting crm_mon is much more complicated than it ought to be. */ /* (1) Close connections, free things, etc. */ + if (io_channel != NULL) { + g_io_channel_shutdown(io_channel, TRUE, NULL); + } + cib__clean_up_connection(&cib); stonith_api_delete(st); free(options.neg_location_prefix); diff --git a/tools/crm_mon.h b/tools/crm_mon.h index c87432d..2228add 100644 --- a/tools/crm_mon.h +++ b/tools/crm_mon.h @@ -71,8 +71,7 @@ void curses_formatted_vprintf(pcmk__output_t *out, const char *format, va_list a void curses_indented_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3); void curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0); -extern GOptionEntry crm_mon_curses_output_entries[]; -#define CRM_MON_SUPPORTED_FORMAT_CURSES { "console", crm_mon_mk_curses_output, crm_mon_curses_output_entries } +#define CRM_MON_SUPPORTED_FORMAT_CURSES { "console", crm_mon_mk_curses_output, NULL } #endif #endif diff --git a/tools/crm_mon_curses.c b/tools/crm_mon_curses.c index 212a400..4fc09ab 100644 --- a/tools/crm_mon_curses.c +++ b/tools/crm_mon_curses.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ #include <crm/common/output.h> #include <crm/common/cmdline_internal.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__history_description() #include <crm/pengine/internal.h> #include <glib.h> #include <pacemaker-internal.h> @@ -24,10 +24,6 @@ #if CURSES_ENABLED -GOptionEntry crm_mon_curses_output_entries[] = { - { NULL } -}; - typedef struct curses_list_data_s { unsigned int len; char *singular_noun; @@ -39,6 +35,14 @@ typedef struct private_data_s { } private_data_t; static void +free_list_data(gpointer data) { + curses_list_data_t *list_data = data; + + free(list_data->singular_noun); + free(list_data->plural_noun); +} + +static void curses_free_priv(pcmk__output_t *out) { private_data_t *priv = NULL; @@ -48,7 +52,7 @@ curses_free_priv(pcmk__output_t *out) { priv = out->priv; - g_queue_free(priv->parent_q); + g_queue_free_full(priv->parent_q, free_list_data); free(priv); out->priv = NULL; } @@ -201,10 +205,10 @@ curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *pl va_end(ap); } - new_list = calloc(1, sizeof(curses_list_data_t)); + new_list = pcmk__assert_alloc(1, sizeof(curses_list_data_t)); new_list->len = 0; - pcmk__str_update(&new_list->singular_noun, singular_noun); - pcmk__str_update(&new_list->plural_noun, plural_noun); + new_list->singular_noun = pcmk__str_copy(singular_noun); + new_list->plural_noun = pcmk__str_copy(plural_noun); g_queue_push_tail(priv->parent_q, new_list); } @@ -262,7 +266,7 @@ curses_end_list(pcmk__output_t *out) { } } - free(node); + free_list_data(node); } static bool @@ -310,7 +314,7 @@ curses_prompt(const char *prompt, bool do_echo, char **dest) free(*dest); } - *dest = calloc(1, 1024); + *dest = pcmk__assert_alloc(1, 1024); /* On older systems, scanw is defined as taking a char * for its first argument, * while newer systems rightly want a const char *. Accomodate both here due * to building with -Werror. diff --git a/tools/crm_node.c b/tools/crm_node.c index 1e7ce6c..3a8d2d1 100644 --- a/tools/crm_node.c +++ b/tools/crm_node.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,11 +19,11 @@ #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/common/mainloop.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/cib.h> #include <crm/cib/internal.h> #include <crm/common/ipc_controld.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/attrs_internal.h> #include <pacemaker-internal.h> @@ -159,17 +159,17 @@ node_id_xml(pcmk__output_t *out, va_list args) { char *id_s = crm_strdup_printf("%" PRIu32, node_id); - pcmk__output_create_xml_node(out, "node-info", - "nodeid", id_s, + pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO, + PCMK_XA_NODEID, id_s, NULL); free(id_s); return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("node-list", "GList *") +PCMK__OUTPUT_ARGS("simple-node-list", "GList *") static int -node_list_default(pcmk__output_t *out, va_list args) +simple_node_list_default(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); @@ -182,22 +182,22 @@ node_list_default(pcmk__output_t *out, va_list args) return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("node-list", "GList *") +PCMK__OUTPUT_ARGS("simple-node-list", "GList *") static int -node_list_xml(pcmk__output_t *out, va_list args) +simple_node_list_xml(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); - out->begin_list(out, NULL, NULL, "nodes"); + out->begin_list(out, NULL, NULL, PCMK_XE_NODES); for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { pcmk_controld_api_node_t *node = node_iter->data; char *id_s = crm_strdup_printf("%" PRIu32, node->id); - pcmk__output_create_xml_node(out, "node", - "id", id_s, - "name", node->uname, - "state", node->state, + pcmk__output_create_xml_node(out, PCMK_XE_NODE, + PCMK_XA_ID, id_s, + PCMK_XA_NAME, node->uname, + PCMK_XA_STATE, node->state, NULL); free(id_s); @@ -226,9 +226,9 @@ node_name_xml(pcmk__output_t *out, va_list args) { char *id_s = crm_strdup_printf("%" PRIu32, node_id); - pcmk__output_create_xml_node(out, "node-info", - "nodeid", id_s, - XML_ATTR_UNAME, node_name, + pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO, + PCMK_XA_NODEID, id_s, + PCMK_XA_UNAME, node_name, NULL); free(id_s); @@ -265,7 +265,7 @@ partition_list_xml(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); - out->begin_list(out, NULL, NULL, "nodes"); + out->begin_list(out, NULL, NULL, PCMK_XE_NODES); for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { pcmk_controld_api_node_t *node = node_iter->data; @@ -273,10 +273,10 @@ partition_list_xml(pcmk__output_t *out, va_list args) if (pcmk__str_eq(node->state, "member", pcmk__str_none)) { char *id_s = crm_strdup_printf("%" PRIu32, node->id); - pcmk__output_create_xml_node(out, "node", - "id", id_s, - "name", node->uname, - "state", node->state, + pcmk__output_create_xml_node(out, PCMK_XE_NODE, + PCMK_XA_ID, id_s, + PCMK_XA_NAME, node->uname, + PCMK_XA_STATE, node->state, NULL); free(id_s); } @@ -300,8 +300,8 @@ static int quorum_xml(pcmk__output_t *out, va_list args) { bool have_quorum = va_arg(args, int); - pcmk__output_create_xml_node(out, "cluster-info", - "quorum", have_quorum ? "true" : "false", + pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_INFO, + PCMK_XA_QUORUM, pcmk__btoa(have_quorum), NULL); return pcmk_rc_ok; } @@ -309,14 +309,14 @@ quorum_xml(pcmk__output_t *out, va_list args) { static pcmk__message_entry_t fmt_functions[] = { { "node-id", "default", node_id_default }, { "node-id", "xml", node_id_xml }, - { "node-list", "default", node_list_default }, - { "node-list", "xml", node_list_xml }, { "node-name", "default", node_name_default }, { "node-name", "xml", node_name_xml }, - { "quorum", "default", quorum_default }, - { "quorum", "xml", quorum_xml }, { "partition-list", "default", partition_list_default }, { "partition-list", "xml", partition_list_xml }, + { "quorum", "default", quorum_default }, + { "quorum", "xml", quorum_xml }, + { "simple-node-list", "default", simple_node_list_default }, + { "simple-node-list", "xml", simple_node_list_xml }, { NULL, NULL, NULL } }; @@ -374,7 +374,7 @@ controller_event_cb(pcmk_ipc_api_t *controld_api, if (options.command == 'p') { out->message(out, "partition-list", reply->data.nodes); } else if (options.command == 'l') { - out->message(out, "node-list", reply->data.nodes); + out->message(out, "simple-node-list", reply->data.nodes); } // Success @@ -465,10 +465,11 @@ print_node_name(uint32_t nodeid) if (nodeid == 0) { // Check environment first (i.e. when called by resource agent) - const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET); + const char *name = getenv("OCF_RESKEY_" CRM_META "_" + PCMK__META_ON_NODE); if (name != NULL) { - rc = out->message(out, "node-name", 0, name); + rc = out->message(out, "node-name", 0UL, name); goto done; } } @@ -485,7 +486,7 @@ print_node_name(uint32_t nodeid) return; } - rc = out->message(out, "node-name", 0, node_name); + rc = out->message(out, "node-name", 0UL, node_name); done: if (node_name != NULL) { @@ -543,17 +544,14 @@ static int remove_from_section(cib_t *cib, const char *element, const char *section, const char *node_name, long node_id) { - xmlNode *xml = NULL; int rc = pcmk_rc_ok; + xmlNode *xml = pcmk__xe_create(NULL, element); - xml = create_xml_node(NULL, element); - if (xml == NULL) { - return pcmk_rc_error; - } - crm_xml_add(xml, XML_ATTR_UNAME, node_name); + crm_xml_add(xml, PCMK_XA_UNAME, node_name); if (node_id > 0) { crm_xml_set_id(xml, "%ld", node_id); } + rc = cib->cmds->remove(cib, section, xml, cib_transaction); free_xml(xml); return (rc >= 0)? pcmk_rc_ok : pcmk_legacy2rc(rc); @@ -592,10 +590,10 @@ purge_node_from_cib(const char *node_name, long node_id) } // Remove from configuration and status - rc = remove_from_section(cib, XML_CIB_TAG_NODE, XML_CIB_TAG_NODES, - node_name, node_id); + rc = remove_from_section(cib, PCMK_XE_NODE, PCMK_XE_NODES, node_name, + node_id); if (rc == pcmk_rc_ok) { - rc = remove_from_section(cib, XML_CIB_TAG_STATE, XML_CIB_TAG_STATUS, + rc = remove_from_section(cib, PCMK__XE_NODE_STATE, PCMK_XE_STATUS, node_name, node_id); } @@ -693,7 +691,7 @@ purge_node_from_fencer(const char *node_name, long node_id) if (node_id > 0) { crm_xml_set_id(cmd, "%ld", node_id); } - crm_xml_add(cmd, XML_ATTR_UNAME, node_name); + crm_xml_add(cmd, PCMK_XA_UNAME, node_name); rc = crm_ipc_send(conn, cmd, 0, 0, NULL); if (rc >= 0) { @@ -806,11 +804,6 @@ main(int argc, char **argv) goto done; } - if (!pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname())) { - exit_code = CRM_EX_SOFTWARE; - goto done; - } - if (args->version) { out->version(out, false); goto done; diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 7c4a0a1..c592e86 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,6 +18,7 @@ #include <pacemaker-internal.h> #include <sys/param.h> +#include <stdint.h> // uint32_t #include <stdio.h> #include <sys/types.h> #include <unistd.h> @@ -53,14 +54,15 @@ enum rsc_command { cmd_list_all_ops, cmd_list_alternatives, cmd_list_instances, + cmd_list_options, cmd_list_providers, cmd_list_resources, cmd_list_standards, cmd_locate, cmd_metadata, cmd_move, - cmd_query_raw_xml, cmd_query_xml, + cmd_query_xml_raw, cmd_refresh, cmd_restart, cmd_set_param, @@ -72,18 +74,10 @@ enum rsc_command { struct { enum rsc_command rsc_cmd; // crm_resource command to perform - // Infrastructure that given command needs to work - gboolean require_cib; // Whether command requires CIB IPC - int cib_options; // Options to use with CIB IPC calls - gboolean require_crmd; // Whether command requires controller IPC - gboolean require_scheduler; // Whether command requires scheduler data - gboolean require_resource; // Whether command requires resource specified - gboolean require_node; // Whether command requires node specified - int find_flags; // Flags to use when searching for resource - // Command-line option values gchar *rsc_id; // Value of --resource gchar *rsc_type; // Value of --resource-type + gboolean all; // --all was given gboolean force; // --force was given gboolean clear_expired; // --expired was given gboolean recursive; // --recursive was given @@ -92,18 +86,19 @@ struct { gchar *interval_spec; // Value of --interval gchar *move_lifetime; // Value of --lifetime gchar *operation; // Value of --operation + enum pcmk__opt_flags opt_list; // Parsed from --list-options const char *attr_set_type; // Instance, meta, utilization, or element attribute gchar *prop_id; // --nvpair (attribute XML ID) char *prop_name; // Attribute name gchar *prop_set; // --set-name (attribute block XML ID) gchar *prop_value; // --parameter-value (attribute value) - int timeout_ms; // Parsed from --timeout value + guint timeout_ms; // Parsed from --timeout value char *agent_spec; // Standard and/or provider and/or agent gchar *xml_file; // Value of (deprecated) --xml-file int check_level; // Optional value of --validate or --force-check // Resource configuration specified via command-line arguments - gboolean cmdline_config; // Resource configuration was via arguments + bool cmdline_config; // Resource configuration was via arguments char *v_agent; // Value of --agent char *v_class; // Value of --class char *v_provider; // Value of --provider @@ -113,66 +108,17 @@ struct { gchar **remainder; // Positional arguments as given GHashTable *override_params; // Resource parameter values that override config } options = { - .attr_set_type = XML_TAG_ATTR_SETS, + .attr_set_type = PCMK_XE_INSTANCE_ATTRIBUTES, .check_level = -1, - .cib_options = cib_sync_call, - .require_cib = TRUE, - .require_scheduler = TRUE, - .require_resource = TRUE, + .rsc_cmd = cmd_list_resources, // List all resources if no command given }; -#if 0 -// @COMPAT @TODO enable this at next backward compatibility break -#define SET_COMMAND(cmd) do { \ - if (options.rsc_cmd != cmd_none) { \ - g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE, \ - "Only one command option may be specified"); \ - return FALSE; \ - } \ - options.rsc_cmd = (cmd); \ - } while (0) -#else -#define SET_COMMAND(cmd) do { \ - if (options.rsc_cmd != cmd_none) { \ - reset_options(); \ - } \ - options.rsc_cmd = (cmd); \ - } while (0) -#endif - -gboolean agent_provider_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); gboolean attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean cleanup_refresh_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean expired_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean list_agents_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean list_providers_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean list_standards_cb(const gchar *option_name, const gchar *optarg, +gboolean cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean list_alternatives_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean metadata_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 fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean flag_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean get_param_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean list_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean set_delete_param_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean set_prop_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); -gboolean validate_or_force_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean restart_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean digests_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error); -gboolean wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); -gboolean why_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; @@ -326,77 +272,266 @@ build_constraint_list(xmlNode *root) xmlXPathObjectPtr xpathObj = NULL; int ndx = 0; - cib_constraints = pcmk_find_cib_element(root, XML_CIB_TAG_CONSTRAINTS); - xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION); + cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS); + xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION); for (ndx = 0; ndx < numXpathResults(xpathObj); ndx++) { xmlNode *match = getXpathResult(xpathObj, ndx); - retval = g_list_insert_sorted(retval, (gpointer) ID(match), compare_id); + retval = g_list_insert_sorted(retval, (gpointer) pcmk__xe_id(match), + compare_id); } freeXpathObject(xpathObj); return retval; } +static gboolean +validate_opt_list(const gchar *optarg) +{ + if (pcmk__str_eq(optarg, PCMK_VALUE_FENCING, pcmk__str_none)) { + options.opt_list = pcmk__opt_fencing; + + } else if (pcmk__str_eq(optarg, PCMK__VALUE_PRIMITIVE, pcmk__str_none)) { + options.opt_list = pcmk__opt_primitive; + + } else { + return FALSE; + } + + return TRUE; +} + +/*! + * \internal + * \brief Process options that set the command + * + * Nothing else should set \c options.rsc_cmd. + * + * \param[in] option_name Name of the option being parsed + * \param[in] optarg Value to be parsed + * \param[in] data Ignored + * \param[out] error Where to store recoverable error, if any + * + * \return \c TRUE if the option was successfully parsed, or \c FALSE if an + * error occurred, in which case \p *error is set + */ +static gboolean +command_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + // Sorted by enum rsc_command name + if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) { + options.rsc_cmd = cmd_ban; + + } else if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) { + options.rsc_cmd = cmd_cleanup; + + } else if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) { + options.rsc_cmd = cmd_clear; + + } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) { + options.rsc_cmd = cmd_colocations; + + } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) { + options.rsc_cmd = cmd_colocations; + options.recursive = TRUE; + + } else if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) { + options.rsc_cmd = cmd_cts; + + } else if (pcmk__str_any_of(option_name, "-D", "--delete", NULL)) { + options.rsc_cmd = cmd_delete; + + } else if (pcmk__str_any_of(option_name, "-d", "--delete-parameter", + NULL)) { + options.rsc_cmd = cmd_delete_param; + pcmk__str_update(&options.prop_name, optarg); + + } else if (pcmk__str_eq(option_name, "--digests", pcmk__str_none)) { + options.rsc_cmd = cmd_digests; + + if (options.override_params == NULL) { + options.override_params = pcmk__strkey_table(free, free); + } + + } else if (pcmk__str_any_of(option_name, + "--force-demote", "--force-promote", + "--force-start", "--force-stop", + "--force-check", "--validate", NULL)) { + options.rsc_cmd = cmd_execute_agent; + + g_free(options.operation); + options.operation = g_strdup(option_name + 2); // skip "--" + + if (options.override_params == NULL) { + options.override_params = pcmk__strkey_table(free, free); + } + + if (optarg != NULL) { + if (pcmk__scan_min_int(optarg, &options.check_level, + 0) != pcmk_rc_ok) { + g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, + _("Invalid check level setting: %s"), optarg); + return FALSE; + } + } + + } else if (pcmk__str_any_of(option_name, "-F", "--fail", NULL)) { + options.rsc_cmd = cmd_fail; + + } else if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) { + options.rsc_cmd = cmd_get_param; + pcmk__str_update(&options.prop_name, optarg); + + } else if (pcmk__str_any_of(option_name, "-G", "--get-property", NULL)) { + options.rsc_cmd = cmd_get_property; + pcmk__str_update(&options.prop_name, optarg); + + } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) { + options.rsc_cmd = cmd_list_active_ops; + + } else if (pcmk__str_eq(option_name, "--list-agents", pcmk__str_none)) { + options.rsc_cmd = cmd_list_agents; + pcmk__str_update(&options.agent_spec, optarg); + + } else if (pcmk__str_any_of(option_name, "-o", "--list-all-operations", + NULL)) { + options.rsc_cmd = cmd_list_all_ops; + + } else if (pcmk__str_eq(option_name, "--list-ocf-alternatives", + pcmk__str_none)) { + options.rsc_cmd = cmd_list_alternatives; + pcmk__str_update(&options.agent_spec, optarg); + + } else if (pcmk__str_eq(option_name, "--list-options", pcmk__str_none)) { + options.rsc_cmd = cmd_list_options; + return validate_opt_list(optarg); + + } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) { + options.rsc_cmd = cmd_list_instances; + + } else if (pcmk__str_eq(option_name, "--list-ocf-providers", + pcmk__str_none)) { + options.rsc_cmd = cmd_list_providers; + pcmk__str_update(&options.agent_spec, optarg); + + } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) { + options.rsc_cmd = cmd_list_resources; + + } else if (pcmk__str_eq(option_name, "--list-standards", pcmk__str_none)) { + options.rsc_cmd = cmd_list_standards; + + } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) { + options.rsc_cmd = cmd_locate; + + } else if (pcmk__str_eq(option_name, "--show-metadata", pcmk__str_none)) { + options.rsc_cmd = cmd_metadata; + pcmk__str_update(&options.agent_spec, optarg); + + } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) { + options.rsc_cmd = cmd_move; + + } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) { + options.rsc_cmd = cmd_query_xml; + + } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) { + options.rsc_cmd = cmd_query_xml_raw; + + } else if (pcmk__str_any_of(option_name, "-R", "--refresh", NULL)) { + options.rsc_cmd = cmd_refresh; + + } else if (pcmk__str_eq(option_name, "--restart", pcmk__str_none)) { + options.rsc_cmd = cmd_restart; + + } else if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) { + options.rsc_cmd = cmd_set_param; + pcmk__str_update(&options.prop_name, optarg); + + } else if (pcmk__str_any_of(option_name, "-S", "--set-property", NULL)) { + options.rsc_cmd = cmd_set_property; + pcmk__str_update(&options.prop_name, optarg); + + } else if (pcmk__str_eq(option_name, "--wait", pcmk__str_none)) { + options.rsc_cmd = cmd_wait; + + } else if (pcmk__str_any_of(option_name, "-Y", "--why", NULL)) { + options.rsc_cmd = cmd_why; + } + + return TRUE; +} + /* short option letters still available: eEJkKXyYZ */ static GOptionEntry query_entries[] = { - { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb, + { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "List all cluster resources with status", NULL }, - { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb, + { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "List IDs of all instantiated resources (individual members\n" INDENT "rather than groups etc.)", NULL }, - { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb, + { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, command_cb, NULL, NULL }, - { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb, + { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + command_cb, "List active resource operations, optionally filtered by\n" INDENT "--resource and/or --node", NULL }, - { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb, + { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + command_cb, "List all resource operations, optionally filtered by\n" INDENT "--resource and/or --node", NULL }, + { "list-options", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb, + "List all available options of the given type\n" + INDENT "Allowed values:\n" + INDENT PCMK__VALUE_PRIMITIVE "(primitive resource meta-attributes), " + INDENT PCMK_VALUE_FENCING " (parameters common to all fencing resources)", + "TYPE" }, { "list-standards", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - list_standards_cb, + command_cb, "List supported standards", NULL }, { "list-ocf-providers", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - list_providers_cb, + command_cb, "List all available OCF providers", NULL }, { "list-agents", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, - list_agents_cb, + command_cb, "List all agents available for the named standard and/or provider", "STD:PROV" }, { "list-ocf-alternatives", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, - list_alternatives_cb, + command_cb, "List all available providers for the named OCF agent", "AGENT" }, - { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, - metadata_cb, + { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb, "Show the metadata for the named class:provider:agent", "SPEC" }, - { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Show XML configuration of resource (after any template expansion)", NULL }, - { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + command_cb, "Show XML configuration of resource (before any template expansion)", NULL }, - { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, get_param_prop_cb, + { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, + command_cb, "Display named parameter for resource (use instance attribute\n" INDENT "unless --element, --meta, or --utilization is specified)", "PARAM" }, - { "get-property", 'G', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, get_param_prop_cb, + { "get-property", 'G', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, + command_cb, "Display named property of resource ('class', 'type', or 'provider') " "(requires --resource)", "PROPERTY" }, - { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Show node(s) currently running resource", NULL }, - { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + command_cb, "Display the location and colocation constraints that apply to a\n" INDENT "resource, and if --recursive is specified, to the resources\n" INDENT "directly or indirectly involved in those colocations.\n" @@ -404,10 +539,10 @@ static GOptionEntry query_entries[] = { INDENT "bundle instance, constraints for the collective resource\n" INDENT "will be shown unless --force is given.", NULL }, - { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Equivalent to --constraints --recursive", NULL }, - { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, why_cb, + { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Show why resources are not running, optionally filtered by\n" INDENT "--resource and/or --node", NULL }, @@ -417,7 +552,7 @@ static GOptionEntry query_entries[] = { static GOptionEntry command_entries[] = { { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + command_cb, "Validate resource configuration by calling agent's validate-all\n" INDENT "action. The configuration may be specified either by giving an\n" INDENT "existing resource name with -r, or by specifying --class,\n" @@ -425,7 +560,7 @@ static GOptionEntry command_entries[] = { INDENT "--option arguments. An optional LEVEL argument can be given\n" INDENT "to control the level of checking performed.", "LEVEL" }, - { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb, + { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "If resource has any past failures, clear its history and fail\n" INDENT "count. Optionally filtered by --resource, --node, --operation\n" INDENT "and --interval (otherwise all). --operation and --interval\n" @@ -435,23 +570,26 @@ static GOptionEntry command_entries[] = { INDENT "resource, the clean-up applies to the whole collective resource\n" INDENT "unless --force is given.", NULL }, - { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb, + { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Delete resource's history (including failures) so its current state\n" INDENT "is rechecked. Optionally filtered by --resource and --node\n" INDENT "(otherwise all). If the named resource is part of a group, or one\n" INDENT "numbered instance of a clone or bundled resource, the refresh\n" INDENT "applies to the whole collective resource unless --force is given.", NULL }, - { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, set_delete_param_cb, + { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, + command_cb, "Set named parameter for resource (requires -v). Use instance\n" INDENT "attribute unless --element, --meta, or --utilization is " "specified.", "PARAM" }, - { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, set_delete_param_cb, + { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, + command_cb, "Delete named parameter for resource. Use instance attribute\n" INDENT "unless --element, --meta or, --utilization is specified.", "PARAM" }, - { "set-property", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, set_prop_cb, + { "set-property", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, + command_cb, "Set named property of resource ('class', 'type', or 'provider') " "(requires -r, -t, -v)", "PROPERTY" }, @@ -460,7 +598,7 @@ static GOptionEntry command_entries[] = { }; static GOptionEntry location_entries[] = { - { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Create a constraint to move resource. If --node is specified,\n" INDENT "the constraint will be to move to that node, otherwise it\n" INDENT "will be to ban the current node. Unless --force is specified\n" @@ -471,7 +609,7 @@ static GOptionEntry location_entries[] = { INDENT "resource from running on its previous location until the\n" INDENT "implicit constraint expires or is removed with --clear.", NULL }, - { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Create a constraint to keep resource off a node.\n" INDENT "Optional: --node, --lifetime, --promoted.\n" INDENT "NOTE: This will prevent the resource from running on the\n" @@ -479,10 +617,10 @@ static GOptionEntry location_entries[] = { INDENT "removed with --clear. If --node is not specified, it defaults\n" INDENT "to the node currently running the resource for primitives\n" INDENT "and groups, or the promoted instance of promotable clones with\n" - INDENT "promoted-max=1 (all other situations result in an error as\n" - INDENT "there is no sane default).", + INDENT PCMK_META_PROMOTED_MAX "=1 (all other situations result in an\n" + INDENT "error as there is no sane default).", NULL }, - { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb, + { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Remove all constraints created by the --ban and/or --move\n" INDENT "commands. Requires: --resource. Optional: --node, --promoted,\n" INDENT "--expired. If --node is not specified, all constraints created\n" @@ -492,7 +630,8 @@ static GOptionEntry location_entries[] = { INDENT "node. If --expired is specified, only those constraints whose\n" INDENT "lifetimes have expired will be removed.", NULL }, - { "expired", 'e', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, expired_cb, + { "expired", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, + &options.clear_expired, "Modifies the --clear argument to remove constraints with\n" INDENT "expired lifetimes.", NULL }, @@ -516,20 +655,20 @@ static GOptionEntry location_entries[] = { }; static GOptionEntry advanced_entries[] = { - { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb, + { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Delete a resource from the CIB. Required: -t", NULL }, - { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, fail_cb, + { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Tell the cluster this resource has failed", NULL }, - { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, restart_cb, + { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Tell the cluster to restart this resource and\n" INDENT "anything that depends on it", NULL }, - { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, wait_cb, + { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Wait until the cluster settles into a stable state", NULL }, - { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, digests_cb, + { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Show parameter hashes that Pacemaker uses to detect\n" INDENT "configuration changes (only accurate if there is resource\n" INDENT "history on the specified node). Required: --resource, --node.\n" @@ -538,32 +677,30 @@ static GOptionEntry advanced_entries[] = { INDENT "changes).", NULL }, { "force-demote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + command_cb, "(Advanced) Bypass the cluster and demote a resource on the local\n" INDENT "node. Unless --force is specified, this will refuse to do so if\n" INDENT "the cluster believes the resource is a clone instance already\n" INDENT "running on the local node.", NULL }, - { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Bypass the cluster and stop a resource on the local node", NULL }, - { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Bypass the cluster and start a resource on the local\n" INDENT "node. Unless --force is specified, this will refuse to do so if\n" INDENT "the cluster believes the resource is a clone instance already\n" INDENT "running on the local node.", NULL }, { "force-promote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + command_cb, "(Advanced) Bypass the cluster and promote a resource on the local\n" INDENT "node. Unless --force is specified, this will refuse to do so if\n" INDENT "the cluster believes the resource is a clone instance already\n" INDENT "running on the local node.", NULL }, { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, - validate_or_force_cb, + command_cb, "(Advanced) Bypass the cluster and check the state of a resource on\n" INDENT "the local node. An optional LEVEL argument can be given\n" INDENT "to control the level of checking performed.", @@ -603,15 +740,16 @@ static GOptionEntry addl_entries[] = { { "interval", 'I', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.interval_spec, "Interval of operation to clear (default 0) (with -C -r -n)", "N" }, - { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, class_cb, + { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb, "The standard the resource agent conforms to (for example, ocf).\n" INDENT "Use with --agent, --provider, --option, and --validate.", "CLASS" }, - { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb, + { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb, "The agent to use (for example, IPaddr). Use with --class,\n" INDENT "--provider, --option, and --validate.", "AGENT" }, - { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb, + { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, + cmdline_config_cb, "The vendor that supplies the resource agent (for example,\n" INDENT "heartbeat). Use with --class, --agent, --option, and --validate.", "PROVIDER" }, @@ -630,6 +768,10 @@ static GOptionEntry addl_entries[] = { "(Advanced) Abort if command does not finish in this time (with\n" INDENT "--restart, --wait, --force-*)", "N" }, + { "all", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all, + "List all options, including advanced and deprecated (with\n" + INDENT "--list-options)", + NULL }, { "force", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.force, "Force the action to be performed. See help for individual commands for\n" INDENT "additional behavior.", @@ -644,136 +786,33 @@ static GOptionEntry addl_entries[] = { { NULL } }; -static void -reset_options(void) { - options.require_crmd = FALSE; - options.require_node = FALSE; - - options.require_cib = TRUE; - options.require_scheduler = TRUE; - options.require_resource = TRUE; - - options.find_flags = 0; -} - -gboolean -agent_provider_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.cmdline_config = TRUE; - options.require_resource = FALSE; - - if (pcmk__str_eq(option_name, "--provider", pcmk__str_casei)) { - pcmk__str_update(&options.v_provider, optarg); - } else { - pcmk__str_update(&options.v_agent, optarg); - } - - return TRUE; -} - 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 = XML_TAG_META_SETS; + options.attr_set_type = PCMK_XE_META_ATTRIBUTES; } else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) { - options.attr_set_type = XML_TAG_UTILIZATION; - } else if (pcmk__str_eq(option_name, "--element", pcmk__str_casei)) { + 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 -class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - pcmk__str_update(&options.v_class, optarg); - options.cmdline_config = TRUE; - options.require_resource = FALSE; - return TRUE; -} - -gboolean -cleanup_refresh_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) { - SET_COMMAND(cmd_cleanup); - } else { - SET_COMMAND(cmd_refresh); - } - - options.require_resource = FALSE; - if (getenv("CIB_file") == NULL) { - options.require_crmd = TRUE; - } - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; - return TRUE; -} - -gboolean -delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_delete); - options.require_scheduler = FALSE; - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - return TRUE; -} - -gboolean -expired_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.clear_expired = TRUE; - options.require_resource = FALSE; - return TRUE; -} - -static void -get_agent_spec(const gchar *optarg) -{ - options.require_cib = FALSE; - options.require_scheduler = FALSE; - options.require_resource = FALSE; - pcmk__str_update(&options.agent_spec, optarg); -} - -gboolean -list_agents_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - SET_COMMAND(cmd_list_agents); - get_agent_spec(optarg); - return TRUE; -} - -gboolean -list_providers_cb(const gchar *option_name, const gchar *optarg, gpointer data, +cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_list_providers); - get_agent_spec(optarg); - return TRUE; -} + options.cmdline_config = true; -gboolean -list_standards_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - SET_COMMAND(cmd_list_standards); - options.require_cib = FALSE; - options.require_scheduler = FALSE; - options.require_resource = FALSE; - return TRUE; -} + if (pcmk__str_eq(option_name, "--class", pcmk__str_none)) { + pcmk__str_update(&options.v_class, optarg); -gboolean -list_alternatives_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error) -{ - SET_COMMAND(cmd_list_alternatives); - get_agent_spec(optarg); - return TRUE; -} + } else if (pcmk__str_eq(option_name, "--provider", pcmk__str_none)) { + pcmk__str_update(&options.v_provider, optarg); -gboolean -metadata_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - SET_COMMAND(cmd_metadata); - get_agent_spec(optarg); + } else { // --agent + pcmk__str_update(&options.v_agent, optarg); + } return TRUE; } @@ -795,173 +834,16 @@ option_cb(const gchar *option_name, const gchar *optarg, gpointer data, } gboolean -fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_fail); - options.require_crmd = TRUE; - options.require_node = TRUE; - return TRUE; -} - -gboolean -flag_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) { - SET_COMMAND(cmd_clear); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - } else if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) { - SET_COMMAND(cmd_ban); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) { - SET_COMMAND(cmd_move); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) { - SET_COMMAND(cmd_query_xml); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) { - SET_COMMAND(cmd_query_raw_xml); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) { - SET_COMMAND(cmd_locate); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - - } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) { - SET_COMMAND(cmd_colocations); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - - } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) { - SET_COMMAND(cmd_colocations); - options.find_flags = pcmk_rsc_match_history - |pcmk_rsc_match_anon_basename; - options.recursive = TRUE; - } - - return TRUE; -} - -gboolean -get_param_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) { - SET_COMMAND(cmd_get_param); - } else { - SET_COMMAND(cmd_get_property); - } - - pcmk__str_update(&options.prop_name, optarg); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - return TRUE; -} - -gboolean -list_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) { - SET_COMMAND(cmd_cts); - } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) { - SET_COMMAND(cmd_list_resources); - } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) { - SET_COMMAND(cmd_list_instances); - } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) { - SET_COMMAND(cmd_list_active_ops); - } else { - SET_COMMAND(cmd_list_all_ops); - } - - options.require_resource = FALSE; - return TRUE; -} - -gboolean -set_delete_param_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) { - SET_COMMAND(cmd_set_param); - } else { - SET_COMMAND(cmd_delete_param); - } - - pcmk__str_update(&options.prop_name, optarg); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - return TRUE; -} - -gboolean -set_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_set_property); - options.require_scheduler = FALSE; - pcmk__str_update(&options.prop_name, optarg); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename; - return TRUE; -} - -gboolean timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.timeout_ms = crm_get_msec(optarg); - return TRUE; -} - -gboolean -validate_or_force_cb(const gchar *option_name, const gchar *optarg, - gpointer data, GError **error) -{ - SET_COMMAND(cmd_execute_agent); - if (options.operation) { - g_free(options.operation); - } - options.operation = g_strdup(option_name + 2); // skip "--" - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; - if (options.override_params == NULL) { - options.override_params = pcmk__strkey_table(free, free); - } - - if (optarg != NULL) { - if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) { - g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, - _("Invalid check level setting: %s"), optarg); - return FALSE; - } - } - - return TRUE; -} + long long timeout_ms = crm_get_msec(optarg); -gboolean -restart_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - SET_COMMAND(cmd_restart); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; - return TRUE; -} - -gboolean -digests_cb(const gchar *option_name, const gchar *optarg, gpointer data, - GError **error) -{ - SET_COMMAND(cmd_digests); - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; - if (options.override_params == NULL) { - options.override_params = pcmk__strkey_table(free, free); + if (timeout_ms < 0) { + // @COMPAT When we can break backward compatibilty, return FALSE + crm_warn("Ignoring invalid timeout '%s'", optarg); + options.timeout_ms = 0U; + } else { + options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX); } - options.require_node = TRUE; - options.require_scheduler = TRUE; - return TRUE; -} - -gboolean -wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_wait); - options.require_resource = FALSE; - options.require_scheduler = FALSE; - return TRUE; -} - -gboolean -why_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - SET_COMMAND(cmd_why); - options.require_resource = FALSE; - options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; return TRUE; } @@ -979,8 +861,8 @@ ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc, if (nactive == 1) { rc = cli_resource_ban(out, options.rsc_id, current->details->uname, move_lifetime, - cib_conn, options.cib_options, options.promoted_role_only, - PCMK__ROLE_PROMOTED); + cib_conn, cib_sync_call, + options.promoted_role_only, PCMK_ROLE_PROMOTED); } else if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { int count = 0; @@ -993,14 +875,15 @@ ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc, if (child_role == pcmk_role_promoted) { count++; - current = pe__current_node(child); + current = pcmk__current_node(child); } } if(count == 1 && current) { rc = cli_resource_ban(out, options.rsc_id, current->details->uname, move_lifetime, - cib_conn, options.cib_options, options.promoted_role_only, - PCMK__ROLE_PROMOTED); + cib_conn, cib_sync_call, + options.promoted_role_only, + PCMK_ROLE_PROMOTED); } else { rc = EINVAL; @@ -1066,12 +949,12 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy) if (options.clear_expired) { rc = cli_resource_clear_all_expired(scheduler->input, cib_conn, - options.cib_options, options.rsc_id, + cib_sync_call, options.rsc_id, options.host_uname, options.promoted_role_only); } else if (options.host_uname) { - dest = pe_find_node(scheduler->nodes, options.host_uname); + dest = pcmk_find_node(scheduler, options.host_uname); if (dest == NULL) { rc = pcmk_rc_node_unknown; if (!out->is_quiet(out)) { @@ -1080,11 +963,11 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy) return rc; } rc = cli_resource_clear(options.rsc_id, dest->details->uname, NULL, - cib_conn, options.cib_options, TRUE, options.force); + cib_conn, cib_sync_call, true, options.force); } else { rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes, - cib_conn, options.cib_options, TRUE, options.force); + cib_conn, cib_sync_call, true, options.force); } if (!out->is_quiet(out)) { @@ -1119,35 +1002,12 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy) } static int -delete(void) -{ - int rc = pcmk_rc_ok; - xmlNode *msg_data = NULL; - - if (options.rsc_type == NULL) { - rc = ENXIO; - g_set_error(&error, PCMK__RC_ERROR, rc, - _("You need to specify a resource type with -t")); - return rc; - } - - msg_data = create_xml_node(NULL, options.rsc_type); - crm_xml_add(msg_data, XML_ATTR_ID, options.rsc_id); - - rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_RESOURCES, msg_data, - options.cib_options); - rc = pcmk_legacy2rc(rc); - free_xml(msg_data); - return rc; -} - -static int initialize_scheduler_data(xmlNodePtr *cib_xml_copy) { int rc = pcmk_rc_ok; if (options.xml_file != NULL) { - *cib_xml_copy = filename2xml(options.xml_file); + *cib_xml_copy = pcmk__xml_read(options.xml_file); if (*cib_xml_copy == NULL) { rc = pcmk_rc_cib_corrupt; } @@ -1161,7 +1021,7 @@ initialize_scheduler_data(xmlNodePtr *cib_xml_copy) if (scheduler == NULL) { rc = ENOMEM; } else { - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_counts |pcmk_sched_no_compat); scheduler->priv = out; @@ -1179,6 +1039,26 @@ initialize_scheduler_data(xmlNodePtr *cib_xml_copy) 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) { @@ -1187,10 +1067,10 @@ refresh(pcmk__output_t *out) int attr_options = pcmk__node_attr_none; if (options.host_uname) { - pcmk_node_t *node = pe_find_node(scheduler->nodes, options.host_uname); + pcmk_node_t *node = pcmk_find_node(scheduler, options.host_uname); - if (pe__is_guest_or_remote_node(node)) { - node = pe__current_node(node->details->remote_rsc); + if (pcmk__is_pacemaker_remote_node(node)) { + node = pcmk__current_node(node->details->remote_rsc); if (node == NULL) { rc = ENXIO; g_set_error(&error, PCMK__RC_ERROR, rc, @@ -1268,12 +1148,12 @@ set_property(void) CRM_LOG_ASSERT(options.prop_name != NULL); - msg_data = create_xml_node(NULL, options.rsc_type); - crm_xml_add(msg_data, XML_ATTR_ID, options.rsc_id); + msg_data = pcmk__xe_create(NULL, options.rsc_type); + crm_xml_add(msg_data, PCMK_XA_ID, options.rsc_id); crm_xml_add(msg_data, options.prop_name, options.prop_value); - rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_RESOURCES, msg_data, - options.cib_options); + rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_RESOURCES, msg_data, + cib_sync_call); rc = pcmk_legacy2rc(rc); free_xml(msg_data); @@ -1308,7 +1188,7 @@ show_metadata(pcmk__output_t *out, const char *agent_spec) rc = pcmk_legacy2rc(rc); if (metadata) { - out->output_xml(out, "metadata", metadata); + out->output_xml(out, PCMK_XE_METADATA, metadata); free(metadata); } else { /* We were given a validly formatted spec, but it doesn't necessarily @@ -1366,16 +1246,209 @@ validate_cmdline_config(void) options.v_agent ? options.v_agent : ""); } - if (error != NULL) { - return; + if ((error == NULL) && (options.cmdline_params == NULL)) { + options.cmdline_params = pcmk__strkey_table(free, free); } +} - if (options.cmdline_params == NULL) { - options.cmdline_params = pcmk__strkey_table(free, free); +/*! + * \internal + * \brief Get the <tt>enum pe_find</tt> flags for a given command + * + * \return <tt>enum pe_find</tt> flag group appropriate for \c options.rsc_cmd. + */ +static uint32_t +get_find_flags(void) +{ + switch (options.rsc_cmd) { + case cmd_ban: + case cmd_cleanup: + case cmd_clear: + case cmd_colocations: + case cmd_digests: + case cmd_execute_agent: + case cmd_locate: + case cmd_move: + case cmd_refresh: + case cmd_restart: + case cmd_why: + return pcmk_rsc_match_history|pcmk_rsc_match_anon_basename; + + // @COMPAT See note in is_scheduler_required() + case cmd_delete: + case cmd_delete_param: + case cmd_get_param: + case cmd_get_property: + case cmd_query_xml_raw: + case cmd_query_xml: + case cmd_set_param: + case cmd_set_property: + return pcmk_rsc_match_history|pcmk_rsc_match_basename; + + default: + return 0; + } +} + +/*! + * \internal + * \brief Check whether a node argument is required + * + * \return \c true if a \c --node argument is required, or \c false otherwise + */ +static bool +is_node_required(void) +{ + switch (options.rsc_cmd) { + case cmd_digests: + case cmd_fail: + return true; + default: + return false; + } +} + +/*! + * \internal + * \brief Check whether a resource argument is required + * + * \return \c true if a \c --resource argument is required, or \c false + * otherwise + */ +static bool +is_resource_required(void) +{ + if (options.cmdline_config) { + return false; + } + + switch (options.rsc_cmd) { + case cmd_clear: + return !options.clear_expired; + + case cmd_cleanup: + case cmd_cts: + case cmd_list_active_ops: + case cmd_list_agents: + case cmd_list_all_ops: + case cmd_list_alternatives: + case cmd_list_instances: + case cmd_list_options: + case cmd_list_providers: + case cmd_list_resources: + case cmd_list_standards: + case cmd_metadata: + case cmd_refresh: + case cmd_wait: + case cmd_why: + 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) { + return false; + } + + switch (options.rsc_cmd) { + case cmd_list_agents: + case cmd_list_alternatives: + case cmd_list_options: + case cmd_list_providers: + case cmd_list_standards: + case cmd_metadata: + return false; + default: + return true; + } +} + +/*! + * \internal + * \brief Check whether a controller IPC connection is required + * + * \return \c true if a controller connection is required, or \c false otherwise + */ +static bool +is_controller_required(void) +{ + switch (options.rsc_cmd) { + case cmd_cleanup: + case cmd_refresh: + return getenv("CIB_file") == NULL; + + case cmd_fail: + return true; + + default: + return false; + } +} + +/*! + * \internal + * \brief Check whether a scheduler IPC connection is required + * + * \return \c true if a scheduler connection is required, or \c false otherwise + */ +static bool +is_scheduler_required(void) +{ + if (options.cmdline_config) { + return false; + } + + /* @COMPAT cmd_delete does not actually need the scheduler and should not + * set find_flags. However, crm_resource --delete currently throws a + * "resource not found" error if the resource doesn't exist. This is + * incorrect behavior (deleting a nonexistent resource should be considered + * success); however, we shouldn't change it until 3.0.0. + */ + switch (options.rsc_cmd) { + 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 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) +{ + // @COMPAT At 3.0.0, add cmd_delete; for now, don't throw error + switch (options.rsc_cmd) { + case cmd_ban: + case cmd_clear: + case cmd_move: + case cmd_restart: + return false; + default: + return true; } - options.require_resource = FALSE; - options.require_scheduler = FALSE; - options.require_cib = FALSE; } static GOptionContext * @@ -1407,14 +1480,14 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { "location:\n\n" "\t# crm_resource --resource myResource --clear\n\n" "Stop 'myResource' (and anything that depends on it):\n\n" - "\t# crm_resource --resource myResource --set-parameter target-role " - "--meta --parameter-value Stopped\n\n" + "\t# crm_resource --resource myResource --set-parameter " + PCMK_META_TARGET_ROLE "--meta --parameter-value Stopped\n\n" "Tell the cluster not to manage 'myResource' (the cluster will not " "attempt to start or stop the\n" "resource under any circumstances; useful when performing maintenance " "tasks on a resource):\n\n" - "\t# crm_resource --resource myResource --set-parameter is-managed " - "--meta --parameter-value false\n\n" + "\t# crm_resource --resource myResource --set-parameter " + PCMK_META_IS_MANAGED "--meta --parameter-value false\n\n" "Erase the operation history of 'myResource' on 'aNode' (the cluster " "will 'forget' the existing\n" "resource state, including any errors, and attempt to recover the" @@ -1449,6 +1522,7 @@ main(int argc, char **argv) xmlNode *cib_xml_copy = NULL; pcmk_resource_t *rsc = NULL; pcmk_node_t *node = NULL; + uint32_t find_flags = 0; int rc = pcmk_rc_ok; GOptionGroup *output_group = NULL; @@ -1492,12 +1566,6 @@ main(int argc, char **argv) * Validate option combinations */ - // If the user didn't explicitly specify a command, list resources - if (options.rsc_cmd == cmd_none) { - options.rsc_cmd = cmd_list_resources; - options.require_resource = FALSE; - } - // --expired without --clear/-U doesn't make sense if (options.clear_expired && (options.rsc_cmd != cmd_clear)) { exit_code = CRM_EX_USAGE; @@ -1508,8 +1576,8 @@ main(int argc, char **argv) if ((options.remainder != NULL) && (options.override_params != NULL)) { // Commands that use positional arguments will create override_params for (gchar **s = options.remainder; *s; s++) { - char *name = calloc(1, strlen(*s)); - char *value = calloc(1, strlen(*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 (rc == 2) { @@ -1541,7 +1609,7 @@ main(int argc, char **argv) /* 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 = calloc(len+2, sizeof(char *)); + strv = pcmk__assert_alloc(len+2, sizeof(char *)); strv[0] = strdup("non-option ARGV-elements:\n"); for (gchar **s = options.remainder; *s; s++) { @@ -1566,28 +1634,30 @@ main(int argc, char **argv) } if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { - /* Kind of a hack to display XML lists using a real tag instead of <list>. This just - * saves from having to write custom messages to build the lists around all these things - */ switch (options.rsc_cmd) { - case cmd_execute_agent: - case cmd_list_resources: - case cmd_query_xml: - case cmd_query_raw_xml: - case cmd_list_active_ops: - case cmd_list_all_ops: - case cmd_colocations: - pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname()); + /* These are the only commands that have historically used the <list> + * elements in their XML schema. For all others, use the simple list + * argument. + */ + case cmd_get_param: + case cmd_get_property: + case cmd_list_instances: + case cmd_list_standards: + pcmk__output_enable_list_element(out); break; default: - pcmk__force_args(context, &error, "%s --xml-substitute", g_get_prgname()); break; } + } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) { - if ((options.rsc_cmd == cmd_colocations) || - options.rsc_cmd == cmd_list_resources) { - pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname()); + switch (options.rsc_cmd) { + case cmd_colocations: + case cmd_list_resources: + pcmk__output_text_set_fancy(out, true); + break; + default: + break; } } @@ -1612,13 +1682,13 @@ main(int argc, char **argv) options.cmdline_params = NULL; } - if (options.require_resource && (options.rsc_id == NULL)) { + if (is_resource_required() && (options.rsc_id == NULL)) { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("Must supply a resource id with -r")); goto done; } - if (options.require_node && (options.host_uname == NULL)) { + if (is_node_required() && (options.host_uname == NULL)) { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("Must supply a node name with -N")); @@ -1629,12 +1699,8 @@ main(int argc, char **argv) * Set up necessary connections */ - if (options.find_flags && options.rsc_id) { - options.require_scheduler = TRUE; - } - // Establish a connection to the CIB if needed - if (options.require_cib) { + if (is_cib_required()) { cib_conn = cib_new(); if ((cib_conn == NULL) || (cib_conn->cmds == NULL)) { exit_code = CRM_EX_DISCONNECT; @@ -1653,7 +1719,7 @@ main(int argc, char **argv) } // Populate scheduler data from XML file if specified or CIB query otherwise - if (options.require_scheduler) { + if (is_scheduler_required()) { rc = initialize_scheduler_data(&cib_xml_copy); if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); @@ -1661,10 +1727,12 @@ main(int argc, char **argv) } } + find_flags = get_find_flags(); + // If command requires that resource exist if specified, find it - if (options.find_flags && options.rsc_id) { + if ((find_flags != 0) && (options.rsc_id != NULL)) { rsc = pe_find_resource_with_flags(scheduler->resources, options.rsc_id, - options.find_flags); + find_flags); if (rsc == NULL) { exit_code = CRM_EX_NOSUCH; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, @@ -1675,9 +1743,9 @@ main(int argc, char **argv) /* The --ban, --clear, --move, and --restart commands do not work with * instances of clone resourcs. */ - if (strchr(options.rsc_id, ':') != NULL && pe_rsc_is_clone(rsc->parent) && - (options.rsc_cmd == cmd_ban || options.rsc_cmd == cmd_clear || - options.rsc_cmd == cmd_move || options.rsc_cmd == cmd_restart)) { + if (pcmk__is_clone(rsc->parent) && (strchr(options.rsc_id, ':') != NULL) + && !accept_clone_instance()) { + exit_code = CRM_EX_INVALID_PARAM; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("Cannot operate on clone resource instance '%s'"), options.rsc_id); @@ -1687,7 +1755,7 @@ main(int argc, char **argv) // If user supplied a node name, check whether it exists if ((options.host_uname != NULL) && (scheduler != NULL)) { - node = pe_find_node(scheduler->nodes, options.host_uname); + node = pcmk_find_node(scheduler, options.host_uname); if (node == NULL) { exit_code = CRM_EX_NOSUCH; @@ -1698,7 +1766,7 @@ main(int argc, char **argv) } // Establish a connection to the controller if needed - if (options.require_crmd) { + if (is_controller_required()) { rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); @@ -1725,10 +1793,11 @@ main(int argc, char **argv) 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, - pcmk_show_inactive_rscs | pcmk_show_rsc_only | pcmk_show_pending, - true, all, all, false); + show_opts, true, all, all, false); g_list_free(all); if (rc == pcmk_rc_no_output) { @@ -1746,6 +1815,10 @@ main(int argc, char **argv) break; + case cmd_list_options: + list_options(); + break; + case cmd_list_alternatives: rc = pcmk__list_alternatives(out, options.agent_spec); break; @@ -1774,7 +1847,7 @@ main(int argc, char **argv) */ rc = cli_resource_restart(out, rsc, node, options.move_lifetime, options.timeout_ms, cib_conn, - options.cib_options, options.promoted_role_only, + cib_sync_call, options.promoted_role_only, options.force); break; @@ -1798,7 +1871,7 @@ main(int argc, char **argv) goto done; case cmd_digests: - node = pe_find_node(scheduler->nodes, options.host_uname); + node = pcmk_find_node(scheduler, options.host_uname); if (node == NULL) { rc = pcmk_rc_node_unknown; } else { @@ -1850,7 +1923,7 @@ main(int argc, char **argv) rc = cli_resource_print(rsc, scheduler, true); break; - case cmd_query_raw_xml: + case cmd_query_xml_raw: rc = cli_resource_print(rsc, scheduler, false); break; @@ -1873,7 +1946,7 @@ main(int argc, char **argv) } else { rc = cli_resource_move(rsc, options.rsc_id, options.host_uname, options.move_lifetime, cib_conn, - options.cib_options, scheduler, + cib_sync_call, scheduler, options.promoted_role_only, options.force); } @@ -1893,9 +1966,8 @@ main(int argc, char **argv) } else { rc = cli_resource_ban(out, options.rsc_id, node->details->uname, options.move_lifetime, cib_conn, - options.cib_options, - options.promoted_role_only, - PCMK__ROLE_PROMOTED); + cib_sync_call, options.promoted_role_only, + PCMK_ROLE_PROMOTED); } if (rc == EINVAL) { @@ -1933,15 +2005,17 @@ main(int argc, char **argv) crm_debug("Looking up %s in %s", options.prop_name, rsc->id); - if (pcmk__str_eq(options.attr_set_type, XML_TAG_ATTR_SETS, pcmk__str_none)) { + 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, XML_TAG_META_SETS, pcmk__str_none)) { + } 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, current, scheduler); + get_meta_attributes(params, rsc, NULL, scheduler); value = g_hash_table_lookup(params, options.prop_name); @@ -1951,9 +2025,14 @@ main(int argc, char **argv) free_params = false; } else { + pe_rule_eval_data_t rule_data = { + .now = scheduler->now, + }; + params = pcmk__strkey_table(free, free); - pe__unpack_dataset_nvpairs(rsc->xml, XML_TAG_UTILIZATION, NULL, params, - NULL, FALSE, scheduler); + pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_UTILIZATION, + &rule_data, params, NULL, FALSE, + scheduler); value = g_hash_table_lookup(params, options.prop_name); } @@ -1982,7 +2061,6 @@ main(int argc, char **argv) options.prop_name, options.prop_value, options.recursive, cib_conn, - options.cib_options, options.force); break; @@ -1993,8 +2071,7 @@ main(int argc, char **argv) options.attr_set_type, options.prop_id, options.prop_name, cib_conn, - options.cib_options, - options.force); + cib_sync_call, options.force); break; case cmd_cleanup: @@ -2019,7 +2096,25 @@ main(int argc, char **argv) break; case cmd_delete: - rc = delete(); + /* rsc_id was already checked for NULL much earlier when validating + * command line arguments. + */ + if (options.rsc_type == NULL) { + // @COMPAT @TODO change this to exit_code = CRM_EX_USAGE + rc = ENXIO; + g_set_error(&error, PCMK__RC_ERROR, rc, + _("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)); + } + } + break; default: diff --git a/tools/crm_resource.h b/tools/crm_resource.h index dc86572..de85a5b 100644 --- a/tools/crm_resource.h +++ b/tools/crm_resource.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,7 +14,6 @@ #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/services.h> #include <crm/common/xml.h> #include <crm/common/mainloop.h> @@ -22,7 +21,7 @@ #include <crm/common/scheduler_internal.h> #include <crm/cib.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/attrs_internal.h> #include <crm/pengine/rules.h> #include <crm/pengine/status.h> #include <crm/pengine/internal.h> @@ -35,6 +34,16 @@ typedef struct node_info_s { bool promoted; } node_info_t; +typedef struct { + char *attr_set_type; + char *attr_set_id; + char *attr_name; + char *attr_value; + char *given_rsc_id; + char *found_attr_id; + pcmk_resource_t *rsc; +} attr_update_data_t; + enum resource_check_flags { rsc_remain_stopped = (1 << 0), rsc_unpromotable = (1 << 1), @@ -89,7 +98,7 @@ int cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name, pcmk_scheduler_t *scheduler); int cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, const pcmk_node_t *node, const char *move_lifetime, - int timeout_ms, cib_t *cib, int cib_options, + guint timeout_ms, cib_t *cib, int cib_options, 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, @@ -99,12 +108,13 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc const char *rsc_class, const char *rsc_prov, const char *rsc_type, const char *rsc_action, GHashTable *params, GHashTable *override_hash, - int timeout_ms, int resource_verbose, + guint timeout_ms, + int resource_verbose, gboolean force, int check_level); crm_exit_t cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, const char *rsc_action, GHashTable *override_hash, - int timeout_ms, cib_t *cib, + guint timeout_ms, cib_t *cib, pcmk_scheduler_t *scheduler, int resource_verbose, gboolean force, int check_level); @@ -113,7 +123,7 @@ int cli_resource_update_attribute(pcmk_resource_t *rsc, const char *attr_set, const char *attr_set_type, const char *attr_id, const char *attr_name, const char *attr_value, gboolean recursive, - cib_t *cib, int cib_options, gboolean force); + cib_t *cib, gboolean force); int cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, const char *attr_set, const char *attr_set_type, @@ -121,7 +131,7 @@ int cli_resource_delete_attribute(pcmk_resource_t *rsc, cib_t *cib, int cib_options, gboolean force); int update_scheduler_input(pcmk_scheduler_t *scheduler, xmlNode **xml); -int wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib); +int wait_till_stable(pcmk__output_t *out, guint timeout_ms, cib_t * cib); bool resource_is_running_on(pcmk_resource_t *rsc, const char *host); diff --git a/tools/crm_resource_ban.c b/tools/crm_resource_ban.c index 3b0e4a1..689a178 100644 --- a/tools/crm_resource_ban.c +++ b/tools/crm_resource_ban.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -72,62 +72,66 @@ cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host, return EINVAL; } - fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); + fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS); - location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); + location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION); crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host); - out->info(out, "WARNING: Creating rsc_location constraint '%s' with a " - "score of -INFINITY for resource %s on %s.\n\tThis will " - "prevent %s from %s on %s until the constraint is removed " - "using the clear option or by editing the CIB with an " - "appropriate tool\n\tThis will be the case even if %s " - "is the last node in the cluster", - ID(location), rsc_id, host, rsc_id, - (promoted_role_only? "being promoted" : "running"), - host, host); - - crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id); + out->info(out, + "WARNING: Creating " PCMK_XE_RSC_LOCATION " constraint '%s' with " + "a score of " PCMK_VALUE_MINUS_INFINITY " for resource %s on %s." + "\n\tThis will prevent %s from %s on %s until the constraint is " + "removed using the clear option or by editing the CIB with an " + "appropriate tool.\n" + "\tThis will be the case even if %s is the last node in the " + "cluster", + pcmk__xe_id(location), rsc_id, host, rsc_id, + (promoted_role_only? "being promoted" : "running"), host, host); + + crm_xml_add(location, PCMK_XA_RSC, rsc_id); if(promoted_role_only) { - crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role); + crm_xml_add(location, PCMK_XA_ROLE, promoted_role); } else { - crm_xml_add(location, XML_RULE_ATTR_ROLE, PCMK__ROLE_STARTED); + crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED); } if (later_s == NULL) { /* Short form */ - crm_xml_add(location, XML_CIB_TAG_NODE, host); - crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S); + crm_xml_add(location, PCMK_XE_NODE, host); + crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY); } else { - xmlNode *rule = create_xml_node(location, XML_TAG_RULE); - xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION); + xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE); + xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION); crm_xml_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host); - crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S); - crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); + crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY); + crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND); crm_xml_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host); - crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME); - crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); - crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host); - crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); + crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME); + crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ); + crm_xml_add(expr, PCMK_XA_VALUE, host); + crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING); - expr = create_xml_node(rule, "date_expression"); + expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION); crm_xml_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host); - crm_xml_add(expr, "operation", "lt"); - crm_xml_add(expr, "end", later_s); + crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT); + crm_xml_add(expr, PCMK_XA_END, later_s); } crm_log_xml_notice(fragment, "Modify"); - rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, + rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment, cib_options); rc = pcmk_legacy2rc(rc); free_xml(fragment); free(later_s); - if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) { + if ((rc != pcmk_rc_ok) + && promoted_role_only + && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) { + int banrc = cli_resource_ban(out, rsc_id, host, move_lifetime, cib_conn, cib_options, promoted_role_only, PCMK__ROLE_PROMOTED_LEGACY); @@ -159,52 +163,55 @@ cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host, return ENOTCONN; } - fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); + fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS); - location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); + location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION); crm_xml_set_id(location, "cli-prefer-%s", rsc_id); - crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id); + crm_xml_add(location, PCMK_XA_RSC, rsc_id); if(promoted_role_only) { - crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role); + crm_xml_add(location, PCMK_XA_ROLE, promoted_role); } else { - crm_xml_add(location, XML_RULE_ATTR_ROLE, PCMK__ROLE_STARTED); + crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED); } if (later_s == NULL) { /* Short form */ - crm_xml_add(location, XML_CIB_TAG_NODE, host); - crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_INFINITY_S); + crm_xml_add(location, PCMK_XE_NODE, host); + crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_INFINITY); } else { - xmlNode *rule = create_xml_node(location, XML_TAG_RULE); - xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION); + xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE); + xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION); crm_xml_set_id(rule, "cli-prefer-rule-%s", rsc_id); - crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_INFINITY_S); - crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); + crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_INFINITY); + crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND); crm_xml_set_id(expr, "cli-prefer-expr-%s", rsc_id); - crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME); - crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); - crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host); - crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); + crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME); + crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ); + crm_xml_add(expr, PCMK_XA_VALUE, host); + crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING); - expr = create_xml_node(rule, "date_expression"); + expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION); crm_xml_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id); - crm_xml_add(expr, "operation", "lt"); - crm_xml_add(expr, "end", later_s); + crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT); + crm_xml_add(expr, PCMK_XA_END, later_s); } crm_log_xml_info(fragment, "Modify"); - rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, + rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment, cib_options); rc = pcmk_legacy2rc(rc); free_xml(fragment); free(later_s); - if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) { + if ((rc != pcmk_rc_ok) + && promoted_role_only + && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) { + int preferrc = cli_resource_prefer(out, rsc_id, host, move_lifetime, cib_conn, cib_options, promoted_role_only, PCMK__ROLE_PROMOTED_LEGACY); @@ -229,8 +236,9 @@ cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host, * </rule> * </rsc_location> * - * (2) The mode could be given by node= in an rsc_location XML node. That's - * what resource_clear_node_in_location handles. That XML looks like this: + * (2) The node could be given by node= in a PCMK_XE_RSC_LOCATION XML node. + * That's what resource_clear_node_in_location handles. That XML looks like + * this: * * <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/> * @@ -243,13 +251,13 @@ resource_clear_node_in_expr(const char *rsc_id, const char *host, cib_t * cib_co int rc = pcmk_rc_ok; char *xpath_string = NULL; -#define XPATH_FMT \ - "//" XML_CONS_TAG_RSC_LOCATION "[@" XML_ATTR_ID "='cli-prefer-%s']" \ - "[" XML_TAG_RULE \ - "[@" XML_ATTR_ID "='cli-prefer-rule-%s']" \ - "/" XML_TAG_EXPRESSION \ - "[@" XML_EXPR_ATTR_ATTRIBUTE "='#uname' " \ - "and @" XML_EXPR_ATTR_VALUE "='%s']" \ +#define XPATH_FMT \ + "//" PCMK_XE_RSC_LOCATION "[@" PCMK_XA_ID "='cli-prefer-%s']" \ + "[" PCMK_XE_RULE \ + "[@" PCMK_XA_ID "='cli-prefer-rule-%s']" \ + "/" PCMK_XE_EXPRESSION \ + "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " \ + "and @" PCMK_XA_VALUE "='%s']" \ "]" xpath_string = crm_strdup_printf(XPATH_FMT, rsc_id, rsc_id, host); @@ -274,21 +282,22 @@ resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * ci xmlNode *fragment = NULL; xmlNode *location = NULL; - fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); + fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS); if (clear_ban_constraints == TRUE) { - location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); + location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION); crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host); } - location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); + location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION); crm_xml_set_id(location, "cli-prefer-%s", rsc_id); if (force == FALSE) { - crm_xml_add(location, XML_CIB_TAG_NODE, host); + crm_xml_add(location, PCMK_XE_NODE, host); } crm_log_xml_info(fragment, "Delete"); - rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); + rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment, + cib_options); if (rc == -ENXIO) { rc = pcmk_rc_ok; } else { @@ -349,12 +358,11 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node, const char *rsc, const char *node, bool promoted_role_only) { - const char *cons_id = ID(constraint_node); - const char *cons_rsc = crm_element_value(constraint_node, - XML_LOC_ATTR_SOURCE); + const char *cons_id = pcmk__xe_id(constraint_node); + const char *cons_rsc = crm_element_value(constraint_node, PCMK_XA_RSC); GString *rsc_role_substr = NULL; - const char *promoted_role_rule = "@" XML_RULE_ATTR_ROLE "='" PCMK__ROLE_PROMOTED - "' or @" XML_RULE_ATTR_ROLE "='" + const char *promoted_role_rule = "@" PCMK_XA_ROLE "='" PCMK_ROLE_PROMOTED + "' or @" PCMK_XA_ROLE "='" PCMK__ROLE_PROMOTED_LEGACY "'"; CRM_ASSERT(buf != NULL); @@ -365,13 +373,13 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node, return; } - g_string_append(buf, "//" XML_CONS_TAG_RSC_LOCATION); + g_string_append(buf, "//" PCMK_XE_RSC_LOCATION); if ((node != NULL) || (rsc != NULL) || promoted_role_only) { g_string_append_c(buf, '['); if (node != NULL) { - pcmk__g_strcat(buf, "@" XML_CIB_TAG_NODE "='", node, "'", NULL); + pcmk__g_strcat(buf, "@" PCMK_XE_NODE "='", node, "'", NULL); if (promoted_role_only || (rsc != NULL)) { g_string_append(buf, " and "); @@ -381,13 +389,13 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node, if ((rsc != NULL) && promoted_role_only) { rsc_role_substr = g_string_sized_new(64); pcmk__g_strcat(rsc_role_substr, - "@" XML_LOC_ATTR_SOURCE "='", rsc, "' " + "@" PCMK_XA_RSC "='", rsc, "' " "and (" , promoted_role_rule, ")", NULL); } else if (rsc != NULL) { rsc_role_substr = g_string_sized_new(64); pcmk__g_strcat(rsc_role_substr, - "@" XML_LOC_ATTR_SOURCE "='", rsc, "'", NULL); + "@" PCMK_XA_RSC "='", rsc, "'", NULL); } else if (promoted_role_only) { rsc_role_substr = g_string_sized_new(64); @@ -401,18 +409,18 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node, } if (node != NULL) { - g_string_append(buf, "|//" XML_CONS_TAG_RSC_LOCATION); + g_string_append(buf, "|//" PCMK_XE_RSC_LOCATION); if (rsc_role_substr != NULL) { pcmk__g_strcat(buf, "[", rsc_role_substr, "]", NULL); } pcmk__g_strcat(buf, - "/" XML_TAG_RULE "[" XML_TAG_EXPRESSION - "[@" XML_EXPR_ATTR_ATTRIBUTE "='" CRM_ATTR_UNAME "' " - "and @" XML_EXPR_ATTR_VALUE "='", node, "']]", NULL); + "/" PCMK_XE_RULE "[" PCMK_XE_EXPRESSION + "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " + "and @" PCMK_XA_VALUE "='", node, "']]", NULL); } - g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" XML_ATTR_ID "='"); + g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" PCMK_XA_ID "='"); if (pcmk__starts_with(cons_id, "cli-ban-")) { pcmk__g_strcat(buf, cons_id, "-lifetime']", NULL); @@ -438,13 +446,14 @@ cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options, int i; int rc = pcmk_rc_ok; - cib_constraints = pcmk_find_cib_element(root, XML_CIB_TAG_CONSTRAINTS); - xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION); + cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS); + xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION); for (i = 0; i < numXpathResults(xpathObj); i++) { xmlNode *constraint_node = getXpathResult(xpathObj, i); xmlNode *date_expr_node = NULL; crm_time_t *end = NULL; + int rc = pcmk_rc_ok; if (buf == NULL) { buf = g_string_sized_new(1024); @@ -464,20 +473,25 @@ cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options, /* And then finally, see if the date expression is expired. If so, * clear the constraint. + * + * @COMPAT Check for error once we are rejecting rules with invalid end */ - end = crm_time_new(crm_element_value(date_expr_node, "end")); + rc = pcmk__xe_get_datetime(date_expr_node, PCMK_XA_END, &end); + if (rc != pcmk_rc_ok) { + crm_trace("Invalid " PCMK_XA_END ": %s", pcmk_rc_str(rc)); + } if (crm_time_compare(now, end) == 1) { xmlNode *fragment = NULL; xmlNode *location = NULL; - fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); - location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); - crm_xml_set_id(location, "%s", ID(constraint_node)); + fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS); + location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION); + crm_xml_set_id(location, "%s", pcmk__xe_id(constraint_node)); crm_log_xml_info(fragment, "Delete"); - rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS, - fragment, cib_options); + rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment, + cib_options); rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c index bdf3ad9..811bab0 100644 --- a/tools/crm_resource_print.c +++ b/tools/crm_resource_print.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -23,30 +23,33 @@ print_constraint(xmlNode *xml_obj, void *userdata) pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) userdata; pcmk__output_t *out = scheduler->priv; xmlNode *lifetime = NULL; - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *id = crm_element_value(xml_obj, PCMK_XA_ID); + pcmk_rule_input_t rule_input = { + .now = scheduler->now, + }; if (id == NULL) { return pcmk_rc_ok; } - // @COMPAT lifetime is deprecated - lifetime = first_named_child(xml_obj, "lifetime"); - if (pe_evaluate_rules(lifetime, NULL, scheduler->now, NULL) == FALSE) { + // @COMPAT PCMK__XE_LIFETIME is deprecated + lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL); + if (pcmk__evaluate_rules(lifetime, &rule_input, NULL) != pcmk_rc_ok) { return pcmk_rc_ok; } - if (!pcmk__xe_is(xml_obj, XML_CONS_TAG_RSC_DEPEND)) { + if (!pcmk__xe_is(xml_obj, PCMK_XE_RSC_COLOCATION)) { return pcmk_rc_ok; } out->info(out, "Constraint %s %s %s %s %s %s %s", xml_obj->name, - cons_string(crm_element_value(xml_obj, XML_ATTR_ID)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)), - cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE))); + cons_string(crm_element_value(xml_obj, PCMK_XA_ID)), + cons_string(crm_element_value(xml_obj, PCMK_XA_RSC)), + cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC)), + cons_string(crm_element_value(xml_obj, PCMK_XA_SCORE)), + cons_string(crm_element_value(xml_obj, PCMK_XA_RSC_ROLE)), + cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE))); return pcmk_rc_ok; } @@ -55,7 +58,7 @@ void cli_resource_print_cts_constraints(pcmk_scheduler_t *scheduler) { pcmk__xe_foreach_child(pcmk_find_cib_element(scheduler->input, - XML_CIB_TAG_CONSTRAINTS), + PCMK_XE_CONSTRAINTS), NULL, print_constraint, scheduler); } @@ -64,10 +67,10 @@ cli_resource_print_cts(pcmk_resource_t *rsc, pcmk__output_t *out) { const char *host = NULL; bool needs_quorum = TRUE; - const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); - const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); - const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - pcmk_node_t *node = pe__current_node(rsc); + const char *rtype = crm_element_value(rsc->xml, PCMK_XA_TYPE); + const char *rprov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); + const char *rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS); + pcmk_node_t *node = pcmk__current_node(rsc); if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) { needs_quorum = FALSE; @@ -125,7 +128,8 @@ cli_resource_print(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, all = g_list_prepend(all, (gpointer) "*"); out->begin_list(out, NULL, NULL, "Resource Config"); - out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc, all, all); + out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc, all, + all); out->message(out, "resource-config", rsc, !expanded); out->end_list(out); @@ -133,6 +137,88 @@ cli_resource_print(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, return pcmk_rc_ok; } +PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *") +static int +attribute_changed_default(pcmk__output_t *out, va_list args) +{ + attr_update_data_t *ud = va_arg(args, attr_update_data_t *); + + out->info(out, "Set '%s' option: " + PCMK_XA_ID "=%s%s%s%s%s value=%s", + ud->given_rsc_id, ud->found_attr_id, + ((ud->attr_set_id == NULL)? "" : " " PCMK__XA_SET "="), + pcmk__s(ud->attr_set_id, ""), + ((ud->attr_name == NULL)? "" : " " PCMK_XA_NAME "="), + pcmk__s(ud->attr_name, ""), ud->attr_value); + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *") +static int +attribute_changed_xml(pcmk__output_t *out, va_list args) +{ + attr_update_data_t *ud = va_arg(args, attr_update_data_t *); + + pcmk__output_xml_create_parent(out, (const char *) ud->rsc->xml->name, + PCMK_XA_ID, ud->rsc->id, + NULL); + + pcmk__output_xml_create_parent(out, ud->attr_set_type, + PCMK_XA_ID, ud->attr_set_id, + NULL); + + pcmk__output_create_xml_node(out, PCMK_XE_NVPAIR, + PCMK_XA_ID, ud->found_attr_id, + PCMK_XA_VALUE, ud->attr_value, + PCMK_XA_NAME, ud->attr_name, + NULL); + + pcmk__output_xml_pop_parent(out); + pcmk__output_xml_pop_parent(out); + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *") +static int +attribute_changed_list_default(pcmk__output_t *out, va_list args) +{ + GList *results = va_arg(args, GList *); + + if (results == NULL) { + return pcmk_rc_no_output; + } + + for (GList *iter = results; iter != NULL; iter = iter->next) { + attr_update_data_t *ud = iter->data; + out->message(out, "attribute-changed", ud); + } + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *") +static int +attribute_changed_list_xml(pcmk__output_t *out, va_list args) +{ + GList *results = va_arg(args, GList *); + + if (results == NULL) { + return pcmk_rc_no_output; + } + + pcmk__output_xml_create_parent(out, PCMK__XE_RESOURCE_SETTINGS, NULL); + + for (GList *iter = results; iter != NULL; iter = iter->next) { + attr_update_data_t *ud = iter->data; + out->message(out, "attribute-changed", ud); + } + + pcmk__output_xml_pop_parent(out); + return pcmk_rc_ok; +} + PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *", "const char *") static int @@ -210,19 +296,21 @@ agent_status_xml(pcmk__output_t *out, va_list args) { crm_exit_t rc = va_arg(args, crm_exit_t); const char *exit_reason = va_arg(args, const char *); - char *exit_str = pcmk__itoa(rc); - char *status_str = pcmk__itoa(status); - - pcmk__output_create_xml_node(out, "agent-status", - "code", exit_str, - "message", services_ocf_exitcode_str((int) rc), - "execution_code", status_str, - "execution_message", pcmk_exec_status_str(status), - "reason", exit_reason, + char *exit_s = pcmk__itoa(rc); + const char *message = services_ocf_exitcode_str((int) rc); + char *status_s = pcmk__itoa(status); + const char *execution_message = pcmk_exec_status_str(status); + + pcmk__output_create_xml_node(out, PCMK_XE_AGENT_STATUS, + PCMK_XA_CODE, exit_s, + PCMK_XA_MESSAGE, message, + PCMK_XA_EXECUTION_CODE, status_s, + PCMK_XA_EXECUTION_MESSAGE, execution_message, + PCMK_XA_REASON, exit_reason, NULL); - free(exit_str); - free(status_str); + free(exit_s); + free(status_s); return pcmk_rc_ok; } @@ -268,13 +356,13 @@ override_xml(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_create_xml_node(out, "override", - "name", name, - "value", value, + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_OVERRIDE, + PCMK_XA_NAME, name, + PCMK_XA_VALUE, value, NULL); if (rsc_name != NULL) { - crm_xml_add(node, "rsc", rsc_name); + crm_xml_add(node, PCMK_XA_RSC, rsc_name); } return pcmk_rc_ok; @@ -336,7 +424,7 @@ resource_agent_action_default(pcmk__output_t *out, va_list args) { const char *name = NULL; const char *value = NULL; - out->begin_list(out, NULL, NULL, "overrides"); + out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES); g_hash_table_iter_init(&iter, overrides); while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { @@ -359,10 +447,10 @@ resource_agent_action_default(pcmk__output_t *out, va_list args) { xmlNodePtr doc = NULL; if (stdout_data != NULL) { - doc = string2xml(stdout_data); + doc = pcmk__xml_parse(stdout_data); } if (doc != NULL) { - out->output_xml(out, "command", stdout_data); + out->output_xml(out, PCMK_XE_COMMAND, stdout_data); xmlFreeNode(doc); } else { out->subprocess_output(out, rc, stdout_data, stderr_data); @@ -391,26 +479,26 @@ resource_agent_action_xml(pcmk__output_t *out, va_list args) { const char *stdout_data = va_arg(args, const char *); const char *stderr_data = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action", - "action", action, - "class", class, - "type", type, - NULL); + xmlNodePtr node = NULL; + + node = pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT_ACTION, + PCMK_XA_ACTION, action, + PCMK_XA_CLASS, class, + PCMK_XA_TYPE, type, + NULL); if (rsc_name) { - crm_xml_add(node, "rsc", rsc_name); + crm_xml_add(node, PCMK_XA_RSC, rsc_name); } - if (provider) { - crm_xml_add(node, "provider", provider); - } + crm_xml_add(node, PCMK_XA_PROVIDER, provider); if (overrides) { GHashTableIter iter; const char *name = NULL; const char *value = NULL; - out->begin_list(out, NULL, NULL, "overrides"); + out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES); g_hash_table_iter_init(&iter, overrides); while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { @@ -427,10 +515,10 @@ resource_agent_action_xml(pcmk__output_t *out, va_list args) { xmlNodePtr doc = NULL; if (stdout_data != NULL) { - doc = string2xml(stdout_data); + doc = pcmk__xml_parse(stdout_data); } if (doc != NULL) { - out->output_xml(out, "command", stdout_data); + out->output_xml(out, PCMK_XE_COMMAND, stdout_data); xmlFreeNode(doc); } else { out->subprocess_output(out, rc, stdout_data, stderr_data); @@ -477,10 +565,10 @@ resource_check_list_default(pcmk__output_t *out, va_list args) { if (pcmk_is_set(checks->flags, rsc_node_health)) { out->list_item(out, "check", "'%s' cannot run on unhealthy nodes due to " - PCMK__OPT_NODE_HEALTH_STRATEGY "='%s'", + PCMK_OPT_NODE_HEALTH_STRATEGY "='%s'", parent->id, - pe_pref(checks->rsc->cluster->config_hash, - PCMK__OPT_NODE_HEALTH_STRATEGY)); + pcmk__cluster_option(checks->rsc->cluster->config_hash, + PCMK_OPT_NODE_HEALTH_STRATEGY)); } out->end_list(out); @@ -494,39 +582,39 @@ resource_check_list_xml(pcmk__output_t *out, va_list args) { const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false); - xmlNodePtr node = pcmk__output_create_xml_node(out, "check", - "id", parent->id, + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_CHECK, + PCMK_XA_ID, parent->id, NULL); if (pcmk_is_set(checks->flags, rsc_remain_stopped)) { - pcmk__xe_set_bool_attr(node, "remain_stopped", true); + pcmk__xe_set_bool_attr(node, PCMK_XA_REMAIN_STOPPED, true); } if (pcmk_is_set(checks->flags, rsc_unpromotable)) { - pcmk__xe_set_bool_attr(node, "promotable", false); + pcmk__xe_set_bool_attr(node, PCMK_XA_PROMOTABLE, false); } if (pcmk_is_set(checks->flags, rsc_unmanaged)) { - pcmk__xe_set_bool_attr(node, "unmanaged", true); + pcmk__xe_set_bool_attr(node, PCMK_XA_UNMANAGED, true); } if (pcmk_is_set(checks->flags, rsc_locked)) { - crm_xml_add(node, "locked-to", checks->lock_node); + crm_xml_add(node, PCMK_XA_LOCKED_TO_HYPHEN, checks->lock_node); } if (pcmk_is_set(checks->flags, rsc_node_health)) { - pcmk__xe_set_bool_attr(node, "unhealthy", true); + pcmk__xe_set_bool_attr(node, PCMK_XA_UNHEALTHY, true); } return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const char *") +PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *") static int resource_search_list_default(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); - const char *requested_name = va_arg(args, const char *); + const gchar *requested_name = va_arg(args, const gchar *); bool printed = false; int rc = pcmk_rc_no_output; @@ -554,7 +642,7 @@ resource_search_list_default(pcmk__output_t *out, va_list args) #ifdef PCMK__COMPAT_2_0 role_text = " " PCMK__ROLE_PROMOTED_LEGACY; #else - role_text = " " PCMK__ROLE_PROMOTED; + role_text = " " PCMK_ROLE_PROMOTED; #endif } out->list_item(out, "node", "resource %s is running on: %s%s", @@ -569,26 +657,29 @@ resource_search_list_default(pcmk__output_t *out, va_list args) return rc; } -PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const char *") +PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *") static int resource_search_list_xml(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); - const char *requested_name = va_arg(args, const char *); + const gchar *requested_name = va_arg(args, const gchar *); - pcmk__output_xml_create_parent(out, "nodes", - "resource", requested_name, + pcmk__output_xml_create_parent(out, PCMK_XE_NODES, + PCMK_XA_RESOURCE, requested_name, NULL); for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) { node_info_t *ni = (node_info_t *) lpc->data; - xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out, "node", ni->node_name); + xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out, + PCMK_XE_NODE, + ni->node_name); if (ni->promoted) { - crm_xml_add(sub_node, "state", "promoted"); + crm_xml_add(sub_node, PCMK_XA_STATE, "promoted"); } } + pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } @@ -685,22 +776,25 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args) const char *host_uname = (node == NULL)? NULL : node->details->uname; - xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, "reason", NULL); + xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, PCMK_XE_REASON, + NULL); if ((rsc == NULL) && (host_uname == NULL)) { GList *lpc = NULL; GList *hosts = NULL; - pcmk__output_xml_create_parent(out, "resources", NULL); + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); for (lpc = resources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; + const char *running = NULL; rsc->fns->location(rsc, &hosts, TRUE); + running = pcmk__btoa(hosts != NULL); - pcmk__output_xml_create_parent(out, "resource", - "id", rsc->id, - "running", pcmk__btoa(hosts != NULL), + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, + PCMK_XA_ID, rsc->id, + PCMK_XA_RUNNING, running, NULL); cli_resource_check(out, rsc, NULL); @@ -713,7 +807,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args) } else if ((rsc != NULL) && (host_uname != NULL)) { if (resource_is_running_on(rsc, host_uname)) { - crm_xml_add(xml_node, "running_on", host_uname); + crm_xml_add(xml_node, PCMK_XA_RUNNING_ON, host_uname); } cli_resource_check(out, rsc, node); @@ -725,15 +819,15 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args) GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp); GList *lpc = NULL; - pcmk__output_xml_create_parent(out, "resources", NULL); + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); for (lpc = activeResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; - pcmk__output_xml_create_parent(out, "resource", - "id", rsc->id, - "running", "true", - "host", host_uname, + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, + PCMK_XA_ID, rsc->id, + PCMK_XA_RUNNING, PCMK_VALUE_TRUE, + PCMK_XA_HOST, host_uname, NULL); cli_resource_check(out, rsc, node); @@ -743,10 +837,10 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args) for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; - pcmk__output_xml_create_parent(out, "resource", - "id", rsc->id, - "running", "false", - "host", host_uname, + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, + PCMK_XA_ID, rsc->id, + PCMK_XA_RUNNING, PCMK_VALUE_FALSE, + PCMK_XA_HOST, host_uname, NULL); cli_resource_check(out, rsc, node); @@ -762,11 +856,12 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args) GList *hosts = NULL; rsc->fns->location(rsc, &hosts, TRUE); - crm_xml_add(xml_node, "running", pcmk__btoa(hosts != NULL)); + crm_xml_add(xml_node, PCMK_XA_RUNNING, pcmk__btoa(hosts != NULL)); cli_resource_check(out, rsc, NULL); g_list_free(hosts); } + pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } @@ -774,7 +869,11 @@ static void add_resource_name(pcmk_resource_t *rsc, pcmk__output_t *out) { if (rsc->children == NULL) { - out->list_item(out, "resource", "%s", rsc->id); + /* Sometimes PCMK_XE_RESOURCE might act as a PCMK_XA_NAME instead of an + * XML element name, depending on whether pcmk__output_enable_list_element + * was called. + */ + out->list_item(out, PCMK_XE_RESOURCE, "%s", rsc->id); } else { g_list_foreach(rsc->children, (GFunc) add_resource_name, out); } @@ -799,6 +898,10 @@ resource_names(pcmk__output_t *out, va_list args) { static pcmk__message_entry_t fmt_functions[] = { { "agent-status", "default", agent_status_default }, { "agent-status", "xml", agent_status_xml }, + { "attribute-changed", "default", attribute_changed_default }, + { "attribute-changed", "xml", attribute_changed_xml }, + { "attribute-changed-list", "default", attribute_changed_list_default }, + { "attribute-changed-list", "xml", attribute_changed_list_xml }, { "attribute-list", "default", attribute_list_default }, { "attribute-list", "text", attribute_list_text }, { "override", "default", override_default }, diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c index da360fd..5b4fd2b 100644 --- a/tools/crm_resource_runtime.c +++ b/tools/crm_resource_runtime.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,12 +9,18 @@ #include <crm_internal.h> -#include <crm_resource.h> +#include <stdio.h> +#include <limits.h> +#include <glib.h> +#include <libxml/tree.h> + #include <crm/common/ipc_attrd_internal.h> #include <crm/common/ipc_controld.h> #include <crm/common/lists_internal.h> #include <crm/services_internal.h> +#include <crm_resource.h> + static GList * build_node_info_list(const pcmk_resource_t *rsc) { @@ -27,7 +33,7 @@ build_node_info_list(const pcmk_resource_t *rsc) iter2 != NULL; iter2 = iter2->next) { const pcmk_node_t *node = (const pcmk_node_t *) iter2->data; - node_info_t *ni = calloc(1, sizeof(node_info_t)); + node_info_t *ni = pcmk__assert_alloc(1, sizeof(node_info_t)); ni->node_name = node->details->uname; ni->promoted = pcmk_is_set(rsc->flags, pcmk_rsc_promotable) && @@ -47,22 +53,23 @@ cli_resource_search(pcmk_resource_t *rsc, const char *requested_name, GList *retval = NULL; const pcmk_resource_t *parent = pe__const_top_resource(rsc, false); - if (pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { retval = build_node_info_list(rsc); /* The anonymous clone children's common ID is supplied */ - } else if (pe_rsc_is_clone(parent) + } else if (pcmk__is_clone(parent) && !pcmk_is_set(rsc->flags, pcmk_rsc_unique) && rsc->clone_name - && pcmk__str_eq(requested_name, rsc->clone_name, pcmk__str_casei) - && !pcmk__str_eq(requested_name, rsc->id, pcmk__str_casei)) { + && pcmk__str_eq(requested_name, rsc->clone_name, pcmk__str_none) + && !pcmk__str_eq(requested_name, rsc->id, pcmk__str_none)) { retval = build_node_info_list(parent); } else if (rsc->running_on != NULL) { for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) { pcmk_node_t *node = (pcmk_node_t *) iter->data; - node_info_t *ni = calloc(1, sizeof(node_info_t)); + node_info_t *ni = pcmk__assert_alloc(1, sizeof(node_info_t)); + ni->node_name = node->details->uname; ni->promoted = (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted); @@ -77,83 +84,73 @@ cli_resource_search(pcmk_resource_t *rsc, const char *requested_name, static int find_resource_attr(pcmk__output_t *out, cib_t * the_cib, const char *attr, const char *rsc, const char *attr_set_type, const char *set_name, - const char *attr_id, const char *attr_name, char **value) + const char *attr_id, const char *attr_name, xmlNode **result) { + xmlNode *xml_search; int rc = pcmk_rc_ok; - xmlNode *xml_search = NULL; GString *xpath = NULL; const char *xpath_base = NULL; - if(value) { - *value = NULL; + if (result) { + *result = NULL; } if(the_cib == NULL) { return ENOTCONN; } - xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_RESOURCES); + xpath_base = pcmk_cib_xpath_for(PCMK_XE_RESOURCES); if (xpath_base == NULL) { - crm_err(XML_CIB_TAG_RESOURCES " CIB element not known (bug?)"); + crm_err(PCMK_XE_RESOURCES " CIB element not known (bug?)"); return ENOMSG; } xpath = g_string_sized_new(1024); pcmk__g_strcat(xpath, - xpath_base, "//*[@" XML_ATTR_ID "=\"", rsc, "\"]", NULL); + xpath_base, "//*[@" PCMK_XA_ID "=\"", rsc, "\"]", NULL); if (attr_set_type != NULL) { pcmk__g_strcat(xpath, "/", attr_set_type, NULL); if (set_name != NULL) { - pcmk__g_strcat(xpath, "[@" XML_ATTR_ID "=\"", set_name, "\"]", + pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "=\"", set_name, "\"]", NULL); } } - g_string_append(xpath, "//" XML_CIB_TAG_NVPAIR "["); - if (attr_id != NULL) { - pcmk__g_strcat(xpath, "@" XML_ATTR_ID "=\"", attr_id, "\"", NULL); - } + g_string_append(xpath, "//" PCMK_XE_NVPAIR); - if (attr_name != NULL) { - if (attr_id != NULL) { - g_string_append(xpath, " and "); - } - pcmk__g_strcat(xpath, "@" XML_NVPAIR_ATTR_NAME "=\"", attr_name, "\"", - NULL); + if (attr_id != NULL && attr_name!= NULL) { + pcmk__g_strcat(xpath, + "[@" PCMK_XA_ID "='", attr_id, "' " + "and @" PCMK_XA_NAME "='", attr_name, "']", NULL); + + } else if (attr_id != NULL) { + pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", attr_id, "']", NULL); + + } else if (attr_name != NULL) { + pcmk__g_strcat(xpath, "[@" PCMK_XA_NAME "='", attr_name, "']", NULL); } - g_string_append_c(xpath, ']'); rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, cib_sync_call | cib_scope_local | cib_xpath); rc = pcmk_legacy2rc(rc); - if (rc != pcmk_rc_ok) { - goto done; - } - - crm_log_xml_debug(xml_search, "Match"); - if (xml_search->children != NULL) { - xmlNode *child = NULL; - - rc = ENOTUNIQ; - out->info(out, "Multiple attributes match name=%s", attr_name); - - for (child = pcmk__xml_first_child(xml_search); child != NULL; - child = pcmk__xml_next(child)) { - out->info(out, " Value: %s \t(id=%s)", - crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); + if (rc == pcmk_rc_ok) { + crm_log_xml_debug(xml_search, "Match"); + if (xml_search->children != NULL) { + rc = ENOTUNIQ; + pcmk__warn_multiple_name_matches(out, xml_search, attr_name); + out->spacer(out); } + } - out->spacer(out); - - } else if(value) { - pcmk__str_update(value, crm_element_value(xml_search, attr)); + if (result) { + *result = xml_search; + } else { + free_xml(xml_search); } - done: g_string_free(xpath, TRUE); - free_xml(xml_search); return rc; } @@ -161,29 +158,27 @@ find_resource_attr(pcmk__output_t *out, cib_t * the_cib, const char *attr, static void find_matching_attr_resources_recursive(pcmk__output_t *out, GList /* <pcmk_resource_t*> */ **result, - pcmk_resource_t *rsc, const char *rsc_id, - const char * attr_set, const char * attr_set_type, - const char * attr_id, const char * attr_name, - cib_t * cib, const char * cmd, int depth) + pcmk_resource_t *rsc, const char * attr_set, + const char * attr_set_type, const char * attr_id, + const char * attr_name, cib_t * cib, int depth) { int rc = pcmk_rc_ok; char *lookup_id = clone_strip(rsc->id); - char *local_attr_id = NULL; /* visit the children */ for(GList *gIter = rsc->children; gIter; gIter = gIter->next) { find_matching_attr_resources_recursive(out, result, (pcmk_resource_t *) gIter->data, - rsc_id, attr_set, attr_set_type, - attr_id, attr_name, cib, cmd, depth+1); + attr_set, attr_set_type, attr_id, + attr_name, cib, depth+1); /* do it only once for clones */ - if (rsc->variant == pcmk_rsc_variant_clone) { + if (pcmk__is_clone(rsc)) { break; } } - rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type, - attr_set, attr_id, attr_name, &local_attr_id); + rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type, + attr_set, attr_id, attr_name, NULL); /* Post-order traversal. * The root is always on the list and it is the last item. */ if((0 == depth) || (pcmk_rc_ok == rc)) { @@ -191,7 +186,6 @@ find_matching_attr_resources_recursive(pcmk__output_t *out, *result = g_list_append(*result, rsc); } - free(local_attr_id); free(lookup_id); } @@ -206,7 +200,6 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc, { int rc = pcmk_rc_ok; char *lookup_id = NULL; - char *local_attr_id = NULL; GList * result = NULL; /* If --force is used, update only the requested resource (clone or primitive). * Otherwise, if the primitive has the attribute, use that. @@ -214,13 +207,9 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc, if(force == TRUE) { return g_list_append(result, rsc); } - if ((rsc->parent != NULL) - && (rsc->parent->variant == pcmk_rsc_variant_clone)) { - int rc = pcmk_rc_ok; - char *local_attr_id = NULL; - rc = find_resource_attr(out, cib, XML_ATTR_ID, rsc_id, attr_set_type, - attr_set, attr_id, attr_name, &local_attr_id); - free(local_attr_id); + if (pcmk__is_clone(rsc->parent)) { + int rc = find_resource_attr(out, cib, PCMK_XA_ID, rsc_id, attr_set_type, + attr_set, attr_id, attr_name, NULL); if(rc != pcmk_rc_ok) { rsc = rsc->parent; @@ -230,13 +219,13 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc, return g_list_append(result, rsc); } else if ((rsc->parent == NULL) && (rsc->children != NULL) - && (rsc->variant == pcmk_rsc_variant_clone)) { + && pcmk__is_clone(rsc)) { pcmk_resource_t *child = rsc->children->data; - if (child->variant == pcmk_rsc_variant_primitive) { + if (pcmk__is_primitive(child)) { lookup_id = clone_strip(child->id); /* Could be a cloned group! */ - rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type, - attr_set, attr_id, attr_name, &local_attr_id); + rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, + attr_set_type, attr_set, attr_id, attr_name, NULL); if(rc == pcmk_rc_ok) { rsc = child; @@ -244,110 +233,167 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc, attr_name, lookup_id, cmd, rsc_id); } - free(local_attr_id); free(lookup_id); } return g_list_append(result, rsc); } /* If the resource is a group ==> children inherit the attribute if defined. */ - find_matching_attr_resources_recursive(out, &result, rsc, rsc_id, attr_set, + find_matching_attr_resources_recursive(out, &result, rsc, attr_set, attr_set_type, attr_id, attr_name, - cib, cmd, 0); + cib, 0); return result; } -// \return Standard Pacemaker return code -int -cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name, - const char *attr_set, const char *attr_set_type, - const char *attr_id, const char *attr_name, - const char *attr_value, gboolean recursive, - cib_t *cib, int cib_options, gboolean force) +static int +update_element_attribute(pcmk__output_t *out, pcmk_resource_t *rsc, + cib_t *cib, const char *attr_name, const char *attr_value) { - pcmk__output_t *out = rsc->cluster->priv; int rc = pcmk_rc_ok; - char *found_attr_id = NULL; + if (cib == NULL) { + return ENOTCONN; + } - GList/*<pcmk_resource_t*>*/ *resources = NULL; - const char *top_id = pe__const_top_resource(rsc, false)->id; + crm_xml_add(rsc->xml, attr_name, attr_value); - if ((attr_id == NULL) && !force) { - find_resource_attr(out, cib, XML_ATTR_ID, top_id, NULL, NULL, NULL, - attr_name, NULL); + rc = cib->cmds->replace(cib, PCMK_XE_RESOURCES, rsc->xml, cib_sync_call); + rc = pcmk_legacy2rc(rc); + if (rc == pcmk_rc_ok) { + out->info(out, "Set attribute: " PCMK_XA_NAME "=%s value=%s", + attr_name, attr_value); } - if (pcmk__str_eq(attr_set_type, XML_TAG_ATTR_SETS, pcmk__str_casei)) { + return rc; +} + +static int +resources_with_attr(pcmk__output_t *out, cib_t *cib, pcmk_resource_t *rsc, + const char *requested_name, const char *attr_set, + const char *attr_set_type, const char *attr_id, + const char *attr_name, const char *top_id, gboolean force, + GList **resources) +{ + if (pcmk__str_eq(attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES, + pcmk__str_casei)) { if (!force) { - rc = find_resource_attr(out, cib, XML_ATTR_ID, top_id, - XML_TAG_META_SETS, attr_set, attr_id, - attr_name, &found_attr_id); - if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) { - out->err(out, - "WARNING: There is already a meta attribute " - "for '%s' called '%s' (id=%s)", - top_id, attr_name, found_attr_id); - out->err(out, - " Delete '%s' first or use the force option " - "to override", found_attr_id); - } - free(found_attr_id); - if (rc == pcmk_rc_ok) { + xmlNode *xml_search = NULL; + int rc = pcmk_rc_ok; + + rc = find_resource_attr(out, cib, PCMK_XA_ID, top_id, + PCMK_XE_META_ATTRIBUTES, attr_set, attr_id, + attr_name, &xml_search); + + if (rc == pcmk_rc_ok || rc == ENOTUNIQ) { + char *found_attr_id = NULL; + + found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID); + + if (!out->is_quiet(out)) { + out->err(out, + "WARNING: There is already a meta attribute " + "for '%s' called '%s' (id=%s)", + top_id, attr_name, found_attr_id); + out->err(out, + " Delete '%s' first or use the force option " + "to override", found_attr_id); + } + + free(found_attr_id); + free_xml(xml_search); return ENOTUNIQ; } - } - resources = g_list_append(resources, rsc); - } else if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) { - crm_xml_add(rsc->xml, attr_name, attr_value); - CRM_ASSERT(cib != NULL); - rc = cib->cmds->replace(cib, XML_CIB_TAG_RESOURCES, rsc->xml, - cib_options); - rc = pcmk_legacy2rc(rc); - if (rc == pcmk_rc_ok) { - out->info(out, "Set attribute: name=%s value=%s", - attr_name, attr_value); + free_xml(xml_search); } - return rc; + + *resources = g_list_append(*resources, rsc); } else { - resources = find_matching_attr_resources(out, rsc, requested_name, - attr_set, attr_set_type, - attr_id, attr_name, cib, - "update", force); + *resources = find_matching_attr_resources(out, rsc, requested_name, + attr_set, attr_set_type, + attr_id, attr_name, cib, + "update", force); } /* If the user specified attr_set or attr_id, the intent is to modify a * single resource, which will be the last item in the list. */ if ((attr_set != NULL) || (attr_id != NULL)) { - GList *last = g_list_last(resources); + GList *last = g_list_last(*resources); + + *resources = g_list_remove_link(*resources, last); + g_list_free(*resources); + *resources = last; + } + + return pcmk_rc_ok; +} + +static void +free_attr_update_data(gpointer data) +{ + attr_update_data_t *ud = data; + + if (ud == NULL) { + return; + } + + free(ud->attr_set_type); + free(ud->attr_set_id); + free(ud->attr_name); + free(ud->attr_value); + free(ud->given_rsc_id); + free(ud->found_attr_id); + free(ud); +} + +static int +update_attribute(pcmk_resource_t *rsc, const char *requested_name, + const char *attr_set, const char *attr_set_type, + const char *attr_id, const char *attr_name, + const char *attr_value, gboolean recursive, cib_t *cib, + gboolean force, GList **results) +{ + pcmk__output_t *out = rsc->cluster->priv; + int rc = pcmk_rc_ok; + + GList/*<pcmk_resource_t*>*/ *resources = NULL; + const char *top_id = pe__const_top_resource(rsc, false)->id; + + if ((attr_id == NULL) && !force) { + find_resource_attr(out, cib, PCMK_XA_ID, top_id, NULL, NULL, NULL, + attr_name, NULL); + } - resources = g_list_remove_link(resources, last); - g_list_free(resources); - resources = last; + rc = resources_with_attr(out, cib, rsc, requested_name, attr_set, attr_set_type, + attr_id, attr_name, top_id, force, &resources); + + if (rc != pcmk_rc_ok) { + return rc; } for (GList *iter = resources; iter != NULL; iter = iter->next) { char *lookup_id = NULL; char *local_attr_set = NULL; + char *found_attr_id = NULL; const char *rsc_attr_id = attr_id; const char *rsc_attr_set = attr_set; xmlNode *xml_top = NULL; xmlNode *xml_obj = NULL; - found_attr_id = NULL; + xmlNode *xml_search = NULL; rsc = (pcmk_resource_t *) iter->data; lookup_id = clone_strip(rsc->id); /* Could be a cloned group! */ - rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type, - attr_set, attr_id, attr_name, &found_attr_id); + rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type, + attr_set, attr_id, attr_name, &xml_search); switch (rc) { case pcmk_rc_ok: - crm_debug("Found a match for name=%s: id=%s", - attr_name, found_attr_id); + found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID); + crm_debug("Found a match for " PCMK_XA_NAME "='%s': " + PCMK_XA_ID "='%s'", attr_name, found_attr_id); rsc_attr_id = found_attr_id; break; @@ -363,16 +409,17 @@ cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name, rsc_attr_id = found_attr_id; } - xml_top = create_xml_node(NULL, (const char *) rsc->xml->name); - crm_xml_add(xml_top, XML_ATTR_ID, lookup_id); + xml_top = pcmk__xe_create(NULL, (const char *) rsc->xml->name); + crm_xml_add(xml_top, PCMK_XA_ID, lookup_id); - xml_obj = create_xml_node(xml_top, attr_set_type); - crm_xml_add(xml_obj, XML_ATTR_ID, rsc_attr_set); + xml_obj = pcmk__xe_create(xml_top, attr_set_type); + crm_xml_add(xml_obj, PCMK_XA_ID, rsc_attr_set); break; default: free(lookup_id); free(found_attr_id); + free_xml(xml_search); g_list_free(resources); return rc; } @@ -385,66 +432,114 @@ cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name, crm_log_xml_debug(xml_top, "Update"); - rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top, - cib_options); + rc = cib->cmds->modify(cib, PCMK_XE_RESOURCES, xml_top, cib_sync_call); rc = pcmk_legacy2rc(rc); if (rc == pcmk_rc_ok) { - out->info(out, "Set '%s' option: id=%s%s%s%s%s value=%s", - lookup_id, found_attr_id, - ((rsc_attr_set == NULL)? "" : " set="), - pcmk__s(rsc_attr_set, ""), - ((attr_name == NULL)? "" : " name="), - pcmk__s(attr_name, ""), attr_value); + attr_update_data_t *ud = pcmk__assert_alloc(1, sizeof(attr_update_data_t)); + + if (attr_set_type == NULL) { + attr_set_type = (const char *) xml_search->parent->name; + } + + if (rsc_attr_set == NULL) { + rsc_attr_set = crm_element_value(xml_search->parent, PCMK_XA_ID); + } + + ud->attr_set_type = pcmk__str_copy(attr_set_type); + ud->attr_set_id = pcmk__str_copy(rsc_attr_set); + ud->attr_name = pcmk__str_copy(attr_name); + ud->attr_value = pcmk__str_copy(attr_value); + ud->given_rsc_id = pcmk__str_copy(lookup_id); + ud->found_attr_id = pcmk__str_copy(found_attr_id); + ud->rsc = rsc; + + *results = g_list_append(*results, ud); } free_xml(xml_top); + free_xml(xml_search); free(lookup_id); free(found_attr_id); free(local_attr_set); if (recursive - && pcmk__str_eq(attr_set_type, XML_TAG_META_SETS, + && pcmk__str_eq(attr_set_type, PCMK_XE_META_ATTRIBUTES, pcmk__str_casei)) { - GList *lpc = NULL; - static bool need_init = true; - - if (need_init) { - need_init = false; - pcmk__unpack_constraints(rsc->cluster); - pe__clear_resource_flags_on_all(rsc->cluster, - pcmk_rsc_detect_loop); - } - /* We want to set the attribute only on resources explicitly * colocated with this one, so we use rsc->rsc_cons_lhs directly * rather than the with_this_colocations() method. */ - pe__set_resource_flags(rsc, pcmk_rsc_detect_loop); - for (lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) { + pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop); + for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) { pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data; crm_debug("Checking %s %d", cons->id, cons->score); - if (!pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop) - && (cons->score > 0)) { - crm_debug("Setting %s=%s for dependent resource %s", - attr_name, attr_value, cons->dependent->id); - cli_resource_update_attribute(cons->dependent, - cons->dependent->id, NULL, - attr_set_type, NULL, - attr_name, attr_value, - recursive, cib, cib_options, - force); + + if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop) + || (cons->score <= 0)) { + continue; } + + crm_debug("Setting %s=%s for dependent resource %s", + attr_name, attr_value, cons->dependent->id); + update_attribute(cons->dependent, cons->dependent->id, NULL, + attr_set_type, NULL, attr_name, attr_value, + recursive, cib, force, results); } } } + g_list_free(resources); return rc; } // \return Standard Pacemaker return code int +cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name, + const char *attr_set, const char *attr_set_type, + const char *attr_id, const char *attr_name, + const char *attr_value, gboolean recursive, + cib_t *cib, gboolean force) +{ + static bool need_init = true; + int rc = pcmk_rc_ok; + + GList *results = NULL; + pcmk__output_t *out = rsc->cluster->priv; + + /* If we were asked to update the attribute in a resource element (for + * instance, <primitive class="ocf">) there's really not much we need to do. + */ + if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) { + return update_element_attribute(out, rsc, cib, attr_name, attr_value); + } + + /* One time initialization - clear flags so we can detect loops */ + if (need_init) { + need_init = false; + pcmk__unpack_constraints(rsc->cluster); + pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop); + } + + rc = update_attribute(rsc, requested_name, attr_set, attr_set_type, + attr_id, attr_name, attr_value, recursive, cib, + force, &results); + + if (rc == pcmk_rc_ok) { + if (results == NULL) { + return rc; + } + + out->message(out, "attribute-changed-list", results); + g_list_free_full(results, free_attr_update_data); + } + + return rc; +} + +// \return Standard Pacemaker return code +int cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, const char *attr_set, const char *attr_set_type, const char *attr_id, const char *attr_name, @@ -455,22 +550,21 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, GList/*<pcmk_resource_t*>*/ *resources = NULL; if ((attr_id == NULL) && !force) { - find_resource_attr(out, cib, XML_ATTR_ID, + find_resource_attr(out, cib, PCMK_XA_ID, pe__const_top_resource(rsc, false)->id, NULL, NULL, NULL, attr_name, NULL); } - if (pcmk__str_eq(attr_set_type, XML_TAG_META_SETS, pcmk__str_casei)) { + if (pcmk__str_eq(attr_set_type, PCMK_XE_META_ATTRIBUTES, pcmk__str_casei)) { resources = find_matching_attr_resources(out, rsc, requested_name, attr_set, attr_set_type, attr_id, attr_name, cib, "delete", force); } else if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) { - xml_remove_prop(rsc->xml, attr_name); + pcmk__xe_remove_attr(rsc->xml, attr_name); CRM_ASSERT(cib != NULL); - rc = cib->cmds->replace(cib, XML_CIB_TAG_RESOURCES, rsc->xml, - cib_options); + rc = cib->cmds->replace(cib, PCMK_XE_RESOURCES, rsc->xml, cib_options); rc = pcmk_legacy2rc(rc); if (rc == pcmk_rc_ok) { out->info(out, "Deleted attribute: %s", attr_name); @@ -484,25 +578,29 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, for (GList *iter = resources; iter != NULL; iter = iter->next) { char *lookup_id = NULL; xmlNode *xml_obj = NULL; + xmlNode *xml_search = NULL; char *found_attr_id = NULL; const char *rsc_attr_id = attr_id; rsc = (pcmk_resource_t *) iter->data; lookup_id = clone_strip(rsc->id); - rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type, - attr_set, attr_id, attr_name, &found_attr_id); + rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type, + attr_set, attr_id, attr_name, &xml_search); switch (rc) { case pcmk_rc_ok: + found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID); + free_xml(xml_search); break; case ENXIO: free(lookup_id); - rc = pcmk_rc_ok; + free_xml(xml_search); continue; default: free(lookup_id); + free_xml(xml_search); g_list_free(resources); return rc; } @@ -515,16 +613,15 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name, crm_log_xml_debug(xml_obj, "Delete"); CRM_ASSERT(cib); - rc = cib->cmds->remove(cib, XML_CIB_TAG_RESOURCES, xml_obj, - cib_options); + rc = cib->cmds->remove(cib, PCMK_XE_RESOURCES, xml_obj, cib_options); rc = pcmk_legacy2rc(rc); if (rc == pcmk_rc_ok) { - out->info(out, "Deleted '%s' option: id=%s%s%s%s%s", + out->info(out, "Deleted '%s' option: " PCMK_XA_ID "=%s%s%s%s%s", lookup_id, found_attr_id, ((attr_set == NULL)? "" : " set="), pcmk__s(attr_set, ""), - ((attr_name == NULL)? "" : " name="), + ((attr_name == NULL)? "" : " " PCMK_XA_NAME "="), pcmk__s(attr_name, "")); } @@ -556,21 +653,21 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource, out->err(out, "Resource %s not found", rsc_id); return ENXIO; - } else if (rsc->variant != pcmk_rsc_variant_primitive) { + } else if (!pcmk__is_primitive(rsc)) { out->err(out, "We can only process primitive resources, not %s", rsc_id); return EINVAL; } - rsc_class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - rsc_provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER), - rsc_type = crm_element_value(rsc->xml, XML_ATTR_TYPE); + rsc_class = crm_element_value(rsc->xml, PCMK_XA_CLASS); + rsc_provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER), + rsc_type = crm_element_value(rsc->xml, PCMK_XA_TYPE); if ((rsc_class == NULL) || (rsc_type == NULL)) { out->err(out, "Resource %s does not have a class and type", rsc_id); return EINVAL; } { - pcmk_node_t *node = pe_find_node(scheduler->nodes, host_uname); + pcmk_node_t *node = pcmk_find_node(scheduler, host_uname); if (node == NULL) { out->err(out, "Node %s not found", host_uname); @@ -585,8 +682,8 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource, cib_only = true; } } - if (!cib_only && pe__is_guest_or_remote_node(node)) { - node = pe__current_node(node->details->remote_rsc); + if (!cib_only && pcmk__is_pacemaker_remote_node(node)) { + node = pcmk__current_node(node->details->remote_rsc); if (node == NULL) { out->err(out, "No cluster connection to Pacemaker Remote node %s detected", host_uname); @@ -668,7 +765,7 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, int rc = pcmk_rc_ok; const char *failed_value = NULL; const char *failed_id = NULL; - const char *interval_ms_s = NULL; + char *interval_ms_s = NULL; GHashTable *rscs = NULL; GHashTableIter iter; @@ -680,15 +777,17 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, // Normalize interval to milliseconds for comparison to history entry if (operation) { - interval_ms_s = crm_strdup_printf("%u", - crm_parse_interval_spec(interval_spec)); + guint interval_ms = 0U; + + pcmk_parse_interval_spec(interval_spec, &interval_ms); + interval_ms_s = crm_strdup_printf("%u", interval_ms); } - for (xmlNode *xml_op = pcmk__xml_first_child(scheduler->failed); - xml_op != NULL; - xml_op = pcmk__xml_next(xml_op)) { + for (xmlNode *xml_op = pcmk__xe_first_child(scheduler->failed, NULL, NULL, + NULL); + xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) { - failed_id = crm_element_value(xml_op, XML_LRM_ATTR_RSCID); + failed_id = crm_element_value(xml_op, PCMK__XA_RSC_ID); if (failed_id == NULL) { // Malformed history entry, should never happen continue; @@ -708,20 +807,20 @@ 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, XML_ATTR_UNAME); + failed_value = crm_element_value(xml_op, PCMK_XA_UNAME); if (!pcmk__str_eq(node_name, failed_value, pcmk__str_casei)) { continue; } // No operation specified means all operations match if (operation) { - failed_value = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + failed_value = crm_element_value(xml_op, PCMK_XA_OPERATION); if (!pcmk__str_eq(operation, failed_value, pcmk__str_casei)) { continue; } // Interval (if operation was specified) defaults to 0 (not all) - failed_value = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS); + failed_value = crm_element_value(xml_op, PCMK_META_INTERVAL); if (!pcmk__str_eq(interval_ms_s, failed_value, pcmk__str_casei)) { continue; } @@ -730,6 +829,8 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api, g_hash_table_add(rscs, (gpointer) failed_id); } + free(interval_ms_s); + 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); @@ -751,7 +852,7 @@ clear_rsc_fail_attrs(const pcmk_resource_t *rsc, const char *operation, int attr_options = pcmk__node_attr_none; char *rsc_name = rsc_fail_name(rsc); - if (pe__is_guest_or_remote_node(node)) { + if (pcmk__is_pacemaker_remote_node(node)) { attr_options |= pcmk__node_attr_remote; } @@ -830,7 +931,7 @@ cli_resource_delete(pcmk_ipc_api_t *controld_api, const char *host_uname, return pcmk_rc_ok; } - node = pe_find_node(scheduler->nodes, host_uname); + node = pcmk_find_node(scheduler, host_uname); if (node == NULL) { out->err(out, "Unable to clean up %s because node %s not found", @@ -890,13 +991,13 @@ cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name, } if (node_name) { - pcmk_node_t *node = pe_find_node(scheduler->nodes, 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 (pe__is_guest_or_remote_node(node)) { + if (pcmk__is_pacemaker_remote_node(node)) { attr_options |= pcmk__node_attr_remote; } } @@ -939,12 +1040,12 @@ static void check_role(resource_checks_t *checks) { const char *role_s = g_hash_table_lookup(checks->rsc->meta, - XML_RSC_ATTR_TARGET_ROLE); + PCMK_META_TARGET_ROLE); if (role_s == NULL) { return; } - switch (text2role(role_s)) { + switch (pcmk_parse_role(role_s)) { case pcmk_role_stopped: checks->flags |= rsc_remain_stopped; break; @@ -965,7 +1066,7 @@ static void check_managed(resource_checks_t *checks) { const char *managed_s = g_hash_table_lookup(checks->rsc->meta, - XML_RSC_ATTR_MANAGED); + PCMK_META_IS_MANAGED); if ((managed_s != NULL) && !crm_is_true(managed_s)) { checks->flags |= rsc_unmanaged; @@ -1075,12 +1176,12 @@ generate_resource_params(pcmk_resource_t *rsc, pcmk_node_t *node, if (params != NULL) { g_hash_table_iter_init(&iter, params); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { - g_hash_table_insert(combined, strdup(key), strdup(value)); + pcmk__insert_dup(combined, key, value); } } meta = pcmk__strkey_table(free, free); - get_meta_attributes(meta, rsc, node, scheduler); + get_meta_attributes(meta, rsc, NULL, scheduler); if (meta != NULL) { g_hash_table_iter_init(&iter, meta); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { @@ -1150,7 +1251,7 @@ get_active_resources(const char *host, GList *rsc_list) * other than the first, we can't otherwise tell which resources are * stopping and starting. */ - if (rsc->variant == pcmk_rsc_variant_group) { + if (pcmk__is_group(rsc)) { active = g_list_concat(active, get_active_resources(host, rsc->children)); } else if (resource_is_running_on(rsc, host)) { @@ -1192,18 +1293,16 @@ static void display_list(pcmk__output_t *out, GList *items, const char *tag) * \return Standard Pacemaker return code * \note On success, caller is responsible for freeing memory allocated for * scheduler->now. - * \todo This follows the example of other callers of cli_config_update() - * and returns ENOKEY ("Required key not available") if that fails, - * but perhaps pcmk_rc_schema_validation would be better in that case. */ int update_scheduler_input(pcmk_scheduler_t *scheduler, xmlNode **xml) { - if (cli_config_update(xml, NULL, FALSE) == FALSE) { - return ENOKEY; + int rc = pcmk_update_configured_schema(xml, false); + + if (rc == pcmk_rc_ok) { + scheduler->input = *xml; + scheduler->now = crm_time_new(NULL); } - scheduler->input = *xml; - scheduler->now = crm_time_new(NULL); return pcmk_rc_ok; } @@ -1254,7 +1353,7 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate) pcmk__output_t *out = scheduler->priv; pe_reset_working_set(scheduler); - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_counts|pcmk_sched_no_compat); rc = update_scheduler_input_to_cib(out, scheduler, cib); if (rc != pcmk_rc_ok) { @@ -1274,10 +1373,9 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate) goto done; } - rc = write_xml_file(scheduler->input, shadow_file, FALSE); - - if (rc < 0) { - out->err(out, "Could not populate shadow cib: %s (%d)", pcmk_strerror(rc), rc); + rc = pcmk__xml_write_file(scheduler->input, shadow_file, false, NULL); + if (rc != pcmk_rc_ok) { + out->err(out, "Could not populate shadow cib: %s", pcmk_rc_str(rc)); goto done; } @@ -1285,7 +1383,8 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate) rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { - out->err(out, "Could not connect to shadow cib: %s (%d)", pcmk_rc_str(rc), rc); + out->err(out, "Could not connect to shadow cib: %s", + pcmk_rc_str(rc)); goto done; } @@ -1325,11 +1424,11 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate) * * \return Maximum stop timeout for \p rsc (in milliseconds) */ -static int +static guint max_rsc_stop_timeout(pcmk_resource_t *rsc) { long long result_ll; - int max_delay = 0; + guint max_delay = 0; xmlNode *config = NULL; GHashTable *meta = NULL; @@ -1341,12 +1440,13 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc) if (rsc->children != NULL) { for (GList *iter = rsc->children; iter; iter = iter->next) { pcmk_resource_t *child = iter->data; - int delay = max_rsc_stop_timeout(child); + guint delay = max_rsc_stop_timeout(child); if (delay > max_delay) { - pe_rsc_trace(rsc, - "Maximum stop timeout for %s is now %s due to %s", - rsc->id, pcmk__readable_interval(delay), child->id); + pcmk__rsc_trace(rsc, + "Maximum stop timeout for %s is now %s " + "due to %s", rsc->id, + pcmk__readable_interval(delay), child->id); max_delay = delay; } } @@ -1362,10 +1462,9 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc) * @TODO This currently ignores node (which might matter for rules) */ meta = pcmk__unpack_action_meta(rsc, NULL, PCMK_ACTION_STOP, 0, config); - if ((pcmk__scan_ll(g_hash_table_lookup(meta, XML_ATTR_TIMEOUT), - &result_ll, -1LL) == pcmk_rc_ok) - && (result_ll >= 0) && (result_ll <= INT_MAX)) { - max_delay = (int) result_ll; + if ((pcmk__scan_ll(g_hash_table_lookup(meta, PCMK_META_TIMEOUT), + &result_ll, -1LL) == pcmk_rc_ok) && (result_ll >= 0)) { + max_delay = (guint) QB_MIN(result_ll, UINT_MAX); } g_hash_table_destroy(meta); @@ -1387,26 +1486,26 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc) * throttling, or any demotions needed. It checks the stop timeout, even * if the resources in question are actually being started. */ -static int +static guint wait_time_estimate(pcmk_scheduler_t *scheduler, const GList *resources) { - int max_delay = 0; + guint max_delay = 0U; // Find maximum stop timeout in milliseconds for (const GList *item = resources; item != NULL; item = item->next) { pcmk_resource_t *rsc = pe_find_resource(scheduler->resources, (const char *) item->data); - int delay = max_rsc_stop_timeout(rsc); + guint delay = max_rsc_stop_timeout(rsc); if (delay > max_delay) { - pe_rsc_trace(rsc, - "Wait time is now %s due to %s", - pcmk__readable_interval(delay), rsc->id); + pcmk__rsc_trace(rsc, + "Wait time is now %s due to %s", + pcmk__readable_interval(delay), rsc->id); max_delay = delay; } } - return (max_delay / 1000) + 5; + return (max_delay / 1000U) + 5U; } #define waiting_for_starts(d, r, h) ((d != NULL) || \ @@ -1438,15 +1537,15 @@ wait_time_estimate(pcmk_scheduler_t *scheduler, const GList *resources) int cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, const pcmk_node_t *node, const char *move_lifetime, - int timeout_ms, cib_t *cib, int cib_options, + guint timeout_ms, cib_t *cib, int cib_options, gboolean promoted_role_only, gboolean force) { int rc = pcmk_rc_ok; int lpc = 0; int before = 0; - int step_timeout_s = 0; - int sleep_interval = 2; - int timeout = timeout_ms / 1000; + guint step_timeout_s = 0; + guint sleep_interval = 2U; + guint timeout = timeout_ms / 1000U; bool stop_via_ban = false; char *rsc_id = NULL; @@ -1468,14 +1567,14 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, /* If the implicit resource or primitive resource of a bundle is given, operate on the * bundle itself instead. */ - if (pe_rsc_is_bundled(rsc)) { + if (pcmk__is_bundled(rsc)) { rsc = parent->parent; } running = resource_is_running_on(rsc, host); - if (pe_rsc_is_clone(parent) && !running) { - if (pe_rsc_is_unique_clone(parent)) { + if (pcmk__is_clone(parent) && !running) { + if (pcmk__is_unique_clone(parent)) { lookup_id = strdup(rsc->id); } else { lookup_id = clone_strip(rsc->id); @@ -1504,16 +1603,16 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, rsc_id = strdup(rsc->id); - if (pe_rsc_is_unique_clone(parent)) { + if (pcmk__is_unique_clone(parent)) { lookup_id = strdup(rsc->id); } else { lookup_id = clone_strip(rsc->id); } if (host) { - if (pe_rsc_is_clone(rsc) || pe_bundle_replicas(rsc)) { + if (pcmk__is_clone(rsc) || pe_bundle_replicas(rsc)) { stop_via_ban = true; - } else if (pe_rsc_is_clone(parent)) { + } else if (pcmk__is_clone(parent)) { stop_via_ban = true; free(lookup_id); lookup_id = strdup(parent->id); @@ -1563,22 +1662,33 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, out->quiet = true; rc = cli_resource_ban(out, lookup_id, host, move_lifetime, cib, cib_options, promoted_role_only, - PCMK__ROLE_PROMOTED); + PCMK_ROLE_PROMOTED); } else { - /* Stop the resource by setting target-role to Stopped. - * Remember any existing target-role so we can restore it later - * (though it only makes any difference if it's Unpromoted). + xmlNode *xml_search = NULL; + + /* Stop the resource by setting PCMK_META_TARGET_ROLE to Stopped. + * Remember any existing PCMK_META_TARGET_ROLE so we can restore it + * later (though it only makes any difference if it's Unpromoted). */ - find_resource_attr(out, cib, XML_NVPAIR_ATTR_VALUE, lookup_id, NULL, NULL, - NULL, XML_RSC_ATTR_TARGET_ROLE, &orig_target_role); - rc = cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, - NULL, XML_RSC_ATTR_TARGET_ROLE, + rc = find_resource_attr(out, cib, PCMK_XA_VALUE, lookup_id, NULL, NULL, NULL, + PCMK_META_TARGET_ROLE, &xml_search); + + if (rc == pcmk_rc_ok) { + orig_target_role = crm_element_value_copy(xml_search, PCMK_XA_VALUE); + } + + free_xml(xml_search); + + rc = cli_resource_update_attribute(rsc, rsc_id, NULL, + PCMK_XE_META_ATTRIBUTES, NULL, + PCMK_META_TARGET_ROLE, PCMK_ACTION_STOPPED, FALSE, cib, - cib_options, force); + force); } if(rc != pcmk_rc_ok) { - out->err(out, "Could not set target-role for %s: %s (%d)", rsc_id, pcmk_rc_str(rc), rc); + out->err(out, "Could not set " PCMK_META_TARGET_ROLE " for %s: %s (%d)", + rsc_id, pcmk_rc_str(rc), rc); if (current_active != NULL) { g_list_free_full(current_active, free); current_active = NULL; @@ -1616,7 +1726,7 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, sleep(sleep_interval); if(timeout) { timeout -= sleep_interval; - crm_trace("%ds remaining", timeout); + crm_trace("%us remaining", timeout); } rc = update_dataset(cib, scheduler, FALSE); if(rc != pcmk_rc_ok) { @@ -1651,20 +1761,23 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, rc = cli_resource_clear(lookup_id, host, NULL, cib, cib_options, true, force); } else if (orig_target_role) { - rc = cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, - NULL, XML_RSC_ATTR_TARGET_ROLE, - orig_target_role, FALSE, cib, - cib_options, force); + rc = cli_resource_update_attribute(rsc, rsc_id, NULL, + PCMK_XE_META_ATTRIBUTES, NULL, + PCMK_META_TARGET_ROLE, + orig_target_role, FALSE, cib, force); free(orig_target_role); orig_target_role = NULL; } else { - rc = cli_resource_delete_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, - NULL, XML_RSC_ATTR_TARGET_ROLE, cib, + rc = cli_resource_delete_attribute(rsc, rsc_id, NULL, + PCMK_XE_META_ATTRIBUTES, NULL, + PCMK_META_TARGET_ROLE, cib, cib_options, force); } if(rc != pcmk_rc_ok) { - out->err(out, "Could not unset target-role for %s: %s (%d)", rsc_id, pcmk_rc_str(rc), rc); + out->err(out, + "Could not unset " PCMK_META_TARGET_ROLE " for %s: %s (%d)", + rsc_id, pcmk_rc_str(rc), rc); goto done; } @@ -1731,14 +1844,16 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc, if (stop_via_ban) { cli_resource_clear(lookup_id, host, NULL, cib, cib_options, true, force); } else if (orig_target_role) { - cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, NULL, - XML_RSC_ATTR_TARGET_ROLE, orig_target_role, - FALSE, cib, cib_options, force); + cli_resource_update_attribute(rsc, rsc_id, NULL, + PCMK_XE_META_ATTRIBUTES, NULL, + PCMK_META_TARGET_ROLE, orig_target_role, + FALSE, cib, force); free(orig_target_role); } else { - cli_resource_delete_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, - NULL, XML_RSC_ATTR_TARGET_ROLE, cib, - cib_options, force); + cli_resource_delete_attribute(rsc, rsc_id, NULL, + PCMK_XE_META_ATTRIBUTES, NULL, + PCMK_META_TARGET_ROLE, cib, cib_options, + force); } done: @@ -1809,7 +1924,7 @@ print_pending_actions(pcmk__output_t *out, GList *actions) if (a->node) { out->info(out, "\tAction %d: %s\ton %s", - a->id, a->uuid, pe__node_name(a->node)); + a->id, a->uuid, pcmk__node_name(a->node)); } else { out->info(out, "\tAction %d: %s", a->id, a->uuid); } @@ -1839,32 +1954,47 @@ print_pending_actions(pcmk__output_t *out, GList *actions) * \return Standard Pacemaker return code */ int -wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib) +wait_till_stable(pcmk__output_t *out, guint timeout_ms, cib_t * cib) { pcmk_scheduler_t *scheduler = NULL; xmlXPathObjectPtr search; int rc = pcmk_rc_ok; bool pending_unknown_state_resources; - int timeout_s = timeout_ms? ((timeout_ms + 999) / 1000) : WAIT_DEFAULT_TIMEOUT_S; - time_t expire_time = time(NULL) + timeout_s; + time_t expire_time = time(NULL); time_t time_diff; bool printed_version_warning = out->is_quiet(out); // i.e. don't print if quiet + char *xpath = NULL; + + if (timeout_ms == 0) { + expire_time += WAIT_DEFAULT_TIMEOUT_S; + } else { + expire_time += (timeout_ms + 999) / 1000; + } scheduler = pe_new_working_set(); if (scheduler == NULL) { return ENOMEM; } + xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS + "/" PCMK__XE_NODE_STATE "/" PCMK__XE_LRM + "/" PCMK__XE_LRM_RESOURCES + "/" PCMK__XE_LRM_RESOURCE + "/" PCMK__XE_LRM_RSC_OP + "[@" PCMK__XA_RC_CODE "='%d']", + PCMK_OCF_UNKNOWN); do { /* Abort if timeout is reached */ time_diff = expire_time - time(NULL); - if (time_diff > 0) { - crm_info("Waiting up to %lld seconds for cluster actions to complete", (long long) time_diff); - } else { + if (time_diff <= 0) { print_pending_actions(out, scheduler->actions); - pe_free_working_set(scheduler); - return ETIME; + rc = ETIME; + break; } + + crm_info("Waiting up to %lld seconds for cluster actions to complete", + (long long) time_diff); + if (rc == pcmk_rc_ok) { /* this avoids sleep on first loop iteration */ sleep(WAIT_SLEEP_S); } @@ -1873,8 +2003,7 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib) pe_reset_working_set(scheduler); rc = update_scheduler_input_to_cib(out, scheduler, cib); if (rc != pcmk_rc_ok) { - pe_free_working_set(scheduler); - return rc; + break; } pcmk__schedule_actions(scheduler->input, pcmk_sched_no_counts|pcmk_sched_no_compat, @@ -1890,7 +2019,7 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib) * DC. However, that would have potential problems of its own. */ const char *dc_version = g_hash_table_lookup(scheduler->config_hash, - "dc-version"); + PCMK_OPT_DC_VERSION); if (!pcmk__str_eq(dc_version, PACEMAKER_VERSION "-" BUILD_VERSION, pcmk__str_casei)) { out->info(out, "warning: wait option may not work properly in " @@ -1899,13 +2028,13 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib) } } - search = xpath_search(scheduler->input, "/cib/status/node_state/lrm/lrm_resources/lrm_resource/" - XML_LRM_TAG_RSC_OP "[@" XML_LRM_ATTR_RC "='193']"); + search = xpath_search(scheduler->input, xpath); pending_unknown_state_resources = (numXpathResults(search) > 0); freeXpathObject(search); } while (actions_are_pending(scheduler->actions) || pending_unknown_state_resources); pe_free_working_set(scheduler); + free(xpath); return rc; } @@ -1943,14 +2072,13 @@ get_action(const char *rsc_action) { * \param[in] verbosity Verbosity level */ static void -set_agent_environment(GHashTable *params, int timeout_ms, int check_level, +set_agent_environment(GHashTable *params, guint timeout_ms, int check_level, int verbosity) { - g_hash_table_insert(params, strdup("CRM_meta_timeout"), - crm_strdup_printf("%d", timeout_ms)); + g_hash_table_insert(params, crm_meta_name(PCMK_META_TIMEOUT), + crm_strdup_printf("%u", timeout_ms)); - g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION), - strdup(CRM_FEATURE_SET)); + pcmk__insert_dup(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); if (check_level >= 0) { char *level = crm_strdup_printf("%d", check_level); @@ -1989,7 +2117,7 @@ apply_overrides(GHashTable *params, GHashTable *overrides) g_hash_table_iter_init(&iter, overrides); while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { - g_hash_table_replace(params, strdup(name), strdup(value)); + pcmk__insert_dup(params, name, value); } } } @@ -1999,8 +2127,8 @@ 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, GHashTable *params, GHashTable *override_hash, - int timeout_ms, int resource_verbose, gboolean force, - int check_level) + guint timeout_ms, int resource_verbose, + gboolean force, int check_level) { const char *class = rsc_class; const char *action = get_action(rsc_action); @@ -2008,7 +2136,7 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, svc_action_t *op = NULL; // If no timeout was provided, use the same default as the cluster - if (timeout_ms == 0) { + if (timeout_ms == 0U) { timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS; } @@ -2017,7 +2145,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, op = services__create_resource_action(rsc_name? rsc_name : "test", rsc_class, rsc_prov, rsc_type, action, - 0, timeout_ms, params, 0); + 0, QB_MIN(timeout_ms, INT_MAX), + params, 0); if (op == NULL) { out->err(out, "Could not execute %s using %s%s%s:%s: %s", action, rsc_class, (rsc_prov? ":" : ""), @@ -2060,10 +2189,33 @@ done: return exit_code; } +/*! + * \internal + * \brief Get the timeout the cluster would use for an action + * + * \param[in] rsc Resource that action is for + * \param[in] action Name of action + */ +static guint +get_action_timeout(pcmk_resource_t *rsc, const char *action) +{ + long long timeout_ms = -1LL; + xmlNode *op = pcmk__find_action_config(rsc, action, 0, true); + GHashTable *meta = pcmk__unpack_action_meta(rsc, NULL, action, 0, op); + + if ((pcmk__scan_ll(g_hash_table_lookup(meta, PCMK_META_TIMEOUT), + &timeout_ms, -1LL) != pcmk_rc_ok) + || (timeout_ms <= 0LL)) { + timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS; + } + g_hash_table_destroy(meta); + return (guint) QB_MIN(timeout_ms, UINT_MAX); +} + crm_exit_t cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, const char *rsc_action, GHashTable *override_hash, - int timeout_ms, cib_t *cib, pcmk_scheduler_t *scheduler, + guint timeout_ms, cib_t *cib, pcmk_scheduler_t *scheduler, int resource_verbose, gboolean force, int check_level) { pcmk__output_t *out = scheduler->priv; @@ -2076,13 +2228,15 @@ cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, if (pcmk__strcase_any_of(rsc_action, "force-start", "force-demote", "force-promote", NULL)) { - if(pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { GList *nodes = cli_resource_search(rsc, requested_name, scheduler); if(nodes != NULL && force == FALSE) { out->err(out, "It is not safe to %s %s here: the cluster claims it is already active", rsc_action, rsc->id); - out->err(out, "Try setting target-role=Stopped first or specifying " - "the force option"); + out->err(out, + "Try setting " + PCMK_META_TARGET_ROLE "=" PCMK_ROLE_STOPPED + " first or specifying the force option"); return CRM_EX_UNSAFE; } @@ -2090,32 +2244,31 @@ cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name, } } - if(pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { /* Grab the first child resource in the hope it's not a group */ rsc = rsc->children->data; } - if (rsc->variant == pcmk_rsc_variant_group) { + if (pcmk__is_group(rsc)) { out->err(out, "Sorry, the %s option doesn't support group resources", rsc_action); return CRM_EX_UNIMPLEMENT_FEATURE; - } else if (pe_rsc_is_bundled(rsc)) { + } else if (pcmk__is_bundled(rsc)) { out->err(out, "Sorry, the %s option doesn't support bundled resources", rsc_action); return CRM_EX_UNIMPLEMENT_FEATURE; } - rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); - rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); + rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS); + rprov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); + rtype = crm_element_value(rsc->xml, PCMK_XA_TYPE); params = generate_resource_params(rsc, NULL /* @TODO use local node */, scheduler); - if (timeout_ms == 0) { - timeout_ms = pe_get_configured_timeout(rsc, get_action(rsc_action), - scheduler); + if (timeout_ms == 0U) { + timeout_ms = get_action_timeout(rsc, get_action(rsc_action)); } - rid = pe_rsc_is_anon_clone(rsc->parent)? requested_name : rsc->id; + rid = pcmk__is_anonymous_clone(rsc->parent)? requested_name : rsc->id; exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, rsc_action, params, override_hash, timeout_ms, @@ -2134,7 +2287,7 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, int rc = pcmk_rc_ok; unsigned int count = 0; pcmk_node_t *current = NULL; - pcmk_node_t *dest = pe_find_node(scheduler->nodes, host_name); + pcmk_node_t *dest = pcmk_find_node(scheduler, host_name); bool cur_is_dest = false; if (dest == NULL) { @@ -2170,7 +2323,7 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, if (child_role == pcmk_role_promoted) { rsc = child; - promoted_node = pe__current_node(child); + promoted_node = pcmk__current_node(child); promoted_count++; } } @@ -2182,19 +2335,19 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, } if (count > 1) { - if (pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { current = NULL; } else { return pcmk_rc_multiple; } } - if (current && (current->details == dest->details)) { + 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", - pe__node_name(dest)); + pcmk__node_name(dest)); } else { return pcmk_rc_already; } @@ -2211,11 +2364,11 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, /* Record an explicit preference for 'dest' */ rc = cli_resource_prefer(out, rsc_id, dest->details->uname, move_lifetime, cib, cib_options, promoted_role_only, - PCMK__ROLE_PROMOTED); + PCMK_ROLE_PROMOTED); crm_trace("%s%s now prefers %s%s", rsc->id, (promoted_role_only? " (promoted)" : ""), - pe__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 @@ -2225,12 +2378,12 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id, if(current) { (void)cli_resource_ban(out, rsc_id, current->details->uname, move_lifetime, cib, cib_options, promoted_role_only, - PCMK__ROLE_PROMOTED); + PCMK_ROLE_PROMOTED); } else if(count > 1) { out->info(out, "Resource '%s' is currently %s in %d locations. " "One may now move to %s", rsc_id, (promoted_role_only? "promoted" : "active"), - count, pe__node_name(dest)); + 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")); diff --git a/tools/crm_rule.c b/tools/crm_rule.c index 5cdc118..aa13de2 100644 --- a/tools/crm_rule.c +++ b/tools/crm_rule.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -12,7 +12,7 @@ #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/common/iso8601.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/rules_internal.h> #include <crm/pengine/status.h> #include <pacemaker-internal.h> @@ -174,21 +174,22 @@ main(int argc, char **argv) } // Parse the input XML specified by the command-line options, if any - if (pcmk__str_eq(options.input_xml, "-", pcmk__str_casei)) { - input = stdin2xml(); + if (pcmk__str_eq(options.input_xml, "-", pcmk__str_none)) { + input = pcmk__xml_read(NULL); if (input == NULL) { exit_code = CRM_EX_DATAERR; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Couldn't parse input from STDIN\n"); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Couldn't parse input from STDIN"); goto done; } } else if (options.input_xml != NULL) { - input = string2xml(options.input_xml); + input = pcmk__xml_parse(options.input_xml); if (input == NULL) { exit_code = CRM_EX_DATAERR; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "Couldn't parse input string: %s\n", options.input_xml); + "Couldn't parse input string: %s", options.input_xml); goto done; } } diff --git a/tools/crm_shadow.c b/tools/crm_shadow.c index b86f462..aedf805 100644 --- a/tools/crm_shadow.c +++ b/tools/crm_shadow.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,7 +20,6 @@ #include <stdlib.h> #include <errno.h> #include <fcntl.h> -#include <crm/msg_xml.h> #include <crm/common/cmdline_internal.h> #include <crm/common/ipc.h> @@ -86,12 +85,12 @@ static struct { * \internal * \brief Display an instruction to the user * - * \param[in,out] out Output object - * \param[in] ... Message arguments + * \param[in,out] out Output object + * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * - * \note The variadic message arguments are of the following format: + * \note \p args should contain the following: * -# Instructional message */ PCMK__OUTPUT_ARGS("instruction", "const char *") @@ -110,12 +109,12 @@ instruction_default(pcmk__output_t *out, va_list args) * \internal * \brief Display an instruction to the user * - * \param[in,out] out Output object - * \param[in] ... Message arguments + * \param[in,out] out Output object + * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * - * \note The variadic message arguments are of the following format: + * \note \p args should contain the following: * -# Instructional message */ PCMK__OUTPUT_ARGS("instruction", "const char *") @@ -135,12 +134,12 @@ instruction_xml(pcmk__output_t *out, va_list args) * \internal * \brief Display information about a shadow CIB instance * - * \param[in,out] out Output object - * \param[in] ... Message arguments + * \param[in,out] out Output object + * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * - * \note The variadic message arguments are of the following format: + * \note \p args should contain the following: * -# Instance name (can be \p NULL) * -# Shadow file name (can be \p NULL) * -# Shadow file content (can be \p NULL) @@ -170,12 +169,18 @@ shadow_default(pcmk__output_t *out, va_list args) rc = out->info(out, "Content:"); if (content != NULL) { - char *buf = pcmk__trim(dump_xml_formatted_with_text(content)); + GString *buf = g_string_sized_new(1024); + gchar *str = NULL; - if (!pcmk__str_empty(buf)) { - out->info(out, "%s", buf); + pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, + buf, 0); + + str = g_string_free(buf, FALSE); + str = pcmk__trim(str); + if (!pcmk__str_empty(str)) { + out->info(out, "%s", str); } - free(buf); + g_free(str); } else { out->info(out, "<unknown>"); @@ -198,12 +203,12 @@ shadow_default(pcmk__output_t *out, va_list args) * \internal * \brief Display information about a shadow CIB instance * - * \param[in,out] out Output object - * \param[in] ... Message arguments + * \param[in,out] out Output object + * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * - * \note The variadic message arguments are of the following format: + * \note \p args should contain the following: * -# Instance name (can be \p NULL) * -# Shadow file name (can be \p NULL) * -# Shadow file content (can be \p NULL) @@ -240,10 +245,16 @@ shadow_text(pcmk__output_t *out, va_list args) rc = out->info(out, "%s", filename); } if (pcmk_is_set(flags, shadow_disp_content) && (content != NULL)) { - char *buf = pcmk__trim(dump_xml_formatted_with_text(content)); + GString *buf = g_string_sized_new(1024); + gchar *str = NULL; + + pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, + buf, 0); - rc = out->info(out, "%s", pcmk__trim(buf)); - free(buf); + str = g_string_free(buf, FALSE); + str = pcmk__trim(str); + rc = out->info(out, "%s", str); + g_free(str); } if (pcmk_is_set(flags, shadow_disp_diff) && (diff != NULL)) { rc = out->message(out, "xml-patchset", diff); @@ -258,12 +269,12 @@ shadow_text(pcmk__output_t *out, va_list args) * \internal * \brief Display information about a shadow CIB instance * - * \param[in,out] out Output object - * \param[in] ... Message arguments + * \param[in,out] out Output object + * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * - * \note The variadic message arguments are of the following format: + * \note \p args should contain the following: * -# Instance name (can be \p NULL) * -# Shadow file name (can be \p NULL) * -# Shadow file content (can be \p NULL) @@ -283,16 +294,19 @@ shadow_xml(pcmk__output_t *out, va_list args) enum shadow_disp_flags flags G_GNUC_UNUSED = (enum shadow_disp_flags) va_arg(args, int); - pcmk__output_xml_create_parent(out, "shadow", - "instance", instance, - "file", filename, + pcmk__output_xml_create_parent(out, PCMK_XE_SHADOW, + PCMK_XA_INSTANCE, instance, + PCMK_XA_FILE, filename, NULL); if (content != NULL) { - char *buf = dump_xml_formatted_with_text(content); + GString *buf = g_string_sized_new(1024); + + pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buf, + 0); - out->output_xml(out, "content", buf); - free(buf); + out->output_xml(out, PCMK_XE_CONTENT, buf->str); + g_string_free(buf, TRUE); } if (diff != NULL) { @@ -498,7 +512,7 @@ read_xml(const char *filename, xmlNode **output, GError **error) { int rc = pcmk_rc_ok; - *output = filename2xml(filename); + *output = pcmk__xml_read(filename); if (*output == NULL) { rc = pcmk_rc_no_input; exit_code = pcmk_rc2exitc(rc); @@ -521,18 +535,16 @@ static int write_shadow_file(const xmlNode *xml, const char *filename, bool reset, GError **error) { - int rc = write_xml_file(xml, filename, FALSE); + int rc = pcmk__xml_write_file(xml, filename, false, NULL); - if (rc < 0) { - rc = pcmk_legacy2rc(rc); + if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); g_set_error(error, PCMK__EXITC_ERROR, exit_code, "Could not %s the shadow instance '%s': %s", reset? "reset" : "create", options.instance, pcmk_rc_str(rc)); - return rc; } - return pcmk_rc_ok; + return rc; } /*! @@ -577,7 +589,7 @@ shadow_setup(pcmk__output_t *out, bool do_switch, GError **error) setenv("PS1", new_prompt, 1); setenv("CIB_shadow", options.instance, 1); - out->message(out, "instruction", + out->message(out, PCMK_XE_INSTRUCTION, "Press Ctrl+D to exit the crm_shadow shell"); if (pcmk__str_eq(shell, "(^|/)bash$", pcmk__str_regex)) { @@ -683,8 +695,8 @@ commit_shadow_file(GError **error) section_xml = input; if (!options.full_upload) { - section = XML_CIB_TAG_CONFIGURATION; - section_xml = first_named_child(input, section); + section = PCMK_XE_CONFIGURATION; + section_xml = pcmk__xe_first_child(input, section, NULL, NULL); } rc = real_cib->cmds->replace(real_cib, section, section_xml, @@ -726,9 +738,9 @@ create_shadow_empty(pcmk__output_t *out, GError **error) } output = createEmptyCib(0); - crm_xml_add(output, XML_ATTR_VALIDATION, options.validate_with); + crm_xml_add(output, PCMK_XA_VALIDATE_WITH, options.validate_with); out->info(out, "Created new %s configuration", - crm_element_value(output, XML_ATTR_VALIDATION)); + crm_element_value(output, PCMK_XA_VALIDATE_WITH)); if (write_shadow_file(output, filename, false, error) != pcmk_rc_ok) { goto done; diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c index aab4110..81ff8b3 100644 --- a/tools/crm_simulate.c +++ b/tools/crm_simulate.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the Pacemaker project contributors + * Copyright 2009-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -257,7 +257,7 @@ static GOptionEntry operation_entries[] = { "N" }, /* Deprecated */ { "pending", 'j', G_OPTION_FLAG_NO_ARG|G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, pending_cb, - "Display pending state if 'record-pending' is enabled", + "Display pending state if '" PCMK_META_RECORD_PENDING "' is enabled", NULL }, { NULL } @@ -359,22 +359,30 @@ setup_input(pcmk__output_t *out, const char *input, const char *output, } } else if (pcmk__str_eq(input, "-", pcmk__str_casei)) { - cib_object = filename2xml(NULL); + cib_object = pcmk__xml_read(NULL); } else { - cib_object = filename2xml(input); + cib_object = pcmk__xml_read(input); } - if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) { - create_xml_node(cib_object, XML_CIB_TAG_STATUS); + if (cib_object == NULL) { + rc = pcmk_rc_bad_input; + g_set_error(error, PCMK__EXITC_ERROR, pcmk_rc2exitc(rc), + "Could not read input XML: %s", pcmk_rc_str(rc)); + return rc; + } + + if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) { + pcmk__xe_create(cib_object, PCMK_XE_STATUS); } - if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) { + rc = pcmk_update_configured_schema(&cib_object, false); + if (rc != pcmk_rc_ok) { free_xml(cib_object); - return pcmk_rc_transform_failed; + return rc; } - if (validate_xml(cib_object, NULL, FALSE) != TRUE) { + if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) { free_xml(cib_object); return pcmk_rc_schema_validation; } @@ -388,20 +396,17 @@ setup_input(pcmk__output_t *out, const char *input, const char *output, free(pid); } - rc = write_xml_file(cib_object, output, FALSE); - free_xml(cib_object); - cib_object = NULL; - - if (rc < 0) { - rc = pcmk_legacy2rc(rc); + rc = pcmk__xml_write_file(cib_object, output, false, NULL); + if (rc != pcmk_rc_ok) { g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT, "Could not create '%s': %s", output, pcmk_rc_str(rc)); - return rc; } else { setenv("CIB_file", output, 1); - free(local_output); - return pcmk_rc_ok; } + + free_xml(cib_object); + free(local_output); + return rc; } static GOptionContext * @@ -489,9 +494,8 @@ main(int argc, char **argv) if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches) && !pcmk_is_set(options.flags, pcmk_sim_show_scores) && !pcmk_is_set(options.flags, pcmk_sim_show_utilization)) { - pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname()); - } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { - pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname()); + + pcmk__output_text_set_fancy(out, true); } pe__register_messages(out); @@ -523,12 +527,12 @@ main(int argc, char **argv) } if (pcmk_is_set(options.flags, pcmk_sim_show_scores)) { - pe__set_working_set_flags(scheduler, pcmk_sched_output_scores); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores); } if (pcmk_is_set(options.flags, pcmk_sim_show_utilization)) { - pe__set_working_set_flags(scheduler, pcmk_sched_show_utilization); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization); } - pe__set_working_set_flags(scheduler, pcmk_sched_no_compat); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_compat); if (options.test_dir != NULL) { scheduler->priv = out; diff --git a/tools/crm_ticket.c b/tools/crm_ticket.c index d95b581..2faa74e 100644 --- a/tools/crm_ticket.c +++ b/tools/crm_ticket.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the Pacemaker project contributors + * Copyright 2012-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,7 +22,6 @@ #include <fcntl.h> #include <libgen.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/cmdline_internal.h> @@ -60,9 +59,17 @@ GList *attr_delete; GHashTable *attr_set; bool modified = false; int cib_options = cib_sync_call; +static pcmk__output_t *out = NULL; #define INDENT " " +static pcmk__supported_format_t formats[] = { + PCMK__SUPPORTED_FORMAT_NONE, + PCMK__SUPPORTED_FORMAT_TEXT, + PCMK__SUPPORTED_FORMAT_XML, + { NULL, NULL, NULL } +}; + static gboolean attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { pcmk__str_update(&options.attr_value, optarg); @@ -71,7 +78,7 @@ attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErr return TRUE; } - g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__insert_dup(attr_set, options.attr_name, options.attr_value); pcmk__str_update(&options.attr_name, NULL); pcmk__str_update(&options.attr_value, NULL); @@ -116,16 +123,16 @@ get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError static gboolean grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) { - g_hash_table_insert(attr_set, strdup("granted"), strdup("true")); + pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_TRUE); modified = true; } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) { - g_hash_table_insert(attr_set, strdup("granted"), strdup("false")); + pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_FALSE); modified = true; } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) { - g_hash_table_insert(attr_set, strdup("standby"), strdup("true")); + pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_TRUE); modified = true; } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) { - g_hash_table_insert(attr_set, strdup("standby"), strdup("false")); + pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_FALSE); modified = true; } @@ -140,7 +147,7 @@ set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError return TRUE; } - g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__insert_dup(attr_set, options.attr_name, options.attr_value); pcmk__str_update(&options.attr_name, NULL); pcmk__str_update(&options.attr_value, NULL); @@ -167,7 +174,7 @@ static GOptionEntry query_entries[] = { NULL }, { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, - "Display the rsc_ticket constraints that apply to ticket(s)", + "Display the " PCMK_XE_RSC_TICKET " constraints that apply to ticket(s)", NULL }, { NULL } @@ -240,7 +247,7 @@ static GOptionEntry addl_entries[] = { static GOptionEntry deprecated_entries[] = { { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name, - "(Advanced) ID of the instance_attributes object to change", + "(Advanced) ID of the " PCMK_XE_INSTANCE_ATTRIBUTES " object to change", "ID" }, { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id, @@ -254,420 +261,37 @@ static GOptionEntry deprecated_entries[] = { { NULL } }; -static pcmk_ticket_t * -find_ticket(gchar *ticket_id, pcmk_scheduler_t *scheduler) -{ - return g_hash_table_lookup(scheduler->tickets, ticket_id); -} - static void -print_date(time_t time) +ticket_grant_warning(gchar *ticket_id) { - int lpc = 0; - char date_str[26]; - - asctime_r(localtime(&time), date_str); - for (; lpc < 26; lpc++) { - if (date_str[lpc] == '\n') { - date_str[lpc] = 0; - } - } - fprintf(stdout, "'%s'", date_str); + out->err(out, "This command cannot help you verify whether '%s' has " + "been already granted elsewhere.\n" + "If you really want to grant '%s' to this site now, and " + "you know what you are doing,\n" + "please specify --force.", + ticket_id, ticket_id); } static void -print_ticket(pcmk_ticket_t *ticket, bool raw, bool details) -{ - if (raw) { - fprintf(stdout, "%s\n", ticket->id); - return; - } - - fprintf(stdout, "%s\t%s %s", - ticket->id, ticket->granted ? "granted" : "revoked", - ticket->standby ? "[standby]" : " "); - - if (details && g_hash_table_size(ticket->state) > 0) { - GHashTableIter iter; - const char *name = NULL; - const char *value = NULL; - int lpc = 0; - - fprintf(stdout, " ("); - - g_hash_table_iter_init(&iter, ticket->state); - while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) { - if (lpc > 0) { - fprintf(stdout, ", "); - } - fprintf(stdout, "%s=", name); - if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) { - long long time_ll; - - pcmk__scan_ll(value, &time_ll, 0); - print_date((time_t) time_ll); - } else { - fprintf(stdout, "%s", value); - } - lpc++; - } - - fprintf(stdout, ")\n"); - - } else { - if (ticket->last_granted > -1) { - fprintf(stdout, " last-granted="); - print_date(ticket->last_granted); - } - fprintf(stdout, "\n"); - } - - return; -} - -static void -print_ticket_list(pcmk_scheduler_t *scheduler, bool raw, bool details) -{ - GHashTableIter iter; - pcmk_ticket_t *ticket = NULL; - - g_hash_table_iter_init(&iter, scheduler->tickets); - - while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) { - print_ticket(ticket, raw, details); - } -} - -static int -find_ticket_state(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_state_xml) -{ - int rc = pcmk_rc_ok; - xmlNode *xml_search = NULL; - - GString *xpath = NULL; - - CRM_ASSERT(ticket_state_xml != NULL); - *ticket_state_xml = NULL; - - xpath = g_string_sized_new(1024); - g_string_append(xpath, - "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS - "/" XML_CIB_TAG_TICKETS); - - if (ticket_id != NULL) { - pcmk__g_strcat(xpath, - "/" XML_CIB_TAG_TICKET_STATE - "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL); - } - - rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, - cib_sync_call | cib_scope_local | cib_xpath); - rc = pcmk_legacy2rc(rc); - g_string_free(xpath, TRUE); - - if (rc != pcmk_rc_ok) { - return rc; - } - - crm_log_xml_debug(xml_search, "Match"); - if (xml_search->children != NULL) { - if (ticket_id) { - fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id); - } - *ticket_state_xml = xml_search; - } else { - *ticket_state_xml = xml_search; - } - return rc; -} - -static int -find_ticket_constraints(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_cons_xml) -{ - int rc = pcmk_rc_ok; - xmlNode *xml_search = NULL; - - GString *xpath = NULL; - const char *xpath_base = NULL; - - CRM_ASSERT(ticket_cons_xml != NULL); - *ticket_cons_xml = NULL; - - xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CONSTRAINTS); - if (xpath_base == NULL) { - crm_err(XML_CIB_TAG_CONSTRAINTS " CIB element not known (bug?)"); - return -ENOMSG; - } - - xpath = g_string_sized_new(1024); - pcmk__g_strcat(xpath, xpath_base, "/" XML_CONS_TAG_RSC_TICKET, NULL); - - if (ticket_id != NULL) { - pcmk__g_strcat(xpath, - "[@" XML_TICKET_ATTR_TICKET "=\"", ticket_id, "\"]", - NULL); - } - - rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, - cib_sync_call | cib_scope_local | cib_xpath); - rc = pcmk_legacy2rc(rc); - g_string_free(xpath, TRUE); - - if (rc != pcmk_rc_ok) { - return rc; - } - - crm_log_xml_debug(xml_search, "Match"); - *ticket_cons_xml = xml_search; - - return rc; -} - -static int -dump_ticket_xml(cib_t * the_cib, gchar *ticket_id) -{ - int rc = pcmk_rc_ok; - xmlNode *state_xml = NULL; - - rc = find_ticket_state(the_cib, ticket_id, &state_xml); - - if (state_xml == NULL) { - return rc; - } - - fprintf(stdout, "State XML:\n"); - if (state_xml) { - char *state_xml_str = NULL; - - state_xml_str = dump_xml_formatted(state_xml); - fprintf(stdout, "\n%s", state_xml_str); - free_xml(state_xml); - free(state_xml_str); - } - - return rc; -} - -static int -dump_constraints(cib_t * the_cib, gchar *ticket_id) -{ - int rc = pcmk_rc_ok; - xmlNode *cons_xml = NULL; - char *cons_xml_str = NULL; - - rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml); - - if (cons_xml == NULL) { - return rc; - } - - cons_xml_str = dump_xml_formatted(cons_xml); - fprintf(stdout, "Constraints XML:\n\n%s", cons_xml_str); - free_xml(cons_xml); - free(cons_xml_str); - - return rc; -} - -static int -get_ticket_state_attr(gchar *ticket_id, const char *attr_name, const char **attr_value, - pcmk_scheduler_t *scheduler) -{ - pcmk_ticket_t *ticket = NULL; - - CRM_ASSERT(attr_value != NULL); - *attr_value = NULL; - - ticket = g_hash_table_lookup(scheduler->tickets, ticket_id); - if (ticket == NULL) { - return ENXIO; - } - - *attr_value = g_hash_table_lookup(ticket->state, attr_name); - if (*attr_value == NULL) { - return ENXIO; - } - - return pcmk_rc_ok; -} - -static void -ticket_warning(gchar *ticket_id, const char *action) -{ - GString *warning = g_string_sized_new(1024); - const char *word = NULL; - - CRM_ASSERT(action != NULL); - - if (strcmp(action, "grant") == 0) { - pcmk__g_strcat(warning, - "This command cannot help you verify whether '", - ticket_id, - "' has been already granted elsewhere.\n", NULL); - word = "to"; - - } else { - pcmk__g_strcat(warning, - "Revoking '", ticket_id, "' can trigger the specified " - "'loss-policy'(s) relating to '", ticket_id, "'.\n\n" - "You can check that with:\n" - "crm_ticket --ticket ", ticket_id, " --constraints\n\n" - "Otherwise before revoking '", ticket_id, "', " - "you may want to make '", ticket_id, "' " - "standby with:\n" - "crm_ticket --ticket ", ticket_id, " --standby\n\n", - NULL); - word = "from"; - } - - pcmk__g_strcat(warning, - "If you really want to ", action, " '", ticket_id, "' ", - word, " this site now, and you know what you are doing,\n" - "please specify --force.", NULL); - - fprintf(stdout, "%s\n", (const char *) warning->str); - - g_string_free(warning, TRUE); -} - -static bool -allow_modification(gchar *ticket_id) +ticket_revoke_warning(gchar *ticket_id) { - const char *value = NULL; - GList *list_iter = NULL; - - if (options.force) { - return true; - } - - if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) { - if (crm_is_true(value)) { - ticket_warning(ticket_id, "grant"); - return false; - - } else { - ticket_warning(ticket_id, "revoke"); - return false; - } - } - - for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { - const char *key = (const char *)list_iter->data; - - if (pcmk__str_eq(key, "granted", pcmk__str_casei)) { - ticket_warning(ticket_id, "revoke"); - return false; - } - } - - return true; -} - -static int -modify_ticket_state(gchar *ticket_id, cib_t *cib, pcmk_scheduler_t *scheduler) -{ - int rc = pcmk_rc_ok; - xmlNode *xml_top = NULL; - xmlNode *ticket_state_xml = NULL; - bool found = false; - - GList *list_iter = NULL; - GHashTableIter hash_iter; - - char *key = NULL; - char *value = NULL; - - pcmk_ticket_t *ticket = NULL; - - rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); - if (rc == pcmk_rc_ok) { - crm_debug("Found a match state for ticket: id=%s", ticket_id); - xml_top = ticket_state_xml; - found = true; - - } else if (rc != ENXIO) { - return rc; - - } else if (g_hash_table_size(attr_set) == 0){ - return pcmk_rc_ok; - - } else { - xmlNode *xml_obj = NULL; - - xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); - xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); - ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); - crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); - } - - for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { - const char *key = (const char *)list_iter->data; - xml_remove_prop(ticket_state_xml, key); - } - - ticket = find_ticket(ticket_id, scheduler); - - g_hash_table_iter_init(&hash_iter, attr_set); - while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) { - crm_xml_add(ticket_state_xml, key, value); - - if (pcmk__str_eq(key, "granted", pcmk__str_casei) - && (ticket == NULL || ticket->granted == FALSE) - && crm_is_true(value)) { - - char *now = pcmk__ttoa(time(NULL)); - - crm_xml_add(ticket_state_xml, "last-granted", now); - free(now); - } - } - - if (found && (attr_delete != NULL)) { - crm_log_xml_debug(xml_top, "Replace"); - rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); - rc = pcmk_legacy2rc(rc); - - } else { - crm_log_xml_debug(xml_top, "Update"); - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options); - rc = pcmk_legacy2rc(rc); - } - - free_xml(xml_top); - return rc; -} - -static int -delete_ticket_state(gchar *ticket_id, cib_t * cib) -{ - xmlNode *ticket_state_xml = NULL; - - int rc = pcmk_rc_ok; - - rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); - - if (rc == ENXIO) { - return pcmk_rc_ok; - - } else if (rc != pcmk_rc_ok) { - return rc; - } - - crm_log_xml_debug(ticket_state_xml, "Delete"); - - rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); - rc = pcmk_legacy2rc(rc); - - if (rc == pcmk_rc_ok) { - fprintf(stdout, "Cleaned up %s\n", ticket_id); - } - - free_xml(ticket_state_xml); - return rc; + out->err(out, "Revoking '%s' can trigger the specified '" PCMK_XA_LOSS_POLICY + "'(s) relating to '%s'.\n\n" + "You can check that with:\n" + "crm_ticket --ticket %s --constraints\n\n" + "Otherwise before revoking '%s', you may want to make '%s'" + "standby with:\n" + "crm_ticket --ticket %s --standby\n\n" + "If you really want to revoke '%s' from this site now, and " + "you know what you are doing,\n" + "please specify --force.", + ticket_id, ticket_id, ticket_id, ticket_id, ticket_id, + ticket_id, ticket_id); } static GOptionContext * -build_arg_context(pcmk__common_args_t *args) { +build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) +{ GOptionContext *context = NULL; const char *description = "Examples:\n\n" @@ -677,7 +301,7 @@ build_arg_context(pcmk__common_args_t *args) { "\tcrm_ticket --details\n\n" "Display the XML of 'ticketA':\n\n" "\tcrm_ticket --ticket ticketA --query-xml\n\n" - "Display the rsc_ticket constraints that apply to 'ticketA':\n\n" + "Display the " PCMK_XE_RSC_TICKET " constraints that apply to 'ticketA':\n\n" "\tcrm_ticket --ticket ticketA --constraints\n\n" "Grant 'ticketA' to this cluster site:\n\n" "\tcrm_ticket --ticket ticketA --grant\n\n" @@ -699,7 +323,7 @@ build_arg_context(pcmk__common_args_t *args) { "causing the cluster site to 'forget' the existing ticket state:\n\n" "\tcrm_ticket --ticket ticketA --cleanup\n\n"; - context = pcmk__build_arg_context(args, NULL, NULL, NULL); + context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); g_option_context_set_description(context, description); pcmk__add_arg_group(context, "queries", "Queries:", @@ -726,6 +350,7 @@ main(int argc, char **argv) crm_exit_t exit_code = CRM_EX_OK; int rc = pcmk_rc_ok; + GOptionGroup *output_group = NULL; pcmk__common_args_t *args = NULL; GOptionContext *context = NULL; gchar **processed_args = NULL; @@ -734,9 +359,10 @@ main(int argc, char **argv) attr_delete = NULL; args = pcmk__new_common_args(SUMMARY); - context = build_arg_context(args); + context = build_arg_context(args, &output_group); processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS"); + pcmk__register_formats(output_group, formats); if (!g_option_context_parse_strv(context, &processed_args, &error)) { exit_code = CRM_EX_USAGE; goto done; @@ -744,11 +370,21 @@ main(int argc, char **argv) pcmk__cli_init_logging("crm_ticket", args->verbosity); + rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Error creating output format %s: %s", args->output_ty, + pcmk_rc_str(rc)); + goto done; + } + + pe__register_messages(out); + pcmk__register_lib_messages(out); + if (args->version) { - g_strfreev(processed_args); - pcmk__free_arg_context(context); - /* FIXME: When crm_ticket is converted to use formatted output, this can go. */ - pcmk__cli_help('v'); + out->version(out, false); + goto done; } scheduler = pe_new_working_set(); @@ -759,7 +395,7 @@ main(int argc, char **argv) "Could not allocate scheduler data: %s", pcmk_rc_str(rc)); goto done; } - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_counts|pcmk_sched_no_compat); cib_conn = cib_new(); @@ -780,7 +416,7 @@ main(int argc, char **argv) } if (options.xml_file != NULL) { - cib_xml_copy = filename2xml(options.xml_file); + cib_xml_copy = pcmk__xml_read(options.xml_file); } else { rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call); @@ -794,8 +430,9 @@ main(int argc, char **argv) } } - if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) { - exit_code = CRM_EX_CONFIG; + rc = pcmk_update_configured_schema(&cib_xml_copy, false); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not update local CIB to latest schema version"); goto done; @@ -806,8 +443,9 @@ main(int argc, char **argv) cluster_status(scheduler); - /* For recording the tickets that are referenced in rsc_ticket constraints - * but have never been granted yet. */ + /* For recording the tickets that are referenced in PCMK_XE_RSC_TICKET + * constraints but have never been granted yet. + */ pcmk__unpack_constraints(scheduler); if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') { @@ -820,32 +458,30 @@ main(int argc, char **argv) raw = true; } - if (options.ticket_id) { - pcmk_ticket_t *ticket = find_ticket(options.ticket_id, scheduler); - - if (ticket == NULL) { - exit_code = CRM_EX_NOSUCH; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "No such ticket '%s'", options.ticket_id); - goto done; - } - print_ticket(ticket, raw, details); + rc = pcmk__ticket_info(out, scheduler, options.ticket_id, details, raw); + exit_code = pcmk_rc2exitc(rc); - } else { - print_ticket_list(scheduler, raw, details); + if (rc == ENXIO) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "No such ticket '%s'", options.ticket_id); + } else if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not get ticket info: %s", pcmk_rc_str(rc)); } } else if (options.ticket_cmd == 'q') { - rc = dump_ticket_xml(cib_conn, options.ticket_id); - exit_code = pcmk_rc2exitc(rc); + rc = pcmk__ticket_state(out, cib_conn, options.ticket_id); - if (rc != pcmk_rc_ok) { + if (rc != pcmk_rc_ok && rc != pcmk_rc_duplicate_id) { + exit_code = pcmk_rc2exitc(rc); g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not query ticket XML: %s", pcmk_rc_str(rc)); + } else { + exit_code = CRM_EX_OK; } } else if (options.ticket_cmd == 'c') { - rc = dump_constraints(cib_conn, options.ticket_id); + rc = pcmk__ticket_constraints(out, cib_conn, options.ticket_id); exit_code = pcmk_rc2exitc(rc); if (rc != pcmk_rc_ok) { @@ -854,8 +490,6 @@ main(int argc, char **argv) } } else if (options.ticket_cmd == 'G') { - const char *value = NULL; - if (options.ticket_id == NULL) { exit_code = CRM_EX_NOSUCH; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, @@ -863,14 +497,8 @@ main(int argc, char **argv) goto done; } - rc = get_ticket_state_attr(options.ticket_id, options.get_attr_name, - &value, scheduler); - if (rc == pcmk_rc_ok) { - fprintf(stdout, "%s\n", value); - } else if (rc == ENXIO && options.attr_default) { - fprintf(stdout, "%s\n", options.attr_default); - rc = pcmk_rc_ok; - } + rc = pcmk__ticket_get_attr(out, scheduler, options.ticket_id, + options.get_attr_name, options.attr_default); exit_code = pcmk_rc2exitc(rc); } else if (options.ticket_cmd == 'C') { @@ -881,30 +509,28 @@ main(int argc, char **argv) goto done; } - if (options.force == FALSE) { - pcmk_ticket_t *ticket = NULL; + rc = pcmk__ticket_delete(out, cib_conn, scheduler, options.ticket_id, + options.force); + exit_code = pcmk_rc2exitc(rc); - ticket = find_ticket(options.ticket_id, scheduler); - if (ticket == NULL) { - exit_code = CRM_EX_NOSUCH; + switch (rc) { + case ENXIO: g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No such ticket '%s'", options.ticket_id); - goto done; - } + break; - if (ticket->granted) { - ticket_warning(options.ticket_id, "revoke"); - exit_code = CRM_EX_INSUFFICIENT_PRIV; - goto done; - } - } + case EACCES: + ticket_revoke_warning(options.ticket_id); + break; - rc = delete_ticket_state(options.ticket_id, cib_conn); - exit_code = pcmk_rc2exitc(rc); + case pcmk_rc_ok: + case pcmk_rc_duplicate_id: + break; - if (rc != pcmk_rc_ok) { - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "Could not clean up ticket: %s", pcmk_rc_str(rc)); + default: + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not clean up ticket: %s", pcmk_rc_str(rc)); + break; } } else if (modified) { @@ -931,17 +557,39 @@ main(int argc, char **argv) goto done; } - if (!allow_modification(options.ticket_id)) { - exit_code = CRM_EX_INSUFFICIENT_PRIV; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "Ticket modification not allowed"); - goto done; + if (attr_delete != NULL) { + rc = pcmk__ticket_remove_attr(out, cib_conn, scheduler, options.ticket_id, + attr_delete, options.force); + + if (rc == EACCES) { + ticket_revoke_warning(options.ticket_id); + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Ticket modification not allowed without --force"); + } + } else { + rc = pcmk__ticket_set_attr(out, cib_conn, scheduler, options.ticket_id, + attr_set, options.force); + + if (rc == EACCES) { + const char *value = NULL; + + value = g_hash_table_lookup(attr_set, PCMK__XA_GRANTED); + if (crm_is_true(value)) { + ticket_grant_warning(options.ticket_id); + } else { + ticket_revoke_warning(options.ticket_id); + } + + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Ticket modification not allowed without --force"); + } } - rc = modify_ticket_state(options.ticket_id, cib_conn, scheduler); exit_code = pcmk_rc2exitc(rc); - if (rc != pcmk_rc_ok) { + if (rc != pcmk_rc_ok && error == NULL) { g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not modify ticket: %s", pcmk_rc_str(rc)); } @@ -1005,7 +653,13 @@ main(int argc, char **argv) g_free(options.ticket_id); g_free(options.xml_file); - pcmk__output_and_clear_error(&error, NULL); + pcmk__output_and_clear_error(&error, out); + + if (out != NULL) { + out->finish(out, exit_code, true, NULL); + pcmk__output_free(out); + } + pcmk__unregister_formats(); crm_exit(exit_code); } diff --git a/tools/crm_verify.c b/tools/crm_verify.c index 199814e..81e375a 100644 --- a/tools/crm_verify.c +++ b/tools/crm_verify.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -24,7 +24,6 @@ #include <crm/common/xml.h> #include <crm/common/util.h> -#include <crm/msg_xml.h> #include <crm/cib.h> #include <crm/cib/internal.h> #include <crm/pengine/status.h> @@ -43,6 +42,7 @@ struct { char *xml_file; gboolean xml_stdin; char *xml_string; + unsigned int verbosity; } options; static GOptionEntry data_entries[] = { @@ -112,11 +112,33 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { return context; } +/*! + * \internal + * \brief Output a configuration issue + * + * \param[in] ctx Output object + * \param[in] msg printf(3)-style format string + * \param[in] ... Format string arguments + */ +G_GNUC_PRINTF(2, 3) +static void +output_config_issue(void *ctx, const char *msg, ...) +{ + va_list ap; + char *buf = NULL; + pcmk__output_t *out = ctx; + + va_start(ap, msg); + CRM_ASSERT(vasprintf(&buf, msg, ap) > 0); + if (options.verbosity > 0) { + out->err(out, "%s", buf); + } + va_end(ap); +} + int main(int argc, char **argv) { - xmlNode *cib_object = NULL; - xmlNode *status = NULL; pcmk_scheduler_t *scheduler = NULL; @@ -127,7 +149,13 @@ main(int argc, char **argv) pcmk__output_t *out = NULL; + const char *cib_source = NULL; + xmlNode *cib_object = NULL; + GOptionGroup *output_group = NULL; + + const char *failure_type = NULL; + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX"); GOptionContext *context = build_arg_context(args, &output_group); @@ -159,127 +187,73 @@ main(int argc, char **argv) pcmk__register_lib_messages(out); - pcmk__set_config_error_handler((pcmk__config_error_func) out->err, out); - pcmk__set_config_warning_handler((pcmk__config_warning_func) out->err, out); + pcmk__set_config_error_handler(output_config_issue, out); + pcmk__set_config_warning_handler(output_config_issue, out); - crm_info("=#=#=#=#= Getting XML =#=#=#=#="); - - if (options.use_live_cib) { - crm_info("Reading XML from: live cluster"); - rc = cib__signon_query(out, NULL, &cib_object); - - if (rc != pcmk_rc_ok) { - // cib__signon_query() outputs any relevant error - goto done; - } - - } else if (options.xml_file != NULL) { - cib_object = filename2xml(options.xml_file); - if (cib_object == NULL) { - rc = ENODATA; - g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file); - goto done; - } + if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { + args->verbosity = 1; + } + options.verbosity = args->verbosity; + if (options.xml_file != NULL) { + cib_source = options.xml_file; } else if (options.xml_string != NULL) { - cib_object = string2xml(options.xml_string); - if (cib_object == NULL) { - rc = ENODATA; - g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string); - goto done; - } + cib_source = options.xml_string; } else if (options.xml_stdin) { - cib_object = stdin2xml(); - if (cib_object == NULL) { - rc = ENODATA; - g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN."); - goto done; - } - + cib_source = "-"; + } else if (options.use_live_cib) { + cib_source = NULL; } else { rc = ENODATA; - g_set_error(&error, PCMK__RC_ERROR, rc, - "No configuration source specified. Use --help for usage information."); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No input specified"); goto done; } - if (!pcmk__xe_is(cib_object, XML_TAG_CIB)) { - rc = EBADMSG; - g_set_error(&error, PCMK__RC_ERROR, rc, - "This tool can only check complete configurations (i.e. those starting with <cib>)."); + rc = pcmk__parse_cib(out, cib_source, &cib_object); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, rc, "Couldn't parse input"); goto done; } if (options.cib_save != NULL) { - write_xml_file(cib_object, options.cib_save, FALSE); - } - - status = pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS); - if (status == NULL) { - create_xml_node(cib_object, XML_CIB_TAG_STATUS); - } - - if (pcmk__validate_xml(cib_object, NULL, (xmlRelaxNGValidityErrorFunc) out->err, out) == FALSE) { - pcmk__config_err("CIB did not pass schema validation"); - free_xml(cib_object); - cib_object = NULL; - - } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) { - crm_config_error = TRUE; - free_xml(cib_object); - cib_object = NULL; - out->err(out, "The cluster will NOT be able to use this configuration.\n" - "Please manually update the configuration to conform to the %s syntax.", - xml_latest_schema()); + pcmk__xml_write_file(cib_object, options.cib_save, false, NULL); } scheduler = pe_new_working_set(); + if (scheduler == NULL) { rc = errno; g_set_error(&error, PCMK__RC_ERROR, rc, "Could not allocate scheduler data: %s", pcmk_rc_str(rc)); goto done; } + scheduler->priv = out; - /* Process the configuration to set crm_config_error/crm_config_warning. - * - * @TODO Some parts of the configuration are unpacked only when needed (for - * example, action configuration), so we aren't necessarily checking those. - */ - if (cib_object != NULL) { - unsigned long long flags = pcmk_sched_no_counts|pcmk_sched_no_compat; + rc = pcmk__verify(scheduler, out, cib_object); - if ((status == NULL) && !options.use_live_cib) { - // No status available, so do minimal checks - flags |= pcmk_sched_validate_only; - } - pcmk__schedule_actions(cib_object, flags, scheduler); - } - pe_free_working_set(scheduler); + if (rc == pcmk_rc_schema_validation) { + if (crm_config_error) { + failure_type = "Errors found during check: "; + } else if (crm_config_warning) { + failure_type = "Warnings found during check: "; + } else { + failure_type = ""; + } + + if (args->quiet) { + // User requested no output - if (crm_config_error) { - rc = pcmk_rc_schema_validation; - - if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { - g_set_error(&error, PCMK__RC_ERROR, rc, - "Errors found during check: config not valid"); - } else { - g_set_error(&error, PCMK__RC_ERROR, rc, - "Errors found during check: config not valid\n-V may provide more details"); - } - - } else if (crm_config_warning) { - rc = pcmk_rc_schema_validation; - - if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) { - g_set_error(&error, PCMK__RC_ERROR, rc, - "Warnings found during check: config may not be valid"); - } else { - g_set_error(&error, PCMK__RC_ERROR, rc, - "Warnings found during check: config may not be valid\n-V may provide more details"); + } else if (options.verbosity > 0) { + out->err(out, "%sconfig not valid", failure_type); + + } else { + out->err(out, "%sconfig not valid\n-V may provide more details", failure_type); + } } - } + + pe_free_working_set(scheduler); done: g_strfreev(processed_args); @@ -288,6 +262,10 @@ main(int argc, char **argv) free(options.xml_file); free(options.xml_string); + if (cib_object != NULL) { + free_xml(cib_object); + } + if (exit_code == CRM_EX_OK) { exit_code = pcmk_rc2exitc(rc); } diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 0b400ae..082904f 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2023 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -33,7 +33,7 @@ static enum { struct { gboolean health; - gint timeout; + guint timeout; char *optarg; char *ipc_name; gboolean bash_export; @@ -117,11 +117,7 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError } if (!strcmp(option_name, "--timeout") || !strcmp(option_name, "-t")) { - options.timeout = crm_parse_interval_spec(optarg); - if (errno == EINVAL) { - return FALSE; - } - return TRUE; + return pcmk_parse_interval_spec(optarg, &options.timeout) == pcmk_rc_ok; } pcmk__str_update(&options.optarg, optarg); @@ -205,10 +201,6 @@ main(int argc, char **argv) out->quiet = args->quiet; - if (!pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname())) { - goto done; - } - if (args->version) { out->version(out, false); goto done; diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c index 01f72d5..2268e0d 100644 --- a/tools/stonith_admin.c +++ b/tools/stonith_admin.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the Pacemaker project contributors + * Copyright 2009-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -23,14 +23,13 @@ #include <string.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/ipc.h> #include <crm/cluster/internal.h> #include <crm/common/cmdline_internal.h> #include <crm/common/output_internal.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__register_messages() #include <crm/cib.h> #include <crm/pengine/status.h> @@ -53,7 +52,7 @@ struct { stonith_key_value_t *params; int fence_level; int timeout ; - int tolerance; + long long tolerance_ms; int delay; char *agent; char *confirm_host; @@ -265,7 +264,15 @@ add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, gboolean add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { - options.tolerance = crm_get_msec(optarg) / 1000; + // pcmk__request_fencing() expects an unsigned int + options.tolerance_ms = crm_get_msec(optarg); + + if (options.tolerance_ms < 0) { + crm_warn("Ignoring invalid tolerance '%s'", optarg); + options.tolerance_ms = 0; + } else { + options.tolerance_ms = QB_MIN(options.tolerance_ms, UINT_MAX); + } return TRUE; } @@ -339,8 +346,8 @@ request_fencing(stonith_t *st, const char *target, const char *command, char *reason = NULL; int rc = pcmk__request_fencing(st, target, command, name, options.timeout * 1000, - options.tolerance * 1000, - options.delay, &reason); + options.tolerance_ms, options.delay, + &reason); if (rc != pcmk_rc_ok) { const char *rc_str = pcmk_rc_str(rc); @@ -408,6 +415,8 @@ main(int argc, char **argv) goto done; } + pcmk__output_enable_list_element(out); + stonith__register_messages(out); if (args->version) { |