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 /lib | |
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 'lib')
265 files changed, 30000 insertions, 14508 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 52cf974..dd79a83 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2003-2023 the Pacemaker project contributors +# Copyright 2003-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # diff --git a/lib/cib/Makefile.am b/lib/cib/Makefile.am index a74c4b1..b23c332 100644 --- a/lib/cib/Makefile.am +++ b/lib/cib/Makefile.am @@ -20,7 +20,7 @@ libcib_la_SOURCES += cib_ops.c libcib_la_SOURCES += cib_remote.c libcib_la_SOURCES += cib_utils.c -libcib_la_LDFLAGS = -version-info 32:0:5 +libcib_la_LDFLAGS = -version-info 33:0:6 libcib_la_CPPFLAGS = -I$(top_srcdir) $(AM_CPPFLAGS) libcib_la_CFLAGS = $(CFLAGS_HARDENED_LIB) diff --git a/lib/cib/cib_attrs.c b/lib/cib/cib_attrs.c index 11629b8..2014a0a 100644 --- a/lib/cib/cib_attrs.c +++ b/lib/cib/cib_attrs.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 <fcntl.h> #include <libgen.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/output_internal.h> @@ -66,22 +65,23 @@ find_attr(cib_t *cib, const char *section, const char *node_uuid, if (attr_set_type) { set_type = attr_set_type; } else { - set_type = XML_TAG_ATTR_SETS; + set_type = PCMK_XE_INSTANCE_ATTRIBUTES; } - if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) { + if (pcmk__str_eq(section, PCMK_XE_CRM_CONFIG, pcmk__str_casei)) { node_uuid = NULL; - set_type = XML_CIB_TAG_PROPSET; + set_type = PCMK_XE_CLUSTER_PROPERTY_SET; - } else if (pcmk__strcase_any_of(section, XML_CIB_TAG_OPCONFIG, XML_CIB_TAG_RSCCONFIG, + } else if (pcmk__strcase_any_of(section, + PCMK_XE_OP_DEFAULTS, PCMK_XE_RSC_DEFAULTS, NULL)) { node_uuid = NULL; - set_type = XML_TAG_META_SETS; + set_type = PCMK_XE_META_ATTRIBUTES; - } else if (pcmk__str_eq(section, XML_CIB_TAG_TICKETS, pcmk__str_casei)) { + } else if (pcmk__str_eq(section, PCMK_XE_TICKETS, pcmk__str_casei)) { node_uuid = NULL; - section = XML_CIB_TAG_STATUS; - node_type = XML_CIB_TAG_TICKETS; + section = PCMK_XE_STATUS; + node_type = PCMK_XE_TICKETS; } else if (node_uuid == NULL) { return EINVAL; @@ -96,51 +96,51 @@ find_attr(cib_t *cib, const char *section, const char *node_uuid, xpath = g_string_sized_new(1024); g_string_append(xpath, xpath_base); - if (pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) { + if (pcmk__str_eq(node_type, PCMK_XE_TICKETS, pcmk__str_casei)) { pcmk__g_strcat(xpath, "//", node_type, NULL); } else if (node_uuid) { - const char *node_type = XML_CIB_TAG_NODE; + const char *node_type = PCMK_XE_NODE; - if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) { - node_type = XML_CIB_TAG_STATE; - set_type = XML_TAG_TRANSIENT_NODEATTRS; + if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) { + node_type = PCMK__XE_NODE_STATE; + set_type = PCMK__XE_TRANSIENT_ATTRIBUTES; } pcmk__g_strcat(xpath, - "//", node_type, "[@" XML_ATTR_ID "='", node_uuid, "']", + "//", node_type, "[@" PCMK_XA_ID "='", node_uuid, "']", NULL); } pcmk__g_strcat(xpath, "//", set_type, NULL); if (set_name) { - pcmk__g_strcat(xpath, "[@" XML_ATTR_ID "='", set_name, "']", NULL); + pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", set_name, "']", NULL); } g_string_append(xpath, "//nvpair"); if (attr_id && attr_name) { pcmk__g_strcat(xpath, - "[@" XML_ATTR_ID "='", attr_id, "' " - "and @" XML_ATTR_NAME "='", attr_name, "']", NULL); + "[@" PCMK_XA_ID "='", attr_id, "' " + "and @" PCMK_XA_NAME "='", attr_name, "']", NULL); } else if (attr_id) { - pcmk__g_strcat(xpath, "[@" XML_ATTR_ID "='", attr_id, "']", NULL); + pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", attr_id, "']", NULL); } else if (attr_name) { - pcmk__g_strcat(xpath, "[@" XML_ATTR_NAME "='", attr_name, "']", NULL); + pcmk__g_strcat(xpath, "[@" PCMK_XA_NAME "='", attr_name, "']", NULL); } rc = cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, NULL, (const char *) xpath->str, NULL, &xml_search, cib_sync_call|cib_scope_local|cib_xpath, user_name); - if (rc < 0) { - rc = pcmk_legacy2rc(rc); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { crm_trace("Query failed for attribute %s (section=%s, node=%s, set=%s, xpath=%s): %s", attr_name, section, pcmk__s(node_uuid, "<null>"), pcmk__s(set_name, "<null>"), (const char *) xpath->str, pcmk_rc_str(rc)); } else { - rc = pcmk_rc_ok; crm_log_xml_debug(xml_search, "Match"); } @@ -153,16 +153,8 @@ static int handle_multiples(pcmk__output_t *out, xmlNode *search, const char *attr_name) { if ((search != NULL) && (search->children != NULL)) { - xmlNode *child = NULL; - - out->info(out, "Multiple attributes match name=%s", attr_name); - for (child = pcmk__xml_first_child(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)); - } + pcmk__warn_multiple_name_matches(out, search, attr_name); return ENOTUNIQ; - } else { return pcmk_rc_ok; } @@ -195,7 +187,7 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c free_xml(xml_search); return ENOTUNIQ; } else { - pcmk__str_update(&local_attr_id, crm_element_value(xml_search, XML_ATTR_ID)); + local_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID); attr_id = local_attr_id; free_xml(xml_search); goto do_modify; @@ -211,38 +203,38 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c } else { free_xml(xml_search); crm_trace("%s does not exist, create it", attr_name); - if (pcmk__str_eq(section, XML_CIB_TAG_TICKETS, pcmk__str_casei)) { + if (pcmk__str_eq(section, PCMK_XE_TICKETS, pcmk__str_casei)) { node_uuid = NULL; - section = XML_CIB_TAG_STATUS; - node_type = XML_CIB_TAG_TICKETS; + section = PCMK_XE_STATUS; + node_type = PCMK_XE_TICKETS; - xml_top = create_xml_node(xml_obj, XML_CIB_TAG_STATUS); - xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); + xml_top = pcmk__xe_create(xml_obj, PCMK_XE_STATUS); + xml_obj = pcmk__xe_create(xml_top, PCMK_XE_TICKETS); - } else if (pcmk__str_eq(section, XML_CIB_TAG_NODES, pcmk__str_casei)) { + } else if (pcmk__str_eq(section, PCMK_XE_NODES, pcmk__str_casei)) { if (node_uuid == NULL) { return EINVAL; } - if (pcmk__str_eq(node_type, "remote", pcmk__str_casei)) { - xml_top = create_xml_node(xml_obj, XML_CIB_TAG_NODES); - xml_obj = create_xml_node(xml_top, XML_CIB_TAG_NODE); - crm_xml_add(xml_obj, XML_ATTR_TYPE, "remote"); - crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); - crm_xml_add(xml_obj, XML_ATTR_UNAME, node_uuid); + if (pcmk__str_eq(node_type, PCMK_VALUE_REMOTE, pcmk__str_casei)) { + xml_top = pcmk__xe_create(xml_obj, PCMK_XE_NODES); + xml_obj = pcmk__xe_create(xml_top, PCMK_XE_NODE); + crm_xml_add(xml_obj, PCMK_XA_TYPE, PCMK_VALUE_REMOTE); + crm_xml_add(xml_obj, PCMK_XA_ID, node_uuid); + crm_xml_add(xml_obj, PCMK_XA_UNAME, node_uuid); } else { - tag = XML_CIB_TAG_NODE; + tag = PCMK_XE_NODE; } - } else if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) { - tag = XML_TAG_TRANSIENT_NODEATTRS; + } else if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) { + tag = PCMK__XE_TRANSIENT_ATTRIBUTES; if (node_uuid == NULL) { return EINVAL; } - xml_top = create_xml_node(xml_obj, XML_CIB_TAG_STATE); - crm_xml_add(xml_top, XML_ATTR_ID, node_uuid); + xml_top = pcmk__xe_create(xml_obj, PCMK__XE_NODE_STATE); + crm_xml_add(xml_top, PCMK_XA_ID, node_uuid); xml_obj = xml_top; } else { @@ -251,12 +243,14 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c } if (set_name == NULL) { - if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) { - local_set_name = strdup(CIB_OPTIONS_FIRST); + if (pcmk__str_eq(section, PCMK_XE_CRM_CONFIG, pcmk__str_casei)) { + local_set_name = + pcmk__str_copy(PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS); - } else if (pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) { + } else if (pcmk__str_eq(node_type, PCMK_XE_TICKETS, + pcmk__str_casei)) { local_set_name = crm_strdup_printf("%s-%s", section, - XML_CIB_TAG_TICKETS); + PCMK_XE_TICKETS); } else if (node_uuid) { local_set_name = crm_strdup_printf("%s-%s", section, node_uuid); @@ -285,27 +279,30 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c crm_trace("Creating %s/%s", section, tag); if (tag != NULL) { - xml_obj = create_xml_node(xml_obj, tag); - crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); + xml_obj = pcmk__xe_create(xml_obj, tag); + crm_xml_add(xml_obj, PCMK_XA_ID, node_uuid); if (xml_top == NULL) { xml_top = xml_obj; } } - if (node_uuid == NULL && !pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) { - if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) { - xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_PROPSET); + if ((node_uuid == NULL) + && !pcmk__str_eq(node_type, PCMK_XE_TICKETS, pcmk__str_casei)) { + + if (pcmk__str_eq(section, PCMK_XE_CRM_CONFIG, pcmk__str_casei)) { + xml_obj = pcmk__xe_create(xml_obj, + PCMK_XE_CLUSTER_PROPERTY_SET); } else { - xml_obj = create_xml_node(xml_obj, XML_TAG_META_SETS); + xml_obj = pcmk__xe_create(xml_obj, PCMK_XE_META_ATTRIBUTES); } } else if (set_type) { - xml_obj = create_xml_node(xml_obj, set_type); + xml_obj = pcmk__xe_create(xml_obj, set_type); } else { - xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS); + xml_obj = pcmk__xe_create(xml_obj, PCMK_XE_INSTANCE_ATTRIBUTES); } - crm_xml_add(xml_obj, XML_ATTR_ID, set_name); + crm_xml_add(xml_obj, PCMK_XA_ID, set_name); if (xml_top == NULL) { xml_top = xml_obj; @@ -321,15 +318,20 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c crm_log_xml_trace(xml_top, "update_attr"); rc = cib_internal_op(cib, PCMK__CIB_REQUEST_MODIFY, NULL, section, xml_top, NULL, call_options, user_name); - if (rc < 0) { + + if (!pcmk_is_set(call_options, cib_sync_call) && (cib->variant != cib_file) + && (rc >= 0)) { + // For async call, positive rc is the call ID (file always synchronous) + rc = pcmk_rc_ok; + } else { rc = pcmk_legacy2rc(rc); + } + if (rc != pcmk_rc_ok) { out->err(out, "Error setting %s=%s (section=%s, set=%s): %s", attr_name, attr_value, section, pcmk__s(set_name, "<null>"), pcmk_rc_str(rc)); crm_log_xml_info(xml_top, "Update"); - } else { - rc = pcmk_rc_ok; } free(local_set_name); @@ -387,7 +389,7 @@ cib__delete_node_attr(pcmk__output_t *out, cib_t *cib, int options, const char * free_xml(xml_search); return rc; } else { - pcmk__str_update(&local_attr_id, crm_element_value(xml_search, XML_ATTR_ID)); + local_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID); attr_id = local_attr_id; free_xml(xml_search); } @@ -397,16 +399,21 @@ cib__delete_node_attr(pcmk__output_t *out, cib_t *cib, int options, const char * rc = cib_internal_op(cib, PCMK__CIB_REQUEST_DELETE, NULL, section, xml_obj, NULL, options, user_name); - if (rc < 0) { - rc = pcmk_legacy2rc(rc); - } else { + + if (!pcmk_is_set(options, cib_sync_call) && (cib->variant != cib_file) + && (rc >= 0)) { + // For async call, positive rc is the call ID (file always synchronous) rc = pcmk_rc_ok; + } else { + rc = pcmk_legacy2rc(rc); + } + + if (rc == pcmk_rc_ok) { out->info(out, "Deleted %s %s: id=%s%s%s%s%s", section, node_uuid ? "attribute" : "option", local_attr_id, set_name ? " set=" : "", set_name ? set_name : "", attr_name ? " name=" : "", attr_name ? attr_name : ""); } - free(local_attr_id); free_xml(xml_obj); return rc; @@ -487,7 +494,8 @@ read_attr_delegate(cib_t *cib, const char *section, const char *node_uuid, if (rc == pcmk_rc_ok) { if (result->children == NULL) { - pcmk__str_update(attr_value, crm_element_value(result, XML_NVPAIR_ATTR_VALUE)); + pcmk__str_update(attr_value, + crm_element_value(result, PCMK_XA_VALUE)); } else { rc = ENOTUNIQ; } @@ -535,7 +543,6 @@ static int get_uuid_from_result(const xmlNode *result, char **uuid, int *is_remote) { int rc = -ENXIO; - const char *tag; const char *parsed_uuid = NULL; int parsed_is_remote = FALSE; @@ -544,41 +551,42 @@ get_uuid_from_result(const xmlNode *result, char **uuid, int *is_remote) } /* If there are multiple results, the first is sufficient */ - tag = (const char *) (result->name); - if (pcmk__str_eq(tag, "xpath-query", pcmk__str_casei)) { - result = pcmk__xml_first_child(result); + if (pcmk__xe_is(result, PCMK__XE_XPATH_QUERY)) { + result = pcmk__xe_first_child(result, NULL, NULL, NULL); CRM_CHECK(result != NULL, return rc); - tag = (const char *) (result->name); } - if (pcmk__str_eq(tag, XML_CIB_TAG_NODE, pcmk__str_casei)) { - /* Result is <node> tag from <nodes> section */ + if (pcmk__xe_is(result, PCMK_XE_NODE)) { + // Result is PCMK_XE_NODE element from PCMK_XE_NODES section - if (pcmk__str_eq(crm_element_value(result, XML_ATTR_TYPE), "remote", pcmk__str_casei)) { - parsed_uuid = crm_element_value(result, XML_ATTR_UNAME); + if (pcmk__str_eq(crm_element_value(result, PCMK_XA_TYPE), + PCMK_VALUE_REMOTE, pcmk__str_casei)) { + parsed_uuid = crm_element_value(result, PCMK_XA_UNAME); parsed_is_remote = TRUE; } else { - parsed_uuid = ID(result); + parsed_uuid = pcmk__xe_id(result); parsed_is_remote = FALSE; } - } else if (pcmk__str_eq(tag, XML_CIB_TAG_RESOURCE, pcmk__str_casei)) { + } else if (pcmk__xe_is(result, PCMK_XE_PRIMITIVE)) { /* Result is <primitive> for ocf:pacemaker:remote resource */ - parsed_uuid = ID(result); + parsed_uuid = pcmk__xe_id(result); parsed_is_remote = TRUE; - } else if (pcmk__str_eq(tag, XML_CIB_TAG_NVPAIR, pcmk__str_casei)) { - /* Result is remote-node parameter of <primitive> for guest node */ + } else if (pcmk__xe_is(result, PCMK_XE_NVPAIR)) { + /* Result is PCMK_META_REMOTE_NODE parameter of <primitive> for guest + * node + */ - parsed_uuid = crm_element_value(result, XML_NVPAIR_ATTR_VALUE); + parsed_uuid = crm_element_value(result, PCMK_XA_VALUE); parsed_is_remote = TRUE; - } else if (pcmk__str_eq(tag, XML_CIB_TAG_STATE, pcmk__str_casei)) { - /* Result is <node_state> tag from <status> section */ + } else if (pcmk__xe_is(result, PCMK__XE_NODE_STATE)) { + // Result is PCMK__XE_NODE_STATE element from PCMK_XE_STATUS section - parsed_uuid = crm_element_value(result, XML_ATTR_UNAME); - if (pcmk__xe_attr_is_true(result, XML_NODE_IS_REMOTE)) { + parsed_uuid = crm_element_value(result, PCMK_XA_UNAME); + if (pcmk__xe_attr_is_true(result, PCMK_XA_REMOTE_NODE)) { parsed_is_remote = TRUE; } } @@ -605,16 +613,16 @@ get_uuid_from_result(const xmlNode *result, char **uuid, int *is_remote) #define XPATH_UPPER_TRANS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define XPATH_LOWER_TRANS "abcdefghijklmnopqrstuvwxyz" #define XPATH_NODE \ - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES \ - "/" XML_CIB_TAG_NODE "[translate(@" XML_ATTR_UNAME ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \ - "|/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES \ - "/" XML_CIB_TAG_RESOURCE \ + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_NODES \ + "/" PCMK_XE_NODE "[translate(@" PCMK_XA_UNAME ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \ + "|/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RESOURCES \ + "/" PCMK_XE_PRIMITIVE \ "[@class='ocf'][@provider='pacemaker'][@type='remote'][translate(@id,'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \ - "|/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES \ - "/" XML_CIB_TAG_RESOURCE "/" XML_TAG_META_SETS "/" XML_CIB_TAG_NVPAIR \ - "[@name='" XML_RSC_ATTR_REMOTE_NODE "'][translate(@value,'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \ - "|/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS "/" XML_CIB_TAG_STATE \ - "[@" XML_NODE_IS_REMOTE "='true'][translate(@" XML_ATTR_ID ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" + "|/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RESOURCES \ + "/" PCMK_XE_PRIMITIVE "/" PCMK_XE_META_ATTRIBUTES "/" PCMK_XE_NVPAIR \ + "[@name='" PCMK_META_REMOTE_NODE "'][translate(@value,'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \ + "|/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK__XE_NODE_STATE \ + "[@" PCMK_XA_REMOTE_NODE "='true'][translate(@" PCMK_XA_ID ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" int query_node_uuid(cib_t * the_cib, const char *uname, char **uuid, int *is_remote_node) @@ -657,6 +665,11 @@ query_node_uuid(cib_t * the_cib, const char *uname, char **uuid, int *is_remote_ return rc; } +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/cib/util_compat.h> + int query_node_uname(cib_t * the_cib, const char *uuid, char **uname) { @@ -669,33 +682,31 @@ query_node_uname(cib_t * the_cib, const char *uuid, char **uname) CRM_ASSERT(uname != NULL); CRM_ASSERT(uuid != NULL); - rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment, + rc = the_cib->cmds->query(the_cib, PCMK_XE_NODES, &fragment, cib_sync_call | cib_scope_local); if (rc != pcmk_ok) { return rc; } xml_obj = fragment; - CRM_CHECK(pcmk__xe_is(xml_obj, XML_CIB_TAG_NODES), return -ENOMSG); + CRM_CHECK(pcmk__xe_is(xml_obj, PCMK_XE_NODES), return -ENOMSG); crm_log_xml_trace(xml_obj, "Result section"); rc = -ENXIO; *uname = NULL; - for (a_child = pcmk__xml_first_child(xml_obj); a_child != NULL; - a_child = pcmk__xml_next(a_child)) { - - if (pcmk__str_eq((const char *)a_child->name, XML_CIB_TAG_NODE, - pcmk__str_none)) { - child_name = ID(a_child); - if (pcmk__str_eq(uuid, child_name, pcmk__str_casei)) { - child_name = crm_element_value(a_child, XML_ATTR_UNAME); - if (child_name != NULL) { - *uname = strdup(child_name); - rc = pcmk_ok; - } - break; + for (a_child = pcmk__xe_first_child(xml_obj, PCMK_XE_NODE, NULL, NULL); + a_child != NULL; a_child = pcmk__xe_next_same(a_child)) { + + child_name = pcmk__xe_id(a_child); + + if (pcmk__str_eq(uuid, child_name, pcmk__str_casei)) { + child_name = crm_element_value(a_child, PCMK_XA_UNAME); + if (child_name != NULL) { + *uname = strdup(child_name); + rc = pcmk_ok; } + break; } } @@ -712,18 +723,22 @@ set_standby(cib_t * the_cib, const char *uuid, const char *scope, const char *st CRM_CHECK(uuid != NULL, return -EINVAL); CRM_CHECK(standby_value != NULL, return -EINVAL); - if (pcmk__strcase_any_of(scope, "reboot", XML_CIB_TAG_STATUS, NULL)) { - scope = XML_CIB_TAG_STATUS; + if (pcmk__strcase_any_of(scope, "reboot", PCMK_XE_STATUS, NULL)) { + scope = PCMK_XE_STATUS; attr_id = crm_strdup_printf("transient-standby-%.256s", uuid); } else { - scope = XML_CIB_TAG_NODES; + scope = PCMK_XE_NODES; attr_id = crm_strdup_printf("standby-%.256s", uuid); } rc = update_attr_delegate(the_cib, cib_sync_call, scope, uuid, NULL, NULL, - attr_id, "standby", standby_value, TRUE, NULL, NULL); + attr_id, PCMK_NODE_ATTR_STANDBY, standby_value, + TRUE, NULL, NULL); free(attr_id); return rc; } + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 32e1f83..c980429 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.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 <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> static GHashTable *cib_op_callback_table = NULL; @@ -89,7 +88,7 @@ cib_client_add_notify_callback(cib_t * cib, const char *event, crm_trace("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list)); - new_client = calloc(1, sizeof(cib_notify_client_t)); + new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; @@ -147,7 +146,7 @@ cib_client_del_notify_callback(cib_t *cib, const char *event, crm_debug("Removing callback for %s events", event); - new_client = calloc(1, sizeof(cib_notify_client_t)); + new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; @@ -209,7 +208,7 @@ cib_client_register_callback_full(cib_t *cib, int call_id, int timeout, return FALSE; } - blob = calloc(1, sizeof(cib_callback_client_t)); + blob = pcmk__assert_alloc(1, sizeof(cib_callback_client_t)); blob->id = callback_name; blob->only_success = only_success; blob->user_data = user_data; @@ -217,9 +216,9 @@ cib_client_register_callback_full(cib_t *cib, int call_id, int timeout, blob->free_func = free_func; if (timeout > 0) { - struct timer_rec_s *async_timer = NULL; + struct timer_rec_s *async_timer = + pcmk__assert_alloc(1, sizeof(struct timer_rec_s)); - async_timer = calloc(1, sizeof(struct timer_rec_s)); blob->timer = async_timer; async_timer->cib = cib; @@ -401,10 +400,7 @@ cib_client_init_transaction(cib_t *cib) } if (rc == pcmk_rc_ok) { - cib->transaction = create_xml_node(NULL, T_CIB_TRANSACTION); - if (cib->transaction == NULL) { - rc = ENOMEM; - } + cib->transaction = pcmk__xe_create(NULL, PCMK__XE_CIB_TRANSACTION); } if (rc != pcmk_rc_ok) { @@ -451,6 +447,21 @@ cib_client_end_transaction(cib_t *cib, bool commit, int call_options) return rc; } +static int +cib_client_fetch_schemas(cib_t *cib, xmlNode **output_data, const char *after_ver, + int call_options) +{ + xmlNode *data = pcmk__xe_create(NULL, PCMK__XA_SCHEMA); + int rc = pcmk_ok; + + crm_xml_add(data, PCMK_XA_VERSION, after_ver); + + rc = cib_internal_op(cib, PCMK__CIB_REQUEST_SCHEMAS, NULL, NULL, data, + output_data, call_options, NULL); + free_xml(data); + return rc; +} + static void cib_client_set_user(cib_t *cib, const char *user) { @@ -736,6 +747,8 @@ cib_new_variant(void) new_cib->cmds->set_user = cib_client_set_user; + new_cib->cmds->fetch_schemas = cib_client_fetch_schemas; + return new_cib; } diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index a279823..24bd029 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -1,6 +1,6 @@ /* * Original copyright 2004 International Business Machines - * Later changes copyright 2008-2023 the Pacemaker project contributors + * Later changes copyright 2008-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -24,7 +24,6 @@ #include <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/ipc.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> @@ -219,9 +218,11 @@ cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output) int call_id = 0; int call_options = cib_none; - const char *op = crm_element_value(request, F_CIB_OPERATION); - const char *section = crm_element_value(request, F_CIB_SECTION); - xmlNode *data = get_message_xml(request, F_CIB_CALLDATA); + const char *op = crm_element_value(request, PCMK__XA_CIB_OP); + const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION); + xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, + NULL, NULL); + xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); bool changed = false; bool read_only = false; @@ -234,18 +235,18 @@ cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output) cib__get_operation(op, &operation); op_function = file_get_op_function(operation); - crm_element_value_int(request, F_CIB_CALLID, &call_id); - crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); + crm_element_value_int(request, PCMK__XA_CIB_CALLID, &call_id); + crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options); read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies); // Mirror the logic in prepare_input() in pacemaker-based - if ((section != NULL) && pcmk__xe_is(data, XML_TAG_CIB)) { + if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) { data = pcmk_find_cib_element(data, section); } - rc = cib_perform_op(op, call_options, op_function, read_only, section, + rc = cib_perform_op(cib, op, call_options, op_function, read_only, section, request, data, true, &changed, &private->cib_xml, &result_cib, &cib_diff, output); @@ -257,7 +258,8 @@ cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output) } if (rc == -pcmk_err_schema_validation) { - validate_xml_verbose(result_cib); + // Show validation errors to stderr + pcmk__validate_xml(result_cib, NULL, NULL, NULL); } else if ((rc == pcmk_ok) && !read_only) { pcmk__log_xml_patchset(LOG_DEBUG, cib_diff); @@ -327,8 +329,8 @@ cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (rc != pcmk_ok) { return rc; } - crm_xml_add(request, XML_ACL_TAG_USER, user_name); - crm_xml_add(request, F_CIB_CLIENTID, private->id); + crm_xml_add(request, PCMK_XE_ACL_TARGET, user_name); + crm_xml_add(request, PCMK__XA_CIB_CLIENTID, private->id); if (pcmk_is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, request); @@ -339,7 +341,7 @@ cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if ((output_data != NULL) && (output != NULL)) { if (output->doc == private->cib_xml->doc) { - *output_data = copy_xml(output); + *output_data = pcmk__xml_copy(NULL, output); } else { *output_data = output; } @@ -383,21 +385,21 @@ load_file_cib(const char *filename, xmlNode **output) } /* Parse XML from file */ - root = filename2xml(filename); + root = pcmk__xml_read(filename); if (root == NULL) { return -pcmk_err_schema_validation; } /* Add a status section if not already present */ - if (find_xml_node(root, XML_CIB_TAG_STATUS, FALSE) == NULL) { - create_xml_node(root, XML_CIB_TAG_STATUS); + if (pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL) == NULL) { + pcmk__xe_create(root, PCMK_XE_STATUS); } /* Validate XML against its specified schema */ - if (validate_xml(root, NULL, TRUE) == FALSE) { - const char *schema = crm_element_value(root, XML_ATTR_VALIDATION); + if (!pcmk__configured_schema_validates(root)) { + const char *schema = crm_element_value(root, PCMK_XA_VALIDATE_WITH); - crm_err("CIB does not validate against %s", schema); + crm_err("CIB does not validate against %s, or that schema is unknown", schema); free_xml(root); return -pcmk_err_schema_validation; } @@ -437,8 +439,8 @@ cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type) * \internal * \brief Write out the in-memory CIB to a live CIB file * - * param[in] cib_root Root of XML tree to write - * param[in,out] path Full path to file to write + * \param[in] cib_root Root of XML tree to write + * \param[in,out] path Full path to file to write * * \return 0 on success, -1 on failure */ @@ -547,10 +549,10 @@ cib_file_signoff(cib_t *cib) /* Otherwise, it's a simple write */ } else { - gboolean do_bzip = pcmk__ends_with_ext(private->filename, ".bz2"); + bool compress = pcmk__ends_with_ext(private->filename, ".bz2"); - if (write_xml_file(private->cib_xml, private->filename, - do_bzip) <= 0) { + if (pcmk__xml_write_file(private->cib_xml, private->filename, + compress, NULL) != pcmk_rc_ok) { rc = pcmk_err_generic; } } @@ -764,7 +766,7 @@ cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **ro } /* Parse XML */ - local_root = filename2xml(filename); + local_root = pcmk__xml_read(filename); if (local_root == NULL) { crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename); return -pcmk_err_cib_corrupt; @@ -876,8 +878,8 @@ cib_file_backup(const char *cib_dirname, const char *cib_filename) * \internal * \brief Prepare CIB XML to be written to disk * - * Set num_updates to 0, set cib-last-written to the current timestamp, - * and strip out the status section. + * Set \c PCMK_XA_NUM_UPDATES to 0, set \c PCMK_XA_CIB_LAST_WRITTEN to the + * current timestamp, and strip out the status section. * * \param[in,out] root Root of CIB XML tree * @@ -889,16 +891,14 @@ cib_file_prepare_xml(xmlNode *root) xmlNode *cib_status_root = NULL; /* Always write out with num_updates=0 and current last-written timestamp */ - crm_xml_add(root, XML_ATTR_NUMUPDATES, "0"); + crm_xml_add(root, PCMK_XA_NUM_UPDATES, "0"); pcmk__xe_add_last_written(root); /* Delete status section before writing to file, because * we discard it on startup anyway, and users get confused by it */ - cib_status_root = find_xml_node(root, XML_CIB_TAG_STATUS, TRUE); - CRM_LOG_ASSERT(cib_status_root != NULL); - if (cib_status_root != NULL) { - free_xml(cib_status_root); - } + cib_status_root = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL); + CRM_CHECK(cib_status_root != NULL, return); + free_xml(cib_status_root); } /*! @@ -923,9 +923,8 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, char *digest = NULL; /* Detect CIB version for diagnostic purposes */ - const char *epoch = crm_element_value(cib_root, XML_ATTR_GENERATION); - const char *admin_epoch = crm_element_value(cib_root, - XML_ATTR_GENERATION_ADMIN); + const char *epoch = crm_element_value(cib_root, PCMK_XA_EPOCH); + const char *admin_epoch = crm_element_value(cib_root, PCMK_XA_ADMIN_EPOCH); /* Determine full CIB and signature pathnames */ char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename); @@ -935,9 +934,6 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname); char *tmp_digest = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname); - CRM_ASSERT((cib_path != NULL) && (digest_path != NULL) - && (tmp_cib != NULL) && (tmp_digest != NULL)); - /* Ensure the admin didn't modify the existing CIB underneath us */ crm_trace("Reading cluster configuration file %s", cib_path); rc = cib_file_read_and_verify(cib_path, NULL, NULL); @@ -982,7 +978,7 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, } /* Write out the CIB */ - if (write_xml_fd(cib_root, tmp_cib, fd, FALSE) <= 0) { + if (pcmk__xml_write_fd(cib_root, tmp_cib, fd, false, NULL) != pcmk_rc_ok) { crm_err("Changes couldn't be written to %s", tmp_cib); exit_rc = pcmk_err_cib_save; goto cleanup; @@ -1063,11 +1059,13 @@ cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction) { cib_file_opaque_t *private = cib->variant_opaque; - for (xmlNode *request = first_named_child(transaction, T_CIB_COMMAND); - request != NULL; request = crm_next_same_xml(request)) { + for (xmlNode *request = pcmk__xe_first_child(transaction, + PCMK__XE_CIB_COMMAND, NULL, + NULL); + request != NULL; request = pcmk__xe_next_same(request)) { xmlNode *output = NULL; - const char *op = crm_element_value(request, F_CIB_OPERATION); + const char *op = crm_element_value(request, PCMK__XA_CIB_OP); int rc = cib_file_process_request(cib, request, &output); @@ -1111,7 +1109,7 @@ cib_file_commit_transaction(cib_t *cib, xmlNode *transaction, cib_file_opaque_t *private = cib->variant_opaque; xmlNode *saved_cib = private->cib_xml; - CRM_CHECK(pcmk__xe_is(transaction, T_CIB_TRANSACTION), + CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), return pcmk_rc_no_transaction); /* *result_cib should be a copy of private->cib_xml (created by @@ -1122,7 +1120,7 @@ cib_file_commit_transaction(cib_t *cib, xmlNode *transaction, * * cib_perform_op() will infer changes for the commit request at the end. */ CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml), - *result_cib = copy_xml(private->cib_xml)); + *result_cib = pcmk__xml_copy(NULL, private->cib_xml)); crm_trace("Committing transaction for CIB file client (%s) on file '%s' to " "working CIB", @@ -1158,7 +1156,7 @@ cib_file_process_commit_transaction(const char *op, int options, xmlNode **result_cib, xmlNode **answer) { int rc = pcmk_rc_ok; - const char *client_id = crm_element_value(req, F_CIB_CLIENTID); + const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID); cib_t *cib = NULL; CRM_CHECK(client_id != NULL, return -EINVAL); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index c5e8b9e..b014223 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -1,6 +1,6 @@ /* * Copyright 2004 International Business Machines - * Later changes copyright 2004-2023 the Pacemaker project contributors + * Later changes copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -27,8 +27,8 @@ #include <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/mainloop.h> +#include <crm/common/xml.h> typedef struct cib_native_opaque_s { char *token; @@ -94,26 +94,28 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (!(call_options & cib_sync_call)) { crm_trace("Async call, returning %d", cib->call_id); - CRM_CHECK(cib->call_id != 0, return -ENOMSG); - free_xml(op_reply); - return cib->call_id; + CRM_CHECK(cib->call_id != 0, + rc = -ENOMSG; goto done); + rc = cib->call_id; + goto done; } rc = pcmk_ok; - crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); + crm_element_value_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id); if (reply_id == cib->call_id) { - xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); + xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, + NULL, NULL); + xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); crm_trace("Synchronous reply %d received", reply_id); - if (crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { + if (crm_element_value_int(op_reply, PCMK__XA_CIB_RC, &rc) != 0) { rc = -EPROTO; } if (output_data == NULL || (call_options & cib_discard_reply)) { crm_trace("Discarding reply"); - - } else if (tmp != NULL) { - *output_data = copy_xml(tmp); + } else { + *output_data = pcmk__xml_copy(NULL, tmp); } } else if (reply_id <= 0) { @@ -188,7 +190,7 @@ cib_native_dispatch_internal(const char *buffer, ssize_t length, return 0; } - msg = string2xml(buffer); + msg = pcmk__xml_parse(buffer); if (msg == NULL) { crm_warn("Received a NULL message from the CIB manager"); @@ -196,14 +198,14 @@ cib_native_dispatch_internal(const char *buffer, ssize_t length, } /* do callbacks */ - type = crm_element_value(msg, F_TYPE); + type = crm_element_value(msg, PCMK__XA_T); crm_trace("Activating %s callbacks...", type); crm_log_xml_explicit(msg, "cib-reply"); - if (pcmk__str_eq(type, T_CIB, pcmk__str_casei)) { + if (pcmk__str_eq(type, PCMK__VALUE_CIB, pcmk__str_none)) { cib_native_callback(cib, msg, 0, 0); - } else if (pcmk__str_eq(type, T_CIB_NOTIFY, pcmk__str_casei)) { + } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { g_list_foreach(cib->notify_list, cib_native_notify, msg); } else { @@ -332,7 +334,7 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type, if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply) > 0) { - const char *msg_type = crm_element_value(reply, F_CIB_OPERATION); + const char *msg_type = crm_element_value(reply, PCMK__XA_CIB_OP); crm_log_xml_trace(reply, "reg-reply"); @@ -343,7 +345,8 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type, rc = -EPROTO; } else { - native->token = crm_element_value_copy(reply, F_CIB_CLIENTID); + native->token = crm_element_value_copy(reply, + PCMK__XA_CIB_CLIENTID); if (native->token == NULL) { rc = -EPROTO; } @@ -399,13 +402,13 @@ static int cib_native_register_notification(cib_t *cib, const char *callback, int enabled) { int rc = pcmk_ok; - xmlNode *notify_msg = create_xml_node(NULL, "cib-callback"); + xmlNode *notify_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_CALLBACK); cib_native_opaque_t *native = cib->variant_opaque; if (cib->state != cib_disconnected) { - crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); - crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); - crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); + crm_xml_add(notify_msg, PCMK__XA_CIB_OP, PCMK__VALUE_CIB_NOTIFY); + crm_xml_add(notify_msg, PCMK__XA_CIB_NOTIFY_TYPE, callback); + crm_xml_add_int(notify_msg, PCMK__XA_CIB_NOTIFY_ACTIVATE, enabled); rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, 1000 * cib->call_timeout, NULL); if (rc <= 0) { diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index c324304..fd42317 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.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/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> @@ -127,6 +126,9 @@ static const cib__operation_t cib_ops[] = { |cib__op_attr_writes_through |cib__op_attr_transaction }, + { + PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local + } }; /*! @@ -180,7 +182,7 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re CRM_CHECK(*answer == NULL, free_xml(*answer)); *answer = NULL; - if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) { + if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; } @@ -190,10 +192,10 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re result = -ENXIO; } else if (options & cib_no_children) { - xmlNode *shallow = create_xml_node(*answer, + xmlNode *shallow = pcmk__xe_create(*answer, (const char *) obj_root->name); - copy_in_properties(shallow, obj_root); + pcmk__xe_copy_attrs(shallow, obj_root, pcmk__xaf_none); *answer = shallow; } else { @@ -222,8 +224,7 @@ update_counter(xmlNode *xml_obj, const char *field, bool reset) int_value = atoi(old_value); new_value = pcmk__itoa(++int_value); } else { - new_value = strdup("1"); - CRM_ASSERT(new_value != NULL); + new_value = pcmk__str_copy("1"); } crm_trace("Update %s from %s to %s", @@ -248,8 +249,8 @@ cib_process_erase(const char *op, int options, const char *section, xmlNode * re free_xml(*result_cib); } *result_cib = createEmptyCib(0); - copy_in_properties(*result_cib, existing_cib); - update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false); + pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none); + update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); *answer = NULL; return result; @@ -261,29 +262,23 @@ cib_process_upgrade(const char *op, int options, const char *section, xmlNode * xmlNode ** answer) { int rc = 0; - int new_version = 0; - int current_version = 0; - int max_version = 0; - const char *max = crm_element_value(req, F_CIB_SCHEMA_MAX); - const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION); + const char *max_schema = crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX); + const char *original_schema = NULL; + const char *new_schema = NULL; *answer = NULL; - crm_trace("Processing \"%s\" event with max=%s", op, max); - - if (value != NULL) { - current_version = get_schema_version(value); - } - - if (max) { - max_version = get_schema_version(max); - } - - rc = update_validation(result_cib, &new_version, max_version, TRUE, - !(options & cib_verbose)); - if (new_version > current_version) { - update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false); - update_counter(*result_cib, XML_ATTR_GENERATION, true); - update_counter(*result_cib, XML_ATTR_NUMUPDATES, true); + crm_trace("Processing \"%s\" event with max=%s", op, max_schema); + + original_schema = crm_element_value(existing_cib, PCMK_XA_VALIDATE_WITH); + rc = pcmk__update_schema(result_cib, max_schema, true, + !pcmk_is_set(options, cib_verbose)); + rc = pcmk_rc2legacy(rc); + new_schema = crm_element_value(*result_cib, PCMK_XA_VALIDATE_WITH); + + if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { + update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); + update_counter(*result_cib, PCMK_XA_EPOCH, true); + update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true); return pcmk_ok; } @@ -297,10 +292,10 @@ cib_process_bump(const char *op, int options, const char *section, xmlNode * req int result = pcmk_ok; crm_trace("Processing %s for epoch='%s'", op, - pcmk__s(crm_element_value(existing_cib, XML_ATTR_GENERATION), "")); + pcmk__s(crm_element_value(existing_cib, PCMK_XA_EPOCH), "")); *answer = NULL; - update_counter(*result_cib, XML_ATTR_GENERATION, false); + update_counter(*result_cib, PCMK_XA_EPOCH, false); return result; } @@ -326,14 +321,14 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * return -EINVAL; } - if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) { + if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; } else if (pcmk__xe_is(input, section)) { section = NULL; } - if (pcmk__xe_is(input, XML_TAG_CIB)) { + if (pcmk__xe_is(input, PCMK_XE_CIB)) { int updates = 0; int epoch = 0; int admin_epoch = 0; @@ -343,11 +338,12 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * int replace_admin_epoch = 0; const char *reason = NULL; - const char *peer = crm_element_value(req, F_ORIG); - const char *digest = crm_element_value(req, XML_ATTR_DIGEST); + const char *peer = crm_element_value(req, PCMK__XA_SRC); + const char *digest = crm_element_value(req, PCMK__XA_DIGEST); if (digest) { - const char *version = crm_element_value(req, XML_ATTR_CRM_VERSION); + const char *version = crm_element_value(req, + PCMK_XA_CRM_FEATURE_SET); char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE, version ? version : CRM_FEATURE_SET); @@ -370,19 +366,19 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); if (replace_admin_epoch < admin_epoch) { - reason = XML_ATTR_GENERATION_ADMIN; + reason = PCMK_XA_ADMIN_EPOCH; } else if (replace_admin_epoch > admin_epoch) { /* no more checks */ } else if (replace_epoch < epoch) { - reason = XML_ATTR_GENERATION; + reason = PCMK_XA_EPOCH; } else if (replace_epoch > epoch) { /* no more checks */ } else if (replace_updates < updates) { - reason = XML_ATTR_NUMUPDATES; + reason = PCMK_XA_NUM_UPDATES; } if (reason != NULL) { @@ -400,23 +396,35 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * if (*result_cib != existing_cib) { free_xml(*result_cib); } - *result_cib = copy_xml(input); + *result_cib = pcmk__xml_copy(NULL, input); } else { xmlNode *obj_root = NULL; - gboolean ok = TRUE; obj_root = pcmk_find_cib_element(*result_cib, section); - ok = replace_xml_child(NULL, obj_root, input, FALSE); - if (ok == FALSE) { + result = pcmk__xe_replace_match(obj_root, input); + result = pcmk_rc2legacy(result); + if (result != pcmk_ok) { crm_trace("No matching object to replace"); - result = -ENXIO; } } return result; } +static int +delete_child(xmlNode *child, void *userdata) +{ + xmlNode *obj_root = userdata; + + if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) { + crm_trace("No matching object to delete: %s=%s", + child->name, pcmk__xe_id(child)); + } + + return pcmk_rc_ok; +} + int cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -437,16 +445,9 @@ cib_process_delete(const char *op, int options, const char *section, xmlNode * r obj_root = pcmk_find_cib_element(*result_cib, section); if (pcmk__xe_is(input, section)) { - xmlNode *child = NULL; - for (child = pcmk__xml_first_child(input); child; - child = pcmk__xml_next(child)) { - if (replace_xml_child(NULL, obj_root, child, TRUE) == FALSE) { - crm_trace("No matching object to delete: %s=%s", child->name, ID(child)); - } - } - - } else if (replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) { - crm_trace("No matching object to delete: %s=%s", input->name, ID(input)); + pcmk__xe_foreach_child(input, NULL, delete_child, obj_root); + } else { + delete_child(input, obj_root); } return pcmk_ok; @@ -457,6 +458,7 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { xmlNode *obj_root = NULL; + uint32_t flags = pcmk__xaf_none; crm_trace("Processing \"%s\" event", op); @@ -479,7 +481,7 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r return -EINVAL; } - tmp_section = create_xml_node(NULL, section); + tmp_section = pcmk__xe_create(NULL, section); cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, NULL, result_cib, answer); free_xml(tmp_section); @@ -489,9 +491,13 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r CRM_CHECK(obj_root != NULL, return -EINVAL); - if (update_xml_child(obj_root, input) == FALSE) { + if (pcmk_is_set(options, cib_score_update)) { + flags |= pcmk__xaf_score_update; + } + + if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) { if (options & cib_can_create) { - add_node_copy(obj_root, input); + pcmk__xml_copy(obj_root, input); } else { return -ENXIO; } @@ -522,123 +528,10 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r } static int -update_cib_object(xmlNode * parent, xmlNode * update) -{ - int result = pcmk_ok; - xmlNode *target = NULL; - xmlNode *a_child = NULL; - const char *replace = NULL; - const char *object_id = NULL; - const char *object_name = NULL; - - CRM_CHECK(update != NULL, return -EINVAL); - CRM_CHECK(parent != NULL, return -EINVAL); - - object_name = (const char *) update->name; - CRM_CHECK(object_name != NULL, return -EINVAL); - - object_id = ID(update); - crm_trace("Processing update for <%s%s%s%s>", object_name, - ((object_id == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(object_id, ""), - ((object_id == NULL)? "" : "'")); - - if (object_id == NULL) { - /* placeholder object */ - target = find_xml_node(parent, object_name, FALSE); - - } else { - target = pcmk__xe_match(parent, object_name, XML_ATTR_ID, object_id); - } - - if (target == NULL) { - target = create_xml_node(parent, object_name); - } - - crm_trace("Found node <%s%s%s%s> to update", object_name, - ((object_id == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(object_id, ""), - ((object_id == NULL)? "" : "'")); - - // @COMPAT: XML_CIB_ATTR_REPLACE is unused internally. Remove at break. - replace = crm_element_value(update, XML_CIB_ATTR_REPLACE); - if (replace != NULL) { - int last = 0; - int len = strlen(replace); - - for (int lpc = 0; lpc <= len; ++lpc) { - if (replace[lpc] == ',' || replace[lpc] == 0) { - if (last != lpc) { - char *replace_item = strndup(replace + last, lpc - last); - xmlNode *remove = find_xml_node(target, replace_item, - FALSE); - - if (remove != NULL) { - crm_trace("Replacing node <%s> in <%s>", - replace_item, target->name); - free_xml(remove); - } - free(replace_item); - } - last = lpc + 1; - } - } - xml_remove_prop(update, XML_CIB_ATTR_REPLACE); - xml_remove_prop(target, XML_CIB_ATTR_REPLACE); - } - - copy_in_properties(target, update); - - if (xml_acl_denied(target)) { - crm_notice("Cannot update <%s " XML_ATTR_ID "=%s>", - pcmk__s(object_name, "<null>"), - pcmk__s(object_id, "<null>")); - return -EACCES; - } - - crm_trace("Processing children of <%s%s%s%s>", object_name, - ((object_id == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(object_id, ""), - ((object_id == NULL)? "" : "'")); - - for (a_child = pcmk__xml_first_child(update); a_child != NULL; - a_child = pcmk__xml_next(a_child)) { - int tmp_result = 0; - - crm_trace("Updating child <%s%s%s%s>", a_child->name, - ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(ID(a_child), ""), ((ID(a_child) == NULL)? "" : "'")); - - tmp_result = update_cib_object(target, a_child); - - /* only the first error is likely to be interesting */ - if (tmp_result != pcmk_ok) { - crm_err("Error updating child <%s%s%s%s>", - a_child->name, - ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(ID(a_child), ""), - ((ID(a_child) == NULL)? "" : "'")); - - if (result == pcmk_ok) { - result = tmp_result; - } - } - } - - crm_trace("Finished handling update for <%s%s%s%s>", object_name, - ((object_id == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(object_id, ""), - ((object_id == NULL)? "" : "'")); - - return result; -} - -static int add_cib_object(xmlNode * parent, xmlNode * new_obj) { const char *object_name = NULL; const char *object_id = NULL; - xmlNode *equiv_node = NULL; if ((parent == NULL) || (new_obj == NULL)) { return -EINVAL; @@ -649,24 +542,33 @@ add_cib_object(xmlNode * parent, xmlNode * new_obj) return -EINVAL; } - object_id = ID(new_obj); - - crm_trace("Processing creation of <%s%s%s%s>", object_name, - ((object_id == NULL)? "" : " " XML_ATTR_ID "='"), - pcmk__s(object_id, ""), - ((object_id == NULL)? "" : "'")); + object_id = pcmk__xe_id(new_obj); + if (pcmk__xe_first_child(parent, object_name, + ((object_id != NULL)? PCMK_XA_ID : NULL), + object_id)) { + return -EEXIST; + } - if (object_id == NULL) { - equiv_node = find_xml_node(parent, object_name, FALSE); + if (object_id != NULL) { + crm_trace("Processing creation of <%s " PCMK_XA_ID "='%s'>", + object_name, object_id); } else { - equiv_node = pcmk__xe_match(parent, object_name, XML_ATTR_ID, - object_id); - } - if (equiv_node != NULL) { - return -EEXIST; + crm_trace("Processing creation of <%s>", object_name); } - return update_cib_object(parent, new_obj); + /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use + * case, PCMK__XA_REPLACE has special meaning and should not be included in + * the newly created object until we can break behavioral backward + * compatibility. + * + * At a compatibility break, drop this and drop the definition of + * PCMK__XA_REPLACE. Treat it like any other attribute. + */ + pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb, + (void *) PCMK__XA_REPLACE); + + pcmk__xml_copy(parent, new_obj); + return pcmk_ok; } static bool @@ -681,14 +583,13 @@ update_results(xmlNode *failed, xmlNode *target, const char *operation, error_msg = pcmk_strerror(return_code); was_error = true; - xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB); - add_node_copy(xml_node, target); + xml_node = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE); + pcmk__xml_copy(xml_node, target); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target)); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, - (const char *) target->name); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg); + crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(target)); + crm_xml_add(xml_node, PCMK_XA_OBJECT_TYPE, (const char *) target->name); + crm_xml_add(xml_node, PCMK_XA_OPERATION, operation); + crm_xml_add(xml_node, PCMK_XA_REASON, error_msg); crm_warn("Action %s failed: %s (cde=%d)", operation, error_msg, return_code); @@ -707,13 +608,13 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r crm_trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); - if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) { + if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; - } else if (pcmk__str_eq(XML_TAG_CIB, section, pcmk__str_casei)) { + } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) { section = NULL; - } else if (pcmk__xe_is(input, XML_TAG_CIB)) { + } else if (pcmk__xe_is(input, PCMK_XE_CIB)) { section = NULL; } @@ -729,7 +630,8 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r answer); } - failed = create_xml_node(NULL, XML_TAG_FAILED); + // @COMPAT Deprecated since 2.1.8 + failed = pcmk__xe_create(NULL, PCMK__XE_FAILED); update_section = pcmk_find_cib_element(*result_cib, section); if (pcmk__xe_is(input, section)) { @@ -770,7 +672,7 @@ cib_process_diff(const char *op, int options, const char *section, xmlNode * req const char *originator = NULL; if (req != NULL) { - originator = crm_element_value(req, F_ORIG); + originator = crm_element_value(req, PCMK__XA_SRC); } crm_trace("Processing \"%s\" event from %s%s", @@ -780,7 +682,7 @@ cib_process_diff(const char *op, int options, const char *section, xmlNode * req if (*result_cib != existing_cib) { free_xml(*result_cib); } - *result_cib = copy_xml(existing_cib); + *result_cib = pcmk__xml_copy(NULL, existing_cib); return xml_apply_patchset(*result_cib, input, TRUE); } @@ -797,7 +699,7 @@ cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff) CRM_ASSERT(diff != NULL); if (*diff == NULL && last != NULL && next != NULL) { - *diff = diff_xml_object(last, next, FALSE); + *diff = pcmk__diff_v1_xml_object(last, next, false); } if (*diff == NULL) { @@ -807,7 +709,7 @@ cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff) crm_element_value_int(*diff, PCMK_XA_FORMAT, &format); CRM_LOG_ASSERT(format == 1); - xpathObj = xpath_search(*diff, "//" XML_CIB_TAG_CONFIGURATION); + xpathObj = xpath_search(*diff, "//" PCMK_XE_CONFIGURATION); if (numXpathResults(xpathObj) > 0) { config_changes = true; goto done; @@ -815,38 +717,38 @@ cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff) freeXpathObject(xpathObj); /* - * Do not check XML_TAG_DIFF_ADDED "//" XML_TAG_CIB + * Do not check PCMK__XE_DIFF_ADDED "//" PCMK_XE_CIB * This always contains every field and would produce a false positive * every time if the checked value existed */ - xpathObj = xpath_search(*diff, "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_CIB); + xpathObj = xpath_search(*diff, "//" PCMK__XE_DIFF_REMOVED "//" PCMK_XE_CIB); max = numXpathResults(xpathObj); for (lpc = 0; lpc < max; lpc++) { xmlNode *top = getXpathResult(xpathObj, lpc); - if (crm_element_value(top, XML_ATTR_GENERATION) != NULL) { + if (crm_element_value(top, PCMK_XA_EPOCH) != NULL) { config_changes = true; goto done; } - if (crm_element_value(top, XML_ATTR_GENERATION_ADMIN) != NULL) { + if (crm_element_value(top, PCMK_XA_ADMIN_EPOCH) != NULL) { config_changes = true; goto done; } - if (crm_element_value(top, XML_ATTR_VALIDATION) != NULL) { + if (crm_element_value(top, PCMK_XA_VALIDATE_WITH) != NULL) { config_changes = true; goto done; } - if (crm_element_value(top, XML_ATTR_CRM_VERSION) != NULL) { + if (crm_element_value(top, PCMK_XA_CRM_FEATURE_SET) != NULL) { config_changes = true; goto done; } - if (crm_element_value(top, "remote-clear-port") != NULL) { + if (crm_element_value(top, PCMK_XA_REMOTE_CLEAR_PORT) != NULL) { config_changes = true; goto done; } - if (crm_element_value(top, "remote-tls-port") != NULL) { + if (crm_element_value(top, PCMK_XA_REMOTE_TLS_PORT) != NULL) { config_changes = true; goto done; } @@ -889,7 +791,7 @@ cib_process_xpath(const char *op, int options, const char *section, } else if (is_query) { if (max > 1) { - *answer = create_xml_node(NULL, "xpath-query"); + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); } } @@ -924,23 +826,29 @@ cib_process_xpath(const char *op, int options, const char *section, } } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { - if (update_xml_child(match, input) == FALSE) { + uint32_t flags = pcmk__xaf_none; + + if (pcmk_is_set(options, cib_score_update)) { + flags |= pcmk__xaf_score_update; + } + + if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { rc = -ENXIO; } else if ((options & cib_multiple) == 0) { break; } } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { - add_node_copy(match, input); + pcmk__xml_copy(match, input); break; } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { if (options & cib_no_children) { - xmlNode *shallow = create_xml_node(*answer, + xmlNode *shallow = pcmk__xe_create(*answer, (const char *) match->name); - copy_in_properties(shallow, match); + pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); if (*answer == NULL) { *answer = shallow; @@ -951,11 +859,11 @@ cib_process_xpath(const char *op, int options, const char *section, xmlNode *parent = match; while (parent && parent->type == XML_ELEMENT_NODE) { - const char *id = crm_element_value(parent, XML_ATTR_ID); + const char *id = crm_element_value(parent, PCMK_XA_ID); char *new_path = NULL; if (id) { - new_path = crm_strdup_printf("/%s[@" XML_ATTR_ID "='%s']" + new_path = crm_strdup_printf("/%s[@" PCMK_XA_ID "='%s']" "%s", parent->name, id, pcmk__s(path, "")); @@ -970,14 +878,14 @@ cib_process_xpath(const char *op, int options, const char *section, crm_trace("Got: %s", path); if (*answer == NULL) { - *answer = create_xml_node(NULL, "xpath-query"); + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); } - parent = create_xml_node(*answer, "xpath-query-path"); - crm_xml_add(parent, XML_ATTR_ID, path); + parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH); + crm_xml_add(parent, PCMK_XA_ID, path); free(path); } else if (*answer) { - add_node_copy(*answer, match); + pcmk__xml_copy(*answer, match); } else { *answer = match; @@ -988,9 +896,7 @@ cib_process_xpath(const char *op, int options, const char *section, xmlNode *parent = match->parent; free_xml(match); - if (input != NULL) { - add_node_copy(parent, input); - } + pcmk__xml_copy(parent, input); if ((options & cib_multiple) == 0) { break; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 77479d7..fa558a5 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2023 the Pacemaker project contributors + * Copyright 2008-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,9 +22,9 @@ #include <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/ipc_internal.h> #include <crm/common/mainloop.h> +#include <crm/common/xml.h> #include <crm/common/remote_internal.h> #include <crm/common/output_internal.h> @@ -126,7 +126,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, break; } - crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); + crm_element_value_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id); if (reply_id == msg_id) { break; @@ -167,7 +167,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, crm_trace("Synchronous reply received"); /* Start processing the reply... */ - if (crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { + if (crm_element_value_int(op_reply, PCMK__XA_CIB_RC, &rc) != 0) { rc = -EPROTO; } @@ -189,12 +189,14 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, /* do nothing more */ } else if (!(call_options & cib_discard_reply)) { - xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); + xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, + NULL, NULL); + xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (tmp == NULL) { crm_trace("No output in reply to \"%s\" command %d", op, cib->call_id - 1); } else { - *output_data = copy_xml(tmp); + *output_data = pcmk__xml_copy(NULL, tmp); } } @@ -218,14 +220,14 @@ cib_remote_callback_dispatch(gpointer user_data) msg = pcmk__remote_message_xml(&private->callback); while (msg) { - const char *type = crm_element_value(msg, F_TYPE); + const char *type = crm_element_value(msg, PCMK__XA_T); crm_trace("Activating %s callbacks...", type); - if (pcmk__str_eq(type, T_CIB, pcmk__str_casei)) { + if (pcmk__str_eq(type, PCMK__VALUE_CIB, pcmk__str_none)) { cib_native_callback(cib, msg, 0, 0); - } else if (pcmk__str_eq(type, T_CIB_NOTIFY, pcmk__str_casei)) { + } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { g_list_foreach(cib->notify_list, cib_native_notify, msg); } else { @@ -380,11 +382,11 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) } /* login to server */ - login = create_xml_node(NULL, T_CIB_COMMAND); - crm_xml_add(login, "op", "authenticate"); - crm_xml_add(login, "user", private->user); - crm_xml_add(login, "password", private->passwd); - crm_xml_add(login, "hidden", "password"); + login = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND); + crm_xml_add(login, PCMK_XA_OP, "authenticate"); + crm_xml_add(login, PCMK_XA_USER, private->user); + crm_xml_add(login, PCMK__XA_PASSWORD, private->passwd); + crm_xml_add(login, PCMK__XA_HIDDEN, PCMK__VALUE_PASSWORD); pcmk__remote_send_xml(connection, login); free_xml(login); @@ -402,8 +404,9 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) } else { /* grab the token */ - const char *msg_type = crm_element_value(answer, F_CIB_OPERATION); - const char *tmp_ticket = crm_element_value(answer, F_CIB_CLIENTID); + const char *msg_type = crm_element_value(answer, PCMK__XA_CIB_OP); + const char *tmp_ticket = crm_element_value(answer, + PCMK__XA_CIB_CLIENTID); if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { crm_err("Invalid registration message: %s", msg_type); @@ -538,12 +541,12 @@ cib_remote_inputfd(cib_t * cib) static int cib_remote_register_notification(cib_t * cib, const char *callback, int enabled) { - xmlNode *notify_msg = create_xml_node(NULL, T_CIB_COMMAND); + xmlNode *notify_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND); cib_remote_opaque_t *private = cib->variant_opaque; - crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); - crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); - crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); + crm_xml_add(notify_msg, PCMK__XA_CIB_OP, PCMK__VALUE_CIB_NOTIFY); + crm_xml_add(notify_msg, PCMK__XA_CIB_NOTIFY_TYPE, callback); + crm_xml_add_int(notify_msg, PCMK__XA_CIB_NOTIFY_ACTIVATE, enabled); pcmk__remote_send_xml(&private->callback, notify_msg); free_xml(notify_msg); return pcmk_ok; @@ -610,10 +613,9 @@ cib_remote_new(const char *server, const char *user, const char *passwd, int por cib->variant = cib_remote; cib->variant_opaque = private; - pcmk__str_update(&private->server, server); - pcmk__str_update(&private->user, user); - pcmk__str_update(&private->passwd, passwd); - + private->server = pcmk__str_copy(server); + private->user = pcmk__str_copy(user); + private->passwd = pcmk__str_copy(passwd); private->port = port; private->encrypted = encrypted; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0082eef..7ef3789 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -1,6 +1,6 @@ /* * Original copyright 2004 International Business Machines - * Later changes copyright 2008-2023 the Pacemaker project contributors + * Later changes copyright 2008-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,27 +19,11 @@ #include <crm/crm.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/cib_internal.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/pengine/rules.h> -xmlNode * -cib_get_generation(cib_t * cib) -{ - xmlNode *the_cib = NULL; - xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE); - - cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call); - if (the_cib != NULL) { - copy_in_properties(generation, the_cib); - free_xml(the_cib); - } - - return generation; -} - gboolean cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) { @@ -51,9 +35,9 @@ cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) return FALSE; } else { - crm_element_value_int(cib, XML_ATTR_GENERATION, epoch); - crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates); - crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch); + crm_element_value_int(cib, PCMK_XA_EPOCH, epoch); + crm_element_value_int(cib, PCMK_XA_NUM_UPDATES, updates); + crm_element_value_int(cib, PCMK_XA_ADMIN_EPOCH, admin_epoch); } return TRUE; } @@ -91,6 +75,7 @@ int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset) { int rc = pcmk_err_generic; + xmlNode *wrapper = NULL; CRM_ASSERT(patchset != NULL); *patchset = NULL; @@ -100,14 +85,17 @@ cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset) return ENOMSG; } - if ((crm_element_value_int(msg, F_CIB_RC, &rc) != 0) || (rc != pcmk_ok)) { + if ((crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc) != 0) + || (rc != pcmk_ok)) { + crm_warn("Ignore failed CIB update: %s " CRM_XS " rc=%d", pcmk_strerror(rc), rc); crm_log_xml_debug(msg, "failed"); return pcmk_legacy2rc(rc); } - *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT); + wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL); + *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (*patchset == NULL) { crm_err("CIB diff notification received with no patchset"); @@ -116,7 +104,7 @@ cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset) return pcmk_rc_ok; } -#define XPATH_DIFF_V1 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED +#define XPATH_DIFF_V1 "//" PCMK__XE_CIB_UPDATE_RESULT "//" PCMK__XE_DIFF_ADDED /*! * \internal @@ -124,7 +112,7 @@ cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset) * * \param[in] patchset CIB XML patchset * \param[in] element XML tag of CIB element to check (\c NULL is equivalent - * to \c XML_TAG_CIB) + * to \c PCMK_XE_CIB) * * \return \c true if \p element was modified, or \c false otherwise */ @@ -132,7 +120,7 @@ static bool element_in_patchset_v1(const xmlNode *patchset, const char *element) { char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s", - pcmk__s(element, XML_TAG_CIB)); + pcmk__s(element, PCMK_XE_CIB)); xmlXPathObject *xpath_obj = xpath_search(patchset, xpath); free(xpath); @@ -150,7 +138,7 @@ element_in_patchset_v1(const xmlNode *patchset, const char *element) * * \param[in] patchset CIB XML patchset * \param[in] element XML tag of CIB element to check (\c NULL is equivalent - * to \c XML_TAG_CIB). Supported values include any CIB + * to \c PCMK_XE_CIB). Supported values include any CIB * element supported by \c pcmk__cib_abs_xpath_for(). * * \return \c true if \p element was modified, or \c false otherwise @@ -168,11 +156,12 @@ element_in_patchset_v2(const xmlNode *patchset, const char *element) // Matches if and only if element_xpath is part of a changed path element_regex = crm_strdup_printf("^%s(/|$)", element_xpath); - for (const xmlNode *change = first_named_child(patchset, XML_DIFF_CHANGE); - change != NULL; change = crm_next_same_xml(change)) { + for (const xmlNode *change = pcmk__xe_first_child(patchset, PCMK_XE_CHANGE, + NULL, NULL); + change != NULL; change = pcmk__xe_next_same(change)) { - const char *op = crm_element_value(change, F_CIB_OPERATION); - const char *diff_xpath = crm_element_value(change, XML_DIFF_PATH); + const char *op = crm_element_value(change, PCMK__XA_CIB_OP); + const char *diff_xpath = crm_element_value(change, PCMK_XA_PATH); if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) { // Change to an existing element @@ -180,9 +169,10 @@ element_in_patchset_v2(const xmlNode *patchset, const char *element) break; } - if (pcmk__str_eq(op, "create", pcmk__str_none) + if (pcmk__str_eq(op, PCMK_VALUE_CREATE, pcmk__str_none) && pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none) - && pcmk__xe_is(pcmk__xml_first_child(change), element)) { + && pcmk__xe_is(pcmk__xe_first_child(change, NULL, NULL, NULL), + element)) { // Newly added element rc = true; @@ -200,7 +190,7 @@ element_in_patchset_v2(const xmlNode *patchset, const char *element) * * \param[in] patchset CIB XML patchset * \param[in] element XML tag of CIB element to check (\c NULL is equivalent - * to \c XML_TAG_CIB). Supported values include any CIB + * to \c PCMK_XE_CIB). Supported values include any CIB * element supported by \c pcmk__cib_abs_xpath_for(). * * \return \c true if \p element was modified, or \c false otherwise @@ -229,7 +219,7 @@ cib__element_in_patchset(const xmlNode *patchset, const char *element) /*! * \brief Create XML for a new (empty) CIB * - * \param[in] cib_epoch What to use as "epoch" CIB property + * \param[in] cib_epoch What to use as \c PCMK_XA_EPOCH CIB attribute * * \return Newly created XML for empty CIB * \note It is the caller's responsibility to free the result with free_xml(). @@ -239,32 +229,32 @@ createEmptyCib(int cib_epoch) { xmlNode *cib_root = NULL, *config = NULL; - cib_root = create_xml_node(NULL, XML_TAG_CIB); - crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); - crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema()); + cib_root = pcmk__xe_create(NULL, PCMK_XE_CIB); + crm_xml_add(cib_root, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); + crm_xml_add(cib_root, PCMK_XA_VALIDATE_WITH, pcmk__highest_schema_name()); - crm_xml_add_int(cib_root, XML_ATTR_GENERATION, cib_epoch); - crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0); - crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0); + crm_xml_add_int(cib_root, PCMK_XA_EPOCH, cib_epoch); + crm_xml_add_int(cib_root, PCMK_XA_NUM_UPDATES, 0); + crm_xml_add_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0); - config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); - create_xml_node(cib_root, XML_CIB_TAG_STATUS); + config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION); + pcmk__xe_create(cib_root, PCMK_XE_STATUS); - create_xml_node(config, XML_CIB_TAG_CRMCONFIG); - create_xml_node(config, XML_CIB_TAG_NODES); - create_xml_node(config, XML_CIB_TAG_RESOURCES); - create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); + pcmk__xe_create(config, PCMK_XE_CRM_CONFIG); + pcmk__xe_create(config, PCMK_XE_NODES); + pcmk__xe_create(config, PCMK_XE_RESOURCES); + pcmk__xe_create(config, PCMK_XE_CONSTRAINTS); #if PCMK__RESOURCE_STICKINESS_DEFAULT != 0 { - xmlNode *rsc_defaults = create_xml_node(config, XML_CIB_TAG_RSCCONFIG); - xmlNode *meta = create_xml_node(rsc_defaults, XML_TAG_META_SETS); - xmlNode *nvpair = create_xml_node(meta, XML_CIB_TAG_NVPAIR); - - crm_xml_add(meta, XML_ATTR_ID, "build-resource-defaults"); - crm_xml_add(nvpair, XML_ATTR_ID, "build-" XML_RSC_ATTR_STICKINESS); - crm_xml_add(nvpair, XML_NVPAIR_ATTR_NAME, XML_RSC_ATTR_STICKINESS); - crm_xml_add_int(nvpair, XML_NVPAIR_ATTR_VALUE, + xmlNode *rsc_defaults = pcmk__xe_create(config, PCMK_XE_RSC_DEFAULTS); + xmlNode *meta = pcmk__xe_create(rsc_defaults, PCMK_XE_META_ATTRIBUTES); + xmlNode *nvpair = pcmk__xe_create(meta, PCMK_XE_NVPAIR); + + crm_xml_add(meta, PCMK_XA_ID, "build-resource-defaults"); + crm_xml_add(nvpair, PCMK_XA_ID, "build-" PCMK_META_RESOURCE_STICKINESS); + crm_xml_add(nvpair, PCMK_XA_NAME, PCMK_META_RESOURCE_STICKINESS); + crm_xml_add_int(nvpair, PCMK_XA_VALUE, PCMK__RESOURCE_STICKINESS_DEFAULT); } #endif @@ -281,7 +271,7 @@ cib_acl_enabled(xmlNode *xml, const char *user) GHashTable *options = pcmk__strkey_table(free, free); cib_read_config(options, xml); - value = cib_pref(options, "enable-acl"); + value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL); rc = crm_is_true(value); g_hash_table_destroy(options); } @@ -324,7 +314,7 @@ should_copy_cib(const char *op, const char *section, int call_options) return false; } - if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_none)) { + if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) { /* Copying large CIBs accounts for a huge percentage of our CIB usage, * and this avoids some of it. * @@ -339,11 +329,10 @@ should_copy_cib(const char *op, const char *section, int call_options) } int -cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, - const char *section, xmlNode *req, xmlNode *input, - bool manage_counters, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output) +cib_perform_op(cib_t *cib, const char *op, int call_options, cib__op_fn_t fn, + bool is_query, const char *section, xmlNode *req, xmlNode *input, + bool manage_counters, bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output) { int rc = pcmk_ok; bool check_schema = true; @@ -353,8 +342,7 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, xmlNode *patchset_cib = NULL; xmlNode *local_diff = NULL; - const char *new_version = NULL; - const char *user = crm_element_value(req, F_CIB_USER); + const char *user = crm_element_value(req, PCMK__XA_CIB_USER); bool with_digest = false; crm_trace("Begin %s%s%s op", @@ -406,11 +394,11 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, } else if(cib_filtered && (*output)->doc == cib_filtered->doc) { /* We're about to free the document of which *output is a part */ - *output = copy_xml(*output); + *output = pcmk__xml_copy(NULL, *output); } else if ((*output)->doc == (*current_cib)->doc) { /* Give them a copy they can free */ - *output = copy_xml(*output); + *output = pcmk__xml_copy(NULL, *output); } free_xml(cib_filtered); @@ -425,8 +413,8 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, scratch = *current_cib; // Make a copy of the top-level element to store version details - top = create_xml_node(NULL, (const char *) scratch->name); - copy_in_properties(top, scratch); + top = pcmk__xe_create(NULL, (const char *) scratch->name); + pcmk__xe_copy_attrs(top, scratch, pcmk__xaf_none); patchset_cib = top; xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user)); @@ -438,7 +426,7 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, *current_cib = scratch; } else { - scratch = copy_xml(*current_cib); + scratch = pcmk__xml_copy(NULL, *current_cib); patchset_cib = *current_cib; xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user)); @@ -469,13 +457,18 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, goto done; } - if (scratch) { - new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION); - - if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) { - crm_err("Discarding update with feature set '%s' greater than our own '%s'", - new_version, CRM_FEATURE_SET); - rc = -EPROTONOSUPPORT; + /* If the CIB is from a file, we don't need to check that the feature set is + * supported. All we care about in that case is the schema version, which + * is checked elsewhere. + */ + if (scratch && (cib == NULL || cib->variant != cib_file)) { + const char *new_version = crm_element_value(scratch, PCMK_XA_CRM_FEATURE_SET); + + rc = pcmk__check_feature_set(new_version); + if (rc != pcmk_rc_ok) { + pcmk__config_err("Discarding update with feature set '%s' greater than our own '%s'", + new_version, CRM_FEATURE_SET); + rc = pcmk_rc2legacy(rc); goto done; } } @@ -484,22 +477,22 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, int old = 0; int new = 0; - crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new); - crm_element_value_int(patchset_cib, XML_ATTR_GENERATION_ADMIN, &old); + crm_element_value_int(scratch, PCMK_XA_ADMIN_EPOCH, &new); + crm_element_value_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old); if (old > new) { crm_err("%s went backwards: %d -> %d (Opts: %#x)", - XML_ATTR_GENERATION_ADMIN, old, new, call_options); + PCMK_XA_ADMIN_EPOCH, old, new, call_options); crm_log_xml_warn(req, "Bad Op"); crm_log_xml_warn(input, "Bad Data"); rc = -pcmk_err_old_data; } else if (old == new) { - crm_element_value_int(scratch, XML_ATTR_GENERATION, &new); - crm_element_value_int(patchset_cib, XML_ATTR_GENERATION, &old); + crm_element_value_int(scratch, PCMK_XA_EPOCH, &new); + crm_element_value_int(patchset_cib, PCMK_XA_EPOCH, &old); if (old > new) { crm_err("%s went backwards: %d -> %d (Opts: %#x)", - XML_ATTR_GENERATION, old, new, call_options); + PCMK_XA_EPOCH, old, new, call_options); crm_log_xml_warn(req, "Bad Op"); crm_log_xml_warn(input, "Bad Data"); rc = -pcmk_err_old_data; @@ -509,10 +502,10 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, crm_trace("Massaging CIB contents"); pcmk__strip_xml_text(scratch); - fix_plus_plus_recursive(scratch); if (!make_copy) { - /* At this point, patchset_cib is just the "cib" tag and its properties. + /* At this point, patchset_cib is just the PCMK_XE_CIB tag and its + * properties. * * The v1 format would barf on this, but we know the v2 patch * format only needs it for the top-level version fields @@ -549,7 +542,7 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, // Validate the calculated patch set int test_rc = pcmk_ok; int format = 1; - xmlNode *cib_copy = copy_xml(patchset_cib); + xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib); crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format); test_rc = xml_apply_patchset(cib_copy, local_diff, @@ -571,7 +564,7 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, ); } - if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) { + if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) { /* Throttle the amount of costly validation we perform due to status updates * a) we don't really care whats in the status section * b) we don't validate any of its contents at the moment anyway @@ -583,59 +576,53 @@ cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query, * Exceptions, anything in: static filter_t filter[] = { - { 0, XML_ATTR_ORIGIN }, - { 0, XML_CIB_ATTR_WRITTEN }, - { 0, XML_ATTR_UPDATE_ORIG }, - { 0, XML_ATTR_UPDATE_CLIENT }, - { 0, XML_ATTR_UPDATE_USER }, + { 0, PCMK_XA_CRM_DEBUG_ORIGIN }, + { 0, PCMK_XA_CIB_LAST_WRITTEN }, + { 0, PCMK_XA_UPDATE_ORIGIN }, + { 0, PCMK_XA_UPDATE_CLIENT }, + { 0, PCMK_XA_UPDATE_USER }, }; */ if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) { - const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION); + const char *schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH); pcmk__xe_add_last_written(scratch); - if (schema) { - static int minimum_schema = 0; - int current_schema = get_schema_version(schema); + pcmk__warn_if_schema_deprecated(schema); - if (minimum_schema == 0) { - minimum_schema = get_schema_version("pacemaker-1.2"); + /* Make values of origin, client, and user in scratch match + * the ones in req (if the schema allows the attributes) + */ + if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) { + const char *origin = crm_element_value(req, PCMK__XA_SRC); + const char *client = crm_element_value(req, + PCMK__XA_CIB_CLIENTNAME); + + if (origin != NULL) { + crm_xml_add(scratch, PCMK_XA_UPDATE_ORIGIN, origin); + } else { + pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_ORIGIN); } - /* Does the CIB support the "update-*" attributes... */ - if (current_schema >= minimum_schema) { - /* Ensure values of origin, client, and user in scratch match - * the values in req - */ - const char *origin = crm_element_value(req, F_ORIG); - const char *client = crm_element_value(req, F_CIB_CLIENTNAME); - - if (origin != NULL) { - crm_xml_add(scratch, XML_ATTR_UPDATE_ORIG, origin); - } else { - xml_remove_prop(scratch, XML_ATTR_UPDATE_ORIG); - } - - if (client != NULL) { - crm_xml_add(scratch, XML_ATTR_UPDATE_CLIENT, user); - } else { - xml_remove_prop(scratch, XML_ATTR_UPDATE_CLIENT); - } + if (client != NULL) { + crm_xml_add(scratch, PCMK_XA_UPDATE_CLIENT, user); + } else { + pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_CLIENT); + } - if (user != NULL) { - crm_xml_add(scratch, XML_ATTR_UPDATE_USER, user); - } else { - xml_remove_prop(scratch, XML_ATTR_UPDATE_USER); - } + if (user != NULL) { + crm_xml_add(scratch, PCMK_XA_UPDATE_USER, user); + } else { + pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_USER); } } } crm_trace("Perform validation: %s", pcmk__btoa(check_schema)); - if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, true)) { + if ((rc == pcmk_ok) && check_schema + && !pcmk__configured_schema_validates(scratch)) { const char *current_schema = crm_element_value(scratch, - XML_ATTR_VALIDATION); + PCMK_XA_VALIDATE_WITH); crm_warn("Updated CIB does not validate against %s schema", pcmk__s(current_schema, "unspecified")); @@ -677,30 +664,28 @@ cib__create_op(cib_t *cib, const char *op, const char *host, { CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO); - *op_msg = create_xml_node(NULL, T_CIB_COMMAND); - if (*op_msg == NULL) { - return -EPROTO; - } + *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND); cib->call_id++; if (cib->call_id < 1) { cib->call_id = 1; } - crm_xml_add(*op_msg, F_XML_TAGNAME, T_CIB_COMMAND); - crm_xml_add(*op_msg, F_TYPE, T_CIB); - crm_xml_add(*op_msg, F_CIB_OPERATION, op); - crm_xml_add(*op_msg, F_CIB_HOST, host); - crm_xml_add(*op_msg, F_CIB_SECTION, section); - crm_xml_add(*op_msg, F_CIB_USER, user_name); - crm_xml_add(*op_msg, F_CIB_CLIENTNAME, client_name); - crm_xml_add_int(*op_msg, F_CIB_CALLID, cib->call_id); + crm_xml_add(*op_msg, PCMK__XA_T, PCMK__VALUE_CIB); + crm_xml_add(*op_msg, PCMK__XA_CIB_OP, op); + crm_xml_add(*op_msg, PCMK__XA_CIB_HOST, host); + crm_xml_add(*op_msg, PCMK__XA_CIB_SECTION, section); + crm_xml_add(*op_msg, PCMK__XA_CIB_USER, user_name); + crm_xml_add(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name); + crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLID, cib->call_id); crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options); - crm_xml_add_int(*op_msg, F_CIB_CALLOPTS, call_options); + crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options); if (data != NULL) { - add_message_xml(*op_msg, F_CIB_CALLDATA, data); + xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA); + + pcmk__xml_copy(wrapper, data); } if (pcmk_is_set(call_options, cib_inhibit_bcast)) { @@ -721,8 +706,8 @@ cib__create_op(cib_t *cib, const char *op, const char *host, static int validate_transaction_request(const xmlNode *request) { - const char *op = crm_element_value(request, F_CIB_OPERATION); - const char *host = crm_element_value(request, F_CIB_HOST); + const char *op = crm_element_value(request, PCMK__XA_CIB_OP); + const char *host = crm_element_value(request, PCMK__XA_CIB_HOST); const cib__operation_t *operation = NULL; int rc = cib__get_operation(op, &operation); @@ -768,10 +753,10 @@ cib__extend_transaction(cib_t *cib, xmlNode *request) } if (rc == pcmk_rc_ok) { - add_node_copy(cib->transaction, request); + pcmk__xml_copy(cib->transaction, request); } else { - const char *op = crm_element_value(request, F_CIB_OPERATION); + const char *op = crm_element_value(request, PCMK__XA_CIB_OP); const char *client_id = NULL; cib->cmds->client_id(cib, NULL, &client_id); @@ -789,9 +774,12 @@ cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) cib_callback_client_t *blob = NULL; if (msg != NULL) { - crm_element_value_int(msg, F_CIB_RC, &rc); - crm_element_value_int(msg, F_CIB_CALLID, &call_id); - output = get_message_xml(msg, F_CIB_CALLDATA); + xmlNode *wrapper = NULL; + + crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc); + crm_element_value_int(msg, PCMK__XA_CIB_CALLID, &call_id); + wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL); + output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); } blob = cib__lookup_id(call_id); @@ -843,7 +831,7 @@ cib_native_notify(gpointer data, gpointer user_data) return; } - event = crm_element_value(msg, F_SUBTYPE); + event = crm_element_value(msg, PCMK__XA_SUBT); if (entry == NULL) { crm_warn("Skipping callback - NULL callback client"); @@ -863,55 +851,6 @@ cib_native_notify(gpointer data, gpointer user_data) crm_trace("Callback invoked..."); } -static pcmk__cluster_option_t cib_opts[] = { - /* name, legacy name, type, allowed values, - * default value, validator, - * short description, - * long description - */ - { - "enable-acl", NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("Enable Access Control Lists (ACLs) for the CIB"), - NULL - }, - { - "cluster-ipc-limit", NULL, "integer", NULL, - "500", pcmk__valid_positive_number, - N_("Maximum IPC message backlog before disconnecting a cluster daemon"), - N_("Raise this if log has \"Evicting client\" messages for cluster daemon" - " PIDs (a good value is the number of resources in the cluster" - " multiplied by the number of nodes).") - }, -}; - -void -cib_metadata(void) -{ - const char *desc_short = "Cluster Information Base manager options"; - const char *desc_long = "Cluster options used by Pacemaker's Cluster " - "Information Base manager"; - - gchar *s = pcmk__format_option_metadata("pacemaker-based", desc_short, - desc_long, cib_opts, - PCMK__NELEM(cib_opts)); - printf("%s", s); - g_free(s); -} - -static void -verify_cib_options(GHashTable *options) -{ - pcmk__validate_cluster_options(options, cib_opts, PCMK__NELEM(cib_opts)); -} - -const char * -cib_pref(GHashTable * options, const char *name) -{ - return pcmk__cluster_option(options, cib_opts, PCMK__NELEM(cib_opts), - name); -} - gboolean cib_read_config(GHashTable * options, xmlNode * current_cib) { @@ -926,13 +865,14 @@ cib_read_config(GHashTable * options, xmlNode * current_cib) g_hash_table_remove_all(options); - config = pcmk_find_cib_element(current_cib, XML_CIB_TAG_CRMCONFIG); + config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG); if (config) { - pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL, - options, CIB_OPTIONS_FIRST, TRUE, now, NULL); + pe_unpack_nvpairs(current_cib, config, PCMK_XE_CLUSTER_PROPERTY_SET, + NULL, options, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, TRUE, + now, NULL); } - verify_cib_options(options); + pcmk__validate_cluster_options(options); crm_time_free(now); @@ -973,14 +913,17 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, { int rc = pcmk_err_generic; + xmlNode *wrapper = NULL; xmlNode *diff = NULL; CRM_ASSERT(event); CRM_ASSERT(input); CRM_ASSERT(output); - crm_element_value_int(event, F_CIB_RC, &rc); - diff = get_message_xml(event, F_CIB_UPDATE_RESULT); + crm_element_value_int(event, PCMK__XA_CIB_RC, &rc); + wrapper = pcmk__xe_first_child(event, PCMK__XE_CIB_UPDATE_RESULT, NULL, + NULL); + diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (rc < pcmk_ok || diff == NULL) { return rc; @@ -1092,6 +1035,21 @@ cib__clean_up_connection(cib_t **cib) #include <crm/cib/util_compat.h> +xmlNode * +cib_get_generation(cib_t * cib) +{ + xmlNode *the_cib = NULL; + xmlNode *generation = pcmk__xe_create(NULL, PCMK__XE_GENERATION_TUPLE); + + cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call); + if (the_cib != NULL) { + pcmk__xe_copy_attrs(generation, the_cib, pcmk__xaf_none); + free_xml(the_cib); + } + + return generation; +} + const char * get_object_path(const char *object_type) { @@ -1110,5 +1068,32 @@ get_object_root(const char *object_type, xmlNode *the_root) return pcmk_find_cib_element(the_root, object_type); } +const char * +cib_pref(GHashTable * options, const char *name) +{ + return pcmk__cluster_option(options, name); +} + +void +cib_metadata(void) +{ + pcmk__output_t *out = NULL; + int rc = pcmk__output_new(&out, "text", NULL, NULL); + + if (rc != pcmk_rc_ok) { + crm_err("Unable to output metadata: %s", pcmk_rc_str(rc)); + return; + } + + pcmk__daemon_metadata(out, "pacemaker-based", + "Cluster Information Base manager options", + "Cluster options used by Pacemaker's Cluster " + "Information Base manager", + pcmk__opt_based); + + out->finish(out, CRM_EX_OK, true, NULL); + pcmk__output_free(out); +} + // LCOV_EXCL_STOP // End deprecated API diff --git a/lib/cluster/Makefile.am b/lib/cluster/Makefile.am index 2ddbffb..85ba22d 100644 --- a/lib/cluster/Makefile.am +++ b/lib/cluster/Makefile.am @@ -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. # @@ -8,12 +8,14 @@ # include $(top_srcdir)/mk/common.mk +SUBDIRS = tests + noinst_HEADERS = crmcluster_private.h ## libraries lib_LTLIBRARIES = libcrmcluster.la -libcrmcluster_la_LDFLAGS = -version-info 31:0:2 +libcrmcluster_la_LDFLAGS = -version-info 32:0:3 libcrmcluster_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcluster_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index f2cd428..d650ca5 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.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,6 +10,8 @@ #include <crm_internal.h> #include <dlfcn.h> +#include <inttypes.h> // PRIu32 +#include <stdbool.h> #include <stdio.h> #include <unistd.h> #include <string.h> @@ -17,128 +19,182 @@ #include <time.h> #include <sys/param.h> #include <sys/types.h> +#include <sys/utsname.h> // uname() + +#include <glib.h> // gboolean #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/ipc.h> +#include <crm/common/xml.h> #include <crm/cluster/internal.h> #include "crmcluster_private.h" CRM_TRACE_INIT_DATA(cluster); /*! - * \brief Get (and set if needed) a node's UUID + * \internal + * \brief Get the message type equivalent of a string * - * \param[in,out] peer Node to check + * \param[in] text String of message type * - * \return Node UUID of \p peer, or NULL if unknown + * \return Message type equivalent of \p text + */ +enum crm_ais_msg_types +pcmk__cluster_parse_msg_type(const char *text) +{ + CRM_CHECK(text != NULL, return crm_msg_none); + + text = pcmk__message_name(text); + + if (pcmk__str_eq(text, "ais", pcmk__str_none)) { + return crm_msg_ais; + } + if (pcmk__str_eq(text, CRM_SYSTEM_CIB, pcmk__str_none)) { + return crm_msg_cib; + } + if (pcmk__str_any_of(text, CRM_SYSTEM_CRMD, CRM_SYSTEM_DC, NULL)) { + return crm_msg_crmd; + } + if (pcmk__str_eq(text, CRM_SYSTEM_TENGINE, pcmk__str_none)) { + return crm_msg_te; + } + if (pcmk__str_eq(text, CRM_SYSTEM_PENGINE, pcmk__str_none)) { + return crm_msg_pe; + } + if (pcmk__str_eq(text, CRM_SYSTEM_LRMD, pcmk__str_none)) { + return crm_msg_lrmd; + } + if (pcmk__str_eq(text, CRM_SYSTEM_STONITHD, pcmk__str_none)) { + return crm_msg_stonithd; + } + if (pcmk__str_eq(text, "stonith-ng", pcmk__str_none)) { + return crm_msg_stonith_ng; + } + if (pcmk__str_eq(text, "attrd", pcmk__str_none)) { + return crm_msg_attrd; + } + return crm_msg_none; +} + +/*! + * \internal + * \brief Get a node's cluster-layer UUID, setting it if not already set + * + * \param[in,out] node Node to check + * + * \return Cluster-layer node UUID of \p node, or \c NULL if unknown */ const char * -crm_peer_uuid(crm_node_t *peer) +pcmk__cluster_node_uuid(crm_node_t *node) { - char *uuid = NULL; + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); - // Check simple cases first, to avoid any calls that might block - if (peer == NULL) { + if (node == NULL) { return NULL; } - if (peer->uuid != NULL) { - return peer->uuid; + if (node->uuid != NULL) { + return node->uuid; } - switch (get_cluster_type()) { - case pcmk_cluster_corosync: + switch (cluster_layer) { #if SUPPORT_COROSYNC - uuid = pcmk__corosync_uuid(peer); -#endif - break; + case pcmk_cluster_layer_corosync: + node->uuid = pcmk__corosync_uuid(node); + return node->uuid; +#endif // SUPPORT_COROSYNC - case pcmk_cluster_unknown: - case pcmk_cluster_invalid: - crm_err("Unsupported cluster type"); - break; + default: + crm_err("Unsupported cluster layer %s", + pcmk_cluster_layer_text(cluster_layer)); + return NULL; } - - peer->uuid = uuid; - return peer->uuid; } /*! + * \internal * \brief Connect to the cluster layer * - * \param[in,out] Initialized cluster object to connect + * \param[in,out] cluster Initialized cluster object to connect * - * \return TRUE on success, otherwise FALSE + * \return Standard Pacemaker return code */ -gboolean -crm_cluster_connect(crm_cluster_t *cluster) +int +pcmk_cluster_connect(pcmk_cluster_t *cluster) { - enum cluster_type_e type = get_cluster_type(); + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); + const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); - crm_notice("Connecting to %s cluster infrastructure", - name_for_cluster_type(type)); - switch (type) { - case pcmk_cluster_corosync: + // cts-lab looks for this message + crm_notice("Connecting to %s cluster layer", cluster_layer_s); + + switch (cluster_layer) { #if SUPPORT_COROSYNC - crm_peer_init(); + case pcmk_cluster_layer_corosync: return pcmk__corosync_connect(cluster); -#else - break; #endif // SUPPORT_COROSYNC + default: break; } - return FALSE; + + crm_err("Failed to connect to unsupported cluster layer %s", + cluster_layer_s); + return EPROTONOSUPPORT; } /*! * \brief Disconnect from the cluster layer * * \param[in,out] cluster Cluster object to disconnect + * + * \return Standard Pacemaker return code */ -void -crm_cluster_disconnect(crm_cluster_t *cluster) +int +pcmk_cluster_disconnect(pcmk_cluster_t *cluster) { - enum cluster_type_e type = get_cluster_type(); + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); + const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); - crm_info("Disconnecting from %s cluster infrastructure", - name_for_cluster_type(type)); - switch (type) { - case pcmk_cluster_corosync: + crm_info("Disconnecting from %s cluster layer", cluster_layer_s); + + switch (cluster_layer) { #if SUPPORT_COROSYNC - crm_peer_destroy(); + case pcmk_cluster_layer_corosync: pcmk__corosync_disconnect(cluster); + pcmk__cluster_destroy_node_caches(); + return pcmk_rc_ok; #endif // SUPPORT_COROSYNC - break; + default: break; } + + crm_err("Failed to disconnect from unsupported cluster layer %s", + cluster_layer_s); + return EPROTONOSUPPORT; } /*! - * \brief Allocate a new \p crm_cluster_t object + * \brief Allocate a new \p pcmk_cluster_t object * - * \return A newly allocated \p crm_cluster_t object (guaranteed not \p NULL) + * \return A newly allocated \p pcmk_cluster_t object (guaranteed not \c NULL) * \note The caller is responsible for freeing the return value using * \p pcmk_cluster_free(). */ -crm_cluster_t * +pcmk_cluster_t * pcmk_cluster_new(void) { - crm_cluster_t *cluster = calloc(1, sizeof(crm_cluster_t)); - - CRM_ASSERT(cluster != NULL); - return cluster; + return (pcmk_cluster_t *) pcmk__assert_alloc(1, sizeof(pcmk_cluster_t)); } /*! - * \brief Free a \p crm_cluster_t object and its dynamically allocated members + * \brief Free a \p pcmk_cluster_t object and its dynamically allocated members * * \param[in,out] cluster Cluster object to free */ void -pcmk_cluster_free(crm_cluster_t *cluster) +pcmk_cluster_free(pcmk_cluster_t *cluster) { if (cluster == NULL) { return; @@ -149,257 +205,339 @@ pcmk_cluster_free(crm_cluster_t *cluster) } /*! + * \brief Set the destroy function for a cluster object + * + * \param[in,out] cluster Cluster object + * \param[in] fn Destroy function to set + * + * \return Standard Pacemaker return code + */ +int +pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer)) +{ + if (cluster == NULL) { + return EINVAL; + } + cluster->destroy = fn; + return pcmk_rc_ok; +} + +/*! + * \internal * \brief Send an XML message via the cluster messaging layer * * \param[in] node Cluster node to send message to * \param[in] service Message type to use in message host info * \param[in] data XML message to send - * \param[in] ordered Ignored for currently supported messaging layers * - * \return TRUE on success, otherwise FALSE + * \return \c true on success, or \c false otherwise */ -gboolean -send_cluster_message(const crm_node_t *node, enum crm_ais_msg_types service, - const xmlNode *data, gboolean ordered) +bool +pcmk__cluster_send_message(const crm_node_t *node, + enum crm_ais_msg_types service, const xmlNode *data) { - switch (get_cluster_type()) { - case pcmk_cluster_corosync: + // @TODO Return standard Pacemaker return code + switch (pcmk_get_cluster_layer()) { #if SUPPORT_COROSYNC + case pcmk_cluster_layer_corosync: return pcmk__cpg_send_xml(data, node, service); -#endif - break; +#endif // SUPPORT_COROSYNC + default: break; } - return FALSE; + return false; } /*! - * \brief Get the local node's name + * \internal + * \brief Get the node name corresponding to a cluster-layer node ID * - * \return Local node's name - * \note This will fatally exit if local node name cannot be known. - */ -const char * -get_local_node_name(void) -{ - static char *name = NULL; - - if (name == NULL) { - name = get_node_name(0); - } - return name; -} - -/*! - * \brief Get the node name corresponding to a cluster node ID + * Get the node name from the cluster layer if possible. Otherwise, if for the + * local node, call \c uname() and get the \c nodename member from the + * <tt>struct utsname</tt> object. * - * \param[in] nodeid Node ID to check (or 0 for local node) + * \param[in] nodeid Node ID to check (or 0 for the local node) * * \return Node name corresponding to \p nodeid - * \note This will fatally exit if \p nodeid is 0 and local node name cannot be - * known. + * + * \note This will fatally exit if \c uname() fails to get the local node name + * or we run out of memory. + * \note The caller is responsible for freeing the return value using \c free(). */ char * -get_node_name(uint32_t nodeid) +pcmk__cluster_node_name(uint32_t nodeid) { - char *name = NULL; - enum cluster_type_e stack = get_cluster_type(); + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); + const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); - switch (stack) { - case pcmk_cluster_corosync: + switch (cluster_layer) { #if SUPPORT_COROSYNC - name = pcmk__corosync_name(0, nodeid); + case pcmk_cluster_layer_corosync: + return pcmk__corosync_name(0, nodeid); +#else break; #endif // SUPPORT_COROSYNC default: - crm_err("Unknown cluster type: %s (%d)", name_for_cluster_type(stack), stack); + crm_err("Unsupported cluster layer: %s", cluster_layer_s); + break; } - if ((name == NULL) && (nodeid == 0)) { - name = pcmk_hostname(); - if (name == NULL) { + if (nodeid == 0) { + struct utsname hostinfo; + + crm_notice("Could not get local node name from %s cluster layer, " + "defaulting to local hostname", + cluster_layer_s); + + if (uname(&hostinfo) < 0) { // @TODO Maybe let the caller decide what to do - crm_err("Could not obtain the local %s node name", - name_for_cluster_type(stack)); + crm_err("Failed to get the local hostname"); crm_exit(CRM_EX_FATAL); } - crm_notice("Defaulting to uname -n for the local %s node name", - name_for_cluster_type(stack)); + return pcmk__str_copy(hostinfo.nodename); } + crm_notice("Could not obtain a node name for node with " + PCMK_XA_ID "=" PRIu32, + nodeid); + return NULL; +} + +/*! + * \internal + * \brief Get the local node's cluster-layer node name + * + * If getting the node name from the cluster layer is impossible, call + * \c uname() and get the \c nodename member from the <tt>struct utsname</tt> + * object. + * + * \return Local node's name + * + * \note This will fatally exit if \c uname() fails to get the local node name + * or we run out of memory. + */ +const char * +pcmk__cluster_local_node_name(void) +{ + // @TODO Refactor to avoid trivially leaking name at exit + static char *name = NULL; + if (name == NULL) { - crm_notice("Could not obtain a node name for %s node with id %u", - name_for_cluster_type(stack), nodeid); + name = pcmk__cluster_node_name(0); } return name; } /*! - * \brief Get the node name corresponding to a node UUID + * \internal + * \brief Get the node name corresonding to a node UUID * - * \param[in] uuid UUID of desired node + * Look for the UUID in both the remote node cache and the cluster member cache. * - * \return name of desired node + * \param[in] uuid UUID to search for * - * \note This relies on the remote peer cache being populated with all - * remote nodes in the cluster, so callers should maintain that cache. + * \return Node name corresponding to \p uuid if found, or \c NULL otherwise */ const char * -crm_peer_uname(const char *uuid) +pcmk__node_name_from_uuid(const char *uuid) { + /* @TODO There are too many functions in libcrmcluster that look up a node + * from the node caches (possibly creating a cache entry if none exists). + * There are at least the following: + * * pcmk__cluster_lookup_remote_node() + * * pcmk__get_node() + * * pcmk__node_name_from_uuid() + * * pcmk__search_node_caches() + * + * There's a lot of duplication among them, but they all do slightly + * different things. We should try to clean them up and consolidate them to + * the extent possible, likely with new helper functions. + */ GHashTableIter iter; crm_node_t *node = NULL; CRM_CHECK(uuid != NULL, return NULL); - /* remote nodes have the same uname and uuid */ + // Remote nodes have the same uname and uuid if (g_hash_table_lookup(crm_remote_peer_cache, uuid)) { return uuid; } - /* avoid blocking calls where possible */ g_hash_table_iter_init(&iter, crm_peer_cache); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { if (pcmk__str_eq(node->uuid, uuid, pcmk__str_casei)) { - if (node->uname != NULL) { - return node->uname; - } - break; - } - } - node = NULL; - - if (is_corosync_cluster()) { - long long id; - - if ((pcmk__scan_ll(uuid, &id, 0LL) != pcmk_rc_ok) - || (id < 1LL) || (id > UINT32_MAX)) { - crm_err("Invalid Corosync node ID '%s'", uuid); - return NULL; - } - - node = pcmk__search_cluster_node_cache((uint32_t) id, NULL, NULL); - if (node != NULL) { - crm_info("Setting uuid for node %s[%u] to %s", - node->uname, node->id, uuid); - node->uuid = strdup(uuid); return node->uname; } - return NULL; } - return NULL; } /*! - * \brief Get a log-friendly string equivalent of a cluster type + * \brief Get a log-friendly string equivalent of a cluster layer * - * \param[in] type Cluster type + * \param[in] layer Cluster layer * - * \return Log-friendly string corresponding to \p type + * \return Log-friendly string corresponding to \p layer */ const char * -name_for_cluster_type(enum cluster_type_e type) +pcmk_cluster_layer_text(enum pcmk_cluster_layer layer) { - switch (type) { - case pcmk_cluster_corosync: + switch (layer) { + case pcmk_cluster_layer_corosync: return "corosync"; - case pcmk_cluster_unknown: + case pcmk_cluster_layer_unknown: return "unknown"; - case pcmk_cluster_invalid: + case pcmk_cluster_layer_invalid: + return "invalid"; + default: + crm_err("Invalid cluster layer: %d", layer); return "invalid"; } - crm_err("Invalid cluster type: %d", type); - return "invalid"; } /*! - * \brief Get (and validate) the local cluster type + * \brief Get and validate the local cluster layer + * + * If a cluster layer is not configured via the \c PCMK__ENV_CLUSTER_TYPE local + * option, this will try to detect an active cluster from among the supported + * cluster layers. + * + * \return Local cluster layer * - * \return Local cluster type - * \note This will fatally exit if the local cluster type is invalid. + * \note This will fatally exit if the configured cluster layer is invalid. */ -enum cluster_type_e -get_cluster_type(void) +enum pcmk_cluster_layer +pcmk_get_cluster_layer(void) { - bool detected = false; + static enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown; const char *cluster = NULL; - static enum cluster_type_e cluster_type = pcmk_cluster_unknown; - /* Return the previous calculation, if any */ - if (cluster_type != pcmk_cluster_unknown) { - return cluster_type; + // Cluster layer is stable once set + if (cluster_layer != pcmk_cluster_layer_unknown) { + return cluster_layer; } cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE); + if (cluster != NULL) { + crm_info("Verifying configured cluster layer '%s'", cluster); + cluster_layer = pcmk_cluster_layer_invalid; + #if SUPPORT_COROSYNC - /* If nothing is defined in the environment, try corosync (if supported) */ - if (cluster == NULL) { - crm_debug("Testing with Corosync"); - cluster_type = pcmk__corosync_detect(); - if (cluster_type != pcmk_cluster_unknown) { - detected = true; - goto done; + if (pcmk__str_eq(cluster, PCMK_VALUE_COROSYNC, pcmk__str_casei)) { + cluster_layer = pcmk_cluster_layer_corosync; } - } -#endif +#endif // SUPPORT_COROSYNC - /* Something was defined in the environment, test it against what we support */ - crm_info("Verifying cluster type: '%s'", - ((cluster == NULL)? "-unspecified-" : cluster)); - if (cluster == NULL) { + if (cluster_layer == pcmk_cluster_layer_invalid) { + crm_notice("This installation does not support the '%s' cluster " + "infrastructure: terminating", + cluster); + crm_exit(CRM_EX_FATAL); + } + crm_info("Assuming an active '%s' cluster", cluster); + } else { + // Nothing configured, so test supported cluster layers #if SUPPORT_COROSYNC - } else if (pcmk__str_eq(cluster, "corosync", pcmk__str_casei)) { - cluster_type = pcmk_cluster_corosync; -#endif + crm_debug("Testing with Corosync"); + if (pcmk__corosync_is_active()) { + cluster_layer = pcmk_cluster_layer_corosync; + } +#endif // SUPPORT_COROSYNC - } else { - cluster_type = pcmk_cluster_invalid; - goto done; /* Keep the compiler happy when no stacks are supported */ + if (cluster_layer == pcmk_cluster_layer_unknown) { + crm_notice("Could not determine the current cluster layer"); + } else { + crm_info("Detected an active '%s' cluster", + pcmk_cluster_layer_text(cluster_layer)); + } } - done: - if (cluster_type == pcmk_cluster_unknown) { - crm_notice("Could not determine the current cluster type"); + return cluster_layer; +} - } else if (cluster_type == pcmk_cluster_invalid) { - crm_notice("This installation does not support the '%s' cluster infrastructure: terminating.", - cluster); - crm_exit(CRM_EX_FATAL); +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START - } else { - crm_info("%s an active '%s' cluster", - (detected? "Detected" : "Assuming"), - name_for_cluster_type(cluster_type)); +#include <crm/cluster/compat.h> + +void +set_uuid(xmlNode *xml, const char *attr, crm_node_t *node) +{ + crm_xml_add(xml, attr, pcmk__cluster_node_uuid(node)); +} + +gboolean +crm_cluster_connect(pcmk_cluster_t *cluster) +{ + return pcmk_cluster_connect(cluster) == pcmk_rc_ok; +} + +void +crm_cluster_disconnect(pcmk_cluster_t *cluster) +{ + pcmk_cluster_disconnect(cluster); +} + +const char * +name_for_cluster_type(enum cluster_type_e type) +{ + switch (type) { + case pcmk_cluster_corosync: + return "corosync"; + case pcmk_cluster_unknown: + return "unknown"; + case pcmk_cluster_invalid: + return "invalid"; } + crm_err("Invalid cluster type: %d", type); + return "invalid"; +} - return cluster_type; +enum cluster_type_e +get_cluster_type(void) +{ + return (enum cluster_type_e) pcmk_get_cluster_layer(); } -/*! - * \brief Check whether the local cluster is a Corosync cluster - * - * \return TRUE if the local cluster is a Corosync cluster, otherwise FALSE - */ gboolean is_corosync_cluster(void) { - return get_cluster_type() == pcmk_cluster_corosync; + return pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync; } -// Deprecated functions kept only for backward API compatibility -// LCOV_EXCL_START +gboolean +send_cluster_message(const crm_node_t *node, enum crm_ais_msg_types service, + const xmlNode *data, gboolean ordered) +{ + return pcmk__cluster_send_message(node, service, data); +} -#include <crm/cluster/compat.h> +const char * +crm_peer_uuid(crm_node_t *peer) +{ + return pcmk__cluster_node_uuid(peer); +} -void -set_uuid(xmlNode *xml, const char *attr, crm_node_t *node) +char * +get_node_name(uint32_t nodeid) +{ + return pcmk__cluster_node_name(nodeid); +} + +const char * +get_local_node_name(void) +{ + return pcmk__cluster_local_node_name(); +} + +const char * +crm_peer_uname(const char *uuid) { - crm_xml_add(xml, attr, crm_peer_uuid(node)); + return pcmk__node_name_from_uuid(uuid); } // LCOV_EXCL_STOP diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 08280ce..ff4da60 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,32 +9,30 @@ #include <crm_internal.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <arpa/inet.h> +#include <inttypes.h> // PRIu64, PRIx32 #include <netdb.h> -#include <inttypes.h> // PRIu64 - -#include <bzlib.h> - -#include <crm/common/ipc.h> -#include <crm/cluster/internal.h> -#include <crm/common/mainloop.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <sys/socket.h> #include <sys/utsname.h> -#include <qb/qbipcc.h> -#include <qb/qbutil.h> - +#include <bzlib.h> +#include <corosync/cfg.h> +#include <corosync/cmap.h> #include <corosync/corodefs.h> #include <corosync/corotypes.h> #include <corosync/hdb.h> -#include <corosync/cfg.h> -#include <corosync/cmap.h> #include <corosync/quorum.h> +#include <qb/qbipcc.h> +#include <qb/qbutil.h> -#include <crm/msg_xml.h> +#include <crm/cluster/internal.h> +#include <crm/common/ipc.h> +#include <crm/common/ipc_internal.h> // PCMK__SPECIAL_PID +#include <crm/common/mainloop.h> +#include <crm/common/xml.h> -#include <crm/common/ipc_internal.h> /* PCMK__SPECIAL_PID* */ #include "crmcluster_private.h" static quorum_handle_t pcmk_quorum_handle = 0; @@ -54,7 +52,9 @@ static gboolean (*quorum_app_callback)(unsigned long long seq, char * pcmk__corosync_uuid(const crm_node_t *node) { - if ((node != NULL) && is_corosync_cluster()) { + CRM_ASSERT(pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync); + + if (node != NULL) { if (node->id > 0) { return crm_strdup_printf("%u", node->id); } else { @@ -114,7 +114,7 @@ pcmk__corosync_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid) int rv; if (nodeid == 0) { - nodeid = get_local_nodeid(0); + nodeid = pcmk__cpg_local_nodeid(0); } if (cmap_handle == 0 && local_handle == 0) { @@ -217,13 +217,14 @@ bail: * \internal * \brief Disconnect from Corosync cluster * - * \param[in,out] cluster Cluster connection to disconnect + * \param[in,out] cluster Cluster object to disconnect */ void -pcmk__corosync_disconnect(crm_cluster_t *cluster) +pcmk__corosync_disconnect(pcmk_cluster_t *cluster) { - cluster_disconnect_cpg(cluster); - if (pcmk_quorum_handle) { + pcmk__cpg_disconnect(cluster); + + if (pcmk_quorum_handle != 0) { quorum_finalize(pcmk_quorum_handle); pcmk_quorum_handle = 0; } @@ -309,12 +310,13 @@ quorum_notification_cb(quorum_handle_t handle, uint32_t quorate, crm_debug("Member[%d] %u ", i, id); /* Get this node's peer cache entry (adding one if not already there) */ - node = crm_get_peer(id, NULL); + node = pcmk__get_node(id, NULL, NULL, pcmk__node_search_cluster_member); if (node->uname == NULL) { char *name = pcmk__corosync_name(0, id); crm_info("Obtaining name for new node %u", id); - node = crm_get_peer(id, name); + node = pcmk__get_node(id, name, NULL, + pcmk__node_search_cluster_member); free(name); } @@ -445,106 +447,101 @@ pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, * \internal * \brief Connect to Corosync cluster layer * - * \param[in,out] cluster Initialized cluster object to connect + * \param[in,out] cluster Initialized cluster object to connect + * + * \return Standard Pacemaker return code */ -gboolean -pcmk__corosync_connect(crm_cluster_t *cluster) +int +pcmk__corosync_connect(pcmk_cluster_t *cluster) { crm_node_t *peer = NULL; - enum cluster_type_e stack = get_cluster_type(); + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); + const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); + int rc = pcmk_rc_ok; - crm_peer_init(); + pcmk__cluster_init_node_caches(); - if (stack != pcmk_cluster_corosync) { - crm_err("Invalid cluster type: %s " CRM_XS " stack=%d", - name_for_cluster_type(stack), stack); - return FALSE; + if (cluster_layer != pcmk_cluster_layer_corosync) { + crm_err("Invalid cluster layer: %s " CRM_XS " cluster_layer=%d", + cluster_layer_s, cluster_layer); + return EINVAL; } - if (!cluster_connect_cpg(cluster)) { - // Error message was logged by cluster_connect_cpg() - return FALSE; + rc = pcmk__cpg_connect(cluster); + if (rc != pcmk_rc_ok) { + // Error message was logged by pcmk__cpg_connect() + return rc; } - crm_info("Connection to %s established", name_for_cluster_type(stack)); + crm_info("Connection to %s established", cluster_layer_s); - cluster->nodeid = get_local_nodeid(0); + cluster->nodeid = pcmk__cpg_local_nodeid(0); if (cluster->nodeid == 0) { crm_err("Could not determine local node ID"); - return FALSE; + return ENXIO; } - cluster->uname = get_node_name(0); + cluster->uname = pcmk__cluster_node_name(0); if (cluster->uname == NULL) { crm_err("Could not determine local node name"); - return FALSE; + return ENXIO; } // Ensure local node always exists in peer cache - peer = crm_get_peer(cluster->nodeid, cluster->uname); + peer = pcmk__get_node(cluster->nodeid, cluster->uname, NULL, + pcmk__node_search_cluster_member); cluster->uuid = pcmk__corosync_uuid(peer); - return TRUE; + return pcmk_rc_ok; } /*! * \internal * \brief Check whether a Corosync cluster is active * - * \return pcmk_cluster_corosync if Corosync is found, else pcmk_cluster_unknown + * \return \c true if Corosync is found active, or \c false otherwise */ -enum cluster_type_e -pcmk__corosync_detect(void) +bool +pcmk__corosync_is_active(void) { - int rc = CS_OK; cmap_handle_t handle; + int rc = pcmk__init_cmap(&handle); - rc = pcmk__init_cmap(&handle); - - switch(rc) { - case CS_OK: - break; - case CS_ERR_SECURITY: - crm_debug("Failed to initialize the cmap API: Permission denied (%d)", rc); - /* It's there, we just can't talk to it. - * Good enough for us to identify as 'corosync' - */ - return pcmk_cluster_corosync; - - default: - crm_info("Failed to initialize the cmap API: %s (%d)", - pcmk__cs_err_str(rc), rc); - return pcmk_cluster_unknown; + if (rc == CS_OK) { + cmap_finalize(handle); + return true; } - cmap_finalize(handle); - return pcmk_cluster_corosync; + crm_info("Failed to initialize the cmap API: %s (%d)", + pcmk__cs_err_str(rc), rc); + return false; } /*! + * \internal * \brief Check whether a Corosync cluster peer is active * * \param[in] node Node to check * - * \return TRUE if \p node is an active Corosync peer, otherwise FALSE + * \return \c true if \p node is an active Corosync peer, or \c false otherwise */ -gboolean -crm_is_corosync_peer_active(const crm_node_t *node) +bool +pcmk__corosync_is_peer_active(const crm_node_t *node) { if (node == NULL) { crm_trace("Corosync peer inactive: NULL"); - return FALSE; - - } else if (!pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei)) { + return false; + } + if (!pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_none)) { crm_trace("Corosync peer %s inactive: state=%s", node->uname, node->state); - return FALSE; - - } else if (!pcmk_is_set(node->processes, crm_proc_cpg)) { - crm_trace("Corosync peer %s inactive: processes=%.16x", + return false; + } + if (!pcmk_is_set(node->processes, crm_proc_cpg)) { + crm_trace("Corosync peer %s inactive " CRM_XS " processes=%.16" PRIx32, node->uname, node->processes); - return FALSE; + return false; } - return TRUE; + return true; } /*! @@ -606,7 +603,7 @@ pcmk__corosync_add_nodes(xmlNode *xml_parent) goto bail; } - crm_peer_init(); + pcmk__cluster_init_node_caches(); crm_trace("Initializing Corosync node list"); for (lpc = 0; TRUE; lpc++) { uint32_t nodeid = 0; @@ -640,17 +637,17 @@ pcmk__corosync_add_nodes(xmlNode *xml_parent) if (nodeid > 0 || name != NULL) { crm_trace("Initializing node[%d] %u = %s", lpc, nodeid, name); - crm_get_peer(nodeid, name); + pcmk__get_node(nodeid, name, NULL, pcmk__node_search_cluster_member); } if (nodeid > 0 && name != NULL) { any = true; if (xml_parent) { - xmlNode *node = create_xml_node(xml_parent, XML_CIB_TAG_NODE); + xmlNode *node = pcmk__xe_create(xml_parent, PCMK_XE_NODE); crm_xml_set_id(node, "%u", nodeid); - crm_xml_add(node, XML_ATTR_UNAME, name); + crm_xml_add(node, PCMK_XA_UNAME, name); } } @@ -812,3 +809,17 @@ bail: cmap_finalize(cmap_handle); return result; } + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/cluster/compat.h> + +gboolean +crm_is_corosync_peer_active(const crm_node_t *node) +{ + return pcmk__corosync_is_peer_active(node); +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index d1decc6..62d39a6 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.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. * @@ -8,37 +8,40 @@ */ #include <crm_internal.h> -#include <bzlib.h> -#include <sys/socket.h> -#include <netinet/in.h> + #include <arpa/inet.h> +#include <inttypes.h> // PRIu32 #include <netdb.h> - -#include <crm/common/ipc.h> -#include <crm/cluster/internal.h> -#include <crm/common/mainloop.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdint.h> // uint32_t +#include <sys/socket.h> +#include <sys/types.h> // size_t #include <sys/utsname.h> -#include <qb/qbipc_common.h> -#include <qb/qbipcc.h> -#include <qb/qbutil.h> - +#include <bzlib.h> #include <corosync/corodefs.h> #include <corosync/corotypes.h> #include <corosync/hdb.h> #include <corosync/cpg.h> +#include <qb/qbipc_common.h> +#include <qb/qbipcc.h> +#include <qb/qbutil.h> -#include <crm/msg_xml.h> +#include <crm/cluster/internal.h> +#include <crm/common/ipc.h> +#include <crm/common/ipc_internal.h> // PCMK__SPECIAL_PID +#include <crm/common/mainloop.h> +#include <crm/common/xml.h> -#include <crm/common/ipc_internal.h> /* PCMK__SPECIAL_PID* */ #include "crmcluster_private.h" -/* @TODO Once we can update the public API to require crm_cluster_t* in more +/* @TODO Once we can update the public API to require pcmk_cluster_t* in more * functions, we can ditch this in favor of cluster->cpg_handle. */ static cpg_handle_t pcmk_cpg_handle = 0; -// @TODO These could be moved to crm_cluster_t* at that time as well +// @TODO These could be moved to pcmk_cluster_t* at that time as well static bool cpg_evicted = false; static GList *cs_message_queue = NULL; static int cs_message_timer = 0; @@ -87,26 +90,7 @@ static void crm_cs_flush(gpointer data); } while (counter < max) /*! - * \brief Disconnect from Corosync CPG - * - * \param[in,out] cluster Cluster to disconnect - */ -void -cluster_disconnect_cpg(crm_cluster_t *cluster) -{ - pcmk_cpg_handle = 0; - if (cluster->cpg_handle) { - crm_trace("Disconnecting CPG"); - cpg_leave(cluster->cpg_handle, &cluster->group); - cpg_finalize(cluster->cpg_handle); - cluster->cpg_handle = 0; - - } else { - crm_info("No CPG connection"); - } -} - -/*! + * \internal * \brief Get the local Corosync node ID (via CPG) * * \param[in] handle CPG connection to use (or 0 to use new connection) @@ -114,7 +98,7 @@ cluster_disconnect_cpg(crm_cluster_t *cluster) * \return Corosync ID of local node (or 0 if not known) */ uint32_t -get_local_nodeid(cpg_handle_t handle) +pcmk__cpg_local_nodeid(cpg_handle_t handle) { cs_error_t rc = CS_OK; int retries = 0; @@ -125,15 +109,18 @@ get_local_nodeid(cpg_handle_t handle) uid_t found_uid = 0; gid_t found_gid = 0; pid_t found_pid = 0; - int rv; + int rv = 0; - if(local_nodeid != 0) { + if (local_nodeid != 0) { return local_nodeid; } - if(handle == 0) { + if (handle == 0) { crm_trace("Creating connection"); - cs_repeat(rc, retries, 5, cpg_model_initialize(&local_handle, CPG_MODEL_V1, (cpg_model_data_t *)&cpg_model_info, NULL)); + cs_repeat(rc, retries, 5, + cpg_model_initialize(&local_handle, CPG_MODEL_V1, + (cpg_model_data_t *) &cpg_model_info, + NULL)); if (rc != CS_OK) { crm_err("Could not connect to the CPG API: %s (%d)", cs_strerror(rc), rc); @@ -147,14 +134,16 @@ get_local_nodeid(cpg_handle_t handle) goto bail; } - /* CPG provider run as root (in given user namespace, anyway)? */ - if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid, - &found_uid, &found_gid))) { + // CPG provider run as root (at least in given user namespace)? + rv = crm_ipc_is_authentic_process(fd, (uid_t) 0, (gid_t) 0, &found_pid, + &found_uid, &found_gid); + if (rv == 0) { crm_err("CPG provider is not authentic:" " process %lld (uid: %lld, gid: %lld)", (long long) PCMK__SPECIAL_PID_AS_0(found_pid), (long long) found_uid, (long long) found_gid); goto bail; + } else if (rv < 0) { crm_err("Could not verify authenticity of CPG provider: %s (%d)", strerror(-rv), -rv); @@ -174,7 +163,7 @@ get_local_nodeid(cpg_handle_t handle) } bail: - if(handle == 0) { + if (handle == 0) { crm_trace("Closing connection"); cpg_finalize(local_handle); } @@ -279,7 +268,7 @@ static int pcmk_cpg_dispatch(gpointer user_data) { cs_error_t rc = CS_OK; - crm_cluster_t *cluster = (crm_cluster_t *) user_data; + pcmk_cluster_t *cluster = (pcmk_cluster_t *) user_data; rc = cpg_dispatch(cluster->cpg_handle, CS_DISPATCH_ONE); if (rc != CS_OK) { @@ -422,59 +411,64 @@ check_message_sanity(const pcmk__cpg_msg_t *msg) } /*! + * \internal * \brief Extract text data from a Corosync CPG message * - * \param[in] handle CPG connection (to get local node ID if not known) - * \param[in] nodeid Corosync ID of node that sent message - * \param[in] pid Process ID of message sender (for logging only) - * \param[in,out] content CPG message - * \param[out] kind If not NULL, will be set to CPG header ID - * (which should be an enum crm_ais_msg_class value, - * currently always crm_class_cluster) - * \param[out] from If not NULL, will be set to sender uname - * (valid for the lifetime of \p content) + * \param[in] handle CPG connection (to get local node ID if not known) + * \param[in] sender_id Corosync ID of node that sent message + * \param[in] pid Process ID of message sender (for logging only) + * \param[in,out] content CPG message + * \param[out] kind If not \c NULL, will be set to CPG header ID + * (which should be an <tt>enum crm_ais_msg_class</tt> + * value, currently always \c crm_class_cluster) + * \param[out] from If not \c NULL, will be set to sender uname + * (valid for the lifetime of \p content) * * \return Newly allocated string with message data - * \note It is the caller's responsibility to free the return value with free(). + * + * \note The caller is responsible for freeing the return value using \c free(). */ char * -pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void *content, - uint32_t *kind, const char **from) +pcmk__cpg_message_data(cpg_handle_t handle, uint32_t sender_id, uint32_t pid, + void *content, uint32_t *kind, const char **from) { char *data = NULL; - pcmk__cpg_msg_t *msg = (pcmk__cpg_msg_t *) content; + pcmk__cpg_msg_t *msg = content; - if(handle) { + if (handle != 0) { // Do filtering and field massaging - uint32_t local_nodeid = get_local_nodeid(handle); - const char *local_name = get_local_node_name(); + uint32_t local_nodeid = pcmk__cpg_local_nodeid(handle); + const char *local_name = pcmk__cluster_local_node_name(); - if (msg->sender.id > 0 && msg->sender.id != nodeid) { - crm_err("Nodeid mismatch from %d.%d: claimed nodeid=%u", nodeid, pid, msg->sender.id); + if ((msg->sender.id != 0) && (msg->sender.id != sender_id)) { + crm_err("Nodeid mismatch from %" PRIu32 ".%" PRIu32 + ": claimed nodeid=%" PRIu32, + sender_id, pid, msg->sender.id); return NULL; - - } else if (msg->host.id != 0 && (local_nodeid != msg->host.id)) { - /* Not for us */ - crm_trace("Not for us: %u != %u", msg->host.id, local_nodeid); + } + if ((msg->host.id != 0) && (local_nodeid != msg->host.id)) { + crm_trace("Not for us: %" PRIu32" != %" PRIu32, + msg->host.id, local_nodeid); return NULL; - } else if (msg->host.size != 0 && !pcmk__str_eq(msg->host.uname, local_name, pcmk__str_casei)) { - /* Not for us */ + } + if ((msg->host.size > 0) + && !pcmk__str_eq(msg->host.uname, local_name, pcmk__str_casei)) { + crm_trace("Not for us: %s != %s", msg->host.uname, local_name); return NULL; } - msg->sender.id = nodeid; + msg->sender.id = sender_id; if (msg->sender.size == 0) { - crm_node_t *peer = crm_get_peer(nodeid, NULL); - - if (peer == NULL) { - crm_err("Peer with nodeid=%u is unknown", nodeid); + const crm_node_t *peer = + pcmk__get_node(sender_id, NULL, NULL, + pcmk__node_search_cluster_member); - } else if (peer->uname == NULL) { - crm_err("No uname for peer with nodeid=%u", nodeid); + if (peer->uname == NULL) { + crm_err("No uname for peer with nodeid=%u", sender_id); } else { - crm_notice("Fixing uname for peer with nodeid=%u", nodeid); + crm_notice("Fixing uname for peer with nodeid=%u", sender_id); msg->sender.size = strlen(peer->uname); memset(msg->sender.uname, 0, MAX_NAME); memcpy(msg->sender.uname, peer->uname, msg->sender.size); @@ -493,7 +487,7 @@ pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void *from = msg->sender.uname; } - if (msg->is_compressed && msg->size > 0) { + if (msg->is_compressed && (msg->size > 0)) { int rc = BZ_OK; char *uncompressed = NULL; unsigned int new_size = msg->size + 1; @@ -503,13 +497,15 @@ pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void } crm_trace("Decompressing message data"); - uncompressed = calloc(1, new_size); - rc = BZ2_bzBuffToBuffDecompress(uncompressed, &new_size, msg->data, msg->compressed_size, 1, 0); + uncompressed = pcmk__assert_alloc(1, new_size); + rc = BZ2_bzBuffToBuffDecompress(uncompressed, &new_size, msg->data, + msg->compressed_size, 1, 0); rc = pcmk__bzlib2rc(rc); if (rc != pcmk_rc_ok) { - crm_err("Decompression failed: %s " CRM_XS " rc=%d", pcmk_rc_str(rc), rc); + crm_err("Decompression failed: %s " CRM_XS " rc=%d", + pcmk_rc_str(rc), rc); free(uncompressed); goto badmsg; } @@ -526,7 +522,8 @@ pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void } // Is this necessary? - crm_get_peer(msg->sender.id, msg->sender.uname); + pcmk__get_node(msg->sender.id, msg->sender.uname, NULL, + pcmk__node_search_cluster_member); crm_trace("Payload: %.200s", data); return data; @@ -627,8 +624,9 @@ node_left(const char *cpg_group_name, int event_counter, const struct cpg_address **sorted_member_list, size_t member_list_entries) { - crm_node_t *peer = pcmk__search_cluster_node_cache(cpg_peer->nodeid, - NULL, NULL); + crm_node_t *peer = + pcmk__search_node_caches(cpg_peer->nodeid, NULL, + pcmk__node_search_cluster_member); const struct cpg_address **rival = NULL; /* Most CPG-related Pacemaker code assumes that only one process on a node @@ -656,7 +654,7 @@ node_left(const char *cpg_group_name, int event_counter, cpgreason2str(cpg_peer->reason)); if (peer != NULL) { crm_update_peer_proc(__func__, peer, crm_proc_cpg, - OFFLINESTATUS); + PCMK_VALUE_OFFLINE); } } else if (cpg_peer->nodeid == local_nodeid) { crm_warn("Group %s event %d: duplicate local pid %u left%s", @@ -672,72 +670,81 @@ node_left(const char *cpg_group_name, int event_counter, } /*! + * \internal * \brief Handle a CPG configuration change event * * \param[in] handle CPG connection - * \param[in] cpg_name CPG group name + * \param[in] group_name CPG group name * \param[in] member_list List of current CPG members * \param[in] member_list_entries Number of entries in \p member_list * \param[in] left_list List of CPG members that left * \param[in] left_list_entries Number of entries in \p left_list * \param[in] joined_list List of CPG members that joined * \param[in] joined_list_entries Number of entries in \p joined_list + * + * \note This is of type \c cpg_confchg_fn_t, intended to be used in a + * \c cpg_callbacks_t object. */ void -pcmk_cpg_membership(cpg_handle_t handle, - const struct cpg_name *groupName, - const struct cpg_address *member_list, size_t member_list_entries, - const struct cpg_address *left_list, size_t left_list_entries, - const struct cpg_address *joined_list, size_t joined_list_entries) +pcmk__cpg_confchg_cb(cpg_handle_t handle, + const struct cpg_name *group_name, + const struct cpg_address *member_list, + size_t member_list_entries, + const struct cpg_address *left_list, + size_t left_list_entries, + const struct cpg_address *joined_list, + size_t joined_list_entries) { - int i; - gboolean found = FALSE; static int counter = 0; - uint32_t local_nodeid = get_local_nodeid(handle); - const struct cpg_address **sorted; - sorted = malloc(member_list_entries * sizeof(const struct cpg_address *)); - CRM_ASSERT(sorted != NULL); + bool found = false; + uint32_t local_nodeid = pcmk__cpg_local_nodeid(handle); + const struct cpg_address **sorted = NULL; + + sorted = pcmk__assert_alloc(member_list_entries, + sizeof(const struct cpg_address *)); for (size_t iter = 0; iter < member_list_entries; iter++) { sorted[iter] = member_list + iter; } - /* so that the cross-matching multiply-subscribed nodes is then cheap */ + + // So that the cross-matching of multiply-subscribed nodes is then cheap qsort(sorted, member_list_entries, sizeof(const struct cpg_address *), cmp_member_list_nodeid); - for (i = 0; i < left_list_entries; i++) { - node_left(groupName->value, counter, local_nodeid, &left_list[i], + for (int i = 0; i < left_list_entries; i++) { + node_left(group_name->value, counter, local_nodeid, &left_list[i], sorted, member_list_entries); } free(sorted); sorted = NULL; - for (i = 0; i < joined_list_entries; i++) { + for (int i = 0; i < joined_list_entries; i++) { crm_info("Group %s event %d: node %u pid %u joined%s", - groupName->value, counter, joined_list[i].nodeid, + group_name->value, counter, joined_list[i].nodeid, joined_list[i].pid, cpgreason2str(joined_list[i].reason)); } - for (i = 0; i < member_list_entries; i++) { - crm_node_t *peer = crm_get_peer(member_list[i].nodeid, NULL); + for (int i = 0; i < member_list_entries; i++) { + crm_node_t *peer = pcmk__get_node(member_list[i].nodeid, NULL, NULL, + pcmk__node_search_cluster_member); if (member_list[i].nodeid == local_nodeid && member_list[i].pid != getpid()) { // See the note in node_left() crm_warn("Group %s event %d: detected duplicate local pid %u", - groupName->value, counter, member_list[i].pid); + group_name->value, counter, member_list[i].pid); continue; } crm_info("Group %s event %d: %s (node %u pid %u) is member", - groupName->value, counter, peer_name(peer), + group_name->value, counter, peer_name(peer), member_list[i].nodeid, member_list[i].pid); /* If the caller left auto-reaping enabled, this will also update the * state to member. */ peer = crm_update_peer_proc(__func__, peer, crm_proc_cpg, - ONLINESTATUS); + PCMK_VALUE_ONLINE); if (peer && peer->state && strcmp(peer->state, CRM_NODE_MEMBER)) { /* The node is a CPG member, but we currently think it's not a @@ -755,19 +762,20 @@ pcmk_cpg_membership(cpg_handle_t handle, } else if (now > (peer->when_lost + 60)) { // If it persists for more than a minute, update the state - crm_warn("Node %u is member of group %s but was believed offline", - member_list[i].nodeid, groupName->value); + crm_warn("Node %u is member of group %s but was believed " + "offline", + member_list[i].nodeid, group_name->value); pcmk__update_peer_state(__func__, peer, CRM_NODE_MEMBER, 0); } } if (local_nodeid == member_list[i].nodeid) { - found = TRUE; + found = true; } } if (!found) { - crm_err("Local node was evicted from group %s", groupName->value); + crm_err("Local node was evicted from group %s", group_name->value); cpg_evicted = true; } @@ -775,14 +783,50 @@ pcmk_cpg_membership(cpg_handle_t handle, } /*! - * \brief Connect to Corosync CPG + * \brief Set the CPG deliver callback function for a cluster object * * \param[in,out] cluster Cluster object + * \param[in] fn Deliver callback function to set * - * \return TRUE on success, otherwise FALSE + * \return Standard Pacemaker return code */ -gboolean -cluster_connect_cpg(crm_cluster_t *cluster) +int +pcmk_cpg_set_deliver_fn(pcmk_cluster_t *cluster, cpg_deliver_fn_t fn) +{ + if (cluster == NULL) { + return EINVAL; + } + cluster->cpg.cpg_deliver_fn = fn; + return pcmk_rc_ok; +} + +/*! + * \brief Set the CPG config change callback function for a cluster object + * + * \param[in,out] cluster Cluster object + * \param[in] fn Configuration change callback function to set + * + * \return Standard Pacemaker return code + */ +int +pcmk_cpg_set_confchg_fn(pcmk_cluster_t *cluster, cpg_confchg_fn_t fn) +{ + if (cluster == NULL) { + return EINVAL; + } + cluster->cpg.cpg_confchg_fn = fn; + return pcmk_rc_ok; +} + +/*! + * \brief Connect to Corosync CPG + * + * \param[in,out] cluster Initialized cluster object to connect + * + * \return Standard Pacemaker return code + */ +int +pcmk__cpg_connect(pcmk_cluster_t *cluster) { cs_error_t rc; int fd = -1; @@ -848,7 +892,7 @@ cluster_connect_cpg(crm_cluster_t *cluster) goto bail; } - id = get_local_nodeid(handle); + id = pcmk__cpg_local_nodeid(handle); if (id == 0) { crm_err("Could not get local node id from the CPG API"); goto bail; @@ -870,54 +914,52 @@ cluster_connect_cpg(crm_cluster_t *cluster) bail: if (rc != CS_OK) { cpg_finalize(handle); - return FALSE; + // @TODO Map rc to more specific Pacemaker return code + return ENOTCONN; } - peer = crm_get_peer(id, NULL); - crm_update_peer_proc(__func__, peer, crm_proc_cpg, ONLINESTATUS); - return TRUE; + peer = pcmk__get_node(id, NULL, NULL, pcmk__node_search_cluster_member); + crm_update_peer_proc(__func__, peer, crm_proc_cpg, PCMK_VALUE_ONLINE); + return pcmk_rc_ok; } /*! * \internal - * \brief Send an XML message via Corosync CPG - * - * \param[in] msg XML message to send - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send + * \brief Disconnect from Corosync CPG * - * \return TRUE on success, otherwise FALSE + * \param[in,out] cluster Cluster object to disconnect */ -bool -pcmk__cpg_send_xml(const xmlNode *msg, const crm_node_t *node, - enum crm_ais_msg_types dest) +void +pcmk__cpg_disconnect(pcmk_cluster_t *cluster) { - bool rc = true; - char *data = NULL; + pcmk_cpg_handle = 0; + if (cluster->cpg_handle != 0) { + crm_trace("Disconnecting CPG"); + cpg_leave(cluster->cpg_handle, &cluster->group); + cpg_finalize(cluster->cpg_handle); + cluster->cpg_handle = 0; - data = dump_xml_unformatted(msg); - rc = send_cluster_text(crm_class_cluster, data, FALSE, node, dest); - free(data); - return rc; + } else { + crm_info("No CPG connection"); + } } /*! * \internal * \brief Send string data via Corosync CPG * - * \param[in] msg_class Message class (to set as CPG header ID) - * \param[in] data Data to send - * \param[in] local What to set as host "local" value (which is never used) - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send + * \param[in] data Data to send + * \param[in] local What to set as host "local" value (which is never used) + * \param[in] node Cluster node to send message to + * \param[in] dest Type of message to send * - * \return TRUE on success, otherwise FALSE + * \return \c true on success, or \c false otherwise */ -gboolean -send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, - gboolean local, const crm_node_t *node, - enum crm_ais_msg_types dest) +static bool +send_cpg_text(const char *data, bool local, const crm_node_t *node, + enum crm_ais_msg_types dest) { + // @COMPAT Drop local argument when send_cluster_text is dropped static int msg_id = 0; static int local_pid = 0; static int local_name_len = 0; @@ -926,20 +968,11 @@ send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, char *target = NULL; struct iovec *iov; pcmk__cpg_msg_t *msg = NULL; - enum crm_ais_msg_types sender = text2msg_type(crm_system_name); - switch (msg_class) { - case crm_class_cluster: - break; - default: - crm_err("Invalid message class: %d", msg_class); - return FALSE; - } - - CRM_CHECK(dest != crm_msg_ais, return FALSE); + CRM_CHECK(dest != crm_msg_ais, return false); if (local_name == NULL) { - local_name = get_local_node_name(); + local_name = pcmk__cluster_local_node_name(); } if ((local_name_len == 0) && (local_name != NULL)) { local_name_len = strlen(local_name); @@ -953,39 +986,38 @@ send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, local_pid = getpid(); } - if (sender == crm_msg_none) { - sender = local_pid; - } - - msg = calloc(1, sizeof(pcmk__cpg_msg_t)); + msg = pcmk__assert_alloc(1, sizeof(pcmk__cpg_msg_t)); msg_id++; msg->id = msg_id; - msg->header.id = msg_class; + msg->header.id = crm_class_cluster; msg->header.error = CS_OK; msg->host.type = dest; msg->host.local = local; - if (node) { - if (node->uname) { - target = strdup(node->uname); + if (node != NULL) { + if (node->uname != NULL) { + target = pcmk__str_copy(node->uname); msg->host.size = strlen(node->uname); memset(msg->host.uname, 0, MAX_NAME); memcpy(msg->host.uname, node->uname, msg->host.size); + } else { target = crm_strdup_printf("%u", node->id); } msg->host.id = node->id; + } else { - target = strdup("all"); + target = pcmk__str_copy("all"); } msg->sender.id = 0; - msg->sender.type = sender; + msg->sender.type = pcmk__cluster_parse_msg_type(crm_system_name); msg->sender.pid = local_pid; msg->sender.size = local_name_len; memset(msg->sender.uname, 0, MAX_NAME); + if ((local_name != NULL) && (msg->sender.size != 0)) { memcpy(msg->sender.uname, local_name, msg->sender.size); } @@ -1000,10 +1032,9 @@ send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, } else { char *compressed = NULL; unsigned int new_size = 0; - char *uncompressed = strdup(data); - if (pcmk__compress(uncompressed, (unsigned int) msg->size, 0, - &compressed, &new_size) == pcmk_rc_ok) { + if (pcmk__compress(data, (unsigned int) msg->size, 0, &compressed, + &new_size) == pcmk_rc_ok) { msg->header.size = sizeof(pcmk__cpg_msg_t) + new_size; msg = pcmk__realloc(msg, msg->header.size); @@ -1019,38 +1050,116 @@ send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, memcpy(msg->data, data, msg->size); } - free(uncompressed); free(compressed); } - iov = calloc(1, sizeof(struct iovec)); + iov = pcmk__assert_alloc(1, sizeof(struct iovec)); iov->iov_base = msg; iov->iov_len = msg->header.size; - if (msg->compressed_size) { - crm_trace("Queueing CPG message %u to %s (%llu bytes, %d bytes compressed payload): %.200s", + if (msg->compressed_size > 0) { + crm_trace("Queueing CPG message %u to %s " + "(%llu bytes, %d bytes compressed payload): %.200s", msg->id, target, (unsigned long long) iov->iov_len, msg->compressed_size, data); } else { - crm_trace("Queueing CPG message %u to %s (%llu bytes, %d bytes payload): %.200s", + crm_trace("Queueing CPG message %u to %s " + "(%llu bytes, %d bytes payload): %.200s", msg->id, target, (unsigned long long) iov->iov_len, msg->size, data); } + free(target); cs_message_queue = g_list_append(cs_message_queue, iov); crm_cs_flush(&pcmk_cpg_handle); - return TRUE; + return true; } /*! - * \brief Get the message type equivalent of a string + * \internal + * \brief Send an XML message via Corosync CPG * - * \param[in] text String of message type + * \param[in] msg XML message to send + * \param[in] node Cluster node to send message to + * \param[in] dest Type of message to send * - * \return Message type equivalent of \p text + * \return TRUE on success, otherwise FALSE */ +bool +pcmk__cpg_send_xml(const xmlNode *msg, const crm_node_t *node, + enum crm_ais_msg_types dest) +{ + bool rc = true; + GString *data = g_string_sized_new(1024); + + pcmk__xml_string(msg, 0, data, 0); + + rc = send_cpg_text(data->str, false, node, dest); + g_string_free(data, TRUE); + return rc; +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/cluster/compat.h> + +gboolean +cluster_connect_cpg(pcmk_cluster_t *cluster) +{ + return pcmk__cpg_connect(cluster) == pcmk_rc_ok; +} + +void +cluster_disconnect_cpg(pcmk_cluster_t *cluster) +{ + pcmk__cpg_disconnect(cluster); +} + +uint32_t +get_local_nodeid(cpg_handle_t handle) +{ + return pcmk__cpg_local_nodeid(handle); +} + +void +pcmk_cpg_membership(cpg_handle_t handle, + const struct cpg_name *group_name, + const struct cpg_address *member_list, + size_t member_list_entries, + const struct cpg_address *left_list, + size_t left_list_entries, + const struct cpg_address *joined_list, + size_t joined_list_entries) +{ + pcmk__cpg_confchg_cb(handle, group_name, member_list, member_list_entries, + left_list, left_list_entries, + joined_list, joined_list_entries); +} + +gboolean +send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, + gboolean local, const crm_node_t *node, + enum crm_ais_msg_types dest) +{ + switch (msg_class) { + case crm_class_cluster: + return send_cpg_text(data, local, node, dest); + default: + crm_err("Invalid message class: %d", msg_class); + return FALSE; + } +} + +char * +pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, + void *content, uint32_t *kind, const char **from) +{ + return pcmk__cpg_message_data(handle, nodeid, pid, content, kind, from); +} + enum crm_ais_msg_types text2msg_type(const char *text) { @@ -1090,3 +1199,6 @@ text2msg_type(const char *text) } return type; } + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/cluster/crmcluster_private.h b/lib/cluster/crmcluster_private.h index 370bca5..ef1d54f 100644 --- a/lib/cluster/crmcluster_private.h +++ b/lib/cluster/crmcluster_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,10 +19,14 @@ #include <glib.h> // G_GNUC_INTERNAL, gboolean #include <libxml/tree.h> // xmlNode -#include <crm/cluster.h> // cluster_type_e, crm_node_t +#if SUPPORT_COROSYNC +#include <corosync/cpg.h> // cpg_handle_t +#endif // SUPPORT_COROSYNC + +#include <crm/cluster.h> // crm_node_t G_GNUC_INTERNAL -enum cluster_type_e pcmk__corosync_detect(void); +bool pcmk__corosync_is_active(void); G_GNUC_INTERNAL bool pcmk__corosync_has_nodelist(void); @@ -35,10 +39,22 @@ char *pcmk__corosync_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid); G_GNUC_INTERNAL -gboolean pcmk__corosync_connect(crm_cluster_t *cluster); +int pcmk__corosync_connect(pcmk_cluster_t *cluster); + +G_GNUC_INTERNAL +void pcmk__corosync_disconnect(pcmk_cluster_t *cluster); + +G_GNUC_INTERNAL +bool pcmk__corosync_is_peer_active(const crm_node_t *node); + +G_GNUC_INTERNAL +int pcmk__cpg_connect(pcmk_cluster_t *cluster); + +G_GNUC_INTERNAL +void pcmk__cpg_disconnect(pcmk_cluster_t *cluster); G_GNUC_INTERNAL -void pcmk__corosync_disconnect(crm_cluster_t *cluster); +uint32_t pcmk__cpg_local_nodeid(cpg_handle_t handle); G_GNUC_INTERNAL bool pcmk__cpg_send_xml(const xmlNode *msg, const crm_node_t *node, diff --git a/lib/cluster/election.c b/lib/cluster/election.c index ebbae72..a3b4df0 100644 --- a/lib/cluster/election.c +++ b/lib/cluster/election.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -12,7 +12,6 @@ #include <sys/time.h> #include <sys/resource.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/mainloop.h> @@ -298,8 +297,9 @@ election_vote(election_t *e) return; } - our_node = crm_get_peer(0, e->uname); - if ((our_node == NULL) || (crm_is_peer_active(our_node) == FALSE)) { + our_node = pcmk__get_node(0, e->uname, NULL, + pcmk__node_search_cluster_member); + if (!pcmk__cluster_is_node_active(our_node)) { crm_trace("Cannot vote in %s yet: local node not connected to cluster", e->name); return; @@ -310,13 +310,15 @@ election_vote(election_t *e) vote = create_request(CRM_OP_VOTE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL); e->count++; - crm_xml_add(vote, F_CRM_ELECTION_OWNER, our_node->uuid); - crm_xml_add_int(vote, F_CRM_ELECTION_ID, e->count); + crm_xml_add(vote, PCMK__XA_ELECTION_OWNER, our_node->uuid); + crm_xml_add_int(vote, PCMK__XA_ELECTION_ID, e->count); + // Warning: PCMK__XA_ELECTION_AGE_NANO_SEC value is actually microseconds get_uptime(&age); - crm_xml_add_timeval(vote, F_CRM_ELECTION_AGE_S, F_CRM_ELECTION_AGE_US, &age); + crm_xml_add_timeval(vote, PCMK__XA_ELECTION_AGE_SEC, + PCMK__XA_ELECTION_AGE_NANO_SEC, &age); - send_cluster_message(NULL, crm_msg_crmd, vote, TRUE); + pcmk__cluster_send_message(NULL, crm_msg_crmd, vote); free_xml(vote); crm_debug("Started %s round %d", e->name, e->count); @@ -355,7 +357,7 @@ election_check(election_t *e) } voted_size = g_hash_table_size(e->voted); - num_members = crm_active_peers(); + num_members = pcmk__cluster_num_active_nodes(); /* in the case of #voted > #members, it is better to * wait for the timeout and give the cluster time to @@ -372,7 +374,7 @@ election_check(election_t *e) crm_warn("Received too many votes in %s", e->name); g_hash_table_iter_init(&gIter, crm_peer_cache); while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) & node)) { - if (crm_is_peer_active(node)) { + if (pcmk__cluster_is_node_active(node)) { crm_warn("* expected vote: %s", node->uname); } } @@ -428,12 +430,12 @@ parse_election_message(const election_t *e, const xmlNode *message, vote->age.tv_sec = -1; vote->age.tv_usec = -1; - vote->op = crm_element_value(message, F_CRM_TASK); - vote->from = crm_element_value(message, F_CRM_HOST_FROM); - vote->version = crm_element_value(message, F_CRM_VERSION); - vote->election_owner = crm_element_value(message, F_CRM_ELECTION_OWNER); + vote->op = crm_element_value(message, PCMK__XA_CRM_TASK); + vote->from = crm_element_value(message, PCMK__XA_SRC); + vote->version = crm_element_value(message, PCMK_XA_VERSION); + vote->election_owner = crm_element_value(message, PCMK__XA_ELECTION_OWNER); - crm_element_value_int(message, F_CRM_ELECTION_ID, &(vote->election_id)); + crm_element_value_int(message, PCMK__XA_ELECTION_ID, &(vote->election_id)); if ((vote->op == NULL) || (vote->from == NULL) || (vote->version == NULL) || (vote->election_owner == NULL) || (vote->election_id < 0)) { @@ -448,9 +450,11 @@ parse_election_message(const election_t *e, const xmlNode *message, // Op-specific validation if (pcmk__str_eq(vote->op, CRM_OP_VOTE, pcmk__str_none)) { - // Only vote ops have uptime - crm_element_value_timeval(message, F_CRM_ELECTION_AGE_S, - F_CRM_ELECTION_AGE_US, &(vote->age)); + /* Only vote ops have uptime. + Warning: PCMK__XA_ELECTION_AGE_NANO_SEC value is in microseconds. + */ + crm_element_value_timeval(message, PCMK__XA_ELECTION_AGE_SEC, + PCMK__XA_ELECTION_AGE_NANO_SEC, &(vote->age)); if ((vote->age.tv_sec < 0) || (vote->age.tv_usec < 0)) { crm_warn("Cannot count %s %s from %s because it is missing uptime", (e? e->name : "election"), vote->op, vote->from); @@ -485,19 +489,12 @@ parse_election_message(const election_t *e, const xmlNode *message, static void record_vote(election_t *e, struct vote *vote) { - char *voter_copy = NULL; - char *vote_copy = NULL; - CRM_ASSERT(e && vote && vote->from && vote->op); + if (e->voted == NULL) { e->voted = pcmk__strkey_table(free, free); } - - voter_copy = strdup(vote->from); - vote_copy = strdup(vote->op); - CRM_ASSERT(voter_copy && vote_copy); - - g_hash_table_replace(e->voted, voter_copy, vote_copy); + pcmk__insert_dup(e->voted, vote->from, vote->op); } static void @@ -508,10 +505,10 @@ send_no_vote(crm_node_t *peer, struct vote *vote) xmlNode *novote = create_request(CRM_OP_NOVOTE, NULL, vote->from, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL); - crm_xml_add(novote, F_CRM_ELECTION_OWNER, vote->election_owner); - crm_xml_add_int(novote, F_CRM_ELECTION_ID, vote->election_id); + crm_xml_add(novote, PCMK__XA_ELECTION_OWNER, vote->election_owner); + crm_xml_add_int(novote, PCMK__XA_ELECTION_ID, vote->election_id); - send_cluster_message(peer, crm_msg_crmd, novote, TRUE); + pcmk__cluster_send_message(peer, crm_msg_crmd, novote); free_xml(novote); } @@ -547,8 +544,10 @@ election_count_vote(election_t *e, const xmlNode *message, bool can_win) return election_error; } - your_node = crm_get_peer(0, vote.from); - our_node = crm_get_peer(0, e->uname); + your_node = pcmk__get_node(0, vote.from, NULL, + pcmk__node_search_cluster_member); + our_node = pcmk__get_node(0, e->uname, NULL, + pcmk__node_search_cluster_member); we_are_owner = (our_node != NULL) && pcmk__str_eq(our_node->uuid, vote.election_owner, pcmk__str_none); @@ -557,7 +556,7 @@ election_count_vote(election_t *e, const xmlNode *message, bool can_win) reason = "Not eligible"; we_lose = TRUE; - } else if (our_node == NULL || crm_is_peer_active(our_node) == FALSE) { + } else if (!pcmk__cluster_is_node_active(our_node)) { reason = "We are not part of the cluster"; log_level = LOG_ERR; we_lose = TRUE; @@ -567,7 +566,7 @@ election_count_vote(election_t *e, const xmlNode *message, bool can_win) reason = "Superseded"; done = TRUE; - } else if (your_node == NULL || crm_is_peer_active(your_node) == FALSE) { + } else if (!pcmk__cluster_is_node_active(your_node)) { /* Possibly we cached the message in the FSA queue at a point that it wasn't */ reason = "Peer is not part of our cluster"; log_level = LOG_WARNING; diff --git a/lib/cluster/membership.c b/lib/cluster/membership.c index f856cca..7eedc2e 100644 --- a/lib/cluster/membership.c +++ b/lib/cluster/membership.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. * @@ -13,6 +13,7 @@ # define _GNU_SOURCE #endif +#include <inttypes.h> // PRIu32 #include <sys/param.h> #include <sys/types.h> #include <stdio.h> @@ -22,7 +23,7 @@ #include <crm/common/ipc.h> #include <crm/common/xml_internal.h> #include <crm/cluster/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/stonith-ng.h> #include "crmcluster_private.h" @@ -32,6 +33,9 @@ * * Because cluster nodes can have conflicting names or UUIDs, * the hash table key is a uniquely generated ID. + * + * @COMPAT When this is internal, rename to cluster_node_member_cache and make + * static. */ GHashTable *crm_peer_cache = NULL; @@ -47,22 +51,22 @@ GHashTable *crm_peer_cache = NULL; * so it would be a good idea to merge them one day. * * libcluster provides two avenues for populating the cache: - * crm_remote_peer_get() and crm_remote_peer_cache_remove() directly manage it, - * while crm_remote_peer_cache_refresh() populates it via the CIB. + * pcmk__cluster_lookup_remote_node() and pcmk__cluster_forget_remote_node() + * directly manage it, while refresh_remote_nodes() populates it via the CIB. */ GHashTable *crm_remote_peer_cache = NULL; /* - * The known node cache tracks cluster and remote nodes that have been seen in + * The CIB cluster node cache tracks cluster nodes that have been seen in * the CIB. It is useful mainly when a caller needs to know about a node that * may no longer be in the membership, but doesn't want to add the node to the * main peer cache tables. */ -static GHashTable *known_node_cache = NULL; +static GHashTable *cluster_node_cib_cache = NULL; unsigned long long crm_peer_seq = 0; gboolean crm_have_quorum = FALSE; -static gboolean crm_autoreap = TRUE; +static bool autoreap = true; // Flag setting and clearing for crm_node_t:flags @@ -82,46 +86,80 @@ static gboolean crm_autoreap = TRUE; } while (0) static void update_peer_uname(crm_node_t *node, const char *uname); +static crm_node_t *find_cib_cluster_node(const char *id, const char *uname); -int -crm_remote_peer_cache_size(void) +/*! + * \internal + * \brief Get the number of Pacemaker Remote nodes that have been seen + * + * \return Number of cached Pacemaker Remote nodes + */ +unsigned int +pcmk__cluster_num_remote_nodes(void) { if (crm_remote_peer_cache == NULL) { - return 0; + return 0U; } return g_hash_table_size(crm_remote_peer_cache); } /*! - * \brief Get a remote node peer cache entry, creating it if necessary + * \internal + * \brief Get a remote node cache entry, creating it if necessary * * \param[in] node_name Name of remote node * - * \return Cache entry for node on success, NULL (and set errno) otherwise + * \return Cache entry for node on success, or \c NULL (and set \c errno) + * otherwise * - * \note When creating a new entry, this will leave the node state undetermined, - * so the caller should also call pcmk__update_peer_state() if the state + * \note When creating a new entry, this will leave the node state undetermined. + * The caller should also call \c pcmk__update_peer_state() if the state * is known. + * \note Because this can add and remove cache entries, callers should not + * assume any previously obtained cache entry pointers remain valid. */ crm_node_t * -crm_remote_peer_get(const char *node_name) +pcmk__cluster_lookup_remote_node(const char *node_name) { crm_node_t *node; + char *node_name_copy = NULL; if (node_name == NULL) { - errno = -EINVAL; + errno = EINVAL; return NULL; } + /* It's theoretically possible that the node was added to the cluster peer + * cache before it was known to be a Pacemaker Remote node. Remove that + * entry unless it has a node ID, which means the name actually is + * associated with a cluster node. (@TODO return an error in that case?) + */ + node = pcmk__search_node_caches(0, node_name, + pcmk__node_search_cluster_member); + if ((node != NULL) && (node->uuid == NULL)) { + /* node_name could be a pointer into the cache entry being removed, so + * reassign it to a copy before the original gets freed + */ + node_name_copy = strdup(node_name); + if (node_name_copy == NULL) { + errno = ENOMEM; + return NULL; + } + node_name = node_name_copy; + pcmk__cluster_forget_cluster_node(0, node_name); + } + /* Return existing cache entry if one exists */ node = g_hash_table_lookup(crm_remote_peer_cache, node_name); if (node) { + free(node_name_copy); return node; } /* Allocate a new entry */ node = calloc(1, sizeof(crm_node_t)); if (node == NULL) { + free(node_name_copy); return NULL; } @@ -130,7 +168,8 @@ crm_remote_peer_get(const char *node_name) node->uuid = strdup(node_name); if (node->uuid == NULL) { free(node); - errno = -ENOMEM; + errno = ENOMEM; + free(node_name_copy); return NULL; } @@ -140,14 +179,28 @@ crm_remote_peer_get(const char *node_name) /* Update the entry's uname, ensuring peer status callbacks are called */ update_peer_uname(node, node_name); + free(node_name_copy); return node; } +/*! + * \internal + * \brief Remove a node from the Pacemaker Remote node cache + * + * \param[in] node_name Name of node to remove from cache + * + * \note The caller must be careful not to use \p node_name after calling this + * function if it might be a pointer into the cache entry being removed. + */ void -crm_remote_peer_cache_remove(const char *node_name) +pcmk__cluster_forget_remote_node(const char *node_name) { - if (g_hash_table_remove(crm_remote_peer_cache, node_name)) { - crm_trace("removed %s from remote peer cache", node_name); + /* Do a lookup first, because node_name could be a pointer within the entry + * being removed -- we can't log it *after* removing it. + */ + if (g_hash_table_lookup(crm_remote_peer_cache, node_name) != NULL) { + crm_trace("Removing %s from Pacemaker Remote node cache", node_name); + g_hash_table_remove(crm_remote_peer_cache, node_name); } } @@ -157,8 +210,8 @@ crm_remote_peer_cache_remove(const char *node_name) * * \param[in] node_state XML of node state * - * \return CRM_NODE_LOST if PCMK__XA_IN_CCM is false in node_state, - * CRM_NODE_MEMBER otherwise + * \return \c CRM_NODE_LOST if \c PCMK__XA_IN_CCM is false in + * \c PCMK__XE_NODE_STATE, \c CRM_NODE_MEMBER otherwise * \note Unlike most boolean XML attributes, this one defaults to true, for * backward compatibility with older controllers that don't set it. */ @@ -208,7 +261,7 @@ remote_cache_refresh_helper(xmlNode *result, void *user_data) if (node == NULL) { /* Node is not in cache, so add a new entry for it */ - node = crm_remote_peer_get(remote); + node = pcmk__cluster_lookup_remote_node(remote); CRM_ASSERT(node); if (state) { pcmk__update_peer_state(__func__, node, state, 0); @@ -236,16 +289,17 @@ is_dirty(gpointer key, gpointer value, gpointer user_data) } /*! - * \brief Repopulate the remote peer cache based on CIB XML + * \internal + * \brief Repopulate the remote node cache based on CIB XML * - * \param[in] xmlNode CIB XML to parse + * \param[in] cib CIB XML to parse */ -void -crm_remote_peer_cache_refresh(xmlNode *cib) +static void +refresh_remote_nodes(xmlNode *cib) { struct refresh_data data; - crm_peer_init(); + pcmk__cluster_init_node_caches(); /* First, we mark all existing cache entries as dirty, * so that later we can remove any that weren't in the CIB. @@ -254,7 +308,7 @@ crm_remote_peer_cache_refresh(xmlNode *cib) g_hash_table_foreach(crm_remote_peer_cache, mark_dirty, NULL); /* Look for guest nodes and remote nodes in the status section */ - data.field = "id"; + data.field = PCMK_XA_ID; data.has_state = TRUE; crm_foreach_xpath_result(cib, PCMK__XP_REMOTE_NODE_STATUS, remote_cache_refresh_helper, &data); @@ -265,11 +319,11 @@ crm_remote_peer_cache_refresh(xmlNode *cib) * peer status callback isn't called until we're sure the node started * successfully. */ - data.field = "value"; + data.field = PCMK_XA_VALUE; data.has_state = FALSE; crm_foreach_xpath_result(cib, PCMK__XP_GUEST_NODE_CONFIG, remote_cache_refresh_helper, &data); - data.field = "id"; + data.field = PCMK_XA_ID; data.has_state = FALSE; crm_foreach_xpath_result(cib, PCMK__XP_REMOTE_NODE_CONFIG, remote_cache_refresh_helper, &data); @@ -278,105 +332,183 @@ crm_remote_peer_cache_refresh(xmlNode *cib) g_hash_table_foreach_remove(crm_remote_peer_cache, is_dirty, NULL); } -gboolean -crm_is_peer_active(const crm_node_t * node) +/*! + * \internal + * \brief Check whether a node is an active cluster node + * + * Remote nodes are never considered active. This guarantees that they can never + * become DC. + * + * \param[in] node Node to check + * + * \return \c true if the node is an active cluster node, or \c false otherwise + */ +bool +pcmk__cluster_is_node_active(const crm_node_t *node) { - if(node == NULL) { - return FALSE; - } + const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); - if (pcmk_is_set(node->flags, crm_remote_node)) { - /* remote nodes are never considered active members. This - * guarantees they will never be considered for DC membership.*/ - return FALSE; + if ((node == NULL) || pcmk_is_set(node->flags, crm_remote_node)) { + return false; } + + switch (cluster_layer) { + case pcmk_cluster_layer_corosync: #if SUPPORT_COROSYNC - if (is_corosync_cluster()) { - return crm_is_corosync_peer_active(node); + return pcmk__corosync_is_peer_active(node); +#else + break; +#endif // SUPPORT_COROSYNC + default: + break; } -#endif - crm_err("Unhandled cluster type: %s", name_for_cluster_type(get_cluster_type())); - return FALSE; + + crm_err("Unhandled cluster layer: %s", + pcmk_cluster_layer_text(cluster_layer)); + return false; } +/*! + * \internal + * \brief Check if a node's entry should be removed from the cluster node cache + * + * A node should be removed from the cache if it's inactive and matches another + * \c crm_node_t (the search object). The node is considered a mismatch if any + * of the following are true: + * * The search object is \c NULL. + * * The search object has an ID set and the cached node's ID does not match it. + * * The search object does not have an ID set, and the cached node's name does + * not match the search node's name. (If both names are \c NULL, it's a + * match.) + * + * Otherwise, the node is considered a match. + * + * Note that if the search object has both an ID and a name set, the name is + * ignored for matching purposes. + * + * \param[in] key Ignored + * \param[in] value \c crm_node_t object from cluster node cache + * \param[in] user_data \c crm_node_t object to match against (search object) + * + * \return \c TRUE if the node entry should be removed from \c crm_peer_cache, + * or \c FALSE otherwise + */ static gboolean -crm_reap_dead_member(gpointer key, gpointer value, gpointer user_data) +should_forget_cluster_node(gpointer key, gpointer value, gpointer user_data) { crm_node_t *node = value; crm_node_t *search = user_data; if (search == NULL) { return FALSE; - - } else if (search->id && node->id != search->id) { + } + if ((search->id != 0) && (node->id != search->id)) { return FALSE; - - } else if (search->id == 0 && !pcmk__str_eq(node->uname, search->uname, pcmk__str_casei)) { + } + if ((search->id == 0) + && !pcmk__str_eq(node->uname, search->uname, pcmk__str_casei)) { + // @TODO Consider name even if ID is set? + return FALSE; + } + if (pcmk__cluster_is_node_active(value)) { return FALSE; - - } else if (crm_is_peer_active(value) == FALSE) { - crm_info("Removing node with name %s and id %u from membership cache", - (node->uname? node->uname : "unknown"), node->id); - return TRUE; } - return FALSE; + + crm_info("Removing node with name %s and " PCMK_XA_ID " %u from membership " + "cache", + pcmk__s(node->uname, "(unknown)"), node->id); + return TRUE; } /*! - * \brief Remove all peer cache entries matching a node ID and/or uname + * \internal + * \brief Remove one or more inactive nodes from the cluster node cache * - * \param[in] id ID of node to remove (or 0 to ignore) - * \param[in] name Uname of node to remove (or NULL to ignore) + * All inactive nodes matching \p id and \p node_name as described in + * \c should_forget_cluster_node documentation are removed from the cache. * - * \return Number of cache entries removed + * If \p id is 0 and \p node_name is \c NULL, all inactive nodes are removed + * from the cache regardless of ID and name. This differs from clearing the + * cache, in that entries for active nodes are preserved. + * + * \param[in] id ID of node to remove from cache (0 to ignore) + * \param[in] node_name Name of node to remove from cache (ignored if \p id is + * nonzero) + * + * \note \p node_name is not modified directly, but it will be freed if it's a + * pointer into a cache entry that is removed. */ -guint -reap_crm_member(uint32_t id, const char *name) +void +pcmk__cluster_forget_cluster_node(uint32_t id, const char *node_name) { - int matches = 0; crm_node_t search = { 0, }; + char *criterion = NULL; // For logging + guint matches = 0; if (crm_peer_cache == NULL) { - crm_trace("Membership cache not initialized, ignoring purge request"); - return 0; + crm_trace("Membership cache not initialized, ignoring removal request"); + return; } search.id = id; - pcmk__str_update(&search.uname, name); - matches = g_hash_table_foreach_remove(crm_peer_cache, crm_reap_dead_member, &search); - if(matches) { - crm_notice("Purged %d peer%s with id=%u%s%s from the membership cache", - matches, pcmk__plural_s(matches), search.id, - (search.uname? " and/or uname=" : ""), - (search.uname? search.uname : "")); + search.uname = pcmk__str_copy(node_name); // May log after original freed + + if (id > 0) { + criterion = crm_strdup_printf(PCMK_XA_ID "=%" PRIu32, id); + + } else if (node_name != NULL) { + criterion = crm_strdup_printf(PCMK_XA_UNAME "=%s", node_name); + } + + matches = g_hash_table_foreach_remove(crm_peer_cache, + should_forget_cluster_node, &search); + if (matches > 0) { + if (criterion != NULL) { + crm_notice("Removed %u inactive node%s with %s from the membership " + "cache", + matches, pcmk__plural_s(matches), criterion); + } else { + crm_notice("Removed all (%u) inactive cluster nodes from the " + "membership cache", + matches); + } } else { - crm_info("No peers with id=%u%s%s to purge from the membership cache", - search.id, (search.uname? " and/or uname=" : ""), - (search.uname? search.uname : "")); + crm_info("No inactive cluster nodes%s%s to remove from the membership " + "cache", + ((criterion != NULL)? " with " : ""), pcmk__s(criterion, "")); } free(search.uname); - return matches; + free(criterion); } static void count_peer(gpointer key, gpointer value, gpointer user_data) { - guint *count = user_data; + unsigned int *count = user_data; crm_node_t *node = value; - if (crm_is_peer_active(node)) { + if (pcmk__cluster_is_node_active(node)) { *count = *count + 1; } } -guint -crm_active_peers(void) +/*! + * \internal + * \brief Get the number of active cluster nodes that have been seen + * + * Remote nodes are never considered active. This guarantees that they can never + * become DC. + * + * \return Number of active nodes in the cluster node cache + */ +unsigned int +pcmk__cluster_num_active_nodes(void) { - guint count = 0; + unsigned int count = 0; - if (crm_peer_cache) { + if (crm_peer_cache != NULL) { g_hash_table_foreach(crm_peer_cache, count_peer, &count); } return count; @@ -397,8 +529,12 @@ destroy_crm_node(gpointer data) free(node); } +/*! + * \internal + * \brief Initialize node caches + */ void -crm_peer_init(void) +pcmk__cluster_init_node_caches(void) { if (crm_peer_cache == NULL) { crm_peer_cache = pcmk__strikey_table(free, destroy_crm_node); @@ -408,69 +544,78 @@ crm_peer_init(void) crm_remote_peer_cache = pcmk__strikey_table(NULL, destroy_crm_node); } - if (known_node_cache == NULL) { - known_node_cache = pcmk__strikey_table(free, destroy_crm_node); + if (cluster_node_cib_cache == NULL) { + cluster_node_cib_cache = pcmk__strikey_table(free, destroy_crm_node); } } +/*! + * \internal + * \brief Initialize node caches + */ void -crm_peer_destroy(void) +pcmk__cluster_destroy_node_caches(void) { if (crm_peer_cache != NULL) { - crm_trace("Destroying peer cache with %d members", g_hash_table_size(crm_peer_cache)); + crm_trace("Destroying peer cache with %d members", + g_hash_table_size(crm_peer_cache)); g_hash_table_destroy(crm_peer_cache); crm_peer_cache = NULL; } if (crm_remote_peer_cache != NULL) { - crm_trace("Destroying remote peer cache with %d members", g_hash_table_size(crm_remote_peer_cache)); + crm_trace("Destroying remote peer cache with %d members", + pcmk__cluster_num_remote_nodes()); g_hash_table_destroy(crm_remote_peer_cache); crm_remote_peer_cache = NULL; } - if (known_node_cache != NULL) { - crm_trace("Destroying known node cache with %d members", - g_hash_table_size(known_node_cache)); - g_hash_table_destroy(known_node_cache); - known_node_cache = NULL; + if (cluster_node_cib_cache != NULL) { + crm_trace("Destroying configured cluster node cache with %d members", + g_hash_table_size(cluster_node_cib_cache)); + g_hash_table_destroy(cluster_node_cib_cache); + cluster_node_cib_cache = NULL; } - } static void (*peer_status_callback)(enum crm_status_type, crm_node_t *, const void *) = NULL; /*! + * \internal * \brief Set a client function that will be called after peer status changes * * \param[in] dispatch Pointer to function to use as callback * - * \note Previously, client callbacks were responsible for peer cache - * management. This is no longer the case, and client callbacks should do - * only client-specific handling. Callbacks MUST NOT add or remove entries - * in the peer caches. + * \note Client callbacks should do only client-specific handling. Callbacks + * must not add or remove entries in the peer caches. */ void -crm_set_status_callback(void (*dispatch) (enum crm_status_type, crm_node_t *, const void *)) +pcmk__cluster_set_status_callback(void (*dispatch)(enum crm_status_type, + crm_node_t *, const void *)) { + // @TODO Improve documentation of peer_status_callback peer_status_callback = dispatch; } /*! + * \internal * \brief Tell the library whether to automatically reap lost nodes * - * If TRUE (the default), calling crm_update_peer_proc() will also update the - * peer state to CRM_NODE_MEMBER or CRM_NODE_LOST, and pcmk__update_peer_state() - * will reap peers whose state changes to anything other than CRM_NODE_MEMBER. + * If \c true (the default), calling \c crm_update_peer_proc() will also update + * the peer state to \c CRM_NODE_MEMBER or \c CRM_NODE_LOST, and updating the + * peer state will reap peers whose state changes to anything other than + * \c CRM_NODE_MEMBER. + * * Callers should leave this enabled unless they plan to manage the cache * separately on their own. * - * \param[in] autoreap TRUE to enable automatic reaping, FALSE to disable + * \param[in] enable \c true to enable automatic reaping, \c false to disable */ void -crm_set_autoreap(gboolean autoreap) +pcmk__cluster_set_autoreap(bool enable) { - crm_autoreap = autoreap; + autoreap = enable; } static void @@ -494,82 +639,7 @@ hash_find_by_data(gpointer key, gpointer value, gpointer user_data) /*! * \internal - * \brief Search caches for a node (cluster or Pacemaker Remote) - * - * \param[in] id If not 0, cluster node ID to search for - * \param[in] uname If not NULL, node name to search for - * \param[in] flags Bitmask of enum crm_get_peer_flags - * - * \return Node cache entry if found, otherwise NULL - */ -crm_node_t * -pcmk__search_node_caches(unsigned int id, const char *uname, uint32_t flags) -{ - crm_node_t *node = NULL; - - CRM_ASSERT(id > 0 || uname != NULL); - - crm_peer_init(); - - if ((uname != NULL) && pcmk_is_set(flags, CRM_GET_PEER_REMOTE)) { - node = g_hash_table_lookup(crm_remote_peer_cache, uname); - } - - if ((node == NULL) && pcmk_is_set(flags, CRM_GET_PEER_CLUSTER)) { - node = pcmk__search_cluster_node_cache(id, uname, NULL); - } - return node; -} - -/*! - * \brief Get a node cache entry (cluster or Pacemaker Remote) - * - * \param[in] id If not 0, cluster node ID to search for - * \param[in] uname If not NULL, node name to search for - * \param[in] uuid If not NULL while id is 0, node UUID instead of cluster - * node ID to search for - * \param[in] flags Bitmask of enum crm_get_peer_flags - * - * \return (Possibly newly created) node cache entry - */ -crm_node_t * -pcmk__get_peer_full(unsigned int id, const char *uname, const char *uuid, - int flags) -{ - crm_node_t *node = NULL; - - CRM_ASSERT(id > 0 || uname != NULL); - - crm_peer_init(); - - if (pcmk_is_set(flags, CRM_GET_PEER_REMOTE)) { - node = g_hash_table_lookup(crm_remote_peer_cache, uname); - } - - if ((node == NULL) && pcmk_is_set(flags, CRM_GET_PEER_CLUSTER)) { - node = pcmk__get_peer(id, uname, uuid); - } - return node; -} - -/*! - * \brief Get a node cache entry (cluster or Pacemaker Remote) - * - * \param[in] id If not 0, cluster node ID to search for - * \param[in] uname If not NULL, node name to search for - * \param[in] flags Bitmask of enum crm_get_peer_flags - * - * \return (Possibly newly created) node cache entry - */ -crm_node_t * -crm_get_peer_full(unsigned int id, const char *uname, int flags) -{ - return pcmk__get_peer_full(id, uname, NULL, flags); -} - -/*! - * \internal - * \brief Search cluster node cache + * \brief Search cluster member node cache * * \param[in] id If not 0, cluster node ID to search for * \param[in] uname If not NULL, node name to search for @@ -578,9 +648,9 @@ crm_get_peer_full(unsigned int id, const char *uname, int flags) * * \return Cluster node cache entry if found, otherwise NULL */ -crm_node_t * -pcmk__search_cluster_node_cache(unsigned int id, const char *uname, - const char *uuid) +static crm_node_t * +search_cluster_member_cache(unsigned int id, const char *uname, + const char *uuid) { GHashTableIter iter; crm_node_t *node = NULL; @@ -589,7 +659,7 @@ pcmk__search_cluster_node_cache(unsigned int id, const char *uname, CRM_ASSERT(id > 0 || uname != NULL); - crm_peer_init(); + pcmk__cluster_init_node_caches(); if (uname != NULL) { g_hash_table_iter_init(&iter, crm_peer_cache); @@ -681,6 +751,85 @@ pcmk__search_cluster_node_cache(unsigned int id, const char *uname, return node; } +/*! + * \internal + * \brief Search caches for a node (cluster or Pacemaker Remote) + * + * \param[in] id If not 0, cluster node ID to search for + * \param[in] uname If not NULL, node name to search for + * \param[in] flags Group of enum pcmk__node_search_flags + * + * \return Node cache entry if found, otherwise NULL + */ +crm_node_t * +pcmk__search_node_caches(unsigned int id, const char *uname, uint32_t flags) +{ + crm_node_t *node = NULL; + + CRM_ASSERT(id > 0 || uname != NULL); + + pcmk__cluster_init_node_caches(); + + if ((uname != NULL) && pcmk_is_set(flags, pcmk__node_search_remote)) { + node = g_hash_table_lookup(crm_remote_peer_cache, uname); + } + + if ((node == NULL) + && pcmk_is_set(flags, pcmk__node_search_cluster_member)) { + + node = search_cluster_member_cache(id, uname, NULL); + } + + if ((node == NULL) && pcmk_is_set(flags, pcmk__node_search_cluster_cib)) { + char *id_str = (id == 0)? NULL : crm_strdup_printf("%u", id); + + node = find_cib_cluster_node(id_str, uname); + free(id_str); + } + + return node; +} + +/*! + * \internal + * \brief Purge a node from cache (both cluster and Pacemaker Remote) + * + * \param[in] node_name If not NULL, purge only nodes with this name + * \param[in] node_id If not 0, purge cluster nodes only if they have this ID + * + * \note If \p node_name is NULL and \p node_id is 0, no nodes will be purged. + * If \p node_name is not NULL and \p node_id is not 0, Pacemaker Remote + * nodes that match \p node_name will be purged, and cluster nodes that + * match both \p node_name and \p node_id will be purged. + * \note The caller must be careful not to use \p node_name after calling this + * function if it might be a pointer into a cache entry being removed. + */ +void +pcmk__purge_node_from_cache(const char *node_name, uint32_t node_id) +{ + char *node_name_copy = NULL; + + if ((node_name == NULL) && (node_id == 0U)) { + return; + } + + // Purge from Pacemaker Remote node cache + if ((node_name != NULL) + && (g_hash_table_lookup(crm_remote_peer_cache, node_name) != NULL)) { + /* node_name could be a pointer into the cache entry being purged, + * so reassign it to a copy before the original gets freed + */ + node_name_copy = pcmk__str_copy(node_name); + node_name = node_name_copy; + + crm_trace("Purging %s from Pacemaker Remote node cache", node_name); + g_hash_table_remove(crm_remote_peer_cache, node_name); + } + + pcmk__cluster_forget_cluster_node(node_id, node_name); + free(node_name_copy); +} + #if SUPPORT_COROSYNC static guint remove_conflicting_peer(crm_node_t *node) @@ -704,7 +853,7 @@ remove_conflicting_peer(crm_node_t *node) && existing_node->uname != NULL && strcasecmp(existing_node->uname, node->uname) == 0) { - if (crm_is_peer_active(existing_node)) { + if (pcmk__cluster_is_node_active(existing_node)) { continue; } @@ -721,32 +870,51 @@ remove_conflicting_peer(crm_node_t *node) #endif /*! - * \brief Get a cluster node cache entry + * \internal + * \brief Get a cluster node cache entry, possibly creating one if not found + * + * If \c pcmk__node_search_cluster_member is set in \p flags, the return value + * is guaranteed not to be \c NULL. A new cache entry is created if one does not + * already exist. * * \param[in] id If not 0, cluster node ID to search for * \param[in] uname If not NULL, node name to search for * \param[in] uuid If not NULL while id is 0, node UUID instead of cluster * node ID to search for + * \param[in] flags Group of enum pcmk__node_search_flags * * \return (Possibly newly created) cluster node cache entry */ /* coverity[-alloc] Memory is referenced in one or both hashtables */ crm_node_t * -pcmk__get_peer(unsigned int id, const char *uname, const char *uuid) +pcmk__get_node(unsigned int id, const char *uname, const char *uuid, + uint32_t flags) { crm_node_t *node = NULL; char *uname_lookup = NULL; CRM_ASSERT(id > 0 || uname != NULL); - crm_peer_init(); + pcmk__cluster_init_node_caches(); + + // Check the Pacemaker Remote node cache first + if (pcmk_is_set(flags, pcmk__node_search_remote)) { + node = g_hash_table_lookup(crm_remote_peer_cache, uname); + if (node != NULL) { + return node; + } + } + + if (!pcmk_is_set(flags, pcmk__node_search_cluster_member)) { + return NULL; + } - node = pcmk__search_cluster_node_cache(id, uname, uuid); + node = search_cluster_member_cache(id, uname, uuid); /* if uname wasn't provided, and find_peer did not turn up a uname based on id. * we need to do a lookup of the node name using the id in the cluster membership. */ if ((node == NULL || node->uname == NULL) && (uname == NULL)) { - uname_lookup = get_node_name(id); + uname_lookup = pcmk__cluster_node_name(id); } if (uname_lookup) { @@ -755,16 +923,14 @@ pcmk__get_peer(unsigned int id, const char *uname, const char *uuid) /* try to turn up the node one more time now that we know the uname. */ if (node == NULL) { - node = pcmk__search_cluster_node_cache(id, uname, uuid); + node = search_cluster_member_cache(id, uname, uuid); } } - if (node == NULL) { char *uniqueid = crm_generate_uuid(); - node = calloc(1, sizeof(crm_node_t)); - CRM_ASSERT(node); + node = pcmk__assert_alloc(1, sizeof(crm_node_t)); crm_info("Created entry %s/%p for node %s/%u (%d total)", uniqueid, node, uname, id, 1 + g_hash_table_size(crm_peer_cache)); @@ -785,7 +951,7 @@ pcmk__get_peer(unsigned int id, const char *uname, const char *uuid) if(node->uuid == NULL) { if (uuid == NULL) { - uuid = crm_peer_uuid(node); + uuid = pcmk__cluster_node_uuid(node); } if (uuid) { @@ -802,21 +968,6 @@ pcmk__get_peer(unsigned int id, const char *uname, const char *uuid) } /*! - * \brief Get a cluster node cache entry - * - * \param[in] id If not 0, cluster node ID to search for - * \param[in] uname If not NULL, node name to search for - * - * \return (Possibly newly created) cluster node cache entry - */ -/* coverity[-alloc] Memory is referenced in one or both hashtables */ -crm_node_t * -crm_get_peer(unsigned int id, const char *uname) -{ - return pcmk__get_peer(id, uname, NULL); -} - -/*! * \internal * \brief Update a node's uname * @@ -856,7 +1007,9 @@ update_peer_uname(crm_node_t *node, const char *uname) } #if SUPPORT_COROSYNC - if (is_corosync_cluster() && !pcmk_is_set(node->flags, crm_remote_node)) { + if ((pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) + && !pcmk_is_set(node->flags, crm_remote_node)) { + remove_conflicting_peer(node); } #endif @@ -879,24 +1032,6 @@ proc2text(enum crm_proc_flag proc) case crm_proc_none: text = "none"; break; - case crm_proc_based: - text = "pacemaker-based"; - break; - case crm_proc_controld: - text = "pacemaker-controld"; - break; - case crm_proc_schedulerd: - text = "pacemaker-schedulerd"; - break; - case crm_proc_execd: - text = "pacemaker-execd"; - break; - case crm_proc_attrd: - text = "pacemaker-attrd"; - break; - case crm_proc_fenced: - text = "pacemaker-fenced"; - break; case crm_proc_cpg: text = "corosync-cpg"; break; @@ -942,7 +1077,7 @@ crm_update_peer_proc(const char *source, crm_node_t * node, uint32_t flag, const changed = TRUE; } - } else if (pcmk__str_eq(status, ONLINESTATUS, pcmk__str_casei)) { + } else if (pcmk__str_eq(status, PCMK_VALUE_ONLINE, pcmk__str_casei)) { if ((node->processes & flag) != flag) { node->processes = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, "Peer process", @@ -989,7 +1124,7 @@ crm_update_peer_proc(const char *source, crm_node_t * node, uint32_t flag, const return NULL; } - if (crm_autoreap) { + if (autoreap) { const char *peer_state = NULL; if (pcmk_is_set(node->processes, crm_get_cluster_proc())) { @@ -1099,18 +1234,20 @@ update_peer_state_iter(const char *source, crm_node_t *node, const char *state, } free(last); - if (crm_autoreap && !is_member + if (autoreap && !is_member && !pcmk_is_set(node->flags, crm_remote_node)) { /* We only autoreap from the peer cache, not the remote peer cache, * because the latter should be managed only by - * crm_remote_peer_cache_refresh(). + * refresh_remote_nodes(). */ if(iter) { - crm_notice("Purged 1 peer with id=%u and/or uname=%s from the membership cache", node->id, node->uname); + crm_notice("Purged 1 peer with " PCMK_XA_ID + "=%u and/or uname=%s from the membership cache", + node->id, node->uname); g_hash_table_iter_remove(iter); } else { - reap_crm_member(node->id, node->uname); + pcmk__cluster_forget_cluster_node(node->id, node->uname); } node = NULL; } @@ -1178,7 +1315,7 @@ pcmk__reap_unseen_nodes(uint64_t membership) } static crm_node_t * -find_known_node(const char *id, const char *uname) +find_cib_cluster_node(const char *id, const char *uname) { GHashTableIter iter; crm_node_t *node = NULL; @@ -1186,7 +1323,7 @@ find_known_node(const char *id, const char *uname) crm_node_t *by_name = NULL; if (uname) { - g_hash_table_iter_init(&iter, known_node_cache); + g_hash_table_iter_init(&iter, cluster_node_cib_cache); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { if (node->uname && strcasecmp(node->uname, uname) == 0) { crm_trace("Name match: %s = %p", node->uname, node); @@ -1197,7 +1334,7 @@ find_known_node(const char *id, const char *uname) } if (id) { - g_hash_table_iter_init(&iter, known_node_cache); + g_hash_table_iter_init(&iter, cluster_node_cib_cache); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { if(strcasecmp(node->uuid, id) == 0) { crm_trace("ID match: %s= %p", id, node); @@ -1256,28 +1393,24 @@ find_known_node(const char *id, const char *uname) } static void -known_node_cache_refresh_helper(xmlNode *xml_node, void *user_data) +cluster_node_cib_cache_refresh_helper(xmlNode *xml_node, void *user_data) { - const char *id = crm_element_value(xml_node, XML_ATTR_ID); - const char *uname = crm_element_value(xml_node, XML_ATTR_UNAME); + const char *id = crm_element_value(xml_node, PCMK_XA_ID); + const char *uname = crm_element_value(xml_node, PCMK_XA_UNAME); crm_node_t * node = NULL; CRM_CHECK(id != NULL && uname !=NULL, return); - node = find_known_node(id, uname); + node = find_cib_cluster_node(id, uname); if (node == NULL) { char *uniqueid = crm_generate_uuid(); - node = calloc(1, sizeof(crm_node_t)); - CRM_ASSERT(node != NULL); + node = pcmk__assert_alloc(1, sizeof(crm_node_t)); - node->uname = strdup(uname); - CRM_ASSERT(node->uname != NULL); + node->uname = pcmk__str_copy(uname); + node->uuid = pcmk__str_copy(id); - node->uuid = strdup(id); - CRM_ASSERT(node->uuid != NULL); - - g_hash_table_replace(known_node_cache, uniqueid, node); + g_hash_table_replace(cluster_node_cib_cache, uniqueid, node); } else if (pcmk_is_set(node->flags, crm_node_dirty)) { pcmk__str_update(&node->uname, uname); @@ -1289,77 +1422,148 @@ known_node_cache_refresh_helper(xmlNode *xml_node, void *user_data) } static void -refresh_known_node_cache(xmlNode *cib) +refresh_cluster_node_cib_cache(xmlNode *cib) { - crm_peer_init(); + pcmk__cluster_init_node_caches(); - g_hash_table_foreach(known_node_cache, mark_dirty, NULL); + g_hash_table_foreach(cluster_node_cib_cache, mark_dirty, NULL); crm_foreach_xpath_result(cib, PCMK__XP_MEMBER_NODE_CONFIG, - known_node_cache_refresh_helper, NULL); + cluster_node_cib_cache_refresh_helper, NULL); - /* Remove all old cache entries that weren't seen in the CIB */ - g_hash_table_foreach_remove(known_node_cache, is_dirty, NULL); + // Remove all old cache entries that weren't seen in the CIB + g_hash_table_foreach_remove(cluster_node_cib_cache, is_dirty, NULL); } void pcmk__refresh_node_caches_from_cib(xmlNode *cib) { - crm_remote_peer_cache_refresh(cib); - refresh_known_node_cache(cib); + refresh_remote_nodes(cib); + refresh_cluster_node_cib_cache(cib); +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/cluster/compat.h> + +int +crm_terminate_member(int nodeid, const char *uname, void *unused) +{ + return stonith_api_kick(nodeid, uname, 120, TRUE); +} + +int +crm_terminate_member_no_mainloop(int nodeid, const char *uname, int *connection) +{ + return stonith_api_kick(nodeid, uname, 120, TRUE); } -/*! - * \internal - * \brief Search known node cache - * - * \param[in] id If not 0, cluster node ID to search for - * \param[in] uname If not NULL, node name to search for - * \param[in] flags Bitmask of enum crm_get_peer_flags - * - * \return Known node cache entry if found, otherwise NULL - */ crm_node_t * -pcmk__search_known_node_cache(unsigned int id, const char *uname, - uint32_t flags) +crm_get_peer(unsigned int id, const char *uname) { - crm_node_t *node = NULL; - char *id_str = NULL; + return pcmk__get_node(id, uname, NULL, pcmk__node_search_cluster_member); +} - CRM_ASSERT(id > 0 || uname != NULL); +crm_node_t * +crm_get_peer_full(unsigned int id, const char *uname, int flags) +{ + return pcmk__get_node(id, uname, NULL, flags); +} - node = pcmk__search_node_caches(id, uname, flags); +int +crm_remote_peer_cache_size(void) +{ + unsigned int count = pcmk__cluster_num_remote_nodes(); - if (node || !(flags & CRM_GET_PEER_CLUSTER)) { - return node; - } + return QB_MIN(count, INT_MAX); +} - if (id > 0) { - id_str = crm_strdup_printf("%u", id); - } +void +crm_remote_peer_cache_refresh(xmlNode *cib) +{ + refresh_remote_nodes(cib); +} - node = find_known_node(id_str, uname); +crm_node_t * +crm_remote_peer_get(const char *node_name) +{ + return pcmk__cluster_lookup_remote_node(node_name); +} - free(id_str); - return node; +void +crm_remote_peer_cache_remove(const char *node_name) +{ + pcmk__cluster_forget_remote_node(node_name); } +gboolean +crm_is_peer_active(const crm_node_t * node) +{ + return pcmk__cluster_is_node_active(node); +} -// Deprecated functions kept only for backward API compatibility -// LCOV_EXCL_START +guint +crm_active_peers(void) +{ + return pcmk__cluster_num_active_nodes(); +} -#include <crm/cluster/compat.h> +guint +reap_crm_member(uint32_t id, const char *name) +{ + int matches = 0; + crm_node_t search = { 0, }; -int -crm_terminate_member(int nodeid, const char *uname, void *unused) + if (crm_peer_cache == NULL) { + crm_trace("Membership cache not initialized, ignoring purge request"); + return 0; + } + + search.id = id; + search.uname = pcmk__str_copy(name); + matches = g_hash_table_foreach_remove(crm_peer_cache, + should_forget_cluster_node, &search); + if(matches) { + crm_notice("Purged %d peer%s with " PCMK_XA_ID + "=%u%s%s from the membership cache", + matches, pcmk__plural_s(matches), search.id, + (search.uname? " and/or uname=" : ""), + (search.uname? search.uname : "")); + + } else { + crm_info("No peers with " PCMK_XA_ID + "=%u%s%s to purge from the membership cache", + search.id, (search.uname? " and/or uname=" : ""), + (search.uname? search.uname : "")); + } + + free(search.uname); + return matches; +} + +void +crm_peer_init(void) { - return stonith_api_kick(nodeid, uname, 120, TRUE); + pcmk__cluster_init_node_caches(); } -int -crm_terminate_member_no_mainloop(int nodeid, const char *uname, int *connection) +void +crm_peer_destroy(void) { - return stonith_api_kick(nodeid, uname, 120, TRUE); + pcmk__cluster_destroy_node_caches(); +} + +void +crm_set_autoreap(gboolean enable) +{ + pcmk__cluster_set_autoreap(enable); +} + +void +crm_set_status_callback(void (*dispatch) (enum crm_status_type, crm_node_t *, const void *)) +{ + pcmk__cluster_set_status_callback(dispatch); } // LCOV_EXCL_STOP diff --git a/lib/cluster/tests/Makefile.am b/lib/cluster/tests/Makefile.am new file mode 100644 index 0000000..f4f5658 --- /dev/null +++ b/lib/cluster/tests/Makefile.am @@ -0,0 +1,12 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +SUBDIRS = \ + cluster \ + cpg diff --git a/lib/pengine/tests/rules/Makefile.am b/lib/cluster/tests/cluster/Makefile.am index 261ec16..072a4ee 100644 --- a/lib/pengine/tests/rules/Makefile.am +++ b/lib/cluster/tests/cluster/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2021 the Pacemaker project contributors +# Copyright 2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -10,9 +10,9 @@ include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk -LDADD += $(top_builddir)/lib/pengine/libpe_rules_test.la +LDADD += $(top_builddir)/lib/cluster/libcrmcluster.la # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pe_cron_range_satisfied_test +check_PROGRAMS = pcmk_cluster_set_destroy_fn_test TESTS = $(check_PROGRAMS) diff --git a/lib/cluster/tests/cluster/pcmk_cluster_set_destroy_fn_test.c b/lib/cluster/tests/cluster/pcmk_cluster_set_destroy_fn_test.c new file mode 100644 index 0000000..f6a7ac2 --- /dev/null +++ b/lib/cluster/tests/cluster/pcmk_cluster_set_destroy_fn_test.c @@ -0,0 +1,79 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <glib.h> // gpointer + +#include <crm/cluster.h> // pcmk_cluster_t, etc. +#include <crm/common/unittest_internal.h> + +static void +destroy_fn1(gpointer arg) +{ + return; +} + +static void +destroy_fn2(gpointer arg) +{ + return; +} + +static void +null_cluster(void **state) +{ + assert_int_equal(pcmk_cluster_set_destroy_fn(NULL, NULL), EINVAL); + assert_int_equal(pcmk_cluster_set_destroy_fn(NULL, destroy_fn1), EINVAL); +} + +static void +null_fn(void **state) +{ + pcmk_cluster_t cluster = { + .destroy = NULL, + }; + + assert_int_equal(pcmk_cluster_set_destroy_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.destroy, NULL); + + cluster.destroy = destroy_fn1; + assert_int_equal(pcmk_cluster_set_destroy_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.destroy, NULL); +} + +static void +previous_fn_null(void **state) +{ + pcmk_cluster_t cluster = { + .destroy = NULL, + }; + + assert_int_equal(pcmk_cluster_set_destroy_fn(&cluster, destroy_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.destroy, destroy_fn1); +} + +static void +previous_fn_nonnull(void **state) +{ + pcmk_cluster_t cluster = { + .destroy = destroy_fn2, + }; + + assert_int_equal(pcmk_cluster_set_destroy_fn(&cluster, destroy_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.destroy, destroy_fn1); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_cluster), + cmocka_unit_test(null_fn), + cmocka_unit_test(previous_fn_null), + cmocka_unit_test(previous_fn_nonnull)) diff --git a/lib/cluster/tests/cpg/Makefile.am b/lib/cluster/tests/cpg/Makefile.am new file mode 100644 index 0000000..625f943 --- /dev/null +++ b/lib/cluster/tests/cpg/Makefile.am @@ -0,0 +1,19 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +LDADD += $(top_builddir)/lib/cluster/libcrmcluster.la + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk_cpg_set_confchg_fn_test \ + pcmk_cpg_set_deliver_fn_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/cluster/tests/cpg/pcmk_cpg_set_confchg_fn_test.c b/lib/cluster/tests/cpg/pcmk_cpg_set_confchg_fn_test.c new file mode 100644 index 0000000..b9e1b6b --- /dev/null +++ b/lib/cluster/tests/cpg/pcmk_cpg_set_confchg_fn_test.c @@ -0,0 +1,98 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdint.h> // uint32_t +#include <sys/types.h> // size_t + +#include <crm/cluster.h> // pcmk_cluster_t, etc. +#include <crm/common/unittest_internal.h> + +#if SUPPORT_COROSYNC +#include <corosync/cpg.h> // cpg_handle_t, struct cpg_name + +static void +confchg_fn1(cpg_handle_t handle, const struct cpg_name *group_name, + const struct cpg_address *member_list, size_t member_list_entries, + const struct cpg_address *left_list, size_t left_list_entries, + const struct cpg_address *joined_list, size_t joined_list_entries) +{ + return; +} + +static void +confchg_fn2(cpg_handle_t handle, const struct cpg_name *group_name, + const struct cpg_address *member_list, size_t member_list_entries, + const struct cpg_address *left_list, size_t left_list_entries, + const struct cpg_address *joined_list, size_t joined_list_entries) +{ + return; +} + +static void +null_cluster(void **state) +{ + assert_int_equal(pcmk_cpg_set_confchg_fn(NULL, NULL), EINVAL); + assert_int_equal(pcmk_cpg_set_confchg_fn(NULL, confchg_fn1), EINVAL); +} + +static void +null_fn(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_confchg_fn = NULL, + }, + }; + + assert_int_equal(pcmk_cpg_set_confchg_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_confchg_fn, NULL); + + cluster.cpg.cpg_confchg_fn = confchg_fn1; + assert_int_equal(pcmk_cpg_set_confchg_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_confchg_fn, NULL); +} + +static void +previous_fn_null(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_confchg_fn = NULL, + }, + }; + + assert_int_equal(pcmk_cpg_set_confchg_fn(&cluster, confchg_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_confchg_fn, confchg_fn1); +} + +static void +previous_fn_nonnull(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_confchg_fn = confchg_fn2, + }, + }; + + assert_int_equal(pcmk_cpg_set_confchg_fn(&cluster, confchg_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_confchg_fn, confchg_fn1); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_cluster), + cmocka_unit_test(null_fn), + cmocka_unit_test(previous_fn_null), + cmocka_unit_test(previous_fn_nonnull)) +#else +PCMK__UNIT_TEST(NULL, NULL) +#endif // SUPPORT_COROSYNC diff --git a/lib/cluster/tests/cpg/pcmk_cpg_set_deliver_fn_test.c b/lib/cluster/tests/cpg/pcmk_cpg_set_deliver_fn_test.c new file mode 100644 index 0000000..f682def --- /dev/null +++ b/lib/cluster/tests/cpg/pcmk_cpg_set_deliver_fn_test.c @@ -0,0 +1,94 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdint.h> // uint32_t +#include <sys/types.h> // size_t + +#include <crm/cluster.h> // pcmk_cluster_t, etc. +#include <crm/common/unittest_internal.h> + +#if SUPPORT_COROSYNC +#include <corosync/cpg.h> // cpg_handle_t, struct cpg_name + +static void +deliver_fn1(cpg_handle_t handle, const struct cpg_name *group_name, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +{ + return; +} + +static void +deliver_fn2(cpg_handle_t handle, const struct cpg_name *group_name, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +{ + return; +} + +static void +null_cluster(void **state) +{ + assert_int_equal(pcmk_cpg_set_deliver_fn(NULL, NULL), EINVAL); + assert_int_equal(pcmk_cpg_set_deliver_fn(NULL, deliver_fn1), EINVAL); +} + +static void +null_fn(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_deliver_fn = NULL, + }, + }; + + assert_int_equal(pcmk_cpg_set_deliver_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_deliver_fn, NULL); + + cluster.cpg.cpg_deliver_fn = deliver_fn1; + assert_int_equal(pcmk_cpg_set_deliver_fn(&cluster, NULL), pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_deliver_fn, NULL); +} + +static void +previous_fn_null(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_deliver_fn = NULL, + }, + }; + + assert_int_equal(pcmk_cpg_set_deliver_fn(&cluster, deliver_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_deliver_fn, deliver_fn1); +} + +static void +previous_fn_nonnull(void **state) +{ + pcmk_cluster_t cluster = { + .cpg = { + .cpg_deliver_fn = deliver_fn2, + }, + }; + + assert_int_equal(pcmk_cpg_set_deliver_fn(&cluster, deliver_fn1), + pcmk_rc_ok); + assert_ptr_equal(cluster.cpg.cpg_deliver_fn, deliver_fn1); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_cluster), + cmocka_unit_test(null_fn), + cmocka_unit_test(previous_fn_null), + cmocka_unit_test(previous_fn_nonnull)) +#else +PCMK__UNIT_TEST(NULL, NULL) +#endif // SUPPORT_COROSYNC diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index f9c43b9..bfa5c1d 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -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 @@ SUBDIRS = . tests noinst_HEADERS = crmcommon_private.h \ mock_private.h -libcrmcommon_la_LDFLAGS = -version-info 46:0:12 +libcrmcommon_la_LDFLAGS = -version-info 47:0:13 libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) @@ -70,7 +70,7 @@ libcrmcommon_la_SOURCES += ipc_client.c libcrmcommon_la_SOURCES += ipc_common.c libcrmcommon_la_SOURCES += ipc_controld.c libcrmcommon_la_SOURCES += ipc_pacemakerd.c -libcrmcommon_la_SOURCES += ipc_schedulerd.c +libcrmcommon_la_SOURCES += ipc_schedulerd.c libcrmcommon_la_SOURCES += ipc_server.c libcrmcommon_la_SOURCES += iso8601.c libcrmcommon_la_SOURCES += lists.c @@ -80,6 +80,7 @@ libcrmcommon_la_SOURCES += messages.c libcrmcommon_la_SOURCES += nodes.c libcrmcommon_la_SOURCES += nvpair.c libcrmcommon_la_SOURCES += options.c +libcrmcommon_la_SOURCES += options_display.c libcrmcommon_la_SOURCES += output.c libcrmcommon_la_SOURCES += output_html.c libcrmcommon_la_SOURCES += output_log.c @@ -89,9 +90,13 @@ libcrmcommon_la_SOURCES += output_xml.c libcrmcommon_la_SOURCES += patchset.c libcrmcommon_la_SOURCES += patchset_display.c libcrmcommon_la_SOURCES += pid.c +libcrmcommon_la_SOURCES += probes.c libcrmcommon_la_SOURCES += procfs.c libcrmcommon_la_SOURCES += remote.c +libcrmcommon_la_SOURCES += resources.c libcrmcommon_la_SOURCES += results.c +libcrmcommon_la_SOURCES += roles.c +libcrmcommon_la_SOURCES += rules.c libcrmcommon_la_SOURCES += scheduler.c libcrmcommon_la_SOURCES += schemas.c libcrmcommon_la_SOURCES += scores.c @@ -101,6 +106,7 @@ libcrmcommon_la_SOURCES += watchdog.c libcrmcommon_la_SOURCES += xml.c libcrmcommon_la_SOURCES += xml_attr.c libcrmcommon_la_SOURCES += xml_display.c +libcrmcommon_la_SOURCES += xml_io.c libcrmcommon_la_SOURCES += xpath.c # @@ -112,6 +118,7 @@ include $(top_srcdir)/mk/tap.mk libcrmcommon_test_la_SOURCES = $(libcrmcommon_la_SOURCES) libcrmcommon_test_la_SOURCES += mock.c +libcrmcommon_test_la_SOURCES += unittest.c libcrmcommon_test_la_LDFLAGS = $(libcrmcommon_la_LDFLAGS) \ -rpath $(libdir) \ $(LDFLAGS_WRAP) @@ -126,8 +133,11 @@ libcrmcommon_test_la_CFLAGS = $(libcrmcommon_la_CFLAGS) \ -fno-inline # If -fno-builtin is used, -lm also needs to be added. See the comment at # BUILD_PROFILING above. -libcrmcommon_test_la_LIBADD = $(libcrmcommon_la_LIBADD) \ - -lcmocka \ - -lm +libcrmcommon_test_la_LIBADD = $(libcrmcommon_la_LIBADD) +if BUILD_COVERAGE +libcrmcommon_test_la_LIBADD += -lgcov +endif +libcrmcommon_test_la_LIBADD += -lcmocka +libcrmcommon_test_la_LIBADD += -lm nodist_libcrmcommon_test_la_SOURCES = $(nodist_libcrmcommon_la_SOURCES) diff --git a/lib/common/acl.c b/lib/common/acl.c index 1ebd765..b8914be 100644 --- a/lib/common/acl.c +++ b/lib/common/acl.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,7 +19,6 @@ #include <libxml/tree.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include "crmcommon_private.h" @@ -51,18 +50,18 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode) { xml_acl_t *acl = NULL; - const char *tag = crm_element_value(xml, XML_ACL_ATTR_TAG); - const char *ref = crm_element_value(xml, XML_ACL_ATTR_REF); - const char *xpath = crm_element_value(xml, XML_ACL_ATTR_XPATH); - const char *attr = crm_element_value(xml, XML_ACL_ATTR_ATTRIBUTE); + const char *tag = crm_element_value(xml, PCMK_XA_OBJECT_TYPE); + const char *ref = crm_element_value(xml, PCMK_XA_REFERENCE); + const char *xpath = crm_element_value(xml, PCMK_XA_XPATH); + const char *attr = crm_element_value(xml, PCMK_XA_ATTRIBUTE); if (tag == NULL) { - // @COMPAT rolling upgrades <=1.1.11 - tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1); + // @COMPAT Deprecated since 1.1.12 (needed for rolling upgrades) + tag = crm_element_value(xml, PCMK_XA_TAG); } if (ref == NULL) { - // @COMPAT rolling upgrades <=1.1.11 - ref = crm_element_value(xml, XML_ACL_ATTR_REFv1); + // @COMPAT Deprecated since 1.1.12 (needed for rolling upgrades) + ref = crm_element_value(xml, PCMK__XA_REF); } if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) { @@ -72,8 +71,7 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode) return NULL; } - acl = calloc(1, sizeof (xml_acl_t)); - CRM_ASSERT(acl != NULL); + acl = pcmk__assert_alloc(1, sizeof (xml_acl_t)); acl->mode = mode; if (xpath) { @@ -86,11 +84,11 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode) if ((ref != NULL) && (attr != NULL)) { // NOTE: schema currently does not allow this - pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" XML_ATTR_ID "='", + pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='", ref, "' and @", attr, "]", NULL); } else if (ref != NULL) { - pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" XML_ATTR_ID "='", + pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='", ref, "']", NULL); } else if (attr != NULL) { @@ -127,12 +125,13 @@ parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls) { xmlNode *child = NULL; - for (child = pcmk__xe_first_child(acl_entry); child; - child = pcmk__xe_next(child)) { + for (child = pcmk__xe_first_child(acl_entry, NULL, NULL, NULL); + child != NULL; child = pcmk__xe_next(child)) { + const char *tag = (const char *) child->name; - const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND); + const char *kind = crm_element_value(child, PCMK_XA_KIND); - if (pcmk__xe_is(child, XML_ACL_TAG_PERMISSION)) { + if (pcmk__xe_is(child, PCMK_XE_ACL_PERMISSION)) { CRM_ASSERT(kind != NULL); crm_trace("Unpacking ACL <%s> element of kind '%s'", tag, kind); tag = kind; @@ -140,18 +139,21 @@ parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls) crm_trace("Unpacking ACL <%s> element", tag); } - if (strcmp(XML_ACL_TAG_ROLE_REF, tag) == 0 - || strcmp(XML_ACL_TAG_ROLE_REFv1, tag) == 0) { - const char *ref_role = crm_element_value(child, XML_ATTR_ID); + /* @COMPAT PCMK__XE_ROLE_REF was deprecated in Pacemaker 1.1.12 (needed + * for rolling upgrades) + */ + if (pcmk__str_any_of(tag, PCMK_XE_ROLE, PCMK__XE_ROLE_REF, NULL)) { + const char *ref_role = crm_element_value(child, PCMK_XA_ID); if (ref_role) { xmlNode *role = NULL; - for (role = pcmk__xe_first_child(acl_top); role; - role = pcmk__xe_next(role)) { - if (!strcmp(XML_ACL_TAG_ROLE, (const char *) role->name)) { + for (role = pcmk__xe_first_child(acl_top, NULL, NULL, NULL); + role != NULL; role = pcmk__xe_next(role)) { + + if (!strcmp(PCMK_XE_ACL_ROLE, (const char *) role->name)) { const char *role_id = crm_element_value(role, - XML_ATTR_ID); + PCMK_XA_ID); if (role_id && strcmp(ref_role, role_id) == 0) { crm_trace("Unpacking referenced role '%s' in ACL <%s> element", @@ -163,13 +165,19 @@ parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls) } } - } else if (strcmp(XML_ACL_TAG_READ, tag) == 0) { + /* @COMPAT Use of a tag instead of a PCMK_XA_KIND attribute was + * deprecated in 1.1.12. We still need to look for tags named + * PCMK_VALUE_READ, etc., to support rolling upgrades. However, + * eventually we can clean this up and make the variables more intuitive + * (for example, don't assign a PCMK_XA_KIND value to the tag variable). + */ + } else if (strcmp(tag, PCMK_VALUE_READ) == 0) { acls = create_acl(child, acls, pcmk__xf_acl_read); - } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) { + } else if (strcmp(tag, PCMK_VALUE_WRITE) == 0) { acls = create_acl(child, acls, pcmk__xf_acl_write); - } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) { + } else if (strcmp(tag, PCMK_VALUE_DENY) == 0) { acls = create_acl(child, acls, pcmk__xf_acl_deny); } else { @@ -292,34 +300,36 @@ pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user) user); } else if (docpriv->acls == NULL) { - xmlNode *acls = get_xpath_object("//" XML_CIB_TAG_ACLS, - source, LOG_NEVER); + xmlNode *acls = get_xpath_object("//" PCMK_XE_ACLS, source, LOG_NEVER); pcmk__str_update(&docpriv->user, user); if (acls) { xmlNode *child = NULL; - for (child = pcmk__xe_first_child(acls); child; - child = pcmk__xe_next(child)) { + for (child = pcmk__xe_first_child(acls, NULL, NULL, NULL); + child != NULL; child = pcmk__xe_next(child)) { - if (pcmk__xe_is(child, XML_ACL_TAG_USER) - || pcmk__xe_is(child, XML_ACL_TAG_USERv1)) { - const char *id = crm_element_value(child, XML_ATTR_NAME); + /* @COMPAT PCMK__XE_ACL_USER was deprecated in Pacemaker 1.1.12 + * (needed for rolling upgrades) + */ + if (pcmk__xe_is(child, PCMK_XE_ACL_TARGET) + || pcmk__xe_is(child, PCMK__XE_ACL_USER)) { + const char *id = crm_element_value(child, PCMK_XA_NAME); if (id == NULL) { - id = crm_element_value(child, XML_ATTR_ID); + id = crm_element_value(child, PCMK_XA_ID); } if (id && strcmp(id, user) == 0) { crm_debug("Unpacking ACLs for user '%s'", id); docpriv->acls = parse_acl_entry(acls, child, docpriv->acls); } - } else if (pcmk__xe_is(child, XML_ACL_TAG_GROUP)) { - const char *id = crm_element_value(child, XML_ATTR_NAME); + } else if (pcmk__xe_is(child, PCMK_XE_ACL_GROUP)) { + const char *id = crm_element_value(child, PCMK_XA_NAME); if (id == NULL) { - id = crm_element_value(child, XML_ATTR_ID); + id = crm_element_value(child, PCMK_XA_ID); } if (id && pcmk__is_user_in_group(user,id)) { @@ -388,8 +398,8 @@ purge_xml_attributes(xmlNode *xml) xml_node_private_t *nodepriv = xml->_private; if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) { - crm_trace("%s[@" XML_ATTR_ID "=%s] is readable", - xml->name, ID(xml)); + crm_trace("%s[@" PCMK_XA_ID "=%s] is readable", + xml->name, pcmk__xe_id(xml)); return true; } @@ -399,7 +409,7 @@ purge_xml_attributes(xmlNode *xml) const char *prop_name = (const char *)xIter->name; xIter = xIter->next; - if (strcmp(prop_name, XML_ATTR_ID) == 0) { + if (strcmp(prop_name, PCMK_XA_ID) == 0) { continue; } @@ -447,7 +457,7 @@ xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml, } crm_trace("Filtering XML copy using user '%s' ACLs", user); - target = copy_xml(xml); + target = pcmk__xml_copy(NULL, xml); if (target == NULL) { return true; } @@ -513,7 +523,7 @@ xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml, * * Check whether XML is a "scaffolding" element whose creation is implicitly * allowed regardless of ACLs (that is, it is not in the ACL section and has - * no attributes other than "id"). + * no attributes other than \c PCMK_XA_ID). * * \param[in] xml XML element to check * @@ -525,7 +535,7 @@ implicitly_allowed(const xmlNode *xml) GString *path = NULL; for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) { - if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) { + if (strcmp((const char *) prop->name, PCMK_XA_ID) != 0) { return false; } } @@ -533,7 +543,7 @@ implicitly_allowed(const xmlNode *xml) path = pcmk__element_xpath(xml); CRM_ASSERT(path != NULL); - if (strstr((const char *) path->str, "/" XML_CIB_TAG_ACLS "/") != NULL) { + if (strstr((const char *) path->str, "/" PCMK_XE_ACLS "/") != NULL) { g_string_free(path, TRUE); return false; } @@ -542,7 +552,7 @@ implicitly_allowed(const xmlNode *xml) return true; } -#define display_id(xml) (ID(xml)? ID(xml) : "<unset>") +#define display_id(xml) pcmk__s(pcmk__xe_id(xml), "<unset>") /*! * \internal @@ -551,7 +561,7 @@ implicitly_allowed(const xmlNode *xml) * Given an XML element, free all of its descendant nodes created in violation * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those * that aren't in the ACL section and don't have any attributes other than - * "id"). + * \c PCMK_XA_ID). * * \param[in,out] xml XML to check * \param[in] check_top Whether to apply checks to argument itself @@ -566,22 +576,23 @@ pcmk__apply_creation_acl(xmlNode *xml, bool check_top) if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) { if (implicitly_allowed(xml)) { - crm_trace("Creation of <%s> scaffolding with id=\"%s\"" + crm_trace("Creation of <%s> scaffolding with " PCMK_XA_ID "=\"%s\"" " is implicitly allowed", xml->name, display_id(xml)); } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) { - crm_trace("ACLs allow creation of <%s> with id=\"%s\"", + crm_trace("ACLs allow creation of <%s> with " PCMK_XA_ID "=\"%s\"", xml->name, display_id(xml)); } else if (check_top) { - crm_trace("ACLs disallow creation of <%s> with id=\"%s\"", - xml->name, display_id(xml)); + crm_trace("ACLs disallow creation of <%s> with " + PCMK_XA_ID "=\"%s\"", xml->name, display_id(xml)); pcmk_free_xml_subtree(xml); return; } else { - crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\"", + crm_notice("ACLs would disallow creation of %s<%s> with " + PCMK_XA_ID "=\"%s\"", ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""), xml->name, display_id(xml)); } @@ -757,15 +768,13 @@ pcmk_acl_required(const char *user) char * pcmk__uid2username(uid_t uid) { - char *result = NULL; struct passwd *pwent = getpwuid(uid); if (pwent == NULL) { crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid); return NULL; } - pcmk__str_update(&result, pwent->pw_name); - return result; + return pcmk__str_copy(pwent->pw_name); } /*! @@ -797,25 +806,24 @@ pcmk__update_acl_user(xmlNode *request, const char *field, if (effective_user == NULL) { effective_user = pcmk__uid2username(geteuid()); if (effective_user == NULL) { - effective_user = strdup("#unprivileged"); - CRM_CHECK(effective_user != NULL, return NULL); + effective_user = pcmk__str_copy("#unprivileged"); crm_err("Unable to determine effective user, assuming unprivileged for ACLs"); } } - requested_user = crm_element_value(request, XML_ACL_TAG_USER); + requested_user = crm_element_value(request, PCMK_XE_ACL_TARGET); if (requested_user == NULL) { /* @COMPAT rolling upgrades <=1.1.11 * * field is checked for backward compatibility with older versions that - * did not use XML_ACL_TAG_USER. + * did not use PCMK_XE_ACL_TARGET. */ requested_user = crm_element_value(request, field); } if (!pcmk__is_privileged(effective_user)) { /* We're not running as a privileged user, set or overwrite any existing - * value for $XML_ACL_TAG_USER + * value for PCMK_XE_ACL_TARGET */ user = effective_user; @@ -831,7 +839,7 @@ pcmk__update_acl_user(xmlNode *request, const char *field, } else if (!pcmk__is_privileged(peer_user)) { /* The peer is not a privileged user, set or overwrite any existing - * value for $XML_ACL_TAG_USER + * value for PCMK_XE_ACL_TARGET */ user = peer_user; @@ -845,8 +853,8 @@ pcmk__update_acl_user(xmlNode *request, const char *field, } // This requires pointer comparison, not string comparison - if (user != crm_element_value(request, XML_ACL_TAG_USER)) { - crm_xml_add(request, XML_ACL_TAG_USER, user); + if (user != crm_element_value(request, PCMK_XE_ACL_TARGET)) { + crm_xml_add(request, PCMK_XE_ACL_TARGET, user); } if (field != NULL && user != crm_element_value(request, field)) { diff --git a/lib/common/actions.c b/lib/common/actions.c index e710615..ed1056f 100644 --- a/lib/common/actions.c +++ b/lib/common/actions.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. * @@ -21,10 +21,164 @@ #include <crm/crm.h> #include <crm/lrmd.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/util.h> +#include <crm/common/scheduler.h> + +/*! + * \brief Get string equivalent of an action type + * + * \param[in] action Action type + * + * \return Static string describing \p action + */ +const char * +pcmk_action_text(enum action_tasks action) +{ + switch (action) { + case pcmk_action_stop: + return PCMK_ACTION_STOP; + + case pcmk_action_stopped: + return PCMK_ACTION_STOPPED; + + case pcmk_action_start: + return PCMK_ACTION_START; + + case pcmk_action_started: + return PCMK_ACTION_RUNNING; + + case pcmk_action_shutdown: + return PCMK_ACTION_DO_SHUTDOWN; + + case pcmk_action_fence: + return PCMK_ACTION_STONITH; + + case pcmk_action_monitor: + return PCMK_ACTION_MONITOR; + + case pcmk_action_notify: + return PCMK_ACTION_NOTIFY; + + case pcmk_action_notified: + return PCMK_ACTION_NOTIFIED; + + case pcmk_action_promote: + return PCMK_ACTION_PROMOTE; + + case pcmk_action_promoted: + return PCMK_ACTION_PROMOTED; + + case pcmk_action_demote: + return PCMK_ACTION_DEMOTE; + + case pcmk_action_demoted: + return PCMK_ACTION_DEMOTED; + + default: // pcmk_action_unspecified or invalid + return "no_action"; + } +} + +/*! + * \brief Parse an action type from an action name + * + * \param[in] action_name Action name + * + * \return Action type corresponding to \p action_name + */ +enum action_tasks +pcmk_parse_action(const char *action_name) +{ + if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) { + return pcmk_action_stop; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOPPED, pcmk__str_none)) { + return pcmk_action_stopped; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) { + return pcmk_action_start; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_RUNNING, pcmk__str_none)) { + return pcmk_action_started; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_DO_SHUTDOWN, + pcmk__str_none)) { + return pcmk_action_shutdown; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_STONITH, pcmk__str_none)) { + return pcmk_action_fence; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_MONITOR, pcmk__str_none)) { + return pcmk_action_monitor; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFY, pcmk__str_none)) { + return pcmk_action_notify; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFIED, + pcmk__str_none)) { + return pcmk_action_notified; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) { + return pcmk_action_promote; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)) { + return pcmk_action_demote; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTED, + pcmk__str_none)) { + return pcmk_action_promoted; + + } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTED, pcmk__str_none)) { + return pcmk_action_demoted; + } + return pcmk_action_unspecified; +} + +/*! + * \brief Get string equivalent of a failure handling type + * + * \param[in] on_fail Failure handling type + * + * \return Static string describing \p on_fail + */ +const char * +pcmk_on_fail_text(enum action_fail_response on_fail) +{ + switch (on_fail) { + case pcmk_on_fail_ignore: + return "ignore"; + + case pcmk_on_fail_demote: + return "demote"; + + case pcmk_on_fail_block: + return "block"; + + case pcmk_on_fail_restart: + return "recover"; + + case pcmk_on_fail_ban: + return "migrate"; + + case pcmk_on_fail_stop: + return "stop"; + + case pcmk_on_fail_fence_node: + return "fence"; + + case pcmk_on_fail_standby_node: + return "standby"; + + case pcmk_on_fail_restart_container: + return "restart-container"; + + case pcmk_on_fail_reset_remote: + return "reset-remote"; + } + return "<unknown>"; +} /*! * \brief Generate an operation key (RESOURCE_ACTION_INTERVAL) @@ -166,12 +320,12 @@ parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms) // Set output variables if (rsc_id != NULL) { *rsc_id = strndup(key, action_underbar); - CRM_ASSERT(*rsc_id != NULL); + pcmk__mem_assert(*rsc_id); } if (op_type != NULL) { *op_type = strndup(key + action_underbar + 1, interval_underbar - action_underbar - 1); - CRM_ASSERT(*op_type != NULL); + pcmk__mem_assert(*op_type); } if (interval_ms != NULL) { *interval_ms = local_interval_ms; @@ -220,8 +374,8 @@ decode_transition_magic(const char *magic, char **uuid, int *transition_id, int #ifdef HAVE_SSCANF_M res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key); #else - key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters - CRM_ASSERT(key); + // magic must have >=4 other characters + key = pcmk__assert_alloc(1, strlen(magic) - 3); res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key); #endif if (res == EOF) { @@ -301,8 +455,7 @@ decode_transition_key(const char *key, char **uuid, int *transition_id, int *act crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key); } if (uuid) { - *uuid = strdup(local_uuid); - CRM_ASSERT(*uuid); + *uuid = pcmk__str_copy(local_uuid); } if (transition_id) { *transition_id = local_transition_id; @@ -316,66 +469,6 @@ decode_transition_key(const char *key, char **uuid, int *transition_id, int *act return TRUE; } -// Return true if a is an attribute that should be filtered -static bool -should_filter_for_digest(xmlAttrPtr a, void *user_data) -{ - if (strncmp((const char *) a->name, CRM_META "_", - sizeof(CRM_META " ") - 1) == 0) { - return true; - } - return pcmk__str_any_of((const char *) a->name, - XML_ATTR_ID, - XML_ATTR_CRM_VERSION, - XML_LRM_ATTR_OP_DIGEST, - XML_LRM_ATTR_TARGET, - XML_LRM_ATTR_TARGET_UUID, - "pcmk_external_ip", - NULL); -} - -/*! - * \internal - * \brief Remove XML attributes not needed for operation digest - * - * \param[in,out] param_set XML with operation parameters - */ -void -pcmk__filter_op_for_digest(xmlNode *param_set) -{ - char *key = NULL; - char *timeout = NULL; - guint interval_ms = 0; - - if (param_set == NULL) { - return; - } - - /* Timeout is useful for recurring operation digests, so grab it before - * removing meta-attributes - */ - key = crm_meta_name(XML_LRM_ATTR_INTERVAL_MS); - if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) { - interval_ms = 0; - } - free(key); - key = NULL; - if (interval_ms != 0) { - key = crm_meta_name(XML_ATTR_TIMEOUT); - timeout = crm_element_value_copy(param_set, key); - } - - // Remove all CRM_meta_* attributes and certain other attributes - pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL); - - // Add timeout back for recurring operation digests - if (timeout != NULL) { - crm_xml_add(param_set, key, timeout); - } - free(timeout); - free(key); -} - int rsc_op_expected_rc(const lrmd_event_data_t *op) { @@ -432,12 +525,12 @@ crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task, CRM_CHECK(prefix && task && interval_spec, return NULL); - xml_op = create_xml_node(parent, XML_ATTR_OP); + xml_op = pcmk__xe_create(parent, PCMK_XE_OP); crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec); - crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec); - crm_xml_add(xml_op, "name", task); + crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec); + crm_xml_add(xml_op, PCMK_XA_NAME, task); if (timeout) { - crm_xml_add(xml_op, XML_ATTR_TIMEOUT, timeout); + crm_xml_add(xml_op, PCMK_META_TIMEOUT, timeout); } return xml_op; } @@ -483,50 +576,12 @@ crm_op_needs_metadata(const char *rsc_class, const char *op) * * \param[in] action Action name to check * - * \return true if \p action is "off", "reboot", or "poweroff", otherwise false + * \return \c true if \p action is \c PCMK_ACTION_OFF, \c PCMK_ACTION_REBOOT, + * or \c PCMK__ACTION_POWEROFF, otherwise \c false */ bool pcmk__is_fencing_action(const char *action) { return pcmk__str_any_of(action, PCMK_ACTION_OFF, PCMK_ACTION_REBOOT, - "poweroff", NULL); -} - -bool -pcmk_is_probe(const char *task, guint interval) -{ - if (task == NULL) { - return false; - } - - return (interval == 0) - && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none); -} - -bool -pcmk_xe_is_probe(const xmlNode *xml_op) -{ - const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); - const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS); - int interval_ms; - - pcmk__scan_min_int(interval_ms_s, &interval_ms, 0); - return pcmk_is_probe(task, interval_ms); -} - -bool -pcmk_xe_mask_probe_failure(const xmlNode *xml_op) -{ - int status = PCMK_EXEC_UNKNOWN; - int rc = PCMK_OCF_OK; - - if (!pcmk_xe_is_probe(xml_op)) { - return false; - } - - crm_element_value_int(xml_op, XML_LRM_ATTR_OPSTATUS, &status); - crm_element_value_int(xml_op, XML_LRM_ATTR_RC, &rc); - - return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM || - status == PCMK_EXEC_NOT_INSTALLED; + PCMK__ACTION_POWEROFF, NULL); } diff --git a/lib/common/agents.c b/lib/common/agents.c index d2066c0..dd4ba2e 100644 --- a/lib/common/agents.c +++ b/lib/common/agents.c @@ -46,7 +46,8 @@ pcmk_get_ra_caps(const char *standard) * (which were likely never used as real configurations). * * @TODO Remove pcmk_ra_cap_unique at the next major schema version - * bump, with a transform to remove globally-unique from the config. + * bump, with a transform to remove PCMK_META_GLOBALLY_UNIQUE from the + * config. */ return pcmk_ra_cap_params | pcmk_ra_cap_unique | pcmk_ra_cap_stdin | pcmk_ra_cap_fence_params; diff --git a/lib/common/alerts.c b/lib/common/alerts.c index 98b1e3f..eac3e2e 100644 --- a/lib/common/alerts.c +++ b/lib/common/alerts.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,7 +10,7 @@ #include <crm_internal.h> #include <crm/crm.h> #include <crm/lrmd.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/alerts_internal.h> #include <crm/common/cib_internal.h> #include <crm/common/xml_internal.h> @@ -94,11 +94,11 @@ const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX][3] = pcmk__alert_t * pcmk__alert_new(const char *id, const char *path) { - pcmk__alert_t *entry = calloc(1, sizeof(pcmk__alert_t)); + pcmk__alert_t *entry = pcmk__assert_alloc(1, sizeof(pcmk__alert_t)); - CRM_ASSERT(entry && id && path); - entry->id = strdup(id); - entry->path = strdup(path); + CRM_ASSERT((id != NULL) && (path != NULL)); + entry->id = pcmk__str_copy(id); + entry->path = pcmk__str_copy(path); entry->timeout = PCMK__ALERT_DEFAULT_TIMEOUT_MS; entry->flags = pcmk__alert_default; return entry; @@ -137,8 +137,8 @@ pcmk__dup_alert(const pcmk__alert_t *entry) new_entry->timeout = entry->timeout; new_entry->flags = entry->flags; new_entry->envvars = pcmk__str_table_dup(entry->envvars); - pcmk__str_update(&new_entry->tstamp_format, entry->tstamp_format); - pcmk__str_update(&new_entry->recipient, entry->recipient); + new_entry->tstamp_format = pcmk__str_copy(entry->tstamp_format); + new_entry->recipient = pcmk__str_copy(entry->recipient); if (entry->select_attribute_name) { new_entry->select_attribute_name = g_strdupv(entry->select_attribute_name); } @@ -152,7 +152,7 @@ pcmk__add_alert_key(GHashTable *table, enum pcmk__alert_keys_e name, for (const char **key = pcmk__alert_keys[name]; *key; key++) { crm_trace("Inserting alert key %s = '%s'", *key, value); if (value) { - g_hash_table_insert(table, strdup(*key), strdup(value)); + pcmk__insert_dup(table, *key, value); } else { g_hash_table_remove(table, *key); } @@ -165,6 +165,6 @@ pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name, { for (const char **key = pcmk__alert_keys[name]; *key; key++) { crm_trace("Inserting alert key %s = %d", *key, value); - g_hash_table_insert(table, strdup(*key), pcmk__itoa(value)); + g_hash_table_insert(table, pcmk__str_copy(*key), pcmk__itoa(value)); } } diff --git a/lib/common/attrs.c b/lib/common/attrs.c index 2be03b4..35715df 100644 --- a/lib/common/attrs.c +++ b/lib/common/attrs.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the Pacemaker project contributors + * Copyright 2011-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,10 +15,12 @@ #include <stdio.h> -#include <crm/msg_xml.h> -#include <crm/common/attrd_internal.h> +#include <crm/common/xml.h> +#include <crm/common/scheduler.h> +#include <crm/common/scheduler_internal.h> -#define LRM_TARGET_ENV "OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET +#define OCF_RESKEY_PREFIX "OCF_RESKEY_" +#define LRM_TARGET_ENV OCF_RESKEY_PREFIX CRM_META "_" PCMK__META_ON_NODE /*! * \internal @@ -27,7 +29,8 @@ * If given NULL, "auto", or "localhost" as an argument, check the environment * to detect the node name that should be used to set node attributes. (The * caller might not know the correct name, for example if the target is part of - * a bundle with container-attribute-target set to "host".) + * a bundle with \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET set to + * \c PCMK_VALUE_HOST.) * * \param[in] name NULL, "auto" or "localhost" to check environment variables, * or anything else to return NULL @@ -39,13 +42,22 @@ const char * pcmk__node_attr_target(const char *name) { if (name == NULL || pcmk__strcase_any_of(name, "auto", "localhost", NULL)) { - char *target_var = crm_meta_name(XML_RSC_ATTR_TARGET); - char *phys_var = crm_meta_name(PCMK__ENV_PHYSICAL_HOST); - const char *target = getenv(target_var); - const char *host_physical = getenv(phys_var); + char buf[128] = OCF_RESKEY_PREFIX; + size_t offset = sizeof(OCF_RESKEY_PREFIX) - 1; + char *target_var = crm_meta_name(PCMK_META_CONTAINER_ATTRIBUTE_TARGET); + char *phys_var = crm_meta_name(PCMK__META_PHYSICAL_HOST); + const char *target = NULL; + const char *host_physical = NULL; + + snprintf(buf + offset, sizeof(buf) - offset, "%s", target_var); + target = getenv(buf); + + snprintf(buf + offset, sizeof(buf) - offset, "%s", phys_var); + host_physical = getenv(buf); // It is important to use the name by which the scheduler knows us - if (host_physical && pcmk__str_eq(target, "host", pcmk__str_casei)) { + if (host_physical + && pcmk__str_eq(target, PCMK_VALUE_HOST, pcmk__str_casei)) { name = host_physical; } else { @@ -58,7 +70,7 @@ pcmk__node_attr_target(const char *name) free(target_var); free(phys_var); - // TODO? Call get_local_node_name() if name == NULL + // TODO? Call pcmk__cluster_local_node_name() if name == NULL // (currently would require linkage against libcrmcluster) return name; } else { @@ -87,3 +99,85 @@ pcmk_promotion_score_name(const char *rsc_id) } return crm_strdup_printf("master-%s", rsc_id); } + +/*! + * \internal + * \brief Get the value of a node attribute + * + * \param[in] node Node to get attribute for + * \param[in] name Name of node attribute to get + * \param[in] target If this is \c PCMK_VALUE_HOST and \p node is a guest + * (bundle) node, get the value from the guest's host, + * otherwise get the value from \p node itself + * \param[in] node_type If getting the value from \p node's host, this + * indicates whether to check the current or assigned host + * + * \return Value of \p name attribute for \p node + */ +const char * +pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target, + enum pcmk__rsc_node node_type) +{ + const char *value = NULL; // Attribute value to return + const char *node_type_s = NULL; // Readable equivalent of node_type + const pcmk_node_t *host = NULL; + const pcmk_resource_t *container = NULL; + + if ((node == NULL) || (name == NULL)) { + return NULL; + } + + /* Check the node's own attributes unless this is a guest (bundle) node with + * the container host as the attribute target. + */ + if (!pcmk__is_guest_or_bundle_node(node) + || !pcmk__str_eq(target, PCMK_VALUE_HOST, pcmk__str_casei)) { + value = g_hash_table_lookup(node->details->attrs, name); + crm_trace("%s='%s' on %s", + name, pcmk__s(value, ""), pcmk__node_name(node)); + return value; + } + + /* This resource needs attributes set for the container's host instead of + * for the container itself (useful when the container uses the host's + * storage). + */ + container = node->details->remote_rsc->container; + + switch (node_type) { + case pcmk__rsc_node_assigned: + host = container->allocated_to; + if (host == NULL) { + crm_trace("Skipping %s lookup for %s because " + "its container %s is unassigned", + name, pcmk__node_name(node), container->id); + return NULL; + } + node_type_s = "assigned"; + break; + + case pcmk__rsc_node_current: + if (container->running_on != NULL) { + host = container->running_on->data; + } + if (host == NULL) { + crm_trace("Skipping %s lookup for %s because " + "its container %s is inactive", + name, pcmk__node_name(node), container->id); + return NULL; + } + node_type_s = "current"; + break; + + default: + // Add support for other enum pcmk__rsc_node values if needed + CRM_ASSERT(false); + break; + } + + value = g_hash_table_lookup(host->details->attrs, name); + crm_trace("%s='%s' for %s on %s container host %s", + name, pcmk__s(value, ""), pcmk__node_name(node), node_type_s, + pcmk__node_name(host)); + return value; +} diff --git a/lib/common/cib.c b/lib/common/cib.c index fee7881..fee962b 100644 --- a/lib/common/cib.c +++ b/lib/common/cib.c @@ -1,6 +1,6 @@ /* * Original copyright 2004 International Business Machines - * Later changes copyright 2008-2023 the Pacemaker project contributors + * Later changes copyright 2008-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,7 @@ #include <stdio.h> #include <libxml/tree.h> // xmlNode -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/cib.h> #include <crm/common/cib_internal.h> @@ -29,74 +29,74 @@ static struct { } cib_sections[] = { { // This first entry is also the default if a NULL is compared - XML_TAG_CIB, + PCMK_XE_CIB, NULL, - "//" XML_TAG_CIB + "//" PCMK_XE_CIB }, { - XML_CIB_TAG_STATUS, - "/" XML_TAG_CIB, - "//" XML_TAG_CIB "/" XML_CIB_TAG_STATUS + PCMK_XE_STATUS, + "/" PCMK_XE_CIB, + "//" PCMK_XE_CIB "/" PCMK_XE_STATUS }, { - XML_CIB_TAG_CONFIGURATION, - "/" XML_TAG_CIB, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION + PCMK_XE_CONFIGURATION, + "/" PCMK_XE_CIB, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION }, { - XML_CIB_TAG_CRMCONFIG, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_CRMCONFIG + PCMK_XE_CRM_CONFIG, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_CRM_CONFIG }, { - XML_CIB_TAG_NODES, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES + PCMK_XE_NODES, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_NODES }, { - XML_CIB_TAG_RESOURCES, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES + PCMK_XE_RESOURCES, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RESOURCES }, { - XML_CIB_TAG_CONSTRAINTS, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_CONSTRAINTS + PCMK_XE_CONSTRAINTS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_CONSTRAINTS }, { - XML_CIB_TAG_OPCONFIG, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_OPCONFIG + PCMK_XE_OP_DEFAULTS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_OP_DEFAULTS }, { - XML_CIB_TAG_RSCCONFIG, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RSCCONFIG + PCMK_XE_RSC_DEFAULTS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RSC_DEFAULTS }, { - XML_CIB_TAG_ACLS, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_ACLS + PCMK_XE_ACLS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_ACLS }, { - XML_TAG_FENCING_TOPOLOGY, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_TAG_FENCING_TOPOLOGY + PCMK_XE_FENCING_TOPOLOGY, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_FENCING_TOPOLOGY }, { - XML_CIB_TAG_TAGS, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_TAGS + PCMK_XE_TAGS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_TAGS }, { - XML_CIB_TAG_ALERTS, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION, - "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_ALERTS + PCMK_XE_ALERTS, + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION, + "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_ALERTS }, { - XML_CIB_TAG_SECTION_ALL, + PCMK__XE_ALL, NULL, - "//" XML_TAG_CIB + "//" PCMK_XE_CIB }, }; @@ -173,3 +173,19 @@ pcmk_find_cib_element(xmlNode *cib, const char *element_name) { return get_xpath_object(pcmk_cib_xpath_for(element_name), cib, LOG_TRACE); } + +/*! + * \internal + * \brief Check that the feature set in the CIB is supported on this node + * + * \param[in] new_version PCMK_XA_CRM_FEATURE_SET attribute from the CIB + */ +int +pcmk__check_feature_set(const char *cib_version) +{ + if (cib_version && compare_version(cib_version, CRM_FEATURE_SET) > 0) { + return EPROTONOSUPPORT; + } + + return pcmk_rc_ok; +} diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h index 121d663..d90a64e 100644 --- a/lib/common/crmcommon_private.h +++ b/lib/common/crmcommon_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-2023 the Pacemaker project contributors + * Copyright 2018-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ #include <stdint.h> // uint8_t, uint32_t #include <stdbool.h> // bool #include <sys/types.h> // size_t -#include <glib.h> // GList +#include <glib.h> // gchar, GList #include <libxml/tree.h> // xmlNode, xmlAttr #include <qb/qbipcc.h> // struct qb_ipc_response_header @@ -33,23 +33,33 @@ * (e.g. when checking differences) that something was deleted. */ typedef struct pcmk__deleted_xml_s { - char *path; - int position; + gchar *path; + int position; } pcmk__deleted_xml_t; typedef struct xml_node_private_s { - long check; + uint32_t check; uint32_t flags; } xml_node_private_t; typedef struct xml_doc_private_s { - long check; + uint32_t check; uint32_t flags; char *user; GList *acls; GList *deleted_objs; // List of pcmk__deleted_xml_t } xml_doc_private_t; +// XML entity references + +#define PCMK__XML_ENTITY_AMP "&" +#define PCMK__XML_ENTITY_GT ">" +#define PCMK__XML_ENTITY_LT "<" +#define PCMK__XML_ENTITY_QUOT """ + +//! libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5 +#define PCMK__XML_VERSION ((pcmkXmlStr) "1.0") + #define pcmk__set_xml_flags(xml_priv, flags_to_set) do { \ (xml_priv)->flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_NEVER, "XML", "XML node", (xml_priv)->flags, \ @@ -63,14 +73,10 @@ typedef struct xml_doc_private_s { } while (0) G_GNUC_INTERNAL -void pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer, - int depth); - -G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy); G_GNUC_INTERNAL -void pcmk__mark_xml_created(xmlNode *xml); +void pcmk__xml_mark_created(xmlNode *xml); G_GNUC_INTERNAL int pcmk__xml_position(const xmlNode *xml, @@ -82,7 +88,7 @@ xmlNode *pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, G_GNUC_INTERNAL void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, - bool as_diff); + uint32_t flags, bool as_diff); G_GNUC_INTERNAL xmlNode *pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, @@ -125,6 +131,36 @@ bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data); G_GNUC_INTERNAL void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer); +G_GNUC_INTERNAL +int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value); + +/* + * Date/times + */ + +// For use with pcmk__add_time_from_xml() +enum pcmk__time_component { + pcmk__time_unknown, + pcmk__time_years, + pcmk__time_months, + pcmk__time_weeks, + pcmk__time_days, + pcmk__time_hours, + pcmk__time_minutes, + pcmk__time_seconds, +}; + +G_GNUC_INTERNAL +const char *pcmk__time_component_attr(enum pcmk__time_component component); + +G_GNUC_INTERNAL +int pcmk__add_time_from_xml(crm_time_t *t, enum pcmk__time_component component, + const xmlNode *xml); + +G_GNUC_INTERNAL +void pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source); + + /* * IPC */ @@ -274,8 +310,81 @@ int pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name, const char *filename, char **argv); G_GNUC_INTERNAL +void pcmk__register_option_messages(pcmk__output_t *out); + +G_GNUC_INTERNAL void pcmk__register_patchset_messages(pcmk__output_t *out); +G_GNUC_INTERNAL +bool pcmk__output_text_get_fancy(pcmk__output_t *out); + +/* + * Rules + */ + +// How node attribute values may be compared in rules +enum pcmk__comparison { + pcmk__comparison_unknown, + pcmk__comparison_defined, + pcmk__comparison_undefined, + pcmk__comparison_eq, + pcmk__comparison_ne, + pcmk__comparison_lt, + pcmk__comparison_lte, + pcmk__comparison_gt, + pcmk__comparison_gte, +}; + +// How node attribute values may be parsed in rules +enum pcmk__type { + pcmk__type_unknown, + pcmk__type_string, + pcmk__type_integer, + pcmk__type_number, + pcmk__type_version, +}; + +// Where to obtain reference value for a node attribute comparison +enum pcmk__reference_source { + pcmk__source_unknown, + pcmk__source_literal, + pcmk__source_instance_attrs, + pcmk__source_meta_attrs, +}; + +G_GNUC_INTERNAL +enum pcmk__comparison pcmk__parse_comparison(const char *op); + +G_GNUC_INTERNAL +enum pcmk__type pcmk__parse_type(const char *type, enum pcmk__comparison op, + const char *value1, const char *value2); + +G_GNUC_INTERNAL +enum pcmk__reference_source pcmk__parse_source(const char *source); + +G_GNUC_INTERNAL +int pcmk__cmp_by_type(const char *value1, const char *value2, + enum pcmk__type type); + +G_GNUC_INTERNAL +int pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start, + crm_time_t **end); + +G_GNUC_INTERNAL +int pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now); + +G_GNUC_INTERNAL +int pcmk__evaluate_attr_expression(const xmlNode *expression, + const pcmk_rule_input_t *rule_input); + +G_GNUC_INTERNAL +int pcmk__evaluate_rsc_expression(const xmlNode *expr, + const pcmk_rule_input_t *rule_input); + +G_GNUC_INTERNAL +int pcmk__evaluate_op_expression(const xmlNode *expr, + const pcmk_rule_input_t *rule_input); + /* * Utils @@ -283,4 +392,31 @@ void pcmk__register_patchset_messages(pcmk__output_t *out); #define PCMK__PW_BUFFER_LEN 500 +/* + * Schemas + */ +typedef struct { + unsigned char v[2]; +} pcmk__schema_version_t; + +enum pcmk__schema_validator { + pcmk__schema_validator_none, + pcmk__schema_validator_rng +}; + +typedef struct { + int schema_index; + char *name; + char *transform; + void *cache; + enum pcmk__schema_validator validator; + pcmk__schema_version_t version; + char *transform_enter; + bool transform_onleave; +} pcmk__schema_t; + +G_GNUC_INTERNAL +GList *pcmk__find_x_0_schema(void); + + #endif // CRMCOMMON_PRIVATE__H diff --git a/lib/common/digest.c b/lib/common/digest.c index 4de6f97..9a06ff4 100644 --- a/lib/common/digest.c +++ b/lib/common/digest.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,7 +16,6 @@ #include <md5.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include "crmcommon_private.h" @@ -37,7 +36,7 @@ dump_xml_for_digest(xmlNodePtr xml) /* for compatibility with the old result which is used for v1 digests */ g_string_append_c(buffer, ' '); - pcmk__xml2text(xml, 0, buffer, 0); + pcmk__xml_string(xml, 0, buffer, 0); g_string_append_c(buffer, '\n'); return buffer; @@ -92,13 +91,12 @@ static char * calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter) { char *digest = NULL; - GString *buffer = g_string_sized_new(1024); + GString *buf = g_string_sized_new(1024); crm_trace("Begin digest %s", do_filter?"filtered":""); - pcmk__xml2text(source, (do_filter? pcmk__xml_fmt_filtered : 0), buffer, 0); - CRM_ASSERT(buffer != NULL); - digest = crm_md5sum((const char *) buffer->str); + pcmk__xml_string(source, (do_filter? pcmk__xml_fmt_filtered : 0), buf, 0); + digest = crm_md5sum(buf->str); pcmk__if_tracing( { @@ -106,17 +104,17 @@ calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter) pcmk__get_tmpdir(), digest); crm_trace("Saving %s.%s.%s to %s", - crm_element_value(source, XML_ATTR_GENERATION_ADMIN), - crm_element_value(source, XML_ATTR_GENERATION), - crm_element_value(source, XML_ATTR_NUMUPDATES), + crm_element_value(source, PCMK_XA_ADMIN_EPOCH), + crm_element_value(source, PCMK_XA_EPOCH), + crm_element_value(source, PCMK_XA_NUM_UPDATES), trace_file); save_xml_to_file(source, "digest input", trace_file); free(trace_file); }, {} ); - g_string_free(buffer, TRUE); crm_trace("End digest"); + g_string_free(buf, TRUE); return digest; } @@ -234,11 +232,11 @@ bool pcmk__xa_filterable(const char *name) { static const char *filter[] = { - XML_ATTR_ORIGIN, - XML_CIB_ATTR_WRITTEN, - XML_ATTR_UPDATE_ORIG, - XML_ATTR_UPDATE_CLIENT, - XML_ATTR_UPDATE_USER, + PCMK_XA_CRM_DEBUG_ORIGIN, + PCMK_XA_CIB_LAST_WRITTEN, + PCMK_XA_UPDATE_ORIGIN, + PCMK_XA_UPDATE_CLIENT, + PCMK_XA_UPDATE_USER, }; for (int i = 0; i < PCMK__NELEM(filter); i++) { @@ -276,3 +274,63 @@ crm_md5sum(const char *buffer) } return digest; } + +// Return true if a is an attribute that should be filtered +static bool +should_filter_for_digest(xmlAttrPtr a, void *user_data) +{ + if (strncmp((const char *) a->name, CRM_META "_", + sizeof(CRM_META " ") - 1) == 0) { + return true; + } + return pcmk__str_any_of((const char *) a->name, + PCMK_XA_ID, + PCMK_XA_CRM_FEATURE_SET, + PCMK__XA_OP_DIGEST, + PCMK__META_ON_NODE, + PCMK__META_ON_NODE_UUID, + "pcmk_external_ip", + NULL); +} + +/*! + * \internal + * \brief Remove XML attributes not needed for operation digest + * + * \param[in,out] param_set XML with operation parameters + */ +void +pcmk__filter_op_for_digest(xmlNode *param_set) +{ + char *key = NULL; + char *timeout = NULL; + guint interval_ms = 0; + + if (param_set == NULL) { + return; + } + + /* Timeout is useful for recurring operation digests, so grab it before + * removing meta-attributes + */ + key = crm_meta_name(PCMK_META_INTERVAL); + if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) { + interval_ms = 0; + } + free(key); + key = NULL; + if (interval_ms != 0) { + key = crm_meta_name(PCMK_META_TIMEOUT); + timeout = crm_element_value_copy(param_set, key); + } + + // Remove all CRM_meta_* attributes and certain other attributes + pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL); + + // Add timeout back for recurring operation digests + if (timeout != NULL) { + crm_xml_add(param_set, key, timeout); + } + free(timeout); + free(key); +} diff --git a/lib/common/health.c b/lib/common/health.c index ee412be..20cf36e 100644 --- a/lib/common/health.c +++ b/lib/common/health.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -21,11 +21,11 @@ bool pcmk__validate_health_strategy(const char *value) { return pcmk__strcase_any_of(value, - PCMK__VALUE_NONE, - PCMK__VALUE_CUSTOM, - PCMK__VALUE_ONLY_GREEN, - PCMK__VALUE_PROGRESSIVE, - PCMK__VALUE_MIGRATE_ON_RED, + PCMK_VALUE_NONE, + PCMK_VALUE_CUSTOM, + PCMK_VALUE_ONLY_GREEN, + PCMK_VALUE_PROGRESSIVE, + PCMK_VALUE_MIGRATE_ON_RED, NULL); } @@ -40,29 +40,24 @@ pcmk__validate_health_strategy(const char *value) enum pcmk__health_strategy pcmk__parse_health_strategy(const char *value) { - if (pcmk__str_eq(value, PCMK__VALUE_NONE, + if (pcmk__str_eq(value, PCMK_VALUE_NONE, pcmk__str_null_matches|pcmk__str_casei)) { return pcmk__health_strategy_none; - - } else if (pcmk__str_eq(value, PCMK__VALUE_MIGRATE_ON_RED, - pcmk__str_casei)) { + } + if (pcmk__str_eq(value, PCMK_VALUE_MIGRATE_ON_RED, pcmk__str_casei)) { return pcmk__health_strategy_no_red; - - } else if (pcmk__str_eq(value, PCMK__VALUE_ONLY_GREEN, - pcmk__str_casei)) { + } + if (pcmk__str_eq(value, PCMK_VALUE_ONLY_GREEN, pcmk__str_casei)) { return pcmk__health_strategy_only_green; - - } else if (pcmk__str_eq(value, PCMK__VALUE_PROGRESSIVE, - pcmk__str_casei)) { + } + if (pcmk__str_eq(value, PCMK_VALUE_PROGRESSIVE, pcmk__str_casei)) { return pcmk__health_strategy_progressive; - - } else if (pcmk__str_eq(value, PCMK__VALUE_CUSTOM, - pcmk__str_casei)) { + } + if (pcmk__str_eq(value, PCMK_VALUE_CUSTOM, pcmk__str_casei)) { return pcmk__health_strategy_custom; - } else { - pcmk__config_err("Using default of \"" PCMK__VALUE_NONE "\" for " - PCMK__OPT_NODE_HEALTH_STRATEGY + pcmk__config_err("Using default of \"" PCMK_VALUE_NONE "\" for " + PCMK_OPT_NODE_HEALTH_STRATEGY " because '%s' is not a valid value", value); return pcmk__health_strategy_none; diff --git a/lib/common/io.c b/lib/common/io.c index 35efbe9..ee71af7 100644 --- a/lib/common/io.c +++ b/lib/common/io.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -633,20 +633,13 @@ pcmk__close_fds_in_child(bool all) char * pcmk__full_path(const char *filename, const char *dirname) { - char *path = NULL; - CRM_ASSERT(filename != NULL); if (filename[0] == '/') { - path = strdup(filename); - CRM_ASSERT(path != NULL); - - } else { - CRM_ASSERT(dirname != NULL); - path = crm_strdup_printf("%s/%s", dirname, filename); + return pcmk__str_copy(filename); } - - return path; + CRM_ASSERT(dirname != NULL); + return crm_strdup_printf("%s/%s", dirname, filename); } // Deprecated functions kept only for backward API compatibility diff --git a/lib/common/ipc_attrd.c b/lib/common/ipc_attrd.c index 9caaabe..5ab0f2d 100644 --- a/lib/common/ipc_attrd.c +++ b/lib/common/ipc_attrd.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2023 the Pacemaker project contributors + * Copyright 2011-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,10 +16,10 @@ #include <stdio.h> #include <crm/crm.h> +#include <crm/common/attrs_internal.h> #include <crm/common/ipc.h> #include <crm/common/ipc_attrd_internal.h> -#include <crm/common/attrd_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "crmcommon_private.h" static void @@ -30,13 +30,13 @@ set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data) name = crm_element_value(msg_data, PCMK__XA_ATTR_NAME); - for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE); - node != NULL; node = crm_next_same_xml(node)) { - pair = calloc(1, sizeof(pcmk__attrd_query_pair_t)); + for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL, + NULL); + node != NULL; node = pcmk__xe_next_same(node)) { - CRM_ASSERT(pair != NULL); + pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t)); - pair->node = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME); + pair->node = crm_element_value(node, PCMK__XA_ATTR_HOST); pair->name = name; pair->value = crm_element_value(node, PCMK__XA_ATTR_VALUE); data->data.pairs = g_list_prepend(data->data.pairs, pair); @@ -46,7 +46,7 @@ set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data) static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { - const char *command = crm_element_value(request, PCMK__XA_TASK); + const char *command = crm_element_value(request, PCMK_XA_TASK); return pcmk__str_any_of(command, PCMK__ATTRD_CMD_CLEAR_FAILURE, @@ -68,21 +68,22 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply) pcmk__attrd_reply_unknown }; - if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) { + if (pcmk__xe_is(reply, PCMK__XE_ACK)) { return false; } /* Do some basic validation of the reply */ - value = crm_element_value(reply, F_TYPE); + value = crm_element_value(reply, PCMK__XA_T); if (pcmk__str_empty(value) - || !pcmk__str_eq(value, T_ATTRD, pcmk__str_none)) { + || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) { crm_info("Unrecognizable message from attribute manager: " - "message type '%s' not '" T_ATTRD "'", pcmk__s(value, "")); + "message type '%s' not '" PCMK__VALUE_ATTRD "'", + pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } - value = crm_element_value(reply, F_SUBTYPE); + value = crm_element_value(reply, PCMK__XA_SUBT); /* Only the query command gets a reply for now. NULL counts as query for * backward compatibility with attribute managers <2.1.3 that didn't set it. @@ -139,61 +140,38 @@ pcmk__attrd_api_methods(void) static xmlNode * create_attrd_op(const char *user_name) { - xmlNode *attrd_op = create_xml_node(NULL, __func__); + xmlNode *attrd_op = pcmk__xe_create(NULL, __func__); - crm_xml_add(attrd_op, F_TYPE, T_ATTRD); - crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown")); + crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD); + crm_xml_add(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown")); crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name); return attrd_op; } static int -create_api(pcmk_ipc_api_t **api) -{ - int rc = pcmk_new_ipc_api(api, pcmk_ipc_attrd); - - if (rc != pcmk_rc_ok) { - crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc)); - } - - return rc; -} - -static void -destroy_api(pcmk_ipc_api_t *api) -{ - pcmk_disconnect_ipc(api); - pcmk_free_ipc_api(api); - api = NULL; -} - -static int connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request) { int rc = pcmk_rc_ok; + bool created_api = false; - rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5); - if (rc != pcmk_rc_ok) { - crm_err("Could not connect to %s: %s", - pcmk_ipc_name(api, true), pcmk_rc_str(rc)); - return rc; + if (api == NULL) { + rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd); + if (rc != pcmk_rc_ok) { + return rc; + } + created_api = true; } - rc = pcmk__send_ipc_request(api, request); - if (rc != pcmk_rc_ok) { - crm_err("Could not send request to %s: %s", - pcmk_ipc_name(api, true), pcmk_rc_str(rc)); - return rc; + rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5); + if (rc == pcmk_rc_ok) { + rc = pcmk__send_ipc_request(api, request); } - return pcmk_rc_ok; -} - -static int -send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request) -{ - return pcmk__send_ipc_request(api, request); + if (created_api) { + pcmk_free_ipc_api(api); + } + return rc; } int @@ -212,44 +190,28 @@ pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, node = target; } - crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE); - pcmk__xe_add_node(request, node, 0); - crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource); - crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation); - crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec); - crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE, - pcmk_is_set(options, pcmk__node_attr_remote)); - - if (api == NULL) { - rc = create_api(&api); - if (rc != pcmk_rc_ok) { - return rc; - } - - rc = connect_and_send_attrd_request(api, request); - destroy_api(api); - - } else if (!pcmk_ipc_is_connected(api)) { - rc = connect_and_send_attrd_request(api, request); - - } else { - rc = send_attrd_request(api, request); - } - - free_xml(request); - if (operation) { - interval_desc = interval_spec? interval_spec : "nonrecurring"; + interval_desc = pcmk__s(interval_spec, "nonrecurring"); op_desc = operation; } else { interval_desc = "all"; op_desc = "operations"; } + crm_debug("Asking %s to clear failure of %s %s for %s on %s", + pcmk_ipc_name(api, true), interval_desc, op_desc, + pcmk__s(resource, "all resources"), pcmk__s(node, "all nodes")); - crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)", - interval_desc, op_desc, (resource? resource : "all resources"), - (node? node : "all nodes"), pcmk_rc_str(rc), rc); + crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE); + pcmk__xe_add_node(request, node, 0); + crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource); + crm_xml_add(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation); + crm_xml_add(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec); + crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE, + pcmk_is_set(options, pcmk__node_attr_remote)); + + rc = connect_and_send_attrd_request(api, request); + free_xml(request); return rc; } @@ -277,43 +239,30 @@ pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, } int -pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node) +pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap) { int rc = pcmk_rc_ok; xmlNode *request = NULL; - const char *display_host = (node ? node : "localhost"); const char *target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } + crm_debug("Asking %s to purge transient attributes%s for %s", + pcmk_ipc_name(api, true), + (reap? " and node cache entries" : ""), + pcmk__s(node, "local node")); + request = create_attrd_op(NULL); - crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); + crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); + pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap); pcmk__xe_add_node(request, node, 0); - if (api == NULL) { - rc = create_api(&api); - if (rc != pcmk_rc_ok) { - return rc; - } - - rc = connect_and_send_attrd_request(api, request); - destroy_api(api); - - } else if (!pcmk_ipc_is_connected(api)) { - rc = connect_and_send_attrd_request(api, request); - - } else { - rc = send_attrd_request(api, request); - } + rc = connect_and_send_attrd_request(api, request); free_xml(request); - - crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)", - display_host, pcmk_rc_str(rc), rc); - return rc; } @@ -339,23 +288,18 @@ pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, } } + crm_debug("Querying %s for value of '%s'%s%s", + pcmk_ipc_name(api, true), name, + ((node == NULL)? "" : " on "), pcmk__s(node, "")); + request = create_attrd_op(NULL); crm_xml_add(request, PCMK__XA_ATTR_NAME, name); - crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY); + crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_QUERY); pcmk__xe_add_node(request, node, 0); - rc = send_attrd_request(api, request); + rc = connect_and_send_attrd_request(api, request); free_xml(request); - - if (node) { - crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)", - name, node, pcmk_rc_str(rc), rc); - } else { - crm_debug("Queried pacemaker-attrd for %s: %s (%d)", - name, pcmk_rc_str(rc), rc); - } - return rc; } @@ -364,39 +308,23 @@ pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node) { int rc = pcmk_rc_ok; xmlNode *request = NULL; - const char *display_host = (node ? node : "localhost"); const char *target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } + crm_debug("Asking %s to write all transient attributes for %s to CIB", + pcmk_ipc_name(api, true), pcmk__s(node, "local node")); + request = create_attrd_op(NULL); - crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_REFRESH); + crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_REFRESH); pcmk__xe_add_node(request, node, 0); - if (api == NULL) { - rc = create_api(&api); - if (rc != pcmk_rc_ok) { - return rc; - } - - rc = connect_and_send_attrd_request(api, request); - destroy_api(api); - - } else if (!pcmk_ipc_is_connected(api)) { - rc = connect_and_send_attrd_request(api, request); - - } else { - rc = send_attrd_request(api, request); - } + rc = connect_and_send_attrd_request(api, request); free_xml(request); - - crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)", - display_host, pcmk_rc_str(rc), rc); - return rc; } @@ -404,11 +332,11 @@ static void add_op_attr(xmlNode *op, uint32_t options) { if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) { - crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH); + crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH); } else if (pcmk_is_set(options, pcmk__node_attr_value)) { - crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE); + crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); } else if (pcmk_is_set(options, pcmk__node_attr_delay)) { - crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY); + crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY); } } @@ -417,15 +345,15 @@ populate_update_op(xmlNode *op, const char *node, const char *name, const char * const char *dampen, const char *set, uint32_t options) { if (pcmk_is_set(options, pcmk__node_attr_pattern)) { - crm_xml_add(op, PCMK__XA_ATTR_PATTERN, name); + crm_xml_add(op, PCMK__XA_ATTR_REGEX, name); } else { crm_xml_add(op, PCMK__XA_ATTR_NAME, name); } if (pcmk_is_set(options, pcmk__node_attr_utilization)) { - crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_UTILIZATION); + crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_UTILIZATION); } else { - crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, XML_TAG_ATTR_SETS); + crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_INSTANCE_ATTRIBUTES); } add_op_attr(op, options); @@ -453,7 +381,6 @@ pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, { int rc = pcmk_rc_ok; xmlNode *request = NULL; - const char *display_host = (node ? node : "localhost"); const char *target = NULL; if (name == NULL) { @@ -466,30 +393,16 @@ pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, node = target; } + crm_debug("Asking %s to update '%s' to '%s' for %s", + pcmk_ipc_name(api, true), name, pcmk__s(value, "(null)"), + pcmk__s(node, "local node")); + request = create_attrd_op(user_name); populate_update_op(request, node, name, value, dampen, set, options); - if (api == NULL) { - rc = create_api(&api); - if (rc != pcmk_rc_ok) { - return rc; - } - - rc = connect_and_send_attrd_request(api, request); - destroy_api(api); - - } else if (!pcmk_ipc_is_connected(api)) { - rc = connect_and_send_attrd_request(api, request); - - } else { - rc = send_attrd_request(api, request); - } + rc = connect_and_send_attrd_request(api, request); free_xml(request); - - crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)", - name, display_host, pcmk_rc_str(rc), rc); - return rc; } @@ -545,7 +458,7 @@ pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampe * then we also add the task to each child node in populate_update_op * so attrd_client_update knows what form of update is taking place. */ - child = create_xml_node(request, XML_ATTR_OP); + child = pcmk__xe_create(request, PCMK_XE_OP); target = pcmk__node_attr_target(pair->node); if (target != NULL) { @@ -564,23 +477,8 @@ pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampe * request. Do that now, creating and destroying the API object if needed. */ if (pcmk__is_daemon) { - bool created_api = false; - - if (api == NULL) { - rc = create_api(&api); - if (rc != pcmk_rc_ok) { - return rc; - } - - created_api = true; - } - rc = connect_and_send_attrd_request(api, request); free_xml(request); - - if (created_api) { - destroy_api(api); - } } return rc; diff --git a/lib/common/ipc_client.c b/lib/common/ipc_client.c index 0d38650..37a0982 100644 --- a/lib/common/ipc_client.c +++ b/lib/common/ipc_client.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,7 @@ #include <bzlib.h> #include <crm/crm.h> /* indirectly: pcmk_err_generic */ -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/ipc_internal.h> #include "crmcommon_private.h" @@ -251,7 +251,7 @@ pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log) } switch (api->server) { case pcmk_ipc_attrd: - return for_log? "attribute manager" : T_ATTRD; + return for_log? "attribute manager" : PCMK__VALUE_ATTRD; case pcmk_ipc_based: return for_log? "CIB manager" : NULL /* PCMK__SERVER_BASED_RW */; @@ -338,7 +338,7 @@ dispatch_ipc_data(const char *buffer, pcmk_ipc_api_t *api) return ENOMSG; } - msg = string2xml(buffer); + msg = pcmk__xml_parse(buffer); if (msg == NULL) { crm_warn("Malformed message received from %s IPC", pcmk_ipc_name(api, true)); @@ -755,10 +755,11 @@ create_purge_node_request(const pcmk_ipc_api_t *api, const char *node_name, switch (api->server) { case pcmk_ipc_attrd: - request = create_xml_node(NULL, __func__); - crm_xml_add(request, F_TYPE, T_ATTRD); - crm_xml_add(request, F_ORIG, crm_system_name); - crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); + request = pcmk__xe_create(NULL, __func__); + crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_ATTRD); + crm_xml_add(request, PCMK__XA_SRC, crm_system_name); + crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); + pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, true); pcmk__xe_add_node(request, node_name, nodeid); break; @@ -770,7 +771,7 @@ create_purge_node_request(const pcmk_ipc_api_t *api, const char *node_name, if (nodeid > 0) { crm_xml_set_id(request, "%lu", (unsigned long) nodeid); } - crm_xml_add(request, XML_ATTR_UNAME, node_name); + crm_xml_add(request, PCMK_XA_UNAME, node_name); break; case pcmk_ipc_based: @@ -1122,7 +1123,7 @@ crm_ipc_decompress(crm_ipc_t * client) unsigned int size_u = 1 + header->size_uncompressed; /* never let buf size fall below our max size required for ipc reads. */ unsigned int new_buf_size = QB_MAX((sizeof(pcmk__ipc_header_t) + size_u), client->max_buf_size); - char *uncompressed = calloc(1, new_buf_size); + char *uncompressed = pcmk__assert_alloc(1, new_buf_size); crm_trace("Decompressing message data %u bytes into %u bytes", header->size_compressed, size_u); @@ -1264,13 +1265,13 @@ internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout, /* Got it */ break; } else if (hdr->qb.id < request_id) { - xmlNode *bad = string2xml(crm_ipc_buffer(client)); + xmlNode *bad = pcmk__xml_parse(crm_ipc_buffer(client)); crm_err("Discarding old reply %d (need %d)", hdr->qb.id, request_id); crm_log_xml_notice(bad, "OldIpcReply"); } else { - xmlNode *bad = string2xml(crm_ipc_buffer(client)); + xmlNode *bad = pcmk__xml_parse(crm_ipc_buffer(client)); crm_err("Discarding newer reply %d (need %d)", hdr->qb.id, request_id); crm_log_xml_notice(bad, "ImpossibleReply"); @@ -1429,7 +1430,7 @@ crm_ipc_send(crm_ipc_t *client, const xmlNode *message, crm_ipc_buffer(client)); if (reply) { - *reply = string2xml(crm_ipc_buffer(client)); + *reply = pcmk__xml_parse(crm_ipc_buffer(client)); } } else { @@ -1622,13 +1623,17 @@ pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid, do { poll_rc = poll(&pollfd, 1, 2000); } while ((poll_rc == -1) && (errno == EINTR)); - if ((poll_rc <= 0) || (qb_ipcc_connect_continue(c) != 0)) { + + /* If poll() failed, given that disconnect function is not registered yet, + * qb_ipcc_disconnect() won't clean up the socket. In any case, call + * qb_ipcc_connect_continue() here so that it may fail and do the cleanup + * for us. + */ + if (qb_ipcc_connect_continue(c) != 0) { crm_info("Could not connect to %s IPC: %s", name, (poll_rc == 0)?"timeout":strerror(errno)); rc = pcmk_rc_ipc_unresponsive; - if (poll_rc > 0) { - c = NULL; // qb_ipcc_connect_continue cleaned up for us - } + c = NULL; // qb_ipcc_connect_continue cleaned up for us goto bail; } #endif diff --git a/lib/common/ipc_common.c b/lib/common/ipc_common.c index a48b0e9..5bea139 100644 --- a/lib/common/ipc_common.c +++ b/lib/common/ipc_common.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2021 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,7 @@ #include <stdint.h> // uint64_t #include <sys/types.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "crmcommon_private.h" #define MIN_MSG_SIZE 12336 // sizeof(struct qb_ipc_connection_response) diff --git a/lib/common/ipc_controld.c b/lib/common/ipc_controld.c index 8e2016e..755c034 100644 --- a/lib/common/ipc_controld.c +++ b/lib/common/ipc_controld.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,6 @@ #include <libxml/tree.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/ipc_internal.h> @@ -70,8 +69,8 @@ new_data(pcmk_ipc_api_t *api) /* This is set to the PID because that's how it was always done, but PIDs * are not unique because clients can be remote. The value appears to be - * unused other than as part of F_CRM_SYS_FROM in IPC requests, which is - * only compared against the internal system names (CRM_SYSTEM_TENGINE, + * unused other than as part of PCMK__XA_CRM_SYS_FROM in IPC requests, which + * is only compared against the internal system names (CRM_SYSTEM_TENGINE, * etc.), so it shouldn't be a problem. */ private->client_uuid = pcmk__getpid_s(); @@ -123,19 +122,21 @@ set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) if (msg_data == NULL) { return; } - data->data.node_info.have_quorum = pcmk__xe_attr_is_true(msg_data, XML_ATTR_HAVE_QUORUM); - data->data.node_info.is_remote = pcmk__xe_attr_is_true(msg_data, XML_NODE_IS_REMOTE); + data->data.node_info.have_quorum = + pcmk__xe_attr_is_true(msg_data, PCMK_XA_HAVE_QUORUM); + data->data.node_info.is_remote = + pcmk__xe_attr_is_true(msg_data, PCMK_XA_REMOTE_NODE); /* Integer node_info.id is currently valid only for Corosync nodes. * * @TODO: Improve handling after crm_node_t is refactored to handle layer- * specific data better. */ - crm_element_value_int(msg_data, XML_ATTR_ID, &(data->data.node_info.id)); + crm_element_value_int(msg_data, PCMK_XA_ID, &(data->data.node_info.id)); - data->data.node_info.uuid = crm_element_value(msg_data, XML_ATTR_ID); - data->data.node_info.uname = crm_element_value(msg_data, XML_ATTR_UNAME); - data->data.node_info.state = crm_element_value(msg_data, PCMK__XA_CRMD); + data->data.node_info.uuid = crm_element_value(msg_data, PCMK_XA_ID); + data->data.node_info.uname = crm_element_value(msg_data, PCMK_XA_UNAME); + data->data.node_info.state = crm_element_value(msg_data, PCMK_XA_CRMD); } static void @@ -146,10 +147,10 @@ set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) return; } data->data.ping.sys_from = crm_element_value(msg_data, - XML_PING_ATTR_SYSFROM); + PCMK__XA_CRM_SUBSYSTEM); data->data.ping.fsa_state = crm_element_value(msg_data, - XML_PING_ATTR_CRMDSTATE); - data->data.ping.result = crm_element_value(msg_data, XML_PING_ATTR_STATUS); + PCMK__XA_CRMD_STATE); + data->data.ping.result = crm_element_value(msg_data, PCMK_XA_RESULT); } static void @@ -158,17 +159,18 @@ set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) pcmk_controld_api_node_t *node_info; data->reply_type = pcmk_controld_reply_nodes; - for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE); - node != NULL; node = crm_next_same_xml(node)) { + for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL, + NULL); + node != NULL; node = pcmk__xe_next_same(node)) { long long id_ll = 0; - node_info = calloc(1, sizeof(pcmk_controld_api_node_t)); - crm_element_value_ll(node, XML_ATTR_ID, &id_ll); + node_info = pcmk__assert_alloc(1, sizeof(pcmk_controld_api_node_t)); + crm_element_value_ll(node, PCMK_XA_ID, &id_ll); if (id_ll > 0) { node_info->id = id_ll; } - node_info->uname = crm_element_value(node, XML_ATTR_UNAME); + node_info->uname = crm_element_value(node, PCMK_XA_UNAME); node_info->state = crm_element_value(node, PCMK__XA_IN_CCM); data->data.nodes = g_list_prepend(data->data.nodes, node_info); } @@ -178,7 +180,7 @@ static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { // We only need to handle commands that API functions can send - return pcmk__str_any_of(crm_element_value(request, F_CRM_TASK), + return pcmk__str_any_of(crm_element_value(request, PCMK__XA_CRM_TASK), PCMK__CONTROLD_CMD_NODES, CRM_OP_LRM_DELETE, CRM_OP_LRM_FAIL, @@ -194,13 +196,14 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { struct controld_api_private_s *private = api->api_data; crm_exit_t status = CRM_EX_OK; + xmlNode *wrapper = NULL; xmlNode *msg_data = NULL; const char *value = NULL; pcmk_controld_api_reply_t reply_data = { pcmk_controld_reply_unknown, NULL, NULL, }; - if (pcmk__xe_is(reply, "ack")) { + if (pcmk__xe_is(reply, PCMK__XE_ACK)) { /* ACKs are trivial responses that do not count toward expected replies, * and do not have all the fields that validation requires, so skip that * processing. @@ -219,22 +222,22 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply) * if we fix the controller, we'll still need to handle replies from * old versions (feature set could be used to differentiate). */ - value = crm_element_value(reply, F_CRM_MSG_TYPE); - if (pcmk__str_empty(value) - || !pcmk__str_any_of(value, XML_ATTR_REQUEST, XML_ATTR_RESPONSE, NULL)) { + value = crm_element_value(reply, PCMK__XA_SUBT); + if (!pcmk__str_any_of(value, PCMK__VALUE_REQUEST, PCMK__VALUE_RESPONSE, + NULL)) { crm_info("Unrecognizable message from controller: " "invalid message type '%s'", pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } - if (pcmk__str_empty(crm_element_value(reply, XML_ATTR_REFERENCE))) { + if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) { crm_info("Unrecognizable message from controller: no reference"); status = CRM_EX_PROTOCOL; goto done; } - value = crm_element_value(reply, F_CRM_TASK); + value = crm_element_value(reply, PCMK__XA_CRM_TASK); if (pcmk__str_empty(value)) { crm_info("Unrecognizable message from controller: no command name"); status = CRM_EX_PROTOCOL; @@ -243,9 +246,11 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply) // Parse useful info from reply - reply_data.feature_set = crm_element_value(reply, XML_ATTR_VERSION); - reply_data.host_from = crm_element_value(reply, F_CRM_HOST_FROM); - msg_data = get_message_xml(reply, F_CRM_DATA); + reply_data.feature_set = crm_element_value(reply, PCMK_XA_VERSION); + reply_data.host_from = crm_element_value(reply, PCMK__XA_SRC); + + wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL); + msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (!strcmp(value, CRM_OP_REPROBE)) { reply_data.reply_type = pcmk_controld_reply_reprobe; @@ -332,7 +337,7 @@ static int send_controller_request(pcmk_ipc_api_t *api, const xmlNode *request, bool reply_is_expected) { - if (crm_element_value(request, XML_ATTR_REFERENCE) == NULL) { + if (crm_element_value(request, PCMK_XA_REFERENCE) == NULL) { return EINVAL; } if (reply_is_expected) { @@ -348,10 +353,10 @@ create_reprobe_message_data(const char *target_node, const char *router_node) { xmlNode *msg_data; - msg_data = create_xml_node(NULL, "data_for_" CRM_OP_REPROBE); - crm_xml_add(msg_data, XML_LRM_ATTR_TARGET, target_node); + msg_data = pcmk__xe_create(NULL, "data_for_" CRM_OP_REPROBE); + crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node); if ((router_node != NULL) && !pcmk__str_eq(router_node, target_node, pcmk__str_casei)) { - crm_xml_add(msg_data, XML_LRM_ATTR_ROUTER_NODE, router_node); + crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node); } return msg_data; } @@ -486,7 +491,7 @@ controller_resource_op(pcmk_ipc_api_t *api, const char *op, router_node = target_node; } - msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); + msg_data = pcmk__xe_create(NULL, PCMK__XE_RSC_OP); /* The controller logs the transition key from resource op requests, so we * need to have *something* for it. @@ -494,31 +499,31 @@ controller_resource_op(pcmk_ipc_api_t *api, const char *op, */ key = pcmk__transition_key(0, getpid(), 0, "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx"); - crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key); + crm_xml_add(msg_data, PCMK__XA_TRANSITION_KEY, key); free(key); - crm_xml_add(msg_data, XML_LRM_ATTR_TARGET, target_node); + crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node); if (!pcmk__str_eq(router_node, target_node, pcmk__str_casei)) { - crm_xml_add(msg_data, XML_LRM_ATTR_ROUTER_NODE, router_node); + crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node); } if (cib_only) { // Indicate that only the CIB needs to be cleaned - crm_xml_add(msg_data, PCMK__XA_MODE, XML_TAG_CIB); + crm_xml_add(msg_data, PCMK__XA_MODE, PCMK__VALUE_CIB); } - xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE); - crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id); - crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc_long_id); - crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, standard); - crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, provider); - crm_xml_add(xml_rsc, XML_ATTR_TYPE, type); + xml_rsc = pcmk__xe_create(msg_data, PCMK_XE_PRIMITIVE); + crm_xml_add(xml_rsc, PCMK_XA_ID, rsc_id); + crm_xml_add(xml_rsc, PCMK__XA_LONG_ID, rsc_long_id); + crm_xml_add(xml_rsc, PCMK_XA_CLASS, standard); + crm_xml_add(xml_rsc, PCMK_XA_PROVIDER, provider); + crm_xml_add(xml_rsc, PCMK_XA_TYPE, type); - params = create_xml_node(msg_data, XML_TAG_ATTRS); - crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); + params = pcmk__xe_create(msg_data, PCMK__XE_ATTRIBUTES); + crm_xml_add(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); // The controller parses the timeout from the request - key = crm_meta_name(XML_ATTR_TIMEOUT); + key = crm_meta_name(PCMK_META_TIMEOUT); crm_xml_add(params, key, "60000"); /* 1 minute */ //@TODO pass as arg free(key); @@ -631,17 +636,13 @@ create_hello_message(const char *uuid, const char *client_name, return NULL; } - hello_node = create_xml_node(NULL, XML_TAG_OPTIONS); - if (hello_node == NULL) { - crm_err("Could not create IPC hello message from %s (UUID %s): " - "Message data creation failed", client_name, uuid); - return NULL; - } + hello_node = pcmk__xe_create(NULL, PCMK__XE_OPTIONS); + crm_xml_add(hello_node, PCMK__XA_MAJOR_VERSION, major_version); + crm_xml_add(hello_node, PCMK__XA_MINOR_VERSION, minor_version); + crm_xml_add(hello_node, PCMK__XA_CLIENT_NAME, client_name); - crm_xml_add(hello_node, "major_version", major_version); - crm_xml_add(hello_node, "minor_version", minor_version); - crm_xml_add(hello_node, "client_name", client_name); - crm_xml_add(hello_node, "client_uuid", uuid); + // @TODO Nothing uses this. Drop, or keep for debugging? + crm_xml_add(hello_node, PCMK__XA_CLIENT_UUID, uuid); hello = create_request(CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid); if (hello == NULL) { diff --git a/lib/common/ipc_pacemakerd.c b/lib/common/ipc_pacemakerd.c index 2f03709..7557491 100644 --- a/lib/common/ipc_pacemakerd.c +++ b/lib/common/ipc_pacemakerd.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,6 @@ #include <time.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/ipc_internal.h> @@ -26,13 +25,13 @@ typedef struct pacemakerd_api_private_s { } pacemakerd_api_private_t; static const char *pacemakerd_state_str[] = { - XML_PING_ATTR_PACEMAKERDSTATE_INIT, - XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS, - XML_PING_ATTR_PACEMAKERDSTATE_WAITPING, - XML_PING_ATTR_PACEMAKERDSTATE_RUNNING, - XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN, - XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE, - XML_PING_ATTR_PACEMAKERDSTATE_REMOTE, + PCMK__VALUE_INIT, + PCMK__VALUE_STARTING_DAEMONS, + PCMK__VALUE_WAIT_FOR_PING, + PCMK__VALUE_RUNNING, + PCMK__VALUE_SHUTTING_DOWN, + PCMK__VALUE_SHUTDOWN_COMPLETE, + PCMK_VALUE_REMOTE, }; enum pcmk_pacemakerd_state @@ -180,7 +179,7 @@ post_disconnect(pcmk_ipc_api_t *api) static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { - const char *command = crm_element_value(request, F_CRM_TASK); + const char *command = crm_element_value(request, PCMK__XA_CRM_TASK); if (command == NULL) { return false; @@ -194,6 +193,7 @@ static bool dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { crm_exit_t status = CRM_EX_OK; + xmlNode *wrapper = NULL; xmlNode *msg_data = NULL; pcmk_pacemakerd_api_reply_t reply_data = { pcmk_pacemakerd_reply_unknown @@ -201,51 +201,56 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply) const char *value = NULL; long long value_ll = 0; - if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) { + if (pcmk__xe_is(reply, PCMK__XE_ACK)) { long long int ack_status = 0; - pcmk__scan_ll(crm_element_value(reply, "status"), &ack_status, CRM_EX_OK); + pcmk__scan_ll(crm_element_value(reply, PCMK_XA_STATUS), &ack_status, + CRM_EX_OK); return ack_status == CRM_EX_INDETERMINATE; } - value = crm_element_value(reply, F_CRM_MSG_TYPE); - if (pcmk__str_empty(value) - || !pcmk__str_eq(value, XML_ATTR_RESPONSE, pcmk__str_none)) { - crm_info("Unrecognizable message from pacemakerd: " - "message type '%s' not '" XML_ATTR_RESPONSE "'", - pcmk__s(value, "")); + value = crm_element_value(reply, PCMK__XA_SUBT); + if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) { + crm_info("Unrecognizable message from %s: " + "message type '%s' not '" PCMK__VALUE_RESPONSE "'", + pcmk_ipc_name(api, true), pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } - if (pcmk__str_empty(crm_element_value(reply, XML_ATTR_REFERENCE))) { - crm_info("Unrecognizable message from pacemakerd: no reference"); + if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) { + crm_info("Unrecognizable message from %s: no reference", + pcmk_ipc_name(api, true)); status = CRM_EX_PROTOCOL; goto done; } - value = crm_element_value(reply, F_CRM_TASK); + value = crm_element_value(reply, PCMK__XA_CRM_TASK); // Parse useful info from reply - msg_data = get_message_xml(reply, F_CRM_DATA); - crm_element_value_ll(msg_data, XML_ATTR_TSTAMP, &value_ll); + wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL); + msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + + crm_element_value_ll(msg_data, PCMK_XA_CRM_TIMESTAMP, &value_ll); if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) { reply_data.reply_type = pcmk_pacemakerd_reply_ping; reply_data.data.ping.state = pcmk_pacemakerd_api_daemon_state_text2enum( - crm_element_value(msg_data, XML_PING_ATTR_PACEMAKERDSTATE)); + crm_element_value(msg_data, PCMK__XA_PACEMAKERD_STATE)); reply_data.data.ping.status = - pcmk__str_eq(crm_element_value(msg_data, XML_PING_ATTR_STATUS), "ok", + pcmk__str_eq(crm_element_value(msg_data, PCMK_XA_RESULT), "ok", pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error; reply_data.data.ping.last_good = (value_ll < 0)? 0 : (time_t) value_ll; - reply_data.data.ping.sys_from = crm_element_value(msg_data, - XML_PING_ATTR_SYSFROM); + reply_data.data.ping.sys_from = + crm_element_value(msg_data, PCMK__XA_CRM_SUBSYSTEM); } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) { + const char *op_status = crm_element_value(msg_data, PCMK__XA_OP_STATUS); + reply_data.reply_type = pcmk_pacemakerd_reply_shutdown; - reply_data.data.shutdown.status = atoi(crm_element_value(msg_data, XML_LRM_ATTR_OPSTATUS)); + reply_data.data.shutdown.status = atoi(op_status); } else { - crm_info("Unrecognizable message from pacemakerd: " - "unknown command '%s'", pcmk__s(value, "")); + crm_info("Unrecognizable message from %s: unknown command '%s'", + pcmk_ipc_name(api, true), pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } @@ -292,8 +297,8 @@ do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *ta if (cmd) { rc = pcmk__send_ipc_request(api, cmd); if (rc != pcmk_rc_ok) { - crm_debug("Couldn't send request to pacemakerd: %s rc=%d", - pcmk_rc_str(rc), rc); + crm_debug("Couldn't send request to %s: %s rc=%d", + pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc); } free_xml(cmd); } else { diff --git a/lib/common/ipc_schedulerd.c b/lib/common/ipc_schedulerd.c index cf788e5..45c5803 100644 --- a/lib/common/ipc_schedulerd.c +++ b/lib/common/ipc_schedulerd.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,6 @@ #include <time.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/ipc.h> #include <crm/common/ipc_internal.h> @@ -64,7 +63,7 @@ post_connect(pcmk_ipc_api_t *api) static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { - const char *command = crm_element_value(request, F_CRM_TASK); + const char *command = crm_element_value(request, PCMK__XA_CRM_TASK); if (command == NULL) { return false; @@ -78,39 +77,44 @@ static bool dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { crm_exit_t status = CRM_EX_OK; + xmlNode *wrapper = NULL; xmlNode *msg_data = NULL; pcmk_schedulerd_api_reply_t reply_data = { pcmk_schedulerd_reply_unknown }; const char *value = NULL; - if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_casei)) { + if (pcmk__xe_is(reply, PCMK__XE_ACK)) { return false; } - value = crm_element_value(reply, F_CRM_MSG_TYPE); - if (!pcmk__str_eq(value, XML_ATTR_RESPONSE, pcmk__str_none)) { + value = crm_element_value(reply, PCMK__XA_SUBT); + if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) { crm_info("Unrecognizable message from schedulerd: " - "message type '%s' not '" XML_ATTR_RESPONSE "'", + "message type '%s' not '" PCMK__VALUE_RESPONSE "'", pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } - if (pcmk__str_empty(crm_element_value(reply, XML_ATTR_REFERENCE))) { + if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) { crm_info("Unrecognizable message from schedulerd: no reference"); status = CRM_EX_PROTOCOL; goto done; } // Parse useful info from reply - msg_data = get_message_xml(reply, F_CRM_DATA); - value = crm_element_value(reply, F_CRM_TASK); + wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL); + msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + + value = crm_element_value(reply, PCMK__XA_CRM_TASK); if (pcmk__str_eq(value, CRM_OP_PECALC, pcmk__str_none)) { reply_data.reply_type = pcmk_schedulerd_reply_graph; - reply_data.data.graph.reference = crm_element_value(reply, XML_ATTR_REFERENCE); - reply_data.data.graph.input = crm_element_value(reply, F_CRM_TGRAPH_INPUT); + reply_data.data.graph.reference = crm_element_value(reply, + PCMK_XA_REFERENCE); + reply_data.data.graph.input = crm_element_value(reply, + PCMK__XA_CRM_TGRAPH_IN); reply_data.data.graph.tgraph = msg_data; } else { crm_info("Unrecognizable message from schedulerd: " @@ -164,7 +168,7 @@ do_schedulerd_api_call(pcmk_ipc_api_t *api, const char *task, xmlNode *cib, char pcmk_rc_str(rc), rc); } - *ref = strdup(crm_element_value(cmd, F_CRM_REFERENCE)); + *ref = strdup(crm_element_value(cmd, PCMK_XA_REFERENCE)); free_xml(cmd); } else { rc = ENOMSG; diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index 5cd7e70..a9201b9 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.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. * @@ -16,7 +16,7 @@ #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/ipc_internal.h> #include "crmcommon_private.h" @@ -158,23 +158,17 @@ pcmk__drop_all_clients(qb_ipcs_service_t *service) * \param[in] key Connection table key (NULL to use sane default) * \param[in] uid_client UID corresponding to c (ignored if c is NULL) * - * \return Pointer to new pcmk__client_t (or NULL on error) + * \return Pointer to new pcmk__client_t (guaranteed not to be \c NULL) */ static pcmk__client_t * client_from_connection(qb_ipcs_connection_t *c, void *key, uid_t uid_client) { - pcmk__client_t *client = calloc(1, sizeof(pcmk__client_t)); - - if (client == NULL) { - crm_perror(LOG_ERR, "Allocating client"); - return NULL; - } + pcmk__client_t *client = pcmk__assert_alloc(1, sizeof(pcmk__client_t)); if (c) { client->user = pcmk__uid2username(uid_client); if (client->user == NULL) { - client->user = strdup("#unprivileged"); - CRM_CHECK(client->user != NULL, free(client); return NULL); + client->user = pcmk__str_copy("#unprivileged"); crm_err("Unable to enforce ACLs for user ID %d, assuming unprivileged", uid_client); } @@ -208,10 +202,7 @@ client_from_connection(qb_ipcs_connection_t *c, void *key, uid_t uid_client) pcmk__client_t * pcmk__new_unauth_client(void *key) { - pcmk__client_t *client = client_from_connection(NULL, key, 0); - - CRM_ASSERT(client != NULL); - return client; + return client_from_connection(NULL, key, 0); } pcmk__client_t * @@ -242,9 +233,6 @@ pcmk__new_client(qb_ipcs_connection_t *c, uid_t uid_client, gid_t gid_client) /* TODO: Do our own auth checking, return NULL if unauthorized */ client = client_from_connection(c, NULL, uid_client); - if (client == NULL) { - return NULL; - } if ((uid_client == 0) || (uid_client == uid_cluster)) { /* Remember when a connection came from root or hacluster */ @@ -259,10 +247,7 @@ pcmk__new_client(qb_ipcs_connection_t *c, uid_t uid_client, gid_t gid_client) static struct iovec * pcmk__new_ipc_event(void) { - struct iovec *iov = calloc(2, sizeof(struct iovec)); - - CRM_ASSERT(iov != NULL); - return iov; + return (struct iovec *) pcmk__assert_alloc(2, sizeof(struct iovec)); } /*! @@ -331,6 +316,14 @@ pcmk__free_client(pcmk__client_t *c) if (c->remote->auth_timeout) { g_source_remove(c->remote->auth_timeout); } +#ifdef HAVE_GNUTLS_GNUTLS_H + if (c->remote->tls_session != NULL) { + /* @TODO Reduce duplication at callers. Put here everything + * necessary to tear down and free tls_session. + */ + gnutls_free(c->remote->tls_session); + } +#endif // HAVE_GNUTLS_GNUTLS_H free(c->remote->buffer); free(c->remote); } @@ -413,7 +406,7 @@ pcmk__client_data2xml(pcmk__client_t *c, void *data, uint32_t *id, if (header->size_compressed) { int rc = 0; unsigned int size_u = 1 + header->size_uncompressed; - uncompressed = calloc(1, size_u); + uncompressed = pcmk__assert_alloc(1, size_u); crm_trace("Decompressing message data %u bytes into %u bytes", header->size_compressed, size_u); @@ -433,7 +426,7 @@ pcmk__client_data2xml(pcmk__client_t *c, void *data, uint32_t *id, CRM_ASSERT(text[header->size_uncompressed - 1] == 0); - xml = string2xml(text); + xml = pcmk__xml_parse(text); crm_log_xml_trace(xml, "[IPC received]"); free(uncompressed); @@ -583,23 +576,25 @@ pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message, uint32_t max_send_size, struct iovec **result, ssize_t *bytes) { - static unsigned int biggest = 0; struct iovec *iov; unsigned int total = 0; - char *compressed = NULL; - char *buffer = NULL; + GString *buffer = NULL; pcmk__ipc_header_t *header = NULL; + int rc = pcmk_rc_ok; if ((message == NULL) || (result == NULL)) { - return EINVAL; + rc = EINVAL; + goto done; } header = calloc(1, sizeof(pcmk__ipc_header_t)); if (header == NULL) { - return ENOMEM; /* errno mightn't be set by allocator */ + rc = ENOMEM; + goto done; } - buffer = dump_xml_unformatted(message); + buffer = g_string_sized_new(1024); + pcmk__xml_string(message, 0, buffer, 0); if (max_send_size == 0) { max_send_size = crm_ipc_default_buffer_size(); @@ -612,17 +607,21 @@ pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message, iov[0].iov_base = header; header->version = PCMK__IPC_VERSION; - header->size_uncompressed = 1 + strlen(buffer); + header->size_uncompressed = 1 + buffer->len; total = iov[0].iov_len + header->size_uncompressed; if (total < max_send_size) { - iov[1].iov_base = buffer; + iov[1].iov_base = pcmk__str_copy(buffer->str); iov[1].iov_len = header->size_uncompressed; } else { + static unsigned int biggest = 0; + + char *compressed = NULL; unsigned int new_size = 0; - if (pcmk__compress(buffer, (unsigned int) header->size_uncompressed, + if (pcmk__compress(buffer->str, + (unsigned int) header->size_uncompressed, (unsigned int) max_send_size, &compressed, &new_size) == pcmk_rc_ok) { @@ -632,8 +631,6 @@ pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message, iov[1].iov_len = header->size_compressed; iov[1].iov_base = compressed; - free(buffer); - biggest = QB_MAX(header->size_compressed, biggest); } else { @@ -646,9 +643,9 @@ pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message, header->size_uncompressed, max_send_size, 4 * biggest); free(compressed); - free(buffer); pcmk_free_ipc_event(iov); - return EMSGSIZE; + rc = EMSGSIZE; + goto done; } } @@ -660,7 +657,12 @@ pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message, if (bytes != NULL) { *bytes = header->qb.size; } - return pcmk_rc_ok; + +done: + if (buffer != NULL) { + g_string_free(buffer, TRUE); + } + return rc; } int @@ -786,10 +788,10 @@ pcmk__ipc_create_ack_as(const char *function, int line, uint32_t flags, xmlNode *ack = NULL; if (pcmk_is_set(flags, crm_ipc_client_response)) { - ack = create_xml_node(NULL, tag); - crm_xml_add(ack, "function", function); - crm_xml_add_int(ack, "line", line); - crm_xml_add_int(ack, "status", (int) status); + ack = pcmk__xe_create(NULL, tag); + crm_xml_add(ack, PCMK_XA_FUNCTION, function); + crm_xml_add_int(ack, PCMK__XA_LINE, line); + crm_xml_add_int(ack, PCMK_XA_STATUS, (int) status); crm_xml_add(ack, PCMK__XA_IPC_PROTO_VERSION, ver); } return ack; @@ -911,7 +913,7 @@ void pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { - *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb); + *ipcs = mainloop_add_ipc_server(PCMK__VALUE_ATTRD, QB_IPC_NATIVE, cb); if (*ipcs == NULL) { crm_err("Failed to create pacemaker-attrd server: exiting and inhibiting respawn"); diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c index 9de018f..d24f268 100644 --- a/lib/common/iso8601.c +++ b/lib/common/iso8601.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2022 the Pacemaker project contributors + * Copyright 2005-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,9 +18,12 @@ #include <time.h> #include <ctype.h> #include <inttypes.h> +#include <limits.h> // INT_MIN, INT_MAX #include <string.h> #include <stdbool.h> #include <crm/common/iso8601.h> +#include <crm/common/iso8601_internal.h> +#include "crmcommon_private.h" /* * Andrew's code was originally written for OSes whose "struct tm" contains: @@ -125,10 +128,7 @@ crm_time_new(const char *date_time) crm_time_t * crm_time_new_undefined(void) { - crm_time_t *result = calloc(1, sizeof(crm_time_t)); - - CRM_ASSERT(result != NULL); - return result; + return (crm_time_t *) pcmk__assert_alloc(1, sizeof(crm_time_t)); } /*! @@ -623,7 +623,9 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags, if (pcmk_is_set(flags, crm_time_log_date)) { if (pcmk_is_set(flags, crm_time_weeks)) { // YYYY-WW-D - uint32_t y, w, d; + uint32_t y = 0; + uint32_t w = 0; + uint32_t d = 0; if (crm_time_get_isoweek(dt, &y, &w, &d)) { offset += snprintf(result + offset, DATE_MAX - offset, @@ -632,7 +634,8 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags, } } else if (pcmk_is_set(flags, crm_time_ordinal)) { // YYYY-DDD - uint32_t y, d; + uint32_t y = 0; + uint32_t d = 0; if (crm_time_get_ordinal(dt, &y, &d)) { offset += snprintf(result + offset, DATE_MAX - offset, @@ -640,7 +643,9 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags, } } else { // YYYY-MM-DD - uint32_t y, m, d; + uint32_t y = 0; + uint32_t m = 0; + uint32_t d = 0; if (crm_time_get_gregorian(dt, &y, &m, &d)) { offset += snprintf(result + offset, DATE_MAX - offset, @@ -694,12 +699,9 @@ char * crm_time_as_string(const crm_time_t *dt, int flags) { char result[DATE_MAX] = { '\0', }; - char *result_copy = NULL; time_as_string_common(dt, 0, flags, result); - - pcmk__str_update(&result_copy, result); - return result_copy; + return pcmk__str_copy(result); } /*! @@ -864,7 +866,8 @@ crm_time_parse(const char *time_str, crm_time_t *a_time) * \internal * \brief Parse a time object from an ISO 8601 date/time specification * - * \param[in] date_str ISO 8601 date/time specification (or "epoch") + * \param[in] date_str ISO 8601 date/time specification (or + * \c PCMK__VALUE_EPOCH) * * \return New time object on success, NULL (and set errno) otherwise */ @@ -898,8 +901,10 @@ parse_date(const char *date_str) dt = crm_time_new_undefined(); - if (!strncasecmp("epoch", date_str, 5) - && ((date_str[5] == '\0') || (date_str[5] == '/') || isspace(date_str[5]))) { + if ((strncasecmp(PCMK__VALUE_EPOCH, date_str, 5) == 0) + && ((date_str[5] == '\0') + || (date_str[5] == '/') + || isspace(date_str[5]))) { dt->days = 1; dt->years = 1970; crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday); @@ -1211,8 +1216,7 @@ crm_time_parse_period(const char *period_str) } tzset(); - period = calloc(1, sizeof(crm_time_period_t)); - CRM_ASSERT(period != NULL); + period = pcmk__assert_alloc(1, sizeof(crm_time_period_t)); if (period_str[0] == 'P') { period->diff = crm_time_parse_duration(period_str); @@ -1370,6 +1374,23 @@ crm_time_set_timet(crm_time_t *target, const time_t *source) ha_set_tm_time(target, localtime(source)); } +/*! + * \internal + * \brief Set one time object to another if the other is earlier + * + * \param[in,out] target Time object to set + * \param[in] source Time object to use if earlier + */ +void +pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source) +{ + if ((target != NULL) && (source != NULL) + && (!crm_time_is_defined(target) + || (crm_time_compare(source, target) < 0))) { + crm_time_set(target, source); + } +} + crm_time_t * pcmk_copy_time(const crm_time_t *source) { @@ -1424,6 +1445,127 @@ crm_time_add(const crm_time_t *dt, const crm_time_t *value) return answer; } +/*! + * \internal + * \brief Return the XML attribute name corresponding to a time component + * + * \param[in] component Component to check + * + * \return XML attribute name corresponding to \p component, or NULL if + * \p component is invalid + */ +const char * +pcmk__time_component_attr(enum pcmk__time_component component) +{ + switch (component) { + case pcmk__time_years: + return PCMK_XA_YEARS; + + case pcmk__time_months: + return PCMK_XA_MONTHS; + + case pcmk__time_weeks: + return PCMK_XA_WEEKS; + + case pcmk__time_days: + return PCMK_XA_DAYS; + + case pcmk__time_hours: + return PCMK_XA_HOURS; + + case pcmk__time_minutes: + return PCMK_XA_MINUTES; + + case pcmk__time_seconds: + return PCMK_XA_SECONDS; + + default: + return NULL; + } +} + +typedef void (*component_fn_t)(crm_time_t *, int); + +/*! + * \internal + * \brief Get the addition function corresponding to a time component + * \param[in] component Component to check + * + * \return Addition function corresponding to \p component, or NULL if + * \p component is invalid + */ +static component_fn_t +component_fn(enum pcmk__time_component component) +{ + switch (component) { + case pcmk__time_years: + return crm_time_add_years; + + case pcmk__time_months: + return crm_time_add_months; + + case pcmk__time_weeks: + return crm_time_add_weeks; + + case pcmk__time_days: + return crm_time_add_days; + + case pcmk__time_hours: + return crm_time_add_hours; + + case pcmk__time_minutes: + return crm_time_add_minutes; + + case pcmk__time_seconds: + return crm_time_add_seconds; + + default: + return NULL; + } + +} + +/*! + * \internal + * \brief Add the value of an XML attribute to a time object + * + * \param[in,out] t Time object to add to + * \param[in] component Component of \p t to add to + * \param[in] xml XML with value to add + * + * \return Standard Pacemaker return code + */ +int +pcmk__add_time_from_xml(crm_time_t *t, enum pcmk__time_component component, + const xmlNode *xml) +{ + long long value; + const char *attr = pcmk__time_component_attr(component); + component_fn_t add = component_fn(component); + + if ((t == NULL) || (attr == NULL) || (add == NULL)) { + return EINVAL; + } + + if (xml == NULL) { + return pcmk_rc_ok; + } + + if (pcmk__scan_ll(crm_element_value(xml, attr), &value, + 0LL) != pcmk_rc_ok) { + return pcmk_rc_unpack_error; + } + + if ((value < INT_MIN) || (value > INT_MAX)) { + return ERANGE; + } + + if (value != 0LL) { + add(t, (int) value); + } + return pcmk_rc_ok; +} + crm_time_t * crm_time_calculate_duration(const crm_time_t *dt, const crm_time_t *value) { @@ -1690,8 +1832,11 @@ pcmk__time_hr_convert(pcmk__time_hr_t *target, const crm_time_t *dt) pcmk__time_hr_t *hr_dt = NULL; if (dt) { - hr_dt = target?target:calloc(1, sizeof(pcmk__time_hr_t)); - CRM_ASSERT(hr_dt != NULL); + hr_dt = target; + if (hr_dt == NULL) { + hr_dt = pcmk__assert_alloc(1, sizeof(pcmk__time_hr_t)); + } + *hr_dt = (pcmk__time_hr_t) { .years = dt->years, .months = dt->months, @@ -1772,12 +1917,21 @@ pcmk__time_hr_free(pcmk__time_hr_t * hr_dt) char * pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) { - const char *mark_s; - int max = 128, scanned_pos = 0, printed_pos = 0, fmt_pos = 0, - date_len = 0, nano_digits = 0; - char nano_s[10], date_s[max+1], nanofmt_s[5] = "%", *tmp_fmt_s; - struct tm tm; - crm_time_t dt; +#define DATE_LEN_MAX 128 + const char *mark_s = NULL; + int scanned_pos = 0; + int printed_pos = 0; + int fmt_pos = 0; + size_t date_len = 0; + int nano_digits = 0; + + char nano_s[10] = { '\0', }; + char date_s[DATE_LEN_MAX] = { '\0', }; + char nanofmt_s[5] = "%"; + char *tmp_fmt_s = NULL; + + struct tm tm = { 0, }; + crm_time_t dt = { 0, }; if (!format) { return NULL; @@ -1818,7 +1972,8 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - date_len += strftime(&date_s[date_len], max-date_len, tmp_fmt_s, &tm); + date_len += strftime(&date_s[date_len], DATE_LEN_MAX - date_len, + tmp_fmt_s, &tm); #ifdef HAVE_FORMAT_NONLITERAL #pragma GCC diagnostic pop #endif @@ -1829,7 +1984,7 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - date_len += snprintf(&date_s[date_len], max-date_len, + date_len += snprintf(&date_s[date_len], DATE_LEN_MAX - date_len, nanofmt_s, nano_s); #ifdef HAVE_FORMAT_NONLITERAL #pragma GCC diagnostic pop @@ -1839,6 +1994,7 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) } return (date_len == 0)?NULL:strdup(date_s); +#undef DATE_LEN_MAX } /*! @@ -1858,22 +2014,15 @@ char * pcmk__epoch2str(const time_t *source, uint32_t flags) { time_t epoch_time = (source == NULL)? time(NULL) : *source; - char *result = NULL; if (flags == 0) { - const char *buf = pcmk__trim(ctime(&epoch_time)); - - if (buf != NULL) { - result = strdup(buf); - CRM_ASSERT(result != NULL); - } + return pcmk__str_copy(pcmk__trim(ctime(&epoch_time))); } else { crm_time_t dt; crm_time_set_timet(&dt, &epoch_time); - result = crm_time_as_string(&dt, flags); + return crm_time_as_string(&dt, flags); } - return result; } /*! @@ -1899,7 +2048,6 @@ pcmk__timespec2str(const struct timespec *ts, uint32_t flags) struct timespec tmp_ts; crm_time_t dt; char result[DATE_MAX] = { 0 }; - char *result_copy = NULL; if (ts == NULL) { qb_util_timespec_from_epoch_get(&tmp_ts); @@ -1907,8 +2055,7 @@ pcmk__timespec2str(const struct timespec *ts, uint32_t flags) } crm_time_set_timet(&dt, &ts->tv_sec); time_as_string_common(&dt, ts->tv_nsec / QB_TIME_NS_IN_USEC, flags, result); - pcmk__str_update(&result_copy, result); - return result_copy; + return pcmk__str_copy(result); } /*! @@ -1934,24 +2081,24 @@ pcmk__readable_interval(guint interval_ms) int offset = 0; str[0] = '\0'; - if (interval_ms > MS_IN_D) { + if (interval_ms >= MS_IN_D) { offset += snprintf(str + offset, MAXSTR - offset, "%ud", interval_ms / MS_IN_D); interval_ms -= (interval_ms / MS_IN_D) * MS_IN_D; } - if (interval_ms > MS_IN_H) { + if (interval_ms >= MS_IN_H) { offset += snprintf(str + offset, MAXSTR - offset, "%uh", interval_ms / MS_IN_H); interval_ms -= (interval_ms / MS_IN_H) * MS_IN_H; } - if (interval_ms > MS_IN_M) { + if (interval_ms >= MS_IN_M) { offset += snprintf(str + offset, MAXSTR - offset, "%um", interval_ms / MS_IN_M); interval_ms -= (interval_ms / MS_IN_M) * MS_IN_M; } // Ns, N.NNNs, or NNNms - if (interval_ms > MS_IN_S) { + if (interval_ms >= MS_IN_S) { offset += snprintf(str + offset, MAXSTR - offset, "%u", interval_ms / MS_IN_S); interval_ms -= (interval_ms / MS_IN_S) * MS_IN_S; diff --git a/lib/common/logging.c b/lib/common/logging.c index 7768c35..efdbac3 100644 --- a/lib/common/logging.c +++ b/lib/common/logging.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. * @@ -192,7 +192,7 @@ set_format_string(int method, const char *daemon, pid_t use_pid, static bool logfile_disabled(const char *filename) { - return pcmk__str_eq(filename, PCMK__VALUE_NONE, pcmk__str_casei) + return pcmk__str_eq(filename, PCMK_VALUE_NONE, pcmk__str_casei) || pcmk__str_eq(filename, "/dev/null", pcmk__str_none); } @@ -785,7 +785,7 @@ set_identity(const char *entity, int argc, char *const *argv) } if (entity != NULL) { - crm_system_name = strdup(entity); + crm_system_name = pcmk__str_copy(entity); } else if ((argc > 0) && (argv != NULL)) { char *mutable = strdup(argv[0]); @@ -794,15 +794,13 @@ set_identity(const char *entity, int argc, char *const *argv) if (strstr(modified, "lt-") == modified) { modified += 3; } - crm_system_name = strdup(modified); + crm_system_name = pcmk__str_copy(modified); free(mutable); } else { - crm_system_name = strdup("Unknown"); + crm_system_name = pcmk__str_copy("Unknown"); } - CRM_ASSERT(crm_system_name != NULL); - // Used by fencing.py.py (in fence-agents) pcmk__set_env_option(PCMK__ENV_SERVICE, crm_system_name, false); } @@ -915,12 +913,12 @@ crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_std if (pcmk__is_daemon) { facility = "daemon"; } else { - facility = PCMK__VALUE_NONE; + facility = PCMK_VALUE_NONE; } pcmk__set_env_option(PCMK__ENV_LOGFACILITY, facility, true); } - if (pcmk__str_eq(facility, PCMK__VALUE_NONE, pcmk__str_casei)) { + if (pcmk__str_eq(facility, PCMK_VALUE_NONE, pcmk__str_casei)) { quiet = TRUE; @@ -957,7 +955,7 @@ crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_std { const char *logfile = pcmk__env_option(PCMK__ENV_LOGFILE); - if (!pcmk__str_eq(PCMK__VALUE_NONE, logfile, pcmk__str_casei) + if (!pcmk__str_eq(PCMK_VALUE_NONE, logfile, pcmk__str_casei) && (pcmk__is_daemon || (logfile != NULL))) { // Daemons always get a log file, unless explicitly set to "none" pcmk__add_logfile(logfile); @@ -1296,4 +1294,4 @@ void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler, { pcmk__config_warning_handler = warning_handler; pcmk__config_warning_context = warning_context; -}
\ No newline at end of file +} diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index f971713..7626134 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.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. * @@ -191,7 +191,6 @@ mainloop_add_trigger(int priority, int (*dispatch) (gpointer user_data), CRM_ASSERT(sizeof(crm_trigger_t) > sizeof(GSource)); source = g_source_new(&crm_trigger_funcs, sizeof(crm_trigger_t)); - CRM_ASSERT(source != NULL); return mainloop_setup_trigger(source, priority, dispatch, userdata); } @@ -1256,7 +1255,7 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *pr void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)) { static bool need_init = TRUE; - mainloop_child_t *child = calloc(1, sizeof(mainloop_child_t)); + mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); child->pid = pid; child->timerid = 0; @@ -1264,7 +1263,7 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *pr child->privatedata = privatedata; child->callback = callback; child->flags = flags; - pcmk__str_update(&child->desc, desc); + child->desc = pcmk__str_copy(desc); if (timeout) { child->timerid = g_timeout_add(timeout, child_timeout_callback, child); @@ -1368,21 +1367,19 @@ mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms) mainloop_timer_t * mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata) { - mainloop_timer_t *t = calloc(1, sizeof(mainloop_timer_t)); + mainloop_timer_t *t = pcmk__assert_alloc(1, sizeof(mainloop_timer_t)); - if(t) { - if(name) { - t->name = crm_strdup_printf("%s-%u-%d", name, period_ms, repeat); - } else { - t->name = crm_strdup_printf("%p-%u-%d", t, period_ms, repeat); - } - t->id = 0; - t->period_ms = period_ms; - t->repeat = repeat; - t->cb = cb; - t->userdata = userdata; - crm_trace("Created timer %s with %p %p", t->name, userdata, t->userdata); - } + if (name != NULL) { + t->name = crm_strdup_printf("%s-%u-%d", name, period_ms, repeat); + } else { + t->name = crm_strdup_printf("%p-%u-%d", t, period_ms, repeat); + } + t->id = 0; + t->period_ms = period_ms; + t->repeat = repeat; + t->cb = cb; + t->userdata = userdata; + crm_trace("Created timer %s with %p %p", t->name, userdata, t->userdata); return t; } diff --git a/lib/common/messages.c b/lib/common/messages.c index 2c01eed..fa21169 100644 --- a/lib/common/messages.c +++ b/lib/common/messages.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ #include <glib.h> #include <libxml/tree.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/xml_internal.h> /*! @@ -61,23 +61,25 @@ create_request_adv(const char *task, xmlNode *msg_data, } // host_from will get set for us if necessary by the controller when routed - request = create_xml_node(NULL, __func__); - crm_xml_add(request, F_CRM_ORIGIN, origin); - crm_xml_add(request, F_TYPE, T_CRM); - crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET); - crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST); - crm_xml_add(request, F_CRM_REFERENCE, reference); - crm_xml_add(request, F_CRM_TASK, task); - crm_xml_add(request, F_CRM_SYS_TO, sys_to); - crm_xml_add(request, F_CRM_SYS_FROM, true_from); + request = pcmk__xe_create(NULL, __func__); + crm_xml_add(request, PCMK_XA_ORIGIN, origin); + crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_CRMD); + crm_xml_add(request, PCMK_XA_VERSION, CRM_FEATURE_SET); + crm_xml_add(request, PCMK__XA_SUBT, PCMK__VALUE_REQUEST); + crm_xml_add(request, PCMK_XA_REFERENCE, reference); + crm_xml_add(request, PCMK__XA_CRM_TASK, task); + crm_xml_add(request, PCMK__XA_CRM_SYS_TO, sys_to); + crm_xml_add(request, PCMK__XA_CRM_SYS_FROM, true_from); /* HOSTTO will be ignored if it is to the DC anyway. */ if (host_to != NULL && strlen(host_to) > 0) { - crm_xml_add(request, F_CRM_HOST_TO, host_to); + crm_xml_add(request, PCMK__XA_CRM_HOST_TO, host_to); } if (msg_data != NULL) { - add_message_xml(request, F_CRM_DATA, msg_data); + xmlNode *wrapper = pcmk__xe_create(request, PCMK__XE_CRM_XML); + + pcmk__xml_copy(wrapper, msg_data); } free(reference); free(true_from); @@ -104,67 +106,56 @@ create_reply_adv(const xmlNode *original_request, xmlNode *xml_response_data, { xmlNode *reply = NULL; - const char *host_from = crm_element_value(original_request, F_CRM_HOST_FROM); - const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM); - const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO); - const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE); - const char *operation = crm_element_value(original_request, F_CRM_TASK); - const char *crm_msg_reference = crm_element_value(original_request, F_CRM_REFERENCE); + const char *host_from = crm_element_value(original_request, PCMK__XA_SRC); + const char *sys_from = crm_element_value(original_request, + PCMK__XA_CRM_SYS_FROM); + const char *sys_to = crm_element_value(original_request, + PCMK__XA_CRM_SYS_TO); + const char *type = crm_element_value(original_request, PCMK__XA_SUBT); + const char *operation = crm_element_value(original_request, + PCMK__XA_CRM_TASK); + const char *crm_msg_reference = crm_element_value(original_request, + PCMK_XA_REFERENCE); if (type == NULL) { crm_err("Cannot create new_message, no message type in original message"); CRM_ASSERT(type != NULL); return NULL; -#if 0 - } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) { - crm_err("Cannot create new_message, original message was not a request"); - return NULL; -#endif } - reply = create_xml_node(NULL, __func__); - if (reply == NULL) { - crm_err("Cannot create new_message, malloc failed"); - return NULL; + + if (strcmp(type, PCMK__VALUE_REQUEST) != 0) { + /* Replies should only be generated for request messages, but it's possible + * we expect replies to other messages right now so this can't be enforced. + */ + crm_trace("Creating a reply for a non-request original message"); } - crm_xml_add(reply, F_CRM_ORIGIN, origin); - crm_xml_add(reply, F_TYPE, T_CRM); - crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET); - crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE); - crm_xml_add(reply, F_CRM_REFERENCE, crm_msg_reference); - crm_xml_add(reply, F_CRM_TASK, operation); + reply = pcmk__xe_create(NULL, __func__); + crm_xml_add(reply, PCMK_XA_ORIGIN, origin); + crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_CRMD); + crm_xml_add(reply, PCMK_XA_VERSION, CRM_FEATURE_SET); + crm_xml_add(reply, PCMK__XA_SUBT, PCMK__VALUE_RESPONSE); + crm_xml_add(reply, PCMK_XA_REFERENCE, crm_msg_reference); + crm_xml_add(reply, PCMK__XA_CRM_TASK, operation); /* since this is a reply, we reverse the from and to */ - crm_xml_add(reply, F_CRM_SYS_TO, sys_from); - crm_xml_add(reply, F_CRM_SYS_FROM, sys_to); + crm_xml_add(reply, PCMK__XA_CRM_SYS_TO, sys_from); + crm_xml_add(reply, PCMK__XA_CRM_SYS_FROM, sys_to); /* HOSTTO will be ignored if it is to the DC anyway. */ if (host_from != NULL && strlen(host_from) > 0) { - crm_xml_add(reply, F_CRM_HOST_TO, host_from); + crm_xml_add(reply, PCMK__XA_CRM_HOST_TO, host_from); } if (xml_response_data != NULL) { - add_message_xml(reply, F_CRM_DATA, xml_response_data); + xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CRM_XML); + + pcmk__xml_copy(wrapper, xml_response_data); } return reply; } -xmlNode * -get_message_xml(const xmlNode *msg, const char *field) -{ - return pcmk__xml_first_child(first_named_child(msg, field)); -} - -gboolean -add_message_xml(xmlNode *msg, const char *field, xmlNode *xml) -{ - xmlNode *holder = create_xml_node(msg, field); - - add_node_copy(holder, xml); - return TRUE; -} - /*! * \brief Get name to be used as identifier for cluster messages * @@ -289,3 +280,28 @@ pcmk__reset_request(pcmk__request_t *request) pcmk__reset_result(&(request->result)); } + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/common/xml_compat.h> + +gboolean +add_message_xml(xmlNode *msg, const char *field, xmlNode *xml) +{ + xmlNode *holder = pcmk__xe_create(msg, field); + + pcmk__xml_copy(holder, xml); + return TRUE; +} + +xmlNode * +get_message_xml(const xmlNode *msg, const char *field) +{ + xmlNode *child = pcmk__xe_first_child(msg, field, NULL, NULL); + + return pcmk__xe_first_child(child, NULL, NULL, NULL); +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/common/mock.c b/lib/common/mock.c index 6f837ad..43c6e8f 100644 --- a/lib/common/mock.c +++ b/lib/common/mock.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -25,6 +25,7 @@ #include <grp.h> #include <cmocka.h> +#include <crm/common/unittest_internal.h> #include "mock_private.h" /* This file is only used when running "make check". It is built into @@ -54,6 +55,27 @@ */ // LCOV_EXCL_START + +/* abort() + * + * Always mock abort - there's no pcmk__mock_abort tuneable to control this. + * Because abort calls _exit(), which doesn't run any of the things registered + * with atexit(), coverage numbers do not get written out. This most noticably + * affects places where we are testing that things abort when they should. + * + * The solution is this wrapper that is always enabled when we are running + * unit tests (mock.c does not get included for the regular libcrmcommon.so). + * All it does is dump coverage data and call the real abort(). + */ +_Noreturn void +__wrap_abort(void) +{ +#if (PCMK__WITH_COVERAGE == 1) + __gcov_dump(); +#endif + __real_abort(); +} + /* calloc() * * If pcmk__mock_calloc is set to true, later calls to calloc() will return @@ -103,6 +125,31 @@ __wrap_getenv(const char *name) } +/* realloc() + * + * If pcmk__mock_realloc is set to true, later calls to realloc() will return + * NULL and must be preceded by: + * + * expect_*(__wrap_realloc, ptr[, ...]); + * expect_*(__wrap_realloc, size[, ...]); + * + * expect_* functions: https://api.cmocka.org/group__cmocka__param.html + */ + +bool pcmk__mock_realloc = false; + +void * +__wrap_realloc(void *ptr, size_t size) +{ + if (!pcmk__mock_realloc) { + return __real_realloc(ptr, size); + } + check_expected_ptr(ptr); + check_expected(size); + return NULL; +} + + /* setenv() * * If pcmk__mock_setenv is set to true, later calls to setenv() must be preceded @@ -412,40 +459,4 @@ __wrap_strdup(const char *s) return NULL; } - -/* uname() - * - * If pcmk__mock_uname is set to true, later calls to uname() must be preceded - * by: - * - * expect_*(__wrap_uname, buf[, ...]); - * will_return(__wrap_uname, return_value); - * will_return(__wrap_uname, node_name_for_buf_parameter_to_uname); - * - * expect_* functions: https://api.cmocka.org/group__cmocka__param.html - */ - -bool pcmk__mock_uname = false; - -int -__wrap_uname(struct utsname *buf) -{ - if (pcmk__mock_uname) { - int retval = 0; - char *result = NULL; - - check_expected_ptr(buf); - retval = mock_type(int); - result = mock_ptr_type(char *); - - if (result != NULL) { - strcpy(buf->nodename, result); - } - return retval; - - } else { - return __real_uname(buf); - } -} - // LCOV_EXCL_STOP diff --git a/lib/common/mock_private.h b/lib/common/mock_private.h index b0e0ed2..3beeda4 100644 --- a/lib/common/mock_private.h +++ b/lib/common/mock_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,6 +22,9 @@ /* This header is for the sole use of libcrmcommon_test and unit tests */ +_Noreturn void __real_abort(void); +_Noreturn void __wrap_abort(void); + extern bool pcmk__mock_calloc; void *__real_calloc(size_t nmemb, size_t size); void *__wrap_calloc(size_t nmemb, size_t size); @@ -38,6 +41,10 @@ extern bool pcmk__mock_getenv; char *__real_getenv(const char *name); char *__wrap_getenv(const char *name); +extern bool pcmk__mock_realloc; +void *__real_realloc(void *ptr, size_t size); +void *__wrap_realloc(void *ptr, size_t size); + extern bool pcmk__mock_setenv; int __real_setenv(const char *name, const char *value, int overwrite); int __wrap_setenv(const char *name, const char *value, int overwrite); @@ -74,8 +81,4 @@ extern bool pcmk__mock_strdup; char *__real_strdup(const char *s); char *__wrap_strdup(const char *s); -extern bool pcmk__mock_uname; -int __real_uname(struct utsname *buf); -int __wrap_uname(struct utsname *buf); - #endif // MOCK_PRIVATE__H diff --git a/lib/common/nodes.c b/lib/common/nodes.c index a17d587..4edeafb 100644 --- a/lib/common/nodes.c +++ b/lib/common/nodes.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,16 +9,155 @@ #include <crm_internal.h> +#include <libxml/tree.h> // xmlNode #include <crm/common/nvpair.h> +/*! + * \internal + * \brief Check whether a node is online + * + * \param[in] node Node to check + * + * \return true if \p node is online, otherwise false + */ +bool +pcmk_node_is_online(const pcmk_node_t *node) +{ + return (node != NULL) && node->details->online; +} + +/*! + * \internal + * \brief Check whether a node is pending + * + * Check whether a node is pending. A node is pending if it is a member of the + * cluster but not the controller group, which means it is in the process of + * either joining or leaving the cluster. + * + * \param[in] node Node to check + * + * \return true if \p node is pending, otherwise false + */ +bool +pcmk_node_is_pending(const pcmk_node_t *node) +{ + return (node != NULL) && node->details->pending; +} + +/*! + * \internal + * \brief Check whether a node is clean + * + * Check whether a node is clean. A node is clean if it is a cluster node or + * remote node that has been seen by the cluster at least once, or the + * startup-fencing cluster option is false; and the node, and its host if a + * guest or bundle node, are not scheduled to be fenced. + * + * \param[in] node Node to check + * + * \return true if \p node is clean, otherwise false + */ +bool +pcmk_node_is_clean(const pcmk_node_t *node) +{ + return (node != NULL) && !(node->details->unclean); +} + +/*! + * \internal + * \brief Check whether a node is shutting down + * + * \param[in] node Node to check + * + * \return true if \p node is shutting down, otherwise false + */ +bool +pcmk_node_is_shutting_down(const pcmk_node_t *node) +{ + return (node != NULL) && node->details->shutdown; +} + +/*! + * \internal + * \brief Check whether a node is in maintenance mode + * + * \param[in] node Node to check + * + * \return true if \p node is in maintenance mode, otherwise false + */ +bool +pcmk_node_is_in_maintenance(const pcmk_node_t *node) +{ + return (node != NULL) && node->details->maintenance; +} + +/*! + * \internal + * \brief Call a function for each resource active on a node + * + * Call a caller-supplied function with a caller-supplied argument for each + * resource that is active on a given node. If the function returns false, this + * function will return immediately without processing any remaining resources. + * + * \param[in] node Node to check + * + * \return Result of last call of \p fn (or false if none) + */ +bool +pcmk_foreach_active_resource(pcmk_node_t *node, + bool (*fn)(pcmk_resource_t *, void *), + void *user_data) +{ + bool result = false; + + if ((node != NULL) && (fn != NULL)) { + for (GList *item = node->details->running_rsc; item != NULL; + item = item->next) { + + result = fn((pcmk_resource_t *) item->data, user_data); + if (!result) { + break; + } + } + } + return result; +} + void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid) { + CRM_ASSERT(xml != NULL); + if (node != NULL) { - crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, node); + crm_xml_add(xml, PCMK__XA_ATTR_HOST, node); } if (nodeid > 0) { - crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, nodeid); + crm_xml_add_int(xml, PCMK__XA_ATTR_HOST_ID, nodeid); + } +} + +/*! + * \internal + * \brief Find a node by name in a list of nodes + * + * \param[in] nodes List of nodes (as pcmk_node_t*) + * \param[in] node_name Name of node to find + * + * \return Node from \p nodes that matches \p node_name if any, otherwise NULL + */ +pcmk_node_t * +pcmk__find_node_in_list(const GList *nodes, const char *node_name) +{ + if (node_name != NULL) { + for (const GList *iter = nodes; iter != NULL; iter = iter->next) { + pcmk_node_t *node = (pcmk_node_t *) iter->data; + + if (pcmk__str_eq(node->details->uname, node_name, + pcmk__str_casei)) { + return node; + } + } } + return NULL; } diff --git a/lib/common/nvpair.c b/lib/common/nvpair.c index dbb9c99..295e923 100644 --- a/lib/common/nvpair.c +++ b/lib/common/nvpair.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. * @@ -17,17 +17,17 @@ #include <libxml/tree.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include "crmcommon_private.h" /* - * This file isolates handling of three types of name/value pairs: + * This file isolates handling of various kinds of name/value pairs: * * - pcmk_nvpair_t data type * - XML attributes (<TAG ... NAME=VALUE ...>) * - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>) + * - Meta-attributes (for resources and actions) */ // pcmk_nvpair_t handling @@ -50,11 +50,10 @@ pcmk__new_nvpair(const char *name, const char *value) CRM_ASSERT(name); - nvpair = calloc(1, sizeof(pcmk_nvpair_t)); - CRM_ASSERT(nvpair); + nvpair = pcmk__assert_alloc(1, sizeof(pcmk_nvpair_t)); - pcmk__str_update(&nvpair->name, name); - pcmk__str_update(&nvpair->value, value); + nvpair->name = pcmk__str_copy(name); + nvpair->value = pcmk__str_copy(value); return nvpair; } @@ -630,6 +629,37 @@ crm_element_value_timeval(const xmlNode *xml, const char *name_sec, } /*! + * \internal + * \brief Get a date/time object from an XML attribute value + * + * \param[in] xml XML with attribute to parse (from CIB) + * \param[in] attr Name of attribute to parse + * \param[out] t Where to create date/time object + * (\p *t must be NULL initially) + * + * \return Standard Pacemaker return code + * \note The caller is responsible for freeing \p *t using crm_time_free(). + */ +int +pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t) +{ + const char *value = NULL; + + if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) { + return EINVAL; + } + + value = crm_element_value(xml, attr); + if (value != NULL) { + *t = crm_time_new(value); + if (*t == NULL) { + return pcmk_rc_unpack_error; + } + } + return pcmk_rc_ok; +} + +/*! * \brief Retrieve a copy of the value of an XML attribute * * This is like \c crm_element_value() but allocating new memory for the result. @@ -643,20 +673,18 @@ crm_element_value_timeval(const xmlNode *xml, const char *name_sec, char * crm_element_value_copy(const xmlNode *data, const char *name) { - char *value_copy = NULL; - - pcmk__str_update(&value_copy, crm_element_value(data, name)); - return value_copy; + return pcmk__str_copy(crm_element_value(data, name)); } /*! - * \brief Add hash table entry to XML as (possibly legacy) name/value + * \brief Safely add hash table entry to XML as attribute or name-value pair * * Suitable for \c g_hash_table_foreach(), this function takes a hash table key * and value, with an XML node passed as user data, and adds an XML attribute * with the specified name and value if it does not already exist. If the key - * name starts with a digit, this will instead add a \<param name=NAME - * value=VALUE/> child to the XML (for legacy compatibility with heartbeat). + * name starts with a digit, then it's not a valid XML attribute name. In that + * case, this will instead add a <tt><param name=NAME value=VALUE/></tt> child + * to the XML. * * \param[in] key Key of hash table entry * \param[in] value Value of hash table entry @@ -665,16 +693,24 @@ crm_element_value_copy(const xmlNode *data, const char *name) void hash2smartfield(gpointer key, gpointer value, gpointer user_data) { + /* @TODO Generate PCMK__XE_PARAM nodes for all keys that aren't valid XML + * attribute names (not just those that start with digits), or possibly for + * all keys to simplify parsing. + * + * Consider either deprecating as public API or exposing PCMK__XE_PARAM. + * PCMK__XE_PARAM is currently private because it doesn't appear in any + * output that Pacemaker generates. + */ const char *name = key; const char *s_value = value; xmlNode *xml_node = user_data; if (isdigit(name[0])) { - xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM); + xmlNode *tmp = pcmk__xe_create(xml_node, PCMK__XE_PARAM); - crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name); - crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value); + crm_xml_add(tmp, PCMK_XA_NAME, name); + crm_xml_add(tmp, PCMK_XA_VALUE, s_value); } else if (crm_element_value(xml_node, name) == NULL) { crm_xml_add(xml_node, name, s_value); @@ -770,19 +806,16 @@ crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name, */ CRM_CHECK(id || name, return NULL); - nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR); - CRM_CHECK(nvp, return NULL); + nvp = pcmk__xe_create(parent, PCMK_XE_NVPAIR); if (id) { - crm_xml_add(nvp, XML_ATTR_ID, id); + crm_xml_add(nvp, PCMK_XA_ID, id); } else { - const char *parent_id = ID(parent); - crm_xml_set_id(nvp, "%s-%s", - (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name); + pcmk__s(pcmk__xe_id(parent), PCMK_XE_NVPAIR), name); } - crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name); - crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value); + crm_xml_add(nvp, PCMK_XA_NAME, name); + crm_xml_add(nvp, PCMK_XA_VALUE, value); return nvp; } @@ -832,7 +865,7 @@ xml2list(const xmlNode *parent) CRM_CHECK(parent != NULL, return nvpair_hash); - nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); + nvpair_list = pcmk__xe_first_child(parent, PCMK__XE_ATTRIBUTES, NULL, NULL); if (nvpair_list == NULL) { crm_trace("No attributes in %s", parent->name); crm_log_xml_trace(parent, "No attributes for resource op"); @@ -848,20 +881,18 @@ xml2list(const xmlNode *parent) crm_trace("Added %s=%s", p_name, p_value); - g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value)); + pcmk__insert_dup(nvpair_hash, p_name, p_value); } - for (child = pcmk__xml_first_child(nvpair_list); child != NULL; - child = pcmk__xml_next(child)) { + for (child = pcmk__xe_first_child(nvpair_list, PCMK__XE_PARAM, NULL, NULL); + child != NULL; child = pcmk__xe_next_same(child)) { - if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) { - const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME); - const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE); + const char *key = crm_element_value(child, PCMK_XA_NAME); + const char *value = crm_element_value(child, PCMK_XA_VALUE); - crm_trace("Added %s=%s", key, value); - if (key != NULL && value != NULL) { - g_hash_table_insert(nvpair_hash, strdup(key), strdup(value)); - } + crm_trace("Added %s=%s", key, value); + if (key != NULL && value != NULL) { + pcmk__insert_dup(nvpair_hash, key, value); } } @@ -871,7 +902,7 @@ xml2list(const xmlNode *parent) void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value) { - crm_xml_add(node, name, value ? XML_BOOLEAN_TRUE : XML_BOOLEAN_FALSE); + crm_xml_add(node, name, pcmk__btoa(value)); } int @@ -911,6 +942,60 @@ pcmk__xe_attr_is_true(const xmlNode *node, const char *name) return rc == pcmk_rc_ok && value == true; } +// Meta-attribute handling + +/*! + * \brief Get the environment variable equivalent of a meta-attribute name + * + * \param[in] attr_name Name of meta-attribute + * + * \return Newly allocated string for \p attr_name with "CRM_meta_" prefix and + * underbars instead of dashes + * \note This asserts on an invalid argument or memory allocation error, so + * callers can assume the result is non-NULL. The caller is responsible + * for freeing the result using free(). + */ +char * +crm_meta_name(const char *attr_name) +{ + char *env_name = NULL; + + CRM_ASSERT(!pcmk__str_empty(attr_name)); + + env_name = crm_strdup_printf(CRM_META "_%s", attr_name); + for (char *c = env_name; *c != '\0'; ++c) { + if (*c == '-') { + *c = '_'; + } + } + return env_name; +} + +/*! + * \brief Get the value of a meta-attribute + * + * Get the value of a meta-attribute from a hash table whose keys are + * meta-attribute environment variable names (as crm_meta_name() would + * create, like pcmk__graph_action_t:params, not pcmk_resource_t:meta). + * + * \param[in] meta Hash table of meta-attributes + * \param[in] attr_name Name of meta-attribute to get + * + * \return Value of given meta-attribute + */ +const char * +crm_meta_value(GHashTable *meta, const char *attr_name) +{ + if ((meta != NULL) && (attr_name != NULL)) { + char *key = crm_meta_name(attr_name); + const char *value = g_hash_table_lookup(meta, key); + + free(key); + return value; + } + return NULL; +} + // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START @@ -960,7 +1045,7 @@ crm_xml_replace(xmlNode *node, const char *name, const char *value) return NULL; } else if (old_value && !value) { - xml_remove_prop(node, name); + pcmk__xe_remove_attr(node, name); return NULL; } diff --git a/lib/common/options.c b/lib/common/options.c index 2d86ebc..ba64959 100644 --- a/lib/common/options.c +++ b/lib/common/options.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <crm/crm.h> +#include <crm/common/xml.h> void pcmk__cli_help(char cmd) @@ -39,6 +40,1035 @@ pcmk__cli_help(char cmd) /* + * Option metadata + */ + +static const pcmk__cluster_option_t cluster_options[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_OPT_DC_VERSION, NULL, PCMK_VALUE_VERSION, NULL, + NULL, NULL, + pcmk__opt_controld|pcmk__opt_generated, + N_("Pacemaker version on cluster node elected Designated Controller " + "(DC)"), + N_("Includes a hash which identifies the exact revision the code was " + "built from. Used for diagnostic purposes."), + }, + { + PCMK_OPT_CLUSTER_INFRASTRUCTURE, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_controld|pcmk__opt_generated, + N_("The messaging layer on which Pacemaker is currently running"), + N_("Used for informational and diagnostic purposes."), + }, + { + PCMK_OPT_CLUSTER_NAME, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_controld, + N_("An arbitrary name for the cluster"), + N_("This optional value is mostly for users' convenience as desired " + "in administration, but may also be used in Pacemaker " + "configuration rules via the #cluster-name node attribute, and " + "by higher-level tools and resource agents."), + }, + { + PCMK_OPT_DC_DEADTIME, NULL, PCMK_VALUE_DURATION, NULL, + "20s", pcmk__valid_interval_spec, + pcmk__opt_controld, + N_("How long to wait for a response from other nodes during start-up"), + N_("The optimal value will depend on the speed and load of your " + "network and the type of switches used."), + }, + { + PCMK_OPT_CLUSTER_RECHECK_INTERVAL, NULL, PCMK_VALUE_DURATION, NULL, + "15min", pcmk__valid_interval_spec, + pcmk__opt_controld, + N_("Polling interval to recheck cluster state and evaluate rules " + "with date specifications"), + N_("Pacemaker is primarily event-driven, and looks ahead to know when " + "to recheck cluster state for failure-timeout settings and most " + "time-based rules. However, it will also recheck the cluster after " + "this amount of inactivity, to evaluate rules with date " + "specifications and serve as a fail-safe for certain types of " + "scheduler bugs. A value of 0 disables polling. A positive value " + "sets an interval in seconds, unless other units are specified " + "(for example, \"5min\")."), + }, + { + PCMK_OPT_FENCE_REACTION, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_STOP ", " PCMK_VALUE_PANIC, + PCMK_VALUE_STOP, NULL, + pcmk__opt_controld, + N_("How a cluster node should react if notified of its own fencing"), + N_("A cluster node may receive notification of a \"succeeded\" " + "fencing that targeted it if fencing is misconfigured, or if " + "fabric fencing is in use that doesn't cut cluster communication. " + "Use \"stop\" to attempt to immediately stop Pacemaker and stay " + "stopped, or \"panic\" to attempt to immediately reboot the local " + "node, falling back to stop on failure."), + }, + { + PCMK_OPT_ELECTION_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "2min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Declare an election failed if it is not decided within this much " + "time. If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_SHUTDOWN_ESCALATION, NULL, PCMK_VALUE_DURATION, NULL, + "20min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Exit immediately if shutdown does not complete within this much " + "time. If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_JOIN_INTEGRATION_TIMEOUT, "crmd-integration-timeout", + PCMK_VALUE_DURATION, NULL, + "3min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_JOIN_FINALIZATION_TIMEOUT, "crmd-finalization-timeout", + PCMK_VALUE_DURATION, NULL, + "30min", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("If you need to adjust this value, it probably indicates " + "the presence of a bug."), + NULL, + }, + { + PCMK_OPT_TRANSITION_DELAY, "crmd-transition-delay", PCMK_VALUE_DURATION, + NULL, + "0s", pcmk__valid_interval_spec, + pcmk__opt_controld|pcmk__opt_advanced, + N_("Enabling this option will slow down cluster recovery under all " + "conditions"), + N_("Delay cluster recovery for this much time to allow for additional " + "events to occur. Useful if your configuration is sensitive to " + "the order in which ping updates arrive."), + }, + { + PCMK_OPT_NO_QUORUM_POLICY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_STOP ", " PCMK_VALUE_FREEZE ", " PCMK_VALUE_IGNORE + ", " PCMK_VALUE_DEMOTE ", " PCMK_VALUE_FENCE_LEGACY, + PCMK_VALUE_STOP, pcmk__valid_no_quorum_policy, + pcmk__opt_schedulerd, + N_("What to do when the cluster does not have quorum"), + NULL, + }, + { + PCMK_OPT_SHUTDOWN_LOCK, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to lock resources to a cleanly shut down node"), + N_("When true, resources active on a node when it is cleanly shut down " + "are kept \"locked\" to that node (not allowed to run elsewhere) " + "until they start again on that node after it rejoins (or for at " + "most shutdown-lock-limit, if set). Stonith resources and " + "Pacemaker Remote connections are never locked. Clone and bundle " + "instances and the promoted role of promotable clones are " + "currently never locked, though support could be added in a future " + "release."), + }, + { + PCMK_OPT_SHUTDOWN_LOCK_LIMIT, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Do not lock resources to a cleanly shut down node longer than " + "this"), + N_("If shutdown-lock is true and this is set to a nonzero time " + "duration, shutdown locks will expire after this much time has " + "passed since the shutdown was initiated, even if the node has not " + "rejoined."), + }, + { + PCMK_OPT_ENABLE_ACL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_based, + N_("Enable Access Control Lists (ACLs) for the CIB"), + NULL, + }, + { + PCMK_OPT_SYMMETRIC_CLUSTER, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether resources can run on any node by default"), + NULL, + }, + { + PCMK_OPT_MAINTENANCE_MODE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should refrain from monitoring, starting, and " + "stopping resources"), + NULL, + }, + { + PCMK_OPT_START_FAILURE_IS_FATAL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether a start failure should prevent a resource from being " + "recovered on the same node"), + N_("When true, the cluster will immediately ban a resource from a node " + "if it fails to start there. When false, the cluster will instead " + "check the resource's fail count against its migration-threshold.") + }, + { + PCMK_OPT_ENABLE_STARTUP_PROBES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should check for active resources during " + "start-up"), + NULL, + }, + + // Fencing-related options + { + PCMK_OPT_STONITH_ENABLED, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_advanced, + N_("Whether nodes may be fenced as part of recovery"), + N_("If false, unresponsive nodes are immediately assumed to be " + "harmless, and resources that were active on them may be recovered " + "elsewhere. This can result in a \"split-brain\" situation, " + "potentially leading to data loss and/or service unavailability."), + }, + { + PCMK_OPT_STONITH_ACTION, NULL, PCMK_VALUE_SELECT, + PCMK_ACTION_REBOOT ", " PCMK_ACTION_OFF ", " PCMK__ACTION_POWEROFF, + PCMK_ACTION_REBOOT, pcmk__is_fencing_action, + pcmk__opt_schedulerd, + N_("Action to send to fence device when a node needs to be fenced " + "(\"poweroff\" is a deprecated alias for \"off\")"), + NULL, + }, + { + PCMK_OPT_STONITH_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "60s", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("How long to wait for on, off, and reboot fence actions to complete " + "by default"), + NULL, + }, + { + PCMK_OPT_HAVE_WATCHDOG, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_generated, + N_("Whether watchdog integration is enabled"), + N_("This is set automatically by the cluster according to whether SBD " + "is detected to be in use. User-configured values are ignored. " + "The value `true` is meaningful if diskless SBD is used and " + "`stonith-watchdog-timeout` is nonzero. In that case, if fencing " + "is required, watchdog-based self-fencing will be performed via " + "SBD without requiring a fencing resource explicitly configured."), + }, + { + /* @COMPAT Currently, unparsable values default to -1 (auto-calculate), + * while missing values default to 0 (disable). All values are accepted + * (unless the controller finds that the value conflicts with the + * SBD_WATCHDOG_TIMEOUT). + * + * At a compatibility break: properly validate as a timeout, let + * either negative values or a particular string like "auto" mean auto- + * calculate, and use 0 as the single default for when the option either + * is unset or fails to validate. + */ + PCMK_OPT_STONITH_WATCHDOG_TIMEOUT, NULL, PCMK_VALUE_TIMEOUT, NULL, + "0", NULL, + pcmk__opt_controld, + N_("How long before nodes can be assumed to be safely down when " + "watchdog-based self-fencing via SBD is in use"), + N_("If this is set to a positive value, lost nodes are assumed to " + "achieve self-fencing using watchdog-based SBD within this much " + "time. This does not require a fencing resource to be explicitly " + "configured, though a fence_watchdog resource can be configured, to " + "limit use to specific nodes. If this is set to 0 (the default), " + "the cluster will never assume watchdog-based self-fencing. If this " + "is set to a negative value, the cluster will use twice the local " + "value of the `SBD_WATCHDOG_TIMEOUT` environment variable if that " + "is positive, or otherwise treat this as 0. WARNING: When used, " + "this timeout must be larger than `SBD_WATCHDOG_TIMEOUT` on all " + "nodes that use watchdog-based SBD, and Pacemaker will refuse to " + "start on any of those nodes where this is not true for the local " + "value or SBD is not active. When this is set to a negative value, " + "`SBD_WATCHDOG_TIMEOUT` must be set to the same value on all nodes " + "that use SBD, otherwise data corruption or loss could occur."), + }, + { + PCMK_OPT_STONITH_MAX_ATTEMPTS, NULL, PCMK_VALUE_SCORE, NULL, + "10", pcmk__valid_positive_int, + pcmk__opt_controld, + N_("How many times fencing can fail before it will no longer be " + "immediately re-attempted on a target"), + NULL, + }, + { + PCMK_OPT_CONCURRENT_FENCING, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK__CONCURRENT_FENCING_DEFAULT, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Allow performing fencing operations in parallel"), + NULL, + }, + { + PCMK_OPT_STARTUP_FENCING, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_advanced, + N_("Whether to fence unseen nodes at start-up"), + N_("Setting this to false may lead to a \"split-brain\" situation, " + "potentially leading to data loss and/or service unavailability."), + }, + { + PCMK_OPT_PRIORITY_FENCING_DELAY, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Apply fencing delay targeting the lost nodes with the highest " + "total resource priority"), + N_("Apply specified delay for the fencings that are targeting the lost " + "nodes with the highest total resource priority in case we don't " + "have the majority of the nodes in our cluster partition, so that " + "the more significant nodes potentially win any fencing match, " + "which is especially meaningful under split-brain of 2-node " + "cluster. A promoted resource instance takes the base priority + 1 " + "on calculation if the base priority is not 0. Any static/random " + "delays that are introduced by `pcmk_delay_base/max` configured " + "for the corresponding fencing resources will be added to this " + "delay. This delay should be significantly greater than, safely " + "twice, the maximum `pcmk_delay_base/max`. By default, priority " + "fencing delay is disabled."), + }, + { + PCMK_OPT_NODE_PENDING_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "0", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("How long to wait for a node that has joined the cluster to join " + "the controller process group"), + N_("Fence nodes that do not join the controller process group within " + "this much time after joining the cluster, to allow the cluster " + "to continue managing resources. A value of 0 means never fence " + "pending nodes. Setting the value to 2h means fence nodes after " + "2 hours."), + }, + { + PCMK_OPT_CLUSTER_DELAY, NULL, PCMK_VALUE_DURATION, NULL, + "60s", pcmk__valid_interval_spec, + pcmk__opt_schedulerd, + N_("Maximum time for node-to-node communication"), + N_("The node elected Designated Controller (DC) will consider an action " + "failed if it does not get a response from the node executing the " + "action within this time (after considering the action's own " + "timeout). The \"correct\" value will depend on the speed and " + "load of your network and cluster nodes.") + }, + + // Limits + { + PCMK_OPT_LOAD_THRESHOLD, NULL, PCMK_VALUE_PERCENTAGE, NULL, + "80%", pcmk__valid_percentage, + pcmk__opt_controld, + N_("Maximum amount of system load that should be used by cluster " + "nodes"), + N_("The cluster will slow down its recovery process when the amount of " + "system resources used (currently CPU) approaches this limit"), + }, + { + PCMK_OPT_NODE_ACTION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "0", pcmk__valid_int, + pcmk__opt_controld, + N_("Maximum number of jobs that can be scheduled per node (defaults to " + "2x cores)"), + NULL, + }, + { + PCMK_OPT_BATCH_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("Maximum number of jobs that the cluster may execute in parallel " + "across all nodes"), + N_("The \"correct\" value will depend on the speed and load of your " + "network and cluster nodes. If set to 0, the cluster will " + "impose a dynamically calculated limit when any node has a " + "high load."), + }, + { + PCMK_OPT_MIGRATION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "-1", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of live migration actions that the cluster is allowed " + "to execute in parallel on a node (-1 means no limit)"), + NULL, + }, + { + /* @TODO This is actually ignored if not strictly positive. We should + * overhaul value types in Pacemaker Explained. There are lots of + * inaccurate ranges (assumptions of 32-bit width, "nonnegative" when + * positive is required, etc.). + * + * Maybe a single integer type with the allowed range specified would be + * better. + * + * Drop the PCMK_VALUE_NONNEGATIVE_INTEGER constant if we do this before + * a release. + */ + PCMK_OPT_CLUSTER_IPC_LIMIT, NULL, PCMK_VALUE_NONNEGATIVE_INTEGER, NULL, + "500", pcmk__valid_positive_int, + pcmk__opt_based, + N_("Maximum IPC message backlog before disconnecting a cluster daemon"), + N_("Raise this if log has \"Evicting client\" messages for cluster " + "daemon PIDs (a good value is the number of resources in the " + "cluster multiplied by the number of nodes)."), + }, + + // Orphans and stopping + { + PCMK_OPT_STOP_ALL_RESOURCES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether the cluster should stop all active resources"), + NULL, + }, + { + PCMK_OPT_STOP_ORPHAN_RESOURCES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to stop resources that were removed from the " + "configuration"), + NULL, + }, + { + PCMK_OPT_STOP_ORPHAN_ACTIONS, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, pcmk__valid_boolean, + pcmk__opt_schedulerd, + N_("Whether to cancel recurring actions removed from the " + "configuration"), + NULL, + }, + { + PCMK__OPT_REMOVE_AFTER_STOP, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, pcmk__valid_boolean, + pcmk__opt_schedulerd|pcmk__opt_deprecated, + N_("Whether to remove stopped resources from the executor"), + N_("Values other than default are poorly tested and potentially " + "dangerous."), + }, + + // Storing inputs + { + PCMK_OPT_PE_ERROR_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "-1", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs resulting in errors to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + { + PCMK_OPT_PE_WARN_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "5000", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs resulting in warnings to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + { + PCMK_OPT_PE_INPUT_SERIES_MAX, NULL, PCMK_VALUE_INTEGER, NULL, + "4000", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The number of scheduler inputs without errors or warnings to save"), + N_("Zero to disable, -1 to store unlimited."), + }, + + // Node health + { + PCMK_OPT_NODE_HEALTH_STRATEGY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_NONE ", " PCMK_VALUE_MIGRATE_ON_RED ", " + PCMK_VALUE_ONLY_GREEN ", " PCMK_VALUE_PROGRESSIVE ", " + PCMK_VALUE_CUSTOM, + PCMK_VALUE_NONE, pcmk__validate_health_strategy, + pcmk__opt_schedulerd, + N_("How cluster should react to node health attributes"), + N_("Requires external entities to create node attributes (named with " + "the prefix \"#health\") with values \"red\", \"yellow\", or " + "\"green\".") + }, + { + PCMK_OPT_NODE_HEALTH_BASE, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("Base health score assigned to a node"), + N_("Only used when \"node-health-strategy\" is set to " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_GREEN, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"green\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_YELLOW, NULL, PCMK_VALUE_SCORE, NULL, + "0", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"yellow\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\"."), + }, + { + PCMK_OPT_NODE_HEALTH_RED, NULL, PCMK_VALUE_SCORE, NULL, + "-INFINITY", pcmk__valid_int, + pcmk__opt_schedulerd, + N_("The score to use for a node health attribute whose value is " + "\"red\""), + N_("Only used when \"node-health-strategy\" is set to \"custom\" or " + "\"progressive\".") + }, + + // Placement strategy + { + PCMK_OPT_PLACEMENT_STRATEGY, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_DEFAULT ", " PCMK_VALUE_UTILIZATION ", " + PCMK_VALUE_MINIMAL ", " PCMK_VALUE_BALANCED, + PCMK_VALUE_DEFAULT, pcmk__valid_placement_strategy, + pcmk__opt_schedulerd, + N_("How the cluster should allocate resources to nodes"), + NULL, + }, + + { NULL, }, +}; + +static const pcmk__cluster_option_t fencing_params[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_STONITH_HOST_ARGUMENT, NULL, PCMK_VALUE_STRING, NULL, + "port", NULL, + pcmk__opt_advanced, + N_("An alternate parameter to supply instead of 'port'"), + N_("Some devices do not support the standard 'port' parameter or may " + "provide additional ones. Use this to specify an alternate, device-" + "specific, parameter that should indicate the machine to be " + "fenced. A value of \"none\" can be used to tell the cluster not " + "to supply any additional parameters."), + }, + { + PCMK_STONITH_HOST_MAP, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("A mapping of node names to port numbers for devices that do not " + "support node names."), + N_("For example, \"node1:1;node2:2,3\" would tell the cluster to use " + "port 1 for node1 and ports 2 and 3 for node2."), + }, + { + PCMK_STONITH_HOST_LIST, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Nodes targeted by this device"), + N_("Comma-separated list of nodes that can be targeted by this device " + "(for example, \"node1,node2,node3\"). If pcmk_host_check is " + "\"static-list\", either this or pcmk_host_map must be set."), + }, + { + PCMK_STONITH_HOST_CHECK, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_DYNAMIC_LIST ", " PCMK_VALUE_STATIC_LIST ", " + PCMK_VALUE_STATUS ", " PCMK_VALUE_NONE, + NULL, NULL, + pcmk__opt_none, + N_("How to determine which nodes can be targeted by the device"), + N_("Use \"dynamic-list\" to query the device via the 'list' command; " + "\"static-list\" to check the pcmk_host_list attribute; " + "\"status\" to query the device via the 'status' command; or " + "\"none\" to assume every device can fence every node. " + "The default value is \"static-list\" if pcmk_host_map or " + "pcmk_host_list is set; otherwise \"dynamic-list\" if the device " + "supports the list operation; otherwise \"status\" if the device " + "supports the status operation; otherwise \"none\""), + }, + { + PCMK_STONITH_DELAY_MAX, NULL, PCMK_VALUE_DURATION, NULL, + "0s", NULL, + pcmk__opt_none, + N_("Enable a delay of no more than the time specified before executing " + "fencing actions."), + N_("Enable a delay of no more than the time specified before executing " + "fencing actions. Pacemaker derives the overall delay by taking " + "the value of pcmk_delay_base and adding a random delay value such " + "that the sum is kept below this maximum."), + }, + { + PCMK_STONITH_DELAY_BASE, NULL, PCMK_VALUE_STRING, NULL, + "0s", NULL, + pcmk__opt_none, + N_("Enable a base delay for fencing actions and specify base delay " + "value."), + N_("This enables a static delay for fencing actions, which can help " + "avoid \"death matches\" where two nodes try to fence each other " + "at the same time. If pcmk_delay_max is also used, a random delay " + "will be added such that the total delay is kept below that value. " + "This can be set to a single time value to apply to any node " + "targeted by this device (useful if a separate device is " + "configured for each target), or to a node map (for example, " + "\"node1:1s;node2:5\") to set a different value for each target."), + }, + { + PCMK_STONITH_ACTION_LIMIT, NULL, PCMK_VALUE_INTEGER, NULL, + "1", NULL, + pcmk__opt_none, + N_("The maximum number of actions can be performed in parallel on this " + "device"), + N_("Cluster property concurrent-fencing=\"true\" needs to be " + "configured first. Then use this to specify the maximum number of " + "actions can be performed in parallel on this device. A value of " + "-1 means an unlimited number of actions can be performed in " + "parallel."), + }, + { + "pcmk_reboot_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_REBOOT, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'reboot'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'reboot' action."), + }, + { + "pcmk_reboot_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'reboot' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'reboot' actions."), + }, + { + "pcmk_reboot_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'reboot' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'reboot' action before giving up."), + }, + { + "pcmk_off_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_OFF, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'off'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'off' action."), + }, + { + "pcmk_off_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'off' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'off' actions."), + }, + { + "pcmk_off_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'off' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'off' action before giving up."), + }, + { + "pcmk_on_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_ON, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'on'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'on' action."), + }, + { + "pcmk_on_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'on' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'on' actions."), + }, + { + "pcmk_on_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'on' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'on' action before giving up."), + }, + { + "pcmk_list_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_LIST, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'list'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'list' action."), + }, + { + "pcmk_list_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'list' actions instead of " + "stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'list' actions."), + }, + { + "pcmk_list_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'list' command within the " + "timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'list' action before giving up."), + }, + { + "pcmk_monitor_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_MONITOR, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'monitor'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'monitor' action."), + }, + { + "pcmk_monitor_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'monitor' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'monitor' actions."), + }, + { + "pcmk_monitor_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'monitor' command within " + "the timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'monitor' action before giving up."), + }, + { + "pcmk_status_action", NULL, PCMK_VALUE_STRING, NULL, + PCMK_ACTION_STATUS, NULL, + pcmk__opt_advanced, + N_("An alternate command to run instead of 'status'"), + N_("Some devices do not support the standard commands or may provide " + "additional ones. Use this to specify an alternate, device-" + "specific, command that implements the 'status' action."), + }, + { + "pcmk_status_timeout", NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_advanced, + N_("Specify an alternate timeout to use for 'status' actions instead " + "of stonith-timeout"), + N_("Some devices need much more/less time to complete than normal. " + "Use this to specify an alternate, device-specific, timeout for " + "'status' actions."), + }, + { + "pcmk_status_retries", NULL, PCMK_VALUE_INTEGER, NULL, + "2", NULL, + pcmk__opt_advanced, + N_("The maximum number of times to try the 'status' command within " + "the timeout period"), + N_("Some devices do not support multiple connections. Operations may " + "\"fail\" if the device is busy with another task. In that case, " + "Pacemaker will automatically retry the operation if there is time " + "remaining. Use this option to alter the number of times Pacemaker " + "tries a 'status' action before giving up."), + }, + + { NULL, }, +}; + +static const pcmk__cluster_option_t primitive_meta[] = { + /* name, old name, type, allowed values, + * default value, validator, + * flags, + * short description, + * long description + */ + { + PCMK_META_PRIORITY, NULL, PCMK_VALUE_SCORE, NULL, + "0", NULL, + pcmk__opt_none, + N_("Resource assignment priority"), + N_("If not all resources can be active, the cluster will stop " + "lower-priority resources in order to keep higher-priority ones " + "active."), + }, + { + PCMK_META_CRITICAL, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("Default value for influence in colocation constraints"), + N_("Use this value as the default for influence in all colocation " + "constraints involving this resource, as well as in the implicit " + "colocation constraints created if this resource is in a group."), + }, + { + PCMK_META_TARGET_ROLE, NULL, PCMK_VALUE_SELECT, + PCMK_ROLE_STOPPED ", " PCMK_ROLE_STARTED ", " + PCMK_ROLE_UNPROMOTED ", " PCMK_ROLE_PROMOTED, + PCMK_ROLE_STARTED, NULL, + pcmk__opt_none, + N_("State the cluster should attempt to keep this resource in"), + N_("\"Stopped\" forces the resource to be stopped. " + "\"Started\" allows the resource to be started (and in the case of " + "promotable clone resources, promoted if appropriate). " + "\"Unpromoted\" allows the resource to be started, but only in the " + "unpromoted role if the resource is promotable. " + "\"Promoted\" is equivalent to \"Started\"."), + }, + { + PCMK_META_IS_MANAGED, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("Whether the cluster is allowed to actively change the resource's " + "state"), + N_("If false, the cluster will not start, stop, promote, or demote the " + "resource on any node. Recurring actions for the resource are " + "unaffected. If true, a true value for the maintenance-mode " + "cluster option, the maintenance node attribute, or the " + "maintenance resource meta-attribute overrides this."), + }, + { + PCMK_META_MAINTENANCE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, NULL, + pcmk__opt_none, + N_("If true, the cluster will not schedule any actions involving the " + "resource"), + N_("If true, the cluster will not start, stop, promote, or demote the " + "resource on any node, and will pause any recurring monitors " + "(except those specifying role as \"Stopped\"). If false, a true " + "value for the maintenance-mode cluster option or maintenance node " + "attribute overrides this."), + }, + { + PCMK_META_RESOURCE_STICKINESS, NULL, PCMK_VALUE_SCORE, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Score to add to the current node when a resource is already " + "active"), + N_("Score to add to the current node when a resource is already " + "active. This allows running resources to stay where they are, " + "even if they would be placed elsewhere if they were being started " + "from a stopped state. " + "The default is 1 for individual clone instances, and 0 for all " + "other resources."), + }, + { + PCMK_META_REQUIRES, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_NOTHING ", " PCMK_VALUE_QUORUM ", " + PCMK_VALUE_FENCING ", " PCMK_VALUE_UNFENCING, + NULL, NULL, + pcmk__opt_none, + N_("Conditions under which the resource can be started"), + N_("Conditions under which the resource can be started. " + "\"nothing\" means the cluster can always start this resource. " + "\"quorum\" means the cluster can start this resource only if a " + "majority of the configured nodes are active. " + "\"fencing\" means the cluster can start this resource only if a " + "majority of the configured nodes are active and any failed or " + "unknown nodes have been fenced. " + "\"unfencing\" means the cluster can start this resource only if " + "a majority of the configured nodes are active and any failed or " + "unknown nodes have been fenced, and only on nodes that have been " + "unfenced. " + "The default is \"quorum\" for resources with a class of stonith; " + "otherwise, \"unfencing\" if unfencing is active in the cluster; " + "otherwise, \"fencing\" if the stonith-enabled cluster option is " + "true; " + "otherwise, \"quorum\"."), + }, + { + PCMK_META_MIGRATION_THRESHOLD, NULL, PCMK_VALUE_SCORE, NULL, + PCMK_VALUE_INFINITY, NULL, + pcmk__opt_none, + N_("Number of failures on a node before the resource becomes " + "ineligible to run there."), + N_("Number of failures that may occur for this resource on a node, " + "before that node is marked ineligible to host this resource. A " + "value of 0 indicates that this feature is disabled (the node will " + "never be marked ineligible). By contrast, the cluster treats " + "\"INFINITY\" (the default) as a very large but finite number. " + "This option has an effect only if the failed operation specifies " + "its on-fail attribute as \"restart\" (the default), and " + "additionally for failed start operations, if the " + "start-failure-is-fatal cluster property is set to false."), + }, + { + PCMK_META_FAILURE_TIMEOUT, NULL, PCMK_VALUE_DURATION, NULL, + "0", NULL, + pcmk__opt_none, + N_("Number of seconds before acting as if a failure had not occurred"), + N_("Number of seconds after a failed action for this resource before " + "acting as if the failure had not occurred, and potentially " + "allowing the resource back to the node on which it failed. " + "A value of 0 indicates that this feature is disabled."), + }, + { + PCMK_META_MULTIPLE_ACTIVE, NULL, PCMK_VALUE_SELECT, + PCMK_VALUE_BLOCK ", " PCMK_VALUE_STOP_ONLY ", " + PCMK_VALUE_STOP_START ", " PCMK_VALUE_STOP_UNEXPECTED, + PCMK_VALUE_STOP_START, NULL, + pcmk__opt_none, + N_("What to do if the cluster finds the resource active on more than " + "one node"), + N_("What to do if the cluster finds the resource active on more than " + "one node. " + "\"block\" means to mark the resource as unmanaged. " + "\"stop_only\" means to stop all active instances of this resource " + "and leave them stopped. " + "\"stop_start\" means to stop all active instances of this " + "resource and start the resource in one location only. " + "\"stop_unexpected\" means to stop all active instances of this " + "resource except where the resource should be active. (This should " + "be used only when extra instances are not expected to disrupt " + "existing instances, and the resource agent's monitor of an " + "existing instance is capable of detecting any problems that could " + "be caused. Note that any resources ordered after this one will " + "still need to be restarted.)"), + }, + { + PCMK_META_ALLOW_MIGRATE, NULL, PCMK_VALUE_BOOLEAN, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Whether the cluster should try to \"live migrate\" this resource " + "when it needs to be moved"), + N_("Whether the cluster should try to \"live migrate\" this resource " + "when it needs to be moved. " + "The default is true for ocf:pacemaker:remote resources, and false " + "otherwise."), + }, + { + PCMK_META_ALLOW_UNHEALTHY_NODES, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_FALSE, NULL, + pcmk__opt_none, + N_("Whether the resource should be allowed to run on a node even if " + "the node's health score would otherwise prevent it"), + NULL, + }, + { + PCMK_META_CONTAINER_ATTRIBUTE_TARGET, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Where to check user-defined node attributes"), + N_("Whether to check user-defined node attributes on the physical host " + "where a container is running or on the local node. This is " + "usually set for a bundle resource and inherited by the bundle's " + "primitive resource. " + "A value of \"host\" means to check user-defined node attributes " + "on the underlying physical host. Any other value means to check " + "user-defined node attributes on the local node (for a bundled " + "primitive resource, this is the bundle node)."), + }, + { + PCMK_META_REMOTE_NODE, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("Name of the Pacemaker Remote guest node this resource is " + "associated with, if any"), + N_("Name of the Pacemaker Remote guest node this resource is " + "associated with, if any. If specified, this both enables the " + "resource as a guest node and defines the unique name used to " + "identify the guest node. The guest must be configured to run the " + "Pacemaker Remote daemon when it is started. " + "WARNING: This value cannot overlap with any resource or node " + "IDs."), + }, + { + PCMK_META_REMOTE_ADDR, NULL, PCMK_VALUE_STRING, NULL, + NULL, NULL, + pcmk__opt_none, + N_("If remote-node is specified, the IP address or hostname used to " + "connect to the guest via Pacemaker Remote"), + N_("If remote-node is specified, the IP address or hostname used to " + "connect to the guest via Pacemaker Remote. The Pacemaker Remote " + "daemon on the guest must be configured to accept connections on " + "this address. " + "The default is the value of the remote-node meta-attribute."), + }, + { + PCMK_META_REMOTE_PORT, NULL, PCMK_VALUE_PORT, NULL, + "3121", NULL, + pcmk__opt_none, + N_("If remote-node is specified, port on the guest used for its " + "Pacemaker Remote connection"), + N_("If remote-node is specified, the port on the guest used for its " + "Pacemaker Remote connection. The Pacemaker Remote daemon on the " + "guest must be configured to listen on this port."), + }, + { + PCMK_META_REMOTE_CONNECT_TIMEOUT, NULL, PCMK_VALUE_TIMEOUT, NULL, + "60s", NULL, + pcmk__opt_none, + N_("If remote-node is specified, how long before a pending Pacemaker " + "Remote guest connection times out."), + NULL, + }, + { + PCMK_META_REMOTE_ALLOW_MIGRATE, NULL, PCMK_VALUE_BOOLEAN, NULL, + PCMK_VALUE_TRUE, NULL, + pcmk__opt_none, + N_("If remote-node is specified, this acts as the allow-migrate " + "meta-attribute for the implicit remote connection resource " + "(ocf:pacemaker:remote)."), + NULL, + }, + + { NULL, }, +}; + +/* * Environment variable option handling */ @@ -176,161 +1206,195 @@ pcmk__env_option_enabled(const char *daemon, const char *option) * Cluster option handling */ +/*! + * \internal + * \brief Check whether a string represents a valid interval specification + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid interval specification, or \c false + * otherwise + */ bool pcmk__valid_interval_spec(const char *value) { - (void) crm_parse_interval_spec(value); - return errno == 0; + return pcmk_parse_interval_spec(value, NULL) == pcmk_rc_ok; } +/*! + * \internal + * \brief Check whether a string represents a valid boolean value + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid boolean value, or \c false otherwise + */ bool pcmk__valid_boolean(const char *value) { - int tmp; - - return crm_str_to_boolean(value, &tmp) == 1; + return crm_str_to_boolean(value, NULL) == 1; } +/*! + * \internal + * \brief Check whether a string represents a valid integer + * + * Valid values include \c INFINITY, \c -INFINITY, and all 64-bit integers. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid integer, or \c false otherwise + */ bool -pcmk__valid_number(const char *value) +pcmk__valid_int(const char *value) { - if (value == NULL) { - return false; - - } else if (pcmk_str_is_minus_infinity(value) || - pcmk_str_is_infinity(value)) { - return true; - } - - return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok; + return (value != NULL) + && (pcmk_str_is_infinity(value) + || pcmk_str_is_minus_infinity(value) + || (pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok)); } +/*! + * \internal + * \brief Check whether a string represents a valid positive integer + * + * Valid values include \c INFINITY and all 64-bit positive integers. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid positive integer, or \c false + * otherwise + */ bool -pcmk__valid_positive_number(const char *value) +pcmk__valid_positive_int(const char *value) { long long num = 0LL; return pcmk_str_is_infinity(value) - || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0)); + || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) + && (num > 0)); } +/*! + * \internal + * \brief Check whether a string represents a valid + * \c PCMK__OPT_NO_QUORUM_POLICY value + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid \c PCMK__OPT_NO_QUORUM_POLICY value, + * or \c false otherwise + */ bool -pcmk__valid_quorum(const char *value) +pcmk__valid_no_quorum_policy(const char *value) { - return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL); + return pcmk__strcase_any_of(value, + PCMK_VALUE_STOP, PCMK_VALUE_FREEZE, + PCMK_VALUE_IGNORE, PCMK_VALUE_DEMOTE, + PCMK_VALUE_FENCE_LEGACY, NULL); } +/*! + * \internal + * \brief Check whether a string represents a valid percentage + * + * Valid values include long integers, with an optional trailing string + * beginning with '%'. + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid percentage value, or \c false + * otherwise + */ bool -pcmk__valid_script(const char *value) +pcmk__valid_percentage(const char *value) { - struct stat st; - - if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) { - return true; - } - - if (stat(value, &st) != 0) { - crm_err("Script %s does not exist", value); - return false; - } - - if (S_ISREG(st.st_mode) == 0) { - crm_err("Script %s is not a regular file", value); - return false; - } - - if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) { - crm_err("Script %s is not executable", value); - return false; - } + char *end = NULL; + float number = strtof(value, &end); - return true; + return ((end == NULL) || (end[0] == '%')) && (number >= 0); } +/*! + * \internal + * \brief Check whether a string represents a valid placement strategy + * + * \param[in] value String to validate + * + * \return \c true if \p value is a valid placement strategy, or \c false + * otherwise + */ bool -pcmk__valid_percentage(const char *value) +pcmk__valid_placement_strategy(const char *value) { - char *end = NULL; - long number = strtol(value, &end, 10); - - if (end && (end[0] != '%')) { - return false; - } - return number >= 0; + return pcmk__strcase_any_of(value, + PCMK_VALUE_DEFAULT, PCMK_VALUE_UTILIZATION, + PCMK_VALUE_MINIMAL, PCMK_VALUE_BALANCED, NULL); } /*! * \internal * \brief Check a table of configured options for a particular option * - * \param[in,out] options Name/value pairs for configured options - * \param[in] validate If not NULL, validator function for option value - * \param[in] name Option name to look for - * \param[in] old_name Alternative option name to look for - * \param[in] def_value Default to use if option not configured + * \param[in,out] table Name/value pairs for configured options + * \param[in] option Option to look up * * \return Option value (from supplied options table or default value) */ static const char * -cluster_option_value(GHashTable *options, bool (*validate)(const char *), - const char *name, const char *old_name, - const char *def_value) +cluster_option_value(GHashTable *table, const pcmk__cluster_option_t *option) { const char *value = NULL; - char *new_value = NULL; - CRM_ASSERT(name != NULL); + CRM_ASSERT((option != NULL) && (option->name != NULL)); - if (options) { - value = g_hash_table_lookup(options, name); + if (table != NULL) { + value = g_hash_table_lookup(table, option->name); - if ((value == NULL) && old_name) { - value = g_hash_table_lookup(options, old_name); + if ((value == NULL) && (option->alt_name != NULL)) { + value = g_hash_table_lookup(table, option->alt_name); if (value != NULL) { pcmk__config_warn("Support for legacy name '%s' for cluster " "option '%s' is deprecated and will be " "removed in a future release", - old_name, name); + option->alt_name, option->name); // Inserting copy with current name ensures we only warn once - new_value = strdup(value); - g_hash_table_insert(options, strdup(name), new_value); - value = new_value; + pcmk__insert_dup(table, option->name, value); } } - if (value && validate && (validate(value) == FALSE)) { + if ((value != NULL) && (option->is_valid != NULL) + && !option->is_valid(value)) { + pcmk__config_err("Using default value for cluster option '%s' " - "because '%s' is invalid", name, value); + "because '%s' is invalid", option->name, value); value = NULL; } - if (value) { + if (value != NULL) { return value; } } // No value found, use default - value = def_value; + value = option->default_value; if (value == NULL) { crm_trace("No value or default provided for cluster option '%s'", - name); + option->name); return NULL; } - if (validate) { - CRM_CHECK(validate(value) != FALSE, - crm_err("Bug: default value for cluster option '%s' is invalid", name); - return NULL); - } + CRM_CHECK((option->is_valid == NULL) || option->is_valid(value), + crm_err("Bug: default value for cluster option '%s' is invalid", + option->name); + return NULL); crm_trace("Using default value '%s' for cluster option '%s'", - value, name); - if (options) { - new_value = strdup(value); - g_hash_table_insert(options, strdup(name), new_value); - value = new_value; + value, option->name); + if (table != NULL) { + pcmk__insert_dup(table, option->name, value); } return value; } @@ -339,27 +1403,19 @@ cluster_option_value(GHashTable *options, bool (*validate)(const char *), * \internal * \brief Get the value of a cluster option * - * \param[in,out] options Name/value pairs for configured options - * \param[in] option_list Possible cluster options - * \param[in] len Length of \p option_list - * \param[in] name (Primary) option name to look for + * \param[in,out] options Name/value pairs for configured options + * \param[in] name (Primary) option name to look for * * \return Option value */ const char * -pcmk__cluster_option(GHashTable *options, - const pcmk__cluster_option_t *option_list, - int len, const char *name) +pcmk__cluster_option(GHashTable *options, const char *name) { - const char *value = NULL; + for (const pcmk__cluster_option_t *option = cluster_options; + option->name != NULL; option++) { - for (int lpc = 0; lpc < len; lpc++) { - if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) { - value = cluster_option_value(options, option_list[lpc].is_valid, - option_list[lpc].name, - option_list[lpc].alt_name, - option_list[lpc].default_value); - return value; + if (pcmk__str_eq(name, option->name, pcmk__str_casei)) { + return cluster_option_value(options, option); } } CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name)); @@ -368,143 +1424,142 @@ pcmk__cluster_option(GHashTable *options, /*! * \internal - * \brief Add a description element to a meta-data string - * - * \param[in,out] s Meta-data string to add to - * \param[in] tag Name of element to add ("longdesc" or "shortdesc") - * \param[in] desc Textual description to add - * \param[in] values If not \p NULL, the allowed values for the parameter - * \param[in] spaces If not \p NULL, spaces to insert at the beginning of - * each line + * \brief Output cluster option metadata as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] filter Group of <tt>enum pcmk__opt_flags</tt>; output an + * option only if its \c flags member has all these + * flags set + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options unless + * \c pcmk__opt_advanced and \c pcmk__opt_deprecated + * flags (respectively) are set in \p filter. This is + * always treated as true for XML output objects. + * + * \return Standard Pacemaker return code */ -static void -add_desc(GString *s, const char *tag, const char *desc, const char *values, - const char *spaces) +int +pcmk__output_cluster_options(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + uint32_t filter, bool all) { - char *escaped_en = crm_xml_escape(desc); - - if (spaces != NULL) { - g_string_append(s, spaces); - } - pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL); - - if (values != NULL) { - pcmk__g_strcat(s, " Allowed values: ", values, NULL); - } - pcmk__g_strcat(s, "</", tag, ">\n", NULL); - -#ifdef ENABLE_NLS - { - static const char *locale = NULL; - - char *localized = crm_xml_escape(_(desc)); - - if (strcmp(escaped_en, localized) != 0) { - if (locale == NULL) { - locale = strtok(setlocale(LC_ALL, NULL), "_"); - } - - if (spaces != NULL) { - g_string_append(s, spaces); - } - pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized, - NULL); - - if (values != NULL) { - pcmk__g_strcat(s, _(" Allowed values: "), _(values), NULL); - } - pcmk__g_strcat(s, "</", tag, ">\n", NULL); - } - free(localized); - } -#endif - - free(escaped_en); + return out->message(out, "option-list", name, desc_short, desc_long, filter, + cluster_options, all); } -gchar * -pcmk__format_option_metadata(const char *name, const char *desc_short, - const char *desc_long, - pcmk__cluster_option_t *option_list, int len) +/*! + * \internal + * \brief Output primitive resource meta-attributes as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options. This is always + * treated as true for XML output objects. + * + * \return Standard Pacemaker return code + */ +int +pcmk__output_primitive_meta(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + bool all) { - /* big enough to hold "pacemaker-schedulerd metadata" output */ - GString *s = g_string_sized_new(13000); - - pcmk__g_strcat(s, - "<?xml version=\"1.0\"?>\n" - "<resource-agent name=\"", name, "\" " - "version=\"" PACEMAKER_VERSION "\">\n" - " <version>" PCMK_OCF_VERSION "</version>\n", NULL); - - add_desc(s, "longdesc", desc_long, NULL, " "); - add_desc(s, "shortdesc", desc_short, NULL, " "); - - g_string_append(s, " <parameters>\n"); - - for (int lpc = 0; lpc < len; lpc++) { - const char *opt_name = option_list[lpc].name; - const char *opt_type = option_list[lpc].type; - const char *opt_values = option_list[lpc].values; - const char *opt_default = option_list[lpc].default_value; - const char *opt_desc_short = option_list[lpc].description_short; - const char *opt_desc_long = option_list[lpc].description_long; - - // The standard requires long and short parameter descriptions - CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL)); - - if (opt_desc_short == NULL) { - opt_desc_short = opt_desc_long; - } else if (opt_desc_long == NULL) { - opt_desc_long = opt_desc_short; - } + return out->message(out, "option-list", name, desc_short, desc_long, + pcmk__opt_none, primitive_meta, all); +} - // The standard requires a parameter type - CRM_ASSERT(opt_type != NULL); +/*! + * \internal + * \brief Output fence device common parameter metadata as OCF-like XML + * + * These are parameters that are available for all fencing resources, regardless + * of type. They are processed by Pacemaker, rather than by the fence agent or + * the fencing library. + * + * \param[in,out] out Output object + * \param[in] name Fake resource agent name for the option list + * \param[in] desc_short Short description of the option list + * \param[in] desc_long Long description of the option list + * \param[in] all If \c true, output all options; otherwise, exclude + * advanced and deprecated options. This is always + * treated as true for XML output objects. + * + * \return Standard Pacemaker return code + */ +int +pcmk__output_fencing_params(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + bool all) +{ + return out->message(out, "option-list", name, desc_short, desc_long, + pcmk__opt_none, fencing_params, all); +} - pcmk__g_strcat(s, " <parameter name=\"", opt_name, "\">\n", NULL); +/*! + * \internal + * \brief Output a list of cluster options for a daemon + * + * \brief[in,out] out Output object + * \brief[in] name Daemon name + * \brief[in] desc_short Short description of the option list + * \brief[in] desc_long Long description of the option list + * \brief[in] filter <tt>enum pcmk__opt_flags</tt> flag corresponding + * to daemon + * + * \return Standard Pacemaker return code + */ +int +pcmk__daemon_metadata(pcmk__output_t *out, const char *name, + const char *desc_short, const char *desc_long, + enum pcmk__opt_flags filter) +{ + // @COMPAT Drop this function when we drop daemon metadata + pcmk__output_t *tmp_out = NULL; + xmlNode *top = NULL; + const xmlNode *metadata = NULL; + GString *metadata_s = NULL; - add_desc(s, "longdesc", opt_desc_long, opt_values, " "); - add_desc(s, "shortdesc", opt_desc_short, NULL, " "); + int rc = pcmk__output_new(&tmp_out, "xml", "/dev/null", NULL); - pcmk__g_strcat(s, " <content type=\"", opt_type, "\"", NULL); - if (opt_default != NULL) { - pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL); - } + if (rc != pcmk_rc_ok) { + return rc; + } - if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) { - char *str = strdup(opt_values); - const char *delim = ", "; - char *ptr = strtok(str, delim); + pcmk__output_set_legacy_xml(tmp_out); - g_string_append(s, ">\n"); + if (filter == pcmk__opt_fencing) { + pcmk__output_fencing_params(tmp_out, name, desc_short, desc_long, true); + } else { + pcmk__output_cluster_options(tmp_out, name, desc_short, desc_long, + (uint32_t) filter, true); + } - while (ptr != NULL) { - pcmk__g_strcat(s, " <option value=\"", ptr, "\" />\n", - NULL); - ptr = strtok(NULL, delim); - } - g_string_append_printf(s, " </content>\n"); - free(str); + tmp_out->finish(tmp_out, CRM_EX_OK, false, (void **) &top); + metadata = pcmk__xe_first_child(top, PCMK_XE_RESOURCE_AGENT, NULL, NULL); - } else { - g_string_append(s, "/>\n"); - } + metadata_s = g_string_sized_new(16384); + pcmk__xml_string(metadata, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, + metadata_s, 0); - g_string_append(s, " </parameter>\n"); - } - g_string_append(s, " </parameters>\n</resource-agent>\n"); + out->output_xml(out, PCMK_XE_METADATA, metadata_s->str); - return g_string_free(s, FALSE); + pcmk__output_free(tmp_out); + free_xml(top); + g_string_free(metadata_s, TRUE); + return pcmk_rc_ok; } void -pcmk__validate_cluster_options(GHashTable *options, - pcmk__cluster_option_t *option_list, int len) +pcmk__validate_cluster_options(GHashTable *options) { - for (int lpc = 0; lpc < len; lpc++) { - cluster_option_value(options, option_list[lpc].is_valid, - option_list[lpc].name, - option_list[lpc].alt_name, - option_list[lpc].default_value); + for (const pcmk__cluster_option_t *option = cluster_options; + option->name != NULL; option++) { + + cluster_option_value(options, option); } } diff --git a/lib/common/options_display.c b/lib/common/options_display.c new file mode 100644 index 0000000..9798b31 --- /dev/null +++ b/lib/common/options_display.c @@ -0,0 +1,493 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <glib.h> // GSList, GString + +#include "crmcommon_private.h" + +/*! + * \internal + * \brief Output an option's possible values + * + * \param[in,out] out Output object + * \param[in] option Option whose possible values to add + */ +static void +add_possible_values_default(pcmk__output_t *out, + const pcmk__cluster_option_t *option) +{ + const char *id = _("Possible values"); + GString *buf = g_string_sized_new(256); + + CRM_ASSERT(option->type != NULL); + + if (pcmk_is_set(option->flags, pcmk__opt_generated)) { + id = _("Possible values (generated by Pacemaker)"); + } + + if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) { + const char *delim = ", "; + bool found_default = (option->default_value == NULL); + char *str = pcmk__str_copy(option->values); + + for (const char *value = strtok(str, delim); value != NULL; + value = strtok(NULL, delim)) { + + if (buf->len > 0) { + g_string_append(buf, delim); + } + g_string_append_c(buf, '"'); + g_string_append(buf, value); + g_string_append_c(buf, '"'); + + if (!found_default && (strcmp(value, option->default_value) == 0)) { + found_default = true; + g_string_append(buf, _(" (default)")); + } + } + free(str); + + } else if (option->default_value != NULL) { + pcmk__g_strcat(buf, + option->type, _(" (default: \""), option->default_value, + "\")", NULL); + + } else { + pcmk__g_strcat(buf, option->type, _(" (no default)"), NULL); + } + + out->list_item(out, id, "%s", buf->str); + g_string_free(buf, TRUE); +} + +/*! + * \internal + * \brief Output a single option's metadata + * + * \param[in,out] out Output object + * \param[in] option Option to add + */ +static void +add_option_metadata_default(pcmk__output_t *out, + const pcmk__cluster_option_t *option) +{ + const char *desc_short = option->description_short; + const char *desc_long = option->description_long; + + CRM_ASSERT((desc_short != NULL) || (desc_long != NULL)); + + if (desc_short == NULL) { + desc_short = desc_long; + desc_long = NULL; + } + + out->list_item(out, option->name, "%s", _(desc_short)); + + out->begin_list(out, NULL, NULL, NULL); + + if (desc_long != NULL) { + out->list_item(out, NULL, "%s", _(desc_long)); + } + add_possible_values_default(out, option); + out->end_list(out); +} + +/*! + * \internal + * \brief Output the metadata for a list of options + * + * \param[in,out] out Output object + * \param[in] args Message-specific arguments + * + * \return Standard Pacemaker return code + * + * \note \p args should contain the following: + * -# Fake resource agent name for the option list (ignored) + * -# Short description of option list + * -# Long description of option list + * -# Filter: Group of <tt>enum pcmk__opt_flags</tt>; output an option + * only if its \c flags member has all these flags set + * -# <tt>NULL</tt>-terminated list of options whose metadata to format + * -# All: If \c true, output all options; otherwise, exclude advanced and + * deprecated options unless \c pcmk__opt_advanced and + * \c pcmk__opt_deprecated flags (respectively) are set in the filter. + */ +PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *", + "uint32_t", "const pcmk__cluster_option_t *", "bool") +static int +option_list_default(pcmk__output_t *out, va_list args) +{ + const char *name G_GNUC_UNUSED = va_arg(args, const char *); + const char *desc_short = va_arg(args, const char *); + const char *desc_long = va_arg(args, const char *); + const uint32_t filter = va_arg(args, uint32_t); + const pcmk__cluster_option_t *option_list = + va_arg(args, pcmk__cluster_option_t *); + const bool all = (bool) va_arg(args, int); + + const bool show_deprecated = all + || pcmk_is_set(filter, pcmk__opt_deprecated); + const bool show_advanced = all || pcmk_is_set(filter, pcmk__opt_advanced); + bool old_fancy = false; + + GSList *deprecated = NULL; + GSList *advanced = NULL; + + CRM_ASSERT((out != NULL) && (desc_short != NULL) && (desc_long != NULL) + && (option_list != NULL)); + + old_fancy = pcmk__output_text_get_fancy(out); + pcmk__output_text_set_fancy(out, true); + + out->info(out, "%s", _(desc_short)); + out->spacer(out); + out->info(out, "%s", _(desc_long)); + out->begin_list(out, NULL, NULL, NULL); + + for (const pcmk__cluster_option_t *option = option_list; + option->name != NULL; option++) { + + // Store deprecated and advanced options to display later if appropriate + if (pcmk_all_flags_set(option->flags, filter)) { + if (pcmk_is_set(option->flags, pcmk__opt_deprecated)) { + if (show_deprecated) { + deprecated = g_slist_prepend(deprecated, (gpointer) option); + } + + } else if (pcmk_is_set(option->flags, pcmk__opt_advanced)) { + if (show_advanced) { + advanced = g_slist_prepend(advanced, (gpointer) option); + } + + } else { + out->spacer(out); + add_option_metadata_default(out, option); + } + } + } + + if (advanced != NULL) { + advanced = g_slist_reverse(advanced); + + out->spacer(out); + out->begin_list(out, NULL, NULL, _("ADVANCED OPTIONS")); + for (const GSList *iter = advanced; iter != NULL; iter = iter->next) { + const pcmk__cluster_option_t *option = iter->data; + + out->spacer(out); + add_option_metadata_default(out, option); + } + out->end_list(out); + g_slist_free(advanced); + } + + if (deprecated != NULL) { + deprecated = g_slist_reverse(deprecated); + + out->spacer(out); + out->begin_list(out, NULL, NULL, + _("DEPRECATED OPTIONS (will be removed in a future " + "release)")); + for (const GSList *iter = deprecated; iter != NULL; iter = iter->next) { + const pcmk__cluster_option_t *option = iter->data; + + out->spacer(out); + add_option_metadata_default(out, option); + } + out->end_list(out); + g_slist_free(deprecated); + } + + out->end_list(out); + pcmk__output_text_set_fancy(out, old_fancy); + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Add a description element to an OCF-like metadata XML node + * + * Include a translation based on the current locale if \c ENABLE_NLS is + * defined. + * + * \param[in,out] out Output object + * \param[in] for_long If \c true, add long description; otherwise, add + * short description + * \param[in] desc Textual description to add + */ +static void +add_desc_xml(pcmk__output_t *out, bool for_long, const char *desc) +{ + const char *tag = (for_long? PCMK_XE_LONGDESC : PCMK_XE_SHORTDESC); + xmlNode *node = pcmk__output_create_xml_text_node(out, tag, desc); + + crm_xml_add(node, PCMK_XA_LANG, PCMK__VALUE_EN); + +#ifdef ENABLE_NLS + { + static const char *locale = NULL; + + if (strcmp(desc, _(desc)) == 0) { + return; + } + + if (locale == NULL) { + locale = strtok(setlocale(LC_ALL, NULL), "_"); + } + node = pcmk__output_create_xml_text_node(out, tag, _(desc)); + crm_xml_add(node, PCMK_XA_LANG, locale); + } +#endif +} + +/*! + * \internal + * \brief Output an option's possible values + * + * Add a \c PCMK_XE_OPTION element for each of the option's possible values. + * + * \param[in,out] out Output object + * \param[in] option Option whose possible values to add + */ +static void +add_possible_values_xml(pcmk__output_t *out, + const pcmk__cluster_option_t *option) +{ + if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) { + const char *delim = ", "; + char *str = pcmk__str_copy(option->values); + const char *ptr = strtok(str, delim); + + while (ptr != NULL) { + pcmk__output_create_xml_node(out, PCMK_XE_OPTION, + PCMK_XA_VALUE, ptr, + NULL); + ptr = strtok(NULL, delim); + } + free(str); + } +} + +/*! + * \internal + * \brief Map an option type to one suitable for daemon metadata + * + * \param[in] type Option type to map + * + * \return String suitable for daemon metadata to display as an option type + */ +static const char * +map_legacy_option_type(const char *type) +{ + // @COMPAT Drop this function when we drop daemon metadata commands + if (pcmk__str_any_of(type, PCMK_VALUE_DURATION, PCMK_VALUE_TIMEOUT, NULL)) { + return PCMK__VALUE_TIME; + + } else if (pcmk__str_any_of(type, + PCMK_VALUE_NONNEGATIVE_INTEGER, + PCMK_VALUE_SCORE, NULL)) { + return PCMK_VALUE_INTEGER; + + } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_none)) { + return PCMK_VALUE_STRING; + + } else { + return type; + } +} + +/*! + * \internal + * \brief Add a \c PCMK_XE_PARAMETER element to an OCF-like metadata XML node + * + * \param[in,out] out Output object + * \param[in] option Option to add as a \c PCMK_XE_PARAMETER element + */ +static void +add_option_metadata_xml(pcmk__output_t *out, + const pcmk__cluster_option_t *option) +{ + const char *type = option->type; + const char *desc_long = option->description_long; + const char *desc_short = option->description_short; + const bool advanced = pcmk_is_set(option->flags, pcmk__opt_advanced); + const bool deprecated = pcmk_is_set(option->flags, pcmk__opt_deprecated); + const bool generated = pcmk_is_set(option->flags, pcmk__opt_generated); + + // OCF requires "1"/"0" and does not allow "true"/"false + // @COMPAT Variables no longer needed after we drop legacy mode + const char *advanced_s = advanced? "1" : "0"; + const char *generated_s = generated? "1" : "0"; + + // @COMPAT For daemon metadata only; drop when daemon metadata is dropped + const bool legacy = pcmk__output_get_legacy_xml(out); + char *desc_long_legacy = NULL; + GString *desc_short_legacy = NULL; + + // The standard requires a parameter type + CRM_ASSERT(type != NULL); + + // The standard requires long and short parameter descriptions + CRM_ASSERT((desc_long != NULL) || (desc_short != NULL)); + + if (desc_long == NULL) { + desc_long = desc_short; + } else if (desc_short == NULL) { + desc_short = desc_long; + } + + if (legacy) { + // This is ugly but it will go away at a major release bump + type = map_legacy_option_type(type); + + if (option->values != NULL) { + desc_long_legacy = crm_strdup_printf("%s Allowed values: %s", + desc_long, option->values); + desc_long = desc_long_legacy; + } + + if (deprecated || advanced) { + const size_t init_sz = 1023; + + if (desc_long != option->description_long) { + /* desc_long was NULL and got assigned desc_short, which was + * non-empty. Let desc_long have the "real" description, and put + * the flag in desc_short. + */ + desc_short = ""; + } else { + desc_short = pcmk__s(option->description_short, ""); + } + + if (deprecated) { + pcmk__add_separated_word(&desc_short_legacy, init_sz, + "*** Deprecated ***", NULL); + } + if (advanced) { + pcmk__add_separated_word(&desc_short_legacy, init_sz, + "*** Advanced Use Only ***", NULL); + } + pcmk__add_separated_word(&desc_short_legacy, 0, desc_short, NULL); + + desc_short = desc_short_legacy->str; + } + + /* These must be NULL when used as attribute values later. + * PCMK_XA_ADVANCED and PCMK_XA_GENERATED break validation for some + * legacy tools. + */ + advanced_s = NULL; + generated_s = NULL; + } + + pcmk__output_xml_create_parent(out, PCMK_XE_PARAMETER, + PCMK_XA_NAME, option->name, + PCMK_XA_ADVANCED, advanced_s, + PCMK_XA_GENERATED, generated_s, + NULL); + + if (deprecated && !legacy) { + // No need yet to support "replaced-with" or "desc"; add if needed + pcmk__output_create_xml_node(out, PCMK_XE_DEPRECATED, NULL); + } + add_desc_xml(out, true, desc_long); + add_desc_xml(out, false, desc_short); + + pcmk__output_xml_create_parent(out, PCMK_XE_CONTENT, + PCMK_XA_TYPE, type, + PCMK_XA_DEFAULT, option->default_value, + NULL); + + add_possible_values_xml(out, option); + + pcmk__output_xml_pop_parent(out); + pcmk__output_xml_pop_parent(out); + + free(desc_long_legacy); + if (desc_short_legacy != NULL) { + g_string_free(desc_short_legacy, TRUE); + } +} + +/*! + * \internal + * \brief Output the metadata for a list of options as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] args Message-specific arguments + * + * \return Standard Pacemaker return code + * + * \note \p args should contain the following: + * -# Fake resource agent name for the option list + * -# Short description of option list + * -# Long description of option list + * -# Filter: Group of <tt>enum pcmk__opt_flags</tt>; output an option + * only if its \c flags member has all these flags set + * -# <tt>NULL</tt>-terminated list of options whose metadata to format + * -# Whether to output all options (ignored, treated as \c true) + */ +PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *", + "uint32_t", "const pcmk__cluster_option_t *", "bool") +static int +option_list_xml(pcmk__output_t *out, va_list args) +{ + const char *name = va_arg(args, const char *); + const char *desc_short = va_arg(args, const char *); + const char *desc_long = va_arg(args, const char *); + const uint32_t filter = va_arg(args, uint32_t); + const pcmk__cluster_option_t *option_list = + va_arg(args, pcmk__cluster_option_t *); + + CRM_ASSERT((out != NULL) && (name != NULL) && (desc_short != NULL) + && (desc_long != NULL) && (option_list != NULL)); + + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT, + PCMK_XA_NAME, name, + PCMK_XA_VERSION, PACEMAKER_VERSION, + NULL); + + pcmk__output_create_xml_text_node(out, PCMK_XE_VERSION, PCMK_OCF_VERSION); + add_desc_xml(out, true, desc_long); + add_desc_xml(out, false, desc_short); + + pcmk__output_xml_create_parent(out, PCMK_XE_PARAMETERS, NULL); + + for (const pcmk__cluster_option_t *option = option_list; + option->name != NULL; option++) { + + if (pcmk_all_flags_set(option->flags, filter)) { + add_option_metadata_xml(out, option); + } + } + + pcmk__output_xml_pop_parent(out); + pcmk__output_xml_pop_parent(out); + return pcmk_rc_ok; +} + +static pcmk__message_entry_t fmt_functions[] = { + { "option-list", "default", option_list_default }, + { "option-list", "xml", option_list_xml }, + + { NULL, NULL, NULL } +}; + +/*! + * \internal + * \brief Register the formatting functions for option lists + * + * \param[in,out] out Output object + */ +void +pcmk__register_option_messages(pcmk__output_t *out) { + pcmk__register_messages(out, fmt_functions); +} diff --git a/lib/common/output.c b/lib/common/output.c index 2ea9b0b..92fbfda 100644 --- a/lib/common/output.c +++ b/lib/common/output.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. * @@ -18,10 +18,12 @@ static GHashTable *formatters = NULL; #if defined(PCMK__UNIT_TESTING) +// LCOV_EXCL_START GHashTable * pcmk__output_formatters(void) { return formatters; } +// LCOV_EXCL_STOP #endif void @@ -114,9 +116,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, int rc = pcmk__bare_output_new(out, fmt_name, filename, argv); if (rc == pcmk_rc_ok) { - /* Register libcrmcommon messages (currently they exist only for - * patchset) - */ + // Register libcrmcommon messages + pcmk__register_option_messages(*out); pcmk__register_patchset_messages(*out); } return rc; @@ -127,8 +128,15 @@ pcmk__register_format(GOptionGroup *group, const char *name, pcmk__output_factory_t create, const GOptionEntry *options) { + char *name_copy = NULL; + CRM_ASSERT(create != NULL && !pcmk__str_empty(name)); + name_copy = strdup(name); + if (name_copy == NULL) { + return ENOMEM; + } + if (formatters == NULL) { formatters = pcmk__strkey_table(free, NULL); } @@ -137,7 +145,7 @@ pcmk__register_format(GOptionGroup *group, const char *name, g_option_group_add_entries(group, options); } - g_hash_table_insert(formatters, strdup(name), create); + g_hash_table_insert(formatters, name_copy, create); return pcmk_rc_ok; } @@ -189,7 +197,7 @@ pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn) { CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id) && fn != NULL); - g_hash_table_replace(out->messages, strdup(message_id), fn); + g_hash_table_replace(out->messages, pcmk__str_copy(message_id), fn); } void @@ -228,7 +236,7 @@ pcmk__output_and_clear_error(GError **error, pcmk__output_t *out) * functions that want to free any previous result supplied by the caller). * * \param[out] out Where to put newly created output object - * \param[in,out] xml If non-NULL, this will be freed + * \param[in,out] xml If \c *xml is non-NULL, this will be freed * * \return Standard Pacemaker return code */ @@ -239,6 +247,10 @@ pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) { { NULL, NULL, NULL } }; + if (xml == NULL) { + return EINVAL; + } + if (*xml != NULL) { xmlFreeNode(*xml); *xml = NULL; @@ -251,12 +263,19 @@ pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) { * \internal * \brief Finish and free an XML-only output object * - * \param[in,out] out Output object to free - * \param[out] xml If not NULL, where to store XML output + * \param[in,out] out Output object to free + * \param[in] exit_status The exit value of the whole program + * \param[out] xml If not NULL, where to store XML output */ void -pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml) { - out->finish(out, 0, FALSE, (void **) xml); +pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, + xmlNodePtr *xml) +{ + if (out == NULL) { + return; + } + + out->finish(out, exit_status, FALSE, (void **) xml); pcmk__output_free(out); } diff --git a/lib/common/output_html.c b/lib/common/output_html.c index 92e9010..afb2609 100644 --- a/lib/common/output_html.c +++ b/lib/common/output_html.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,22 +19,22 @@ #include <crm/common/xml.h> static const char *stylesheet_default = - ".bold { font-weight: bold }\n" + "." PCMK__VALUE_BOLD " { font-weight: bold }\n" - ".online { color: green }\n" - ".offline { color: red }\n" - ".maint { color: blue }\n" - ".standby { color: blue }\n" - ".health_red { color: red }\n" - ".health_yellow { color: GoldenRod }\n" + "." PCMK_VALUE_ONLINE " { color: green }\n" + "." PCMK_VALUE_OFFLINE " { color: red }\n" + "." PCMK__VALUE_MAINT " { color: blue }\n" + "." PCMK_VALUE_STANDBY " { color: blue }\n" + "." PCMK__VALUE_HEALTH_RED " { color: red }\n" + "." PCMK__VALUE_HEALTH_YELLOW " { color: GoldenRod }\n" - ".rsc-failed { color: red }\n" - ".rsc-failure-ignored { color: DarkGreen }\n" - ".rsc-managed { color: blue }\n" - ".rsc-multiple { color: orange }\n" - ".rsc-ok { color: green }\n" + "." PCMK__VALUE_RSC_FAILED " { color: red }\n" + "." PCMK__VALUE_RSC_FAILURE_IGNORED " { color: DarkGreen }\n" + "." PCMK__VALUE_RSC_MANAGED " { color: blue }\n" + "." PCMK__VALUE_RSC_MULTIPLE " { color: orange }\n" + "." PCMK__VALUE_RSC_OK " { color: green }\n" - ".warning { color: red; font-weight: bold }"; + "." PCMK__VALUE_WARNING " { color: red; font-weight: bold }"; static gboolean cgi_output = FALSE; static char *stylesheet_link = NULL; @@ -81,9 +81,13 @@ html_free_priv(pcmk__output_t *out) { priv = out->priv; - xmlFreeNode(priv->root); + free_xml(priv->root); + /* The elements of parent_q are xmlNodes that are a part of the + * priv->root document, so the above line already frees them. Don't + * call g_queue_free_full here. + */ g_queue_free(priv->parent_q); - g_slist_free(priv->errors); + g_slist_free_full(priv->errors, free); free(priv); out->priv = NULL; } @@ -108,10 +112,10 @@ html_init(pcmk__output_t *out) { priv->parent_q = g_queue_new(); - priv->root = create_xml_node(NULL, "html"); + priv->root = pcmk__xe_create(NULL, "html"); xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL); - crm_xml_add(priv->root, "lang", "en"); + crm_xml_add(priv->root, PCMK_XA_LANG, PCMK__VALUE_EN); g_queue_push_tail(priv->parent_q, priv->root); priv->errors = NULL; @@ -132,6 +136,7 @@ html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy private_data_t *priv = NULL; htmlNodePtr head_node = NULL; htmlNodePtr charset_node = NULL; + xmlNode *child_node = NULL; CRM_ASSERT(out != NULL); @@ -155,12 +160,14 @@ html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy head_node = xmlNewDocRawNode(NULL, NULL, (pcmkXmlStr) "head", NULL); if (title != NULL ) { - pcmk_create_xml_text_node(head_node, "title", title); + child_node = pcmk__xe_create(head_node, "title"); + pcmk__xe_set_content(child_node, "%s", title); } else if (out->request != NULL) { - pcmk_create_xml_text_node(head_node, "title", out->request); + child_node = pcmk__xe_create(head_node, "title"); + pcmk__xe_set_content(child_node, "%s", out->request); } - charset_node = create_xml_node(head_node, "meta"); + charset_node = pcmk__xe_create(head_node, PCMK__XE_META); crm_xml_add(charset_node, "charset", "utf-8"); /* Add any extra header nodes the caller might have created. */ @@ -174,10 +181,11 @@ html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy * stylesheet. The second can override the first. At least one should be * given. */ - pcmk_create_xml_text_node(head_node, "style", stylesheet_default); + child_node = pcmk__xe_create(head_node, "style"); + pcmk__xe_set_content(child_node, "%s", stylesheet_default); if (stylesheet_link != NULL) { - htmlNodePtr link_node = create_xml_node(head_node, "link"); + htmlNodePtr link_node = pcmk__xe_create(head_node, "link"); pcmk__xe_set_props(link_node, "rel", "stylesheet", "href", stylesheet_link, NULL); @@ -196,7 +204,7 @@ html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy } if (copy_dest != NULL) { - *copy_dest = copy_xml(priv->root); + *copy_dest = pcmk__xml_copy(NULL, priv->root); } g_slist_free_full(extra_headers, (GDestroyNotify) xmlFreeNode); @@ -224,15 +232,17 @@ html_subprocess_output(pcmk__output_t *out, int exit_status, rc_buf = crm_strdup_printf("Return code: %d", exit_status); pcmk__output_create_xml_text_node(out, "h2", "Command Output"); - pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, rc_buf); if (proc_stdout != NULL) { - pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout"); - pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, "Stdout"); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, + PCMK__VALUE_OUTPUT, proc_stdout); } if (proc_stderr != NULL) { - pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr"); - pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, "Stderr"); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, + PCMK__VALUE_OUTPUT, proc_stderr); } free(rc_buf); @@ -243,13 +253,17 @@ html_version(pcmk__output_t *out, bool extended) { CRM_ASSERT(out != NULL); pcmk__output_create_xml_text_node(out, "h2", "Version Information"); - pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker"); - pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION)); - pcmk__output_create_html_node(out, "div", NULL, NULL, + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, + "Program: Pacemaker"); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, + "Version: " PACEMAKER_VERSION); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, "Author: Andrew Beekhof and " "the Pacemaker project contributors"); - pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION)); - pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES)); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, + "Build: " BUILD_VERSION); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, + "Features: " CRM_FEATURES); } G_GNUC_PRINTF(2, 3) @@ -284,7 +298,7 @@ html_output_xml(pcmk__output_t *out, const char *name, const char *buf) { CRM_ASSERT(out != NULL); node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf); - crm_xml_add(node, "lang", "xml"); + crm_xml_add(node, PCMK_XA_LANG, "xml"); } G_GNUC_PRINTF(4, 5) @@ -349,7 +363,7 @@ html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { free(buf); if (name != NULL) { - crm_xml_add(item_node, "class", name); + crm_xml_add(item_node, PCMK_XA_CLASS, name); } } @@ -365,7 +379,9 @@ html_end_list(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; - /* Remove the <ul> tag. */ + /* Remove the <ul> tag, but do not free this result - it's still + * part of the document. + */ g_queue_pop_tail(priv->parent_q); pcmk__output_xml_pop_parent(out); @@ -441,16 +457,42 @@ pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, con node = pcmk__output_create_xml_text_node(out, element_name, text); if (class_name != NULL) { - crm_xml_add(node, "class", class_name); + crm_xml_add(node, PCMK_XA_CLASS, class_name); } if (id != NULL) { - crm_xml_add(node, "id", id); + crm_xml_add(node, PCMK_XA_ID, id); } return node; } +/*! + * \internal + * \brief Create a new HTML element under a given parent with ID and class + * + * \param[in,out] parent XML element that will be the new element's parent + * (\c NULL to create a new XML document with the new + * node as root) + * \param[in] name Name of new element + * \param[in] id CSS ID of new element (can be \c NULL) + * \param[in] class CSS class of new element (can be \c NULL) + * + * \return Newly created XML element (guaranteed not to be \c NULL) + */ +xmlNode * +pcmk__html_create(xmlNode *parent, const char *name, const char *id, + const char *class) +{ + xmlNode *node = pcmk__xe_create(parent, name); + + pcmk__xe_set_props(node, + PCMK_XA_CLASS, class, + PCMK_XA_ID, id, + NULL); + return node; +} + void pcmk__html_add_header(const char *name, ...) { htmlNodePtr header_node; diff --git a/lib/common/output_log.c b/lib/common/output_log.c index 54fa37e..81a39c8 100644 --- a/lib/common/output_log.c +++ b/lib/common/output_log.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. * @@ -16,10 +16,6 @@ #include <stdlib.h> #include <stdio.h> -GOptionEntry pcmk__log_output_entries[] = { - { NULL } -}; - typedef struct private_data_s { /* gathered in log_begin_list */ GQueue/*<char*>*/ *prefixes; @@ -165,8 +161,8 @@ log_output_xml(pcmk__output_t *out, const char *name, const char *buf) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; - node = create_xml_node(NULL, name); - xmlNodeSetContent(node, (pcmkXmlStr) buf); + node = pcmk__xe_create(NULL, name); + pcmk__xe_set_content(node, "%s", buf); do_crm_log_xml(priv->log_level, name, node); free(node); } @@ -353,34 +349,63 @@ pcmk__mk_log_output(char **argv) { return retval; } +/*! + * \internal + * \brief Get the log level for a log output object + * + * This returns 0 if the output object is not of log format. + * + * \param[in] out Output object + * + * \return Current log level for \p out + */ uint8_t pcmk__output_get_log_level(const pcmk__output_t *out) { - private_data_t *priv = NULL; + CRM_ASSERT(out != NULL); - CRM_ASSERT((out != NULL) && (out->priv != NULL)); - CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return 0); + if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) { + private_data_t *priv = out->priv; - priv = out->priv; - return priv->log_level; + CRM_ASSERT(priv != NULL); + return priv->log_level; + } + return 0; } +/*! + * \internal + * \brief Set the log level for a log output object + * + * This does nothing if the output object is not of log format. + * + * \param[in,out] out Output object + * \param[in] log_level Log level constant (\c LOG_ERR, etc.) to use + * + * \note \c LOG_INFO is used by default for new \c pcmk__output_t objects. + * \note Almost all formatted output messages respect this setting. However, + * <tt>out->err</tt> always logs at \c LOG_ERR. + */ void -pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level) { - private_data_t *priv = NULL; +pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level) +{ + CRM_ASSERT(out != NULL); - CRM_ASSERT(out != NULL && out->priv != NULL); - CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return); + if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) { + private_data_t *priv = out->priv; - priv = out->priv; - priv->log_level = log_level; + CRM_ASSERT(priv != NULL); + priv->log_level = log_level; + } } /*! * \internal * \brief Set the file, function, line, and tags used to filter log output * - * \param[in,out] out Logger output object + * This does nothing if the output object is not of log format. + * + * \param[in,out] out Output object * \param[in] file File name to filter with (or NULL for default) * \param[in] function Function name to filter with (or NULL for default) * \param[in] line Line number to filter with (or 0 for default) @@ -394,14 +419,15 @@ void pcmk__output_set_log_filter(pcmk__output_t *out, const char *file, const char *function, uint32_t line, uint32_t tags) { - private_data_t *priv = NULL; + CRM_ASSERT(out != NULL); - CRM_ASSERT((out != NULL) && (out->priv != NULL)); - CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return); + if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) { + private_data_t *priv = out->priv; - priv = out->priv; - priv->file = file; - priv->function = function; - priv->line = line; - priv->tags = tags; + CRM_ASSERT(priv != NULL); + priv->file = file; + priv->function = function; + priv->line = line; + priv->tags = tags; + } } diff --git a/lib/common/output_none.c b/lib/common/output_none.c index 581a8b4..d1cdacc 100644 --- a/lib/common/output_none.c +++ b/lib/common/output_none.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,10 +15,6 @@ #include <crm/crm.h> #include <crm/common/cmdline_internal.h> -GOptionEntry pcmk__none_output_entries[] = { - { NULL } -}; - static void none_free_priv(pcmk__output_t *out) { /* This function intentionally left blank */ @@ -120,7 +116,7 @@ pcmk__mk_none_output(char **argv) { return NULL; } - retval->fmt_name = PCMK__VALUE_NONE; + retval->fmt_name = PCMK_VALUE_NONE; retval->request = pcmk__quote_cmdline(argv); retval->init = none_init; diff --git a/lib/common/output_text.c b/lib/common/output_text.c index 6bd362d..d43e29f 100644 --- a/lib/common/output_text.c +++ b/lib/common/output_text.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,10 +15,14 @@ #include <glib.h> #include <termios.h> +#include "crmcommon_private.h" + +// @COMPAT Drop at 3.0.0 static gboolean fancy = FALSE; +// @COMPAT Drop at 3.0.0 GOptionEntry pcmk__text_output_entries[] = { - { "text-fancy", 0, 0, G_OPTION_ARG_NONE, &fancy, + { "text-fancy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &fancy, "Use more highly formatted output (requires --output-as=text)", NULL }, @@ -33,9 +37,18 @@ typedef struct text_list_data_s { typedef struct private_data_s { GQueue *parent_q; + bool fancy; } private_data_t; static void +free_list_data(gpointer data) { + text_list_data_t *list_data = data; + + free(list_data->singular_noun); + free(list_data->plural_noun); +} + +static void text_free_priv(pcmk__output_t *out) { private_data_t *priv = NULL; @@ -45,7 +58,7 @@ text_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; } @@ -59,15 +72,14 @@ text_init(pcmk__output_t *out) { /* If text_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; - } else { - out->priv = calloc(1, sizeof(private_data_t)); - if (out->priv == NULL) { - return false; - } + } - priv = out->priv; + out->priv = calloc(1, sizeof(private_data_t)); + if (out->priv == NULL) { + return false; } + priv = out->priv; priv->parent_q = g_queue_new(); return true; } @@ -80,6 +92,9 @@ text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy static void text_reset(pcmk__output_t *out) { + private_data_t *priv = NULL; + bool old_fancy = false; + CRM_ASSERT(out != NULL); if (out->dest != stdout) { @@ -88,8 +103,15 @@ text_reset(pcmk__output_t *out) { CRM_ASSERT(out->dest != NULL); + // Save priv->fancy before free/init sequence overwrites it + priv = out->priv; + old_fancy = priv->fancy; + text_free_priv(out); text_init(out); + + priv = out->priv; + priv->fancy = old_fancy; } static void @@ -192,17 +214,17 @@ text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plur va_start(ap, format); - if (fancy && format) { + if ((fancy || priv->fancy) && (format != NULL)) { pcmk__indented_vprintf(out, format, ap); fprintf(out->dest, ":\n"); } va_end(ap); - new_list = calloc(1, sizeof(text_list_data_t)); + new_list = pcmk__assert_alloc(1, sizeof(text_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); } @@ -210,13 +232,15 @@ text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plur G_GNUC_PRINTF(3, 4) static void text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) { + private_data_t *priv = NULL; va_list ap; CRM_ASSERT(out != NULL); + priv = out->priv; va_start(ap, format); - if (fancy) { + if (fancy || priv->fancy) { if (id != NULL) { /* Not really a good way to do this all in one call, so make it two. * The first handles the indentation and list styling. The second @@ -269,7 +293,7 @@ text_end_list(pcmk__output_t *out) { } } - free(node); + free_list_data(node); } static bool @@ -336,6 +360,52 @@ pcmk__mk_text_output(char **argv) { return retval; } +/*! + * \internal + * \brief Check whether fancy output is enabled for a text output object + * + * This returns \c false if the output object is not of text format. + * + * \param[in] out Output object + * + * \return \c true if \p out has fancy output enabled, or \c false otherwise + */ +bool +pcmk__output_text_get_fancy(pcmk__output_t *out) +{ + CRM_ASSERT(out != NULL); + + if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) { + private_data_t *priv = out->priv; + + CRM_ASSERT(priv != NULL); + return priv->fancy; + } + return false; +} + +/*! + * \internal + * \brief Enable or disable fancy output for a text output object + * + * This does nothing if the output object is not of text format. + * + * \param[in,out] out Output object + * \param[in] enabled Whether fancy output should be enabled for \p out + */ +void +pcmk__output_text_set_fancy(pcmk__output_t *out, bool enabled) +{ + CRM_ASSERT(out != NULL); + + if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) { + private_data_t *priv = out->priv; + + CRM_ASSERT(priv != NULL); + priv->fancy = enabled; + } +} + G_GNUC_PRINTF(2, 0) void pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) { @@ -363,10 +433,14 @@ pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) { G_GNUC_PRINTF(2, 0) void pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) { + private_data_t *priv = NULL; + CRM_ASSERT(out != NULL); CRM_CHECK(pcmk__str_eq(out->fmt_name, "text", pcmk__str_none), return); - if (fancy) { + priv = out->priv; + + if (fancy || priv->fancy) { int level = 0; private_data_t *priv = out->priv; @@ -428,7 +502,7 @@ pcmk__text_prompt(const char *prompt, bool echo, char **dest) #if HAVE_SSCANF_M rc = scanf("%ms", dest); #else - *dest = calloc(1, 1024); + *dest = pcmk__assert_alloc(1, 1024); rc = scanf("%1023s", *dest); #endif fprintf(stderr, "\n"); diff --git a/lib/common/output_xml.c b/lib/common/output_xml.c index ba61145..9b0d417 100644 --- a/lib/common/output_xml.c +++ b/lib/common/output_xml.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. * @@ -14,57 +14,59 @@ #include <stdlib.h> #include <stdio.h> #include <crm/crm.h> -#include <crm/common/output.h> -#include <crm/common/xml.h> -#include <crm/common/xml_internal.h> /* pcmk__xml2fd */ #include <glib.h> #include <crm/common/cmdline_internal.h> +#include <crm/common/output.h> #include <crm/common/xml.h> - -static gboolean legacy_xml = FALSE; -static gboolean simple_list = FALSE; -static gboolean substitute = FALSE; - -GOptionEntry pcmk__xml_output_entries[] = { - { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml, - NULL, - NULL }, - { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list, - NULL, - NULL }, - { "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute, - NULL, - NULL }, - - { NULL } -}; +#include <crm/common/xml_internal.h> // pcmk__xml2fd typedef struct subst_s { const char *from; const char *to; } subst_t; -static subst_t substitutions[] = { - { "Active Resources", "resources" }, - { "Assignment Scores", "allocations" }, - { "Assignment Scores and Utilization Information", "allocations_utilizations" }, - { "Cluster Summary", "summary" }, - { "Current cluster status", "cluster_status" }, - { "Executing Cluster Transition", "transition" }, - { "Failed Resource Actions", "failures" }, - { "Fencing History", "fence_history" }, - { "Full List of Resources", "resources" }, - { "Inactive Resources", "resources" }, - { "Migration Summary", "node_history" }, - { "Negative Location Constraints", "bans" }, - { "Node Attributes", "node_attributes" }, - { "Operations", "node_history" }, - { "Resource Config", "resource_config" }, - { "Resource Operations", "operations" }, - { "Revised Cluster Status", "revised_cluster_status" }, - { "Transition Summary", "actions" }, - { "Utilization Information", "utilizations" }, +static const subst_t substitutions[] = { + { "Active Resources", + PCMK_XE_RESOURCES, }, + { "Assignment Scores", + PCMK_XE_ALLOCATIONS, }, + { "Assignment Scores and Utilization Information", + PCMK_XE_ALLOCATIONS_UTILIZATIONS, }, + { "Cluster Summary", + PCMK_XE_SUMMARY, }, + { "Current cluster status", + PCMK_XE_CLUSTER_STATUS, }, + { "Executing Cluster Transition", + PCMK_XE_TRANSITION, }, + { "Failed Resource Actions", + PCMK_XE_FAILURES, }, + { "Fencing History", + PCMK_XE_FENCE_HISTORY, }, + { "Full List of Resources", + PCMK_XE_RESOURCES, }, + { "Inactive Resources", + PCMK_XE_RESOURCES, }, + { "Migration Summary", + PCMK_XE_NODE_HISTORY, }, + { "Negative Location Constraints", + PCMK_XE_BANS, }, + { "Node Attributes", + PCMK_XE_NODE_ATTRIBUTES, }, + { "Operations", + PCMK_XE_NODE_HISTORY, }, + { "Resource Config", + PCMK_XE_RESOURCE_CONFIG, }, + { "Resource Operations", + PCMK_XE_OPERATIONS, }, + { "Revised Cluster Status", + PCMK_XE_REVISED_CLUSTER_STATUS, }, + { "Timings", + PCMK_XE_TIMINGS, }, + { "Transition Summary", + PCMK_XE_ACTIONS, }, + { "Utilization Information", + PCMK_XE_UTILIZATIONS, }, { NULL, NULL } }; @@ -82,8 +84,46 @@ typedef struct private_data_s { GSList *errors; /* End members that must match the HTML version */ bool legacy_xml; + bool list_element; } private_data_t; +static bool +has_root_node(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + priv = out->priv; + return priv != NULL && priv->root != NULL; +} + +static void +add_root_node(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + /* has_root_node will assert if out is NULL, so no need to do it here */ + if (has_root_node(out)) { + return; + } + + priv = out->priv; + + if (priv->legacy_xml) { + priv->root = pcmk__xe_create(NULL, PCMK_XE_CRM_MON); + crm_xml_add(priv->root, PCMK_XA_VERSION, PACEMAKER_VERSION); + } else { + priv->root = pcmk__xe_create(NULL, PCMK_XE_PACEMAKER_RESULT); + crm_xml_add(priv->root, PCMK_XA_API_VERSION, PCMK__API_VERSION); + crm_xml_add(priv->root, PCMK_XA_REQUEST, + pcmk__s(out->request, "libpacemaker")); + } + + priv->parent_q = g_queue_new(); + g_queue_push_tail(priv->parent_q, priv->root); +} + static void xml_free_priv(pcmk__output_t *out) { private_data_t *priv = NULL; @@ -94,9 +134,16 @@ xml_free_priv(pcmk__output_t *out) { priv = out->priv; - free_xml(priv->root); - g_queue_free(priv->parent_q); - g_slist_free(priv->errors); + if (has_root_node(out)) { + free_xml(priv->root); + /* The elements of parent_q are xmlNodes that are a part of the + * priv->root document, so the above line already frees them. Don't + * call g_queue_free_full here. + */ + g_queue_free(priv->parent_q); + } + + g_slist_free_full(priv->errors, free); free(priv); out->priv = NULL; } @@ -119,36 +166,18 @@ xml_init(pcmk__output_t *out) { priv = out->priv; } - if (legacy_xml) { - priv->root = create_xml_node(NULL, "crm_mon"); - crm_xml_add(priv->root, "version", PACEMAKER_VERSION); - } else { - priv->root = create_xml_node(NULL, "pacemaker-result"); - crm_xml_add(priv->root, "api-version", PCMK__API_VERSION); - - if (out->request != NULL) { - crm_xml_add(priv->root, "request", out->request); - } - } - - priv->parent_q = g_queue_new(); priv->errors = NULL; - g_queue_push_tail(priv->parent_q, priv->root); - - /* Copy this from the file-level variable. This means that it is only settable - * as a command line option, and that pcmk__output_new must be called after all - * command line processing is completed. - */ - priv->legacy_xml = legacy_xml; return true; } static void add_error_node(gpointer data, gpointer user_data) { - char *str = (char *) data; + const char *str = (const char *) data; xmlNodePtr node = (xmlNodePtr) user_data; - pcmk_create_xml_text_node(node, "error", str); + + node = pcmk__xe_create(node, PCMK_XE_ERROR); + pcmk__xe_set_content(node, "%s", str); } static void @@ -159,14 +188,13 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ CRM_ASSERT(out != NULL); priv = out->priv; - /* If root is NULL, xml_init failed and we are being called from pcmk__output_free - * in the pcmk__output_new path. - */ - if (priv == NULL || priv->root == NULL) { + if (priv == NULL) { return; } - if (legacy_xml) { + add_root_node(out); + + if (priv->legacy_xml) { GSList *node = priv->errors; if (exit_status != CRM_EX_OK) { @@ -180,13 +208,14 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ } else { char *rc_as_str = pcmk__itoa(exit_status); - node = create_xml_node(priv->root, "status"); - pcmk__xe_set_props(node, "code", rc_as_str, - "message", crm_exit_str(exit_status), + node = pcmk__xe_create(priv->root, PCMK_XE_STATUS); + pcmk__xe_set_props(node, + PCMK_XA_CODE, rc_as_str, + PCMK_XA_MESSAGE, crm_exit_str(exit_status), NULL); if (g_slist_length(priv->errors) > 0) { - xmlNodePtr errors_node = create_xml_node(node, "errors"); + xmlNodePtr errors_node = pcmk__xe_create(node, PCMK_XE_ERRORS); g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node); } @@ -198,7 +227,7 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ } if (copy_dest != NULL) { - *copy_dest = copy_xml(priv->root); + *copy_dest = pcmk__xml_copy(NULL, priv->root); } } @@ -223,18 +252,20 @@ xml_subprocess_output(pcmk__output_t *out, int exit_status, rc_as_str = pcmk__itoa(exit_status); - node = pcmk__output_xml_create_parent(out, "command", - "code", rc_as_str, + node = pcmk__output_xml_create_parent(out, PCMK_XE_COMMAND, + PCMK_XA_CODE, rc_as_str, NULL); if (proc_stdout != NULL) { - child_node = pcmk_create_xml_text_node(node, "output", proc_stdout); - crm_xml_add(child_node, "source", "stdout"); + child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT); + pcmk__xe_set_content(child_node, "%s", proc_stdout); + crm_xml_add(child_node, PCMK_XA_SOURCE, "stdout"); } if (proc_stderr != NULL) { - child_node = pcmk_create_xml_text_node(node, "output", proc_stderr); - crm_xml_add(child_node, "source", "stderr"); + child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT); + pcmk__xe_set_content(child_node, "%s", proc_stderr); + crm_xml_add(child_node, PCMK_XA_SOURCE, "stderr"); } free(rc_as_str); @@ -242,15 +273,16 @@ xml_subprocess_output(pcmk__output_t *out, int exit_status, static void xml_version(pcmk__output_t *out, bool extended) { + const char *author = "Andrew Beekhof and the Pacemaker project " + "contributors"; CRM_ASSERT(out != NULL); - pcmk__output_create_xml_node(out, "version", - "program", "Pacemaker", - "version", PACEMAKER_VERSION, - "author", "Andrew Beekhof and the " - "Pacemaker project contributors", - "build", BUILD_VERSION, - "features", CRM_FEATURES, + pcmk__output_create_xml_node(out, PCMK_XE_VERSION, + PCMK_XA_PROGRAM, "Pacemaker", + PCMK_XA_VERSION, PACEMAKER_VERSION, + PCMK_XA_AUTHOR, author, + PCMK_XA_BUILD, BUILD_VERSION, + PCMK_XA_FEATURES, CRM_FEATURES, NULL); } @@ -265,6 +297,8 @@ xml_err(pcmk__output_t *out, const char *format, ...) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; + add_root_node(out); + va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len > 0); @@ -302,20 +336,20 @@ xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plura char *name = NULL; char *buf = NULL; int len; + private_data_t *priv = NULL; - CRM_ASSERT(out != NULL); + CRM_ASSERT(out != NULL && out->priv != NULL); + priv = out->priv; va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); - if (substitute) { - for (subst_t *s = substitutions; s->from != NULL; s++) { - if (!strcmp(s->from, buf)) { - name = g_strdup(s->to); - break; - } + for (const subst_t *s = substitutions; s->from != NULL; s++) { + if (strcmp(s->from, buf) == 0) { + name = g_strdup(s->to); + break; } } @@ -323,12 +357,12 @@ xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plura name = g_ascii_strdown(buf, -1); } - if (legacy_xml || simple_list) { - pcmk__output_xml_create_parent(out, name, NULL); - } else { - pcmk__output_xml_create_parent(out, "list", - "name", name, + if (priv->list_element) { + pcmk__output_xml_create_parent(out, PCMK_XE_LIST, + PCMK_XA_NAME, name, NULL); + } else { + pcmk__output_xml_create_parent(out, name, NULL); } g_free(name); @@ -350,10 +384,10 @@ xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { CRM_ASSERT(len >= 0); va_end(ap); - item_node = pcmk__output_create_xml_text_node(out, "item", buf); + item_node = pcmk__output_create_xml_text_node(out, PCMK_XE_ITEM, buf); if (name != NULL) { - crm_xml_add(item_node, "name", name); + crm_xml_add(item_node, PCMK_XA_NAME, name); } free(buf); @@ -371,16 +405,18 @@ xml_end_list(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; - if (priv->legacy_xml || simple_list) { - g_queue_pop_tail(priv->parent_q); - } else { + if (priv->list_element) { char *buf = NULL; xmlNodePtr node; + /* Do not free node here - it's still part of the document */ node = g_queue_pop_tail(priv->parent_q); buf = crm_strdup_printf("%lu", xmlChildElementCount(node)); - crm_xml_add(node, "count", buf); + crm_xml_add(node, PCMK_XA_COUNT, buf); free(buf); + } else { + /* Do not free this result - it's still part of the document */ + g_queue_pop_tail(priv->parent_q); } } @@ -465,13 +501,15 @@ pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) { CRM_ASSERT(node != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; parent = g_queue_peek_tail(priv->parent_q); // Shouldn't happen unless the caller popped priv->root CRM_CHECK(parent != NULL, return); - add_node_copy(parent, node); + pcmk__xml_copy(parent, node); } xmlNodePtr @@ -483,9 +521,11 @@ pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); + add_root_node(out); + priv = out->priv; - node = create_xml_node(g_queue_peek_tail(priv->parent_q), name); + node = pcmk__xe_create(g_queue_peek_tail(priv->parent_q), name); va_start(args, name); pcmk__xe_set_propv(node, args); va_end(args); @@ -501,7 +541,7 @@ pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const c CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); node = pcmk__output_create_xml_node(out, name, NULL); - xmlNodeSetContent(node, (pcmkXmlStr) content); + pcmk__xe_set_content(node, "%s", content); return node; } @@ -513,6 +553,8 @@ pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) { CRM_ASSERT(parent != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; g_queue_push_tail(priv->parent_q, parent); @@ -525,9 +567,12 @@ pcmk__output_xml_pop_parent(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0); + /* Do not free this result - it's still part of the document */ g_queue_pop_tail(priv->parent_q); } @@ -538,8 +583,61 @@ pcmk__output_xml_peek_parent(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); + add_root_node(out); + priv = out->priv; /* If queue is empty NULL will be returned */ return g_queue_peek_tail(priv->parent_q); } + +bool +pcmk__output_get_legacy_xml(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return false; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + return priv->legacy_xml; +} + +void +pcmk__output_set_legacy_xml(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + priv->legacy_xml = true; +} + +void +pcmk__output_enable_list_element(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + priv->list_element = true; +} diff --git a/lib/common/patchset.c b/lib/common/patchset.c index 34e27fb..c12a8ef 100644 --- a/lib/common/patchset.c +++ b/lib/common/patchset.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. * @@ -21,14 +21,10 @@ #include <libxml/tree.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> // CRM_XML_LOG_BASE, etc. #include "crmcommon_private.h" -static xmlNode *subtract_xml_comment(xmlNode *parent, xmlNode *left, - xmlNode *right, gboolean *changed); - /* Add changes for specified XML to patchset. * For patchset format, refer to diff schema. */ @@ -56,12 +52,12 @@ add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset) if (xpath != NULL) { int position = pcmk__xml_position(xml, pcmk__xf_deleted); - change = create_xml_node(patchset, XML_DIFF_CHANGE); + change = pcmk__xe_create(patchset, PCMK_XE_CHANGE); - crm_xml_add(change, XML_DIFF_OP, "create"); - crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str); - crm_xml_add_int(change, XML_DIFF_POSITION, position); - add_node_copy(change, xml); + crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_CREATE); + crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str); + crm_xml_add_int(change, PCMK_XE_POSITION, position); + pcmk__xml_copy(change, xml); g_string_free(xpath, TRUE); } @@ -82,35 +78,35 @@ add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset) GString *xpath = pcmk__element_xpath(xml); if (xpath != NULL) { - change = create_xml_node(patchset, XML_DIFF_CHANGE); + change = pcmk__xe_create(patchset, PCMK_XE_CHANGE); - crm_xml_add(change, XML_DIFF_OP, "modify"); - crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str); + crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MODIFY); + crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str); - change = create_xml_node(change, XML_DIFF_LIST); + change = pcmk__xe_create(change, PCMK_XE_CHANGE_LIST); g_string_free(xpath, TRUE); } } - attr = create_xml_node(change, XML_DIFF_ATTR); + attr = pcmk__xe_create(change, PCMK_XE_CHANGE_ATTR); - crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, (const char *)pIter->name); + crm_xml_add(attr, PCMK_XA_NAME, (const char *) pIter->name); if (nodepriv->flags & pcmk__xf_deleted) { - crm_xml_add(attr, XML_DIFF_OP, "unset"); + crm_xml_add(attr, PCMK_XA_OPERATION, "unset"); } else { - crm_xml_add(attr, XML_DIFF_OP, "set"); + crm_xml_add(attr, PCMK_XA_OPERATION, "set"); value = pcmk__xml_attr_value(pIter); - crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value); + crm_xml_add(attr, PCMK_XA_VALUE, value); } } if (change) { xmlNode *result = NULL; - change = create_xml_node(change->parent, XML_DIFF_RESULT); - result = create_xml_node(change, (const char *)xml->name); + change = pcmk__xe_create(change->parent, PCMK_XE_CHANGE_RESULT); + result = pcmk__xe_create(change, (const char *)xml->name); for (pIter = pcmk__xe_first_attr(xml); pIter != NULL; pIter = pIter->next) { @@ -133,14 +129,15 @@ add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset) GString *xpath = pcmk__element_xpath(xml); crm_trace("%s.%s moved to position %d", - xml->name, ID(xml), pcmk__xml_position(xml, pcmk__xf_skip)); + xml->name, pcmk__xe_id(xml), + pcmk__xml_position(xml, pcmk__xf_skip)); if (xpath != NULL) { - change = create_xml_node(patchset, XML_DIFF_CHANGE); + change = pcmk__xe_create(patchset, PCMK_XE_CHANGE); - crm_xml_add(change, XML_DIFF_OP, "move"); - crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str); - crm_xml_add_int(change, XML_DIFF_POSITION, + crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MOVE); + crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str); + crm_xml_add_int(change, PCMK_XE_POSITION, pcmk__xml_position(xml, pcmk__xf_deleted)); g_string_free(xpath, TRUE); } @@ -153,7 +150,8 @@ is_config_change(xmlNode *xml) GList *gIter = NULL; xml_node_private_t *nodepriv = NULL; xml_doc_private_t *docpriv; - xmlNode *config = first_named_child(xml, XML_CIB_TAG_CONFIGURATION); + xmlNode *config = pcmk__xe_first_child(xml, PCMK_XE_CONFIGURATION, NULL, + NULL); if (config) { nodepriv = config->_private; @@ -168,7 +166,7 @@ is_config_change(xmlNode *xml) pcmk__deleted_xml_t *deleted_obj = gIter->data; if (strstr(deleted_obj->path, - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION) != NULL) { + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION) != NULL) { return TRUE; } } @@ -176,6 +174,7 @@ is_config_change(xmlNode *xml) return FALSE; } +// @COMPAT Remove when v1 patchsets are removed static void xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff, gboolean changed) @@ -187,9 +186,9 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff, const char *tag = NULL; const char *vfields[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, }; if (local_diff == NULL) { @@ -197,16 +196,16 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff, return; } - tag = XML_TAG_DIFF_REMOVED; - diff_child = find_xml_node(local_diff, tag, FALSE); + tag = PCMK__XE_DIFF_REMOVED; + diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL); if (diff_child == NULL) { - diff_child = create_xml_node(local_diff, tag); + diff_child = pcmk__xe_create(local_diff, tag); } - tag = XML_TAG_CIB; - cib = find_xml_node(diff_child, tag, FALSE); + tag = PCMK_XE_CIB; + cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL); if (cib == NULL) { - cib = create_xml_node(diff_child, tag); + cib = pcmk__xe_create(diff_child, tag); } for (lpc = 0; (last != NULL) && (lpc < PCMK__NELEM(vfields)); lpc++) { @@ -218,16 +217,16 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff, } } - tag = XML_TAG_DIFF_ADDED; - diff_child = find_xml_node(local_diff, tag, FALSE); + tag = PCMK__XE_DIFF_ADDED; + diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL); if (diff_child == NULL) { - diff_child = create_xml_node(local_diff, tag); + diff_child = pcmk__xe_create(local_diff, tag); } - tag = XML_TAG_CIB; - cib = find_xml_node(diff_child, tag, FALSE); + tag = PCMK_XE_CIB; + cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL); if (cib == NULL) { - cib = create_xml_node(diff_child, tag); + cib = pcmk__xe_create(diff_child, tag); } for (lpc = 0; next && lpc < PCMK__NELEM(vfields); lpc++) { @@ -246,11 +245,12 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff, crm_log_xml_explicit(local_diff, "Repaired-diff"); } +// @COMPAT Remove when v1 patchsets are removed static xmlNode * xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config, bool suppress) { - xmlNode *patchset = diff_xml_object(source, target, suppress); + xmlNode *patchset = pcmk__diff_v1_xml_object(source, target, suppress); if (patchset) { CRM_LOG_ASSERT(xml_document_dirty(target)); @@ -271,9 +271,9 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target) xmlNode *version = NULL; xmlNode *patchset = NULL; const char *vfields[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, }; CRM_ASSERT(target); @@ -284,12 +284,12 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target) CRM_ASSERT(target->doc); docpriv = target->doc->_private; - patchset = create_xml_node(NULL, XML_TAG_DIFF); + patchset = pcmk__xe_create(NULL, PCMK_XE_DIFF); crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2); - version = create_xml_node(patchset, XML_DIFF_VERSION); + version = pcmk__xe_create(patchset, PCMK_XE_VERSION); - v = create_xml_node(version, XML_DIFF_VSOURCE); + v = pcmk__xe_create(version, PCMK_XE_SOURCE); for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) { const char *value = crm_element_value(source, vfields[lpc]); @@ -299,7 +299,7 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target) crm_xml_add(v, vfields[lpc], value); } - v = create_xml_node(version, XML_DIFF_VTARGET); + v = pcmk__xe_create(version, PCMK_XE_TARGET); for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) { const char *value = crm_element_value(target, vfields[lpc]); @@ -311,12 +311,12 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target) for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) { pcmk__deleted_xml_t *deleted_obj = gIter->data; - xmlNode *change = create_xml_node(patchset, XML_DIFF_CHANGE); + xmlNode *change = pcmk__xe_create(patchset, PCMK_XE_CHANGE); - crm_xml_add(change, XML_DIFF_OP, "delete"); - crm_xml_add(change, XML_DIFF_PATH, deleted_obj->path); + crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_DELETE); + crm_xml_add(change, PCMK_XA_PATH, deleted_obj->path); if (deleted_obj->position >= 0) { - crm_xml_add_int(change, XML_DIFF_POSITION, deleted_obj->position); + crm_xml_add_int(change, PCMK_XE_POSITION, deleted_obj->position); } } @@ -331,7 +331,7 @@ xml_create_patchset(int format, xmlNode *source, xmlNode *target, int counter = 0; bool config = FALSE; xmlNode *patch = NULL; - const char *version = crm_element_value(source, XML_ATTR_CRM_VERSION); + const char *version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET); xml_acl_disable(target); if (!xml_document_dirty(target)) { @@ -346,16 +346,16 @@ xml_create_patchset(int format, xmlNode *source, xmlNode *target, if (manage_version && config) { crm_trace("Config changed %d", format); - crm_xml_add(target, XML_ATTR_NUMUPDATES, "0"); + crm_xml_add(target, PCMK_XA_NUM_UPDATES, "0"); - crm_element_value_int(target, XML_ATTR_GENERATION, &counter); - crm_xml_add_int(target, XML_ATTR_GENERATION, counter+1); + crm_element_value_int(target, PCMK_XA_EPOCH, &counter); + crm_xml_add_int(target, PCMK_XA_EPOCH, counter+1); } else if (manage_version) { - crm_element_value_int(target, XML_ATTR_NUMUPDATES, &counter); + crm_element_value_int(target, PCMK_XA_NUM_UPDATES, &counter); crm_trace("Status changed %d - %d %s", format, counter, - crm_element_value(source, XML_ATTR_NUMUPDATES)); - crm_xml_add_int(target, XML_ATTR_NUMUPDATES, (counter + 1)); + crm_element_value(source, PCMK_XA_NUM_UPDATES)); + crm_xml_add_int(target, PCMK_XA_NUM_UPDATES, (counter + 1)); } if (format == 0) { @@ -369,6 +369,7 @@ xml_create_patchset(int format, xmlNode *source, xmlNode *target, switch (format) { case 1: + // @COMPAT Remove when v1 patchsets are removed patch = xml_create_patchset_v1(source, target, config, FALSE); break; case 2: @@ -403,23 +404,228 @@ patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, return; } - version = crm_element_value(source, XML_ATTR_CRM_VERSION); + version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET); digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version); - crm_xml_add(patch, XML_ATTR_DIGEST, digest); + crm_xml_add(patch, PCMK__XA_DIGEST, digest); free(digest); return; } -// Return true if attribute name is not "id" +// @COMPAT Remove when v1 patchsets are removed +static xmlNode * +subtract_v1_xml_comment(xmlNode *parent, xmlNode *left, xmlNode *right, + gboolean *changed) +{ + CRM_CHECK(left != NULL, return NULL); + CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL); + + if ((right == NULL) || !pcmk__str_eq((const char *)left->content, + (const char *)right->content, + pcmk__str_casei)) { + xmlNode *deleted = NULL; + + deleted = pcmk__xml_copy(parent, left); + *changed = TRUE; + + return deleted; + } + + return NULL; +} + +// @COMPAT Remove when v1 patchsets are removed +static xmlNode * +subtract_v1_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, + bool full, gboolean *changed, const char *marker) +{ + gboolean dummy = FALSE; + xmlNode *diff = NULL; + xmlNode *right_child = NULL; + xmlNode *left_child = NULL; + xmlAttrPtr xIter = NULL; + + const char *id = NULL; + const char *name = NULL; + const char *value = NULL; + const char *right_val = NULL; + + if (changed == NULL) { + changed = &dummy; + } + + if (left == NULL) { + return NULL; + } + + if (left->type == XML_COMMENT_NODE) { + return subtract_v1_xml_comment(parent, left, right, changed); + } + + id = pcmk__xe_id(left); + name = (const char *) left->name; + if (right == NULL) { + xmlNode *deleted = NULL; + + crm_trace("Processing <%s " PCMK_XA_ID "=%s> (complete copy)", + name, id); + deleted = pcmk__xml_copy(parent, left); + crm_xml_add(deleted, PCMK__XA_CRM_DIFF_MARKER, marker); + + *changed = TRUE; + return deleted; + } + + CRM_CHECK(name != NULL, return NULL); + CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL); + + // Check for PCMK__XA_CRM_DIFF_MARKER in a child + value = crm_element_value(right, PCMK__XA_CRM_DIFF_MARKER); + if ((value != NULL) && (strcmp(value, "removed:top") == 0)) { + crm_trace("We are the root of the deletion: %s.id=%s", name, id); + *changed = TRUE; + return NULL; + } + + // @TODO Avoiding creating the full hierarchy would save work here + diff = pcmk__xe_create(parent, name); + + // Changes to child objects + for (left_child = pcmk__xml_first_child(left); left_child != NULL; + left_child = pcmk__xml_next(left_child)) { + gboolean child_changed = FALSE; + + right_child = pcmk__xml_match(right, left_child, false); + subtract_v1_xml_object(diff, left_child, right_child, full, + &child_changed, marker); + if (child_changed) { + *changed = TRUE; + } + } + + if (!*changed) { + /* Nothing to do */ + + } else if (full) { + xmlAttrPtr pIter = NULL; + + for (pIter = pcmk__xe_first_attr(left); pIter != NULL; + pIter = pIter->next) { + const char *p_name = (const char *)pIter->name; + const char *p_value = pcmk__xml_attr_value(pIter); + + xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value); + } + + // We have everything we need + goto done; + } + + // Changes to name/value pairs + for (xIter = pcmk__xe_first_attr(left); xIter != NULL; + xIter = xIter->next) { + const char *prop_name = (const char *) xIter->name; + xmlAttrPtr right_attr = NULL; + xml_node_private_t *nodepriv = NULL; + + if (strcmp(prop_name, PCMK_XA_ID) == 0) { + // id already obtained when present ~ this case, so just reuse + xmlSetProp(diff, (pcmkXmlStr) PCMK_XA_ID, (pcmkXmlStr) id); + continue; + } + + if (pcmk__xa_filterable(prop_name)) { + continue; + } + + right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name); + if (right_attr) { + nodepriv = right_attr->_private; + } + + right_val = crm_element_value(right, prop_name); + if ((right_val == NULL) || (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted))) { + /* new */ + *changed = TRUE; + if (full) { + xmlAttrPtr pIter = NULL; + + for (pIter = pcmk__xe_first_attr(left); pIter != NULL; + pIter = pIter->next) { + const char *p_name = (const char *) pIter->name; + const char *p_value = pcmk__xml_attr_value(pIter); + + xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value); + } + break; + + } else { + const char *left_value = pcmk__xml_attr_value(xIter); + + xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value); + crm_xml_add(diff, prop_name, left_value); + } + + } else { + /* Only now do we need the left value */ + const char *left_value = pcmk__xml_attr_value(xIter); + + if (strcmp(left_value, right_val) == 0) { + /* unchanged */ + + } else { + *changed = TRUE; + if (full) { + xmlAttrPtr pIter = NULL; + + crm_trace("Changes detected to %s in " + "<%s " PCMK_XA_ID "=%s>", prop_name, name, id); + for (pIter = pcmk__xe_first_attr(left); pIter != NULL; + pIter = pIter->next) { + const char *p_name = (const char *) pIter->name; + const char *p_value = pcmk__xml_attr_value(pIter); + + xmlSetProp(diff, (pcmkXmlStr) p_name, + (pcmkXmlStr) p_value); + } + break; + + } else { + crm_trace("Changes detected to %s (%s -> %s) in " + "<%s " PCMK_XA_ID "=%s>", + prop_name, left_value, right_val, name, id); + crm_xml_add(diff, prop_name, left_value); + } + } + } + } + + if (!*changed) { + free_xml(diff); + return NULL; + + } else if (!full && (id != NULL)) { + crm_xml_add(diff, PCMK_XA_ID, id); + } + done: + return diff; +} + +/* @COMPAT Remove when v1 patchsets are removed. + * + * Return true if attribute name is not \c PCMK_XML_ID. + */ static bool not_id(xmlAttrPtr attr, void *user_data) { - return strcmp((const char *) attr->name, XML_ATTR_ID) != 0; + return strcmp((const char *) attr->name, PCMK_XA_ID) != 0; } -// Apply the removals section of an v1 patchset to an XML node +/* @COMPAT Remove when v1 patchsets are removed. + * + * Apply the removals section of a v1 patchset to an XML node. + */ static void process_v1_removals(xmlNode *target, xmlNode *patch) { @@ -436,15 +642,17 @@ process_v1_removals(xmlNode *target, xmlNode *patch) if (target->type == XML_COMMENT_NODE) { gboolean dummy; - subtract_xml_comment(target->parent, target, patch, &dummy); + subtract_v1_xml_comment(target->parent, target, patch, &dummy); } CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return); - CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return); + CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch), + pcmk__str_none), + return); - // Check for XML_DIFF_MARKER in a child - id = crm_element_value_copy(target, XML_ATTR_ID); - value = crm_element_value(patch, XML_DIFF_MARKER); + // Check for PCMK__XA_CRM_DIFF_MARKER in a child + id = crm_element_value_copy(target, PCMK_XA_ID); + value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER); if ((value != NULL) && (strcmp(value, "removed:top") == 0)) { crm_trace("We are the root of the deletion: %s.id=%s", target->name, id); @@ -468,7 +676,10 @@ process_v1_removals(xmlNode *target, xmlNode *patch) free(id); } -// Apply the additions section of an v1 patchset to an XML node +/* @COMPAT Remove when v1 patchsets are removed. + * + * Apply the additions section of a v1 patchset to an XML node. + */ static void process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch) { @@ -486,18 +697,18 @@ process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch) return; } - // Check for XML_DIFF_MARKER in a child + // Check for PCMK__XA_CRM_DIFF_MARKER in a child name = (const char *) patch->name; - value = crm_element_value(patch, XML_DIFF_MARKER); + value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER); if ((target == NULL) && (value != NULL) && (strcmp(value, "added:top") == 0)) { - id = ID(patch); + id = pcmk__xe_id(patch); crm_trace("We are the root of the addition: %s.id=%s", name, id); - add_node_copy(parent, patch); + pcmk__xml_copy(parent, patch); return; } else if (target == NULL) { - id = ID(patch); + id = pcmk__xe_id(patch); crm_err("Could not locate: %s.id=%s", name, id); return; } @@ -507,14 +718,16 @@ process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch) } CRM_CHECK(pcmk__xe_is(target, name), return); - CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return); + CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch), + pcmk__str_none), + return); for (xIter = pcmk__xe_first_attr(patch); xIter != NULL; xIter = xIter->next) { const char *p_name = (const char *) xIter->name; const char *p_value = pcmk__xml_attr_value(xIter); - xml_remove_prop(target, p_name); // Preserve patch order + pcmk__xe_remove_attr(target, p_name); // Preserve patch order crm_xml_add(target, p_name, p_value); } @@ -547,17 +760,20 @@ find_patch_xml_node(const xmlNode *patchset, int format, bool added, switch (format) { case 1: - label = added? XML_TAG_DIFF_ADDED : XML_TAG_DIFF_REMOVED; - *patch_node = find_xml_node(patchset, label, FALSE); - cib_node = find_xml_node(*patch_node, "cib", FALSE); + // @COMPAT Remove when v1 patchsets are removed + label = added? PCMK__XE_DIFF_ADDED : PCMK__XE_DIFF_REMOVED; + *patch_node = pcmk__xe_first_child(patchset, label, NULL, NULL); + cib_node = pcmk__xe_first_child(*patch_node, PCMK_XE_CIB, NULL, + NULL); if (cib_node != NULL) { *patch_node = cib_node; } break; case 2: - label = added? "target" : "source"; - *patch_node = find_xml_node(patchset, "version", FALSE); - *patch_node = find_xml_node(*patch_node, label, FALSE); + label = added? PCMK_XE_TARGET : PCMK_XE_SOURCE; + *patch_node = pcmk__xe_first_child(patchset, PCMK_XE_VERSION, NULL, + NULL); + *patch_node = pcmk__xe_first_child(*patch_node, label, NULL, NULL); break; default: crm_warn("Unknown patch format: %d", format); @@ -576,9 +792,9 @@ xml_patch_versions(const xmlNode *patchset, int add[3], int del[3]) xmlNode *tmp = NULL; const char *vfields[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, }; @@ -628,9 +844,9 @@ xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset) int del[] = { 0, 0, 0 }; const char *vfields[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, }; for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) { @@ -684,6 +900,22 @@ xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset) return pcmk_rc_ok; } +// @COMPAT Remove when v1 patchsets are removed +static void +purge_v1_diff_markers(xmlNode *node) +{ + xmlNode *child = NULL; + + CRM_CHECK(node != NULL, return); + + pcmk__xe_remove_attr(node, PCMK__XA_CRM_DIFF_MARKER); + for (child = pcmk__xml_first_child(node); child != NULL; + child = pcmk__xml_next(child)) { + purge_v1_diff_markers(child); + } +} + +// @COMPAT Remove when v1 patchsets are removed /*! * \internal * \brief Apply a version 1 patchset to an XML node @@ -700,9 +932,11 @@ apply_v1_patchset(xmlNode *xml, const xmlNode *patchset) int root_nodes_seen = 0; xmlNode *child_diff = NULL; - xmlNode *added = find_xml_node(patchset, XML_TAG_DIFF_ADDED, FALSE); - xmlNode *removed = find_xml_node(patchset, XML_TAG_DIFF_REMOVED, FALSE); - xmlNode *old = copy_xml(xml); + xmlNode *added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL, + NULL); + xmlNode *removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED, + NULL, NULL); + xmlNode *old = pcmk__xml_copy(NULL, xml); crm_trace("Subtraction Phase"); for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL; @@ -741,7 +975,7 @@ apply_v1_patchset(xmlNode *xml, const xmlNode *patchset) rc = ENOTUNIQ; } - purge_diff_markers(xml); // Purge prior to checking digest + purge_v1_diff_markers(xml); // Purge prior to checking digest free_xml(old); return rc; @@ -759,7 +993,7 @@ first_matching_xml_child(const xmlNode *parent, const char *name, if (strcmp((const char *) cIter->name, name) != 0) { continue; } else if (id) { - const char *cid = ID(cIter); + const char *cid = pcmk__xe_id(cIter); if ((cid == NULL) || (strcmp(cid, id) != 0)) { continue; @@ -811,24 +1045,17 @@ search_v2_xpath(const xmlNode *top, const char *key, int target_position) * than key_len - 1 characters plus a null terminator. */ - remainder = calloc(key_len, sizeof(char)); - CRM_ASSERT(remainder != NULL); - - section = calloc(key_len, sizeof(char)); - CRM_ASSERT(section != NULL); - - id = calloc(key_len, sizeof(char)); - CRM_ASSERT(id != NULL); - - tag = calloc(key_len, sizeof(char)); - CRM_ASSERT(tag != NULL); + remainder = pcmk__assert_alloc(key_len, sizeof(char)); + section = pcmk__assert_alloc(key_len, sizeof(char)); + id = pcmk__assert_alloc(key_len, sizeof(char)); + tag = pcmk__assert_alloc(key_len, sizeof(char)); do { // Look for /NEXT_COMPONENT/REMAINING_COMPONENTS rc = sscanf(current, "/%[^/]%s", section, remainder); if (rc > 0) { // Separate FIRST_COMPONENT into TAG[@id='ID'] - int f = sscanf(section, "%[^[][@" XML_ATTR_ID "='%[^']", tag, id); + int f = sscanf(section, "%[^[][@" PCMK_XA_ID "='%[^']", tag, id); int current_position = -1; /* The target position is for the final component tag, so only use @@ -840,6 +1067,7 @@ search_v2_xpath(const xmlNode *top, const char *key, int target_position) switch (f) { case 1: + // @COMPAT Remove when v1 patchsets are removed target = first_matching_xml_child(target, tag, NULL, current_position); break; @@ -886,8 +1114,8 @@ sort_change_obj_by_position(gconstpointer a, gconstpointer b) int position_a = -1; int position_b = -1; - crm_element_value_int(change_obj_a->change, XML_DIFF_POSITION, &position_a); - crm_element_value_int(change_obj_b->change, XML_DIFF_POSITION, &position_b); + crm_element_value_int(change_obj_a->change, PCMK_XE_POSITION, &position_a); + crm_element_value_int(change_obj_b->change, PCMK_XE_POSITION, &position_b); if (position_a < position_b) { return -1; @@ -919,8 +1147,8 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) for (change = pcmk__xml_first_child(patchset); change != NULL; change = pcmk__xml_next(change)) { xmlNode *match = NULL; - const char *op = crm_element_value(change, XML_DIFF_OP); - const char *xpath = crm_element_value(change, XML_DIFF_PATH); + const char *op = crm_element_value(change, PCMK_XA_OPERATION); + const char *xpath = crm_element_value(change, PCMK_XA_PATH); int position = -1; if (op == NULL) { @@ -929,14 +1157,16 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) crm_trace("Processing %s %s", change->name, op); - // "delete" changes for XML comments are generated with "position" - if (strcmp(op, "delete") == 0) { - crm_element_value_int(change, XML_DIFF_POSITION, &position); + /* PCMK_VALUE_DELETE changes for XML comments are generated with + * PCMK_XE_POSITION + */ + if (strcmp(op, PCMK_VALUE_DELETE) == 0) { + crm_element_value_int(change, PCMK_XE_POSITION, &position); } match = search_v2_xpath(xml, xpath, position); crm_trace("Performing %s on %s with %p", op, xpath, match); - if ((match == NULL) && (strcmp(op, "delete") == 0)) { + if ((match == NULL) && (strcmp(op, PCMK_VALUE_DELETE) == 0)) { crm_debug("No %s match for %s in %p", op, xpath, xml->doc); continue; @@ -945,32 +1175,33 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) rc = pcmk_rc_diff_failed; continue; - } else if ((strcmp(op, "create") == 0) || (strcmp(op, "move") == 0)) { - // Delay the adding of a "create" object - xml_change_obj_t *change_obj = calloc(1, sizeof(xml_change_obj_t)); - - CRM_ASSERT(change_obj != NULL); + } else if (pcmk__str_any_of(op, + PCMK_VALUE_CREATE, PCMK_VALUE_MOVE, NULL)) { + // Delay the adding of a PCMK_VALUE_CREATE object + xml_change_obj_t *change_obj = + pcmk__assert_alloc(1, sizeof(xml_change_obj_t)); change_obj->change = change; change_obj->match = match; change_objs = g_list_append(change_objs, change_obj); - if (strcmp(op, "move") == 0) { - // Temporarily put the "move" object after the last sibling + if (strcmp(op, PCMK_VALUE_MOVE) == 0) { + // Temporarily put the PCMK_VALUE_MOVE object after the last sibling if ((match->parent != NULL) && (match->parent->last != NULL)) { xmlAddNextSibling(match->parent->last, match); } } - } else if (strcmp(op, "delete") == 0) { + } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) { free_xml(match); - } else if (strcmp(op, "modify") == 0) { - xmlNode *attrs = NULL; + } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) { + const xmlNode *child = pcmk__xe_first_child(change, + PCMK_XE_CHANGE_RESULT, + NULL, NULL); + const xmlNode *attrs = pcmk__xml_first_child(child); - attrs = pcmk__xml_first_child(first_named_child(change, - XML_DIFF_RESULT)); if (attrs == NULL) { rc = ENOMSG; continue; @@ -1002,18 +1233,18 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) change = change_obj->change; - op = crm_element_value(change, XML_DIFF_OP); - xpath = crm_element_value(change, XML_DIFF_PATH); + op = crm_element_value(change, PCMK_XA_OPERATION); + xpath = crm_element_value(change, PCMK_XA_PATH); crm_trace("Continue performing %s on %s with %p", op, xpath, match); - if (strcmp(op, "create") == 0) { + if (strcmp(op, PCMK_VALUE_CREATE) == 0) { int position = 0; xmlNode *child = NULL; xmlNode *match_child = NULL; match_child = match->children; - crm_element_value_int(change, XML_DIFF_POSITION, &position); + crm_element_value_int(change, PCMK_XE_POSITION, &position); while ((match_child != NULL) && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) { @@ -1040,12 +1271,12 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) CRM_LOG_ASSERT(position == 0); xmlAddChild(match, child); } - pcmk__mark_xml_created(child); + pcmk__xml_mark_created(child); - } else if (strcmp(op, "move") == 0) { + } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) { int position = 0; - crm_element_value_int(change, XML_DIFF_POSITION, &position); + crm_element_value_int(change, PCMK_XE_POSITION, &position); if (position != pcmk__xml_position(match, pcmk__xf_skip)) { xmlNode *match_child = NULL; int p = position; @@ -1083,7 +1314,7 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset) if (position != pcmk__xml_position(match, pcmk__xf_skip)) { crm_err("Moved %s.%s to position %d instead of %d (%p)", - match->name, ID(match), + match->name, pcmk__xe_id(match), pcmk__xml_position(match, pcmk__xf_skip), position, match->prev); rc = pcmk_rc_diff_failed; @@ -1116,18 +1347,19 @@ xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version) } } - digest = crm_element_value(patchset, XML_ATTR_DIGEST); + digest = crm_element_value(patchset, PCMK__XA_DIGEST); if (digest != NULL) { /* Make original XML available for logging in case result doesn't have * expected digest */ - pcmk__if_tracing(old = copy_xml(xml), {}); + pcmk__if_tracing(old = pcmk__xml_copy(NULL, xml), {}); } if (rc == pcmk_ok) { crm_element_value_int(patchset, PCMK_XA_FORMAT, &format); switch (format) { case 1: + // @COMPAT Remove when v1 patchsets are removed rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset)); break; case 2: @@ -1141,7 +1373,7 @@ xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version) if ((rc == pcmk_ok) && (digest != NULL)) { char *new_digest = NULL; - char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION); + char *version = crm_element_value_copy(xml, PCMK_XA_CRM_FEATURE_SET); new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version); if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) { @@ -1168,242 +1400,75 @@ xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version) return rc; } -void -purge_diff_markers(xmlNode *a_node) -{ - xmlNode *child = NULL; - - CRM_CHECK(a_node != NULL, return); - - xml_remove_prop(a_node, XML_DIFF_MARKER); - for (child = pcmk__xml_first_child(a_node); child != NULL; - child = pcmk__xml_next(child)) { - purge_diff_markers(child); - } -} - -xmlNode * -diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress) +// @COMPAT Remove when v1 patchsets are removed +static bool +can_prune_leaf_v1(xmlNode *node) { - xmlNode *tmp1 = NULL; - xmlNode *diff = create_xml_node(NULL, XML_TAG_DIFF); - xmlNode *removed = create_xml_node(diff, XML_TAG_DIFF_REMOVED); - xmlNode *added = create_xml_node(diff, XML_TAG_DIFF_ADDED); + xmlNode *cIter = NULL; + bool can_prune = true; - crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); + CRM_CHECK(node != NULL, return false); - tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top"); - if (suppress && (tmp1 != NULL) && can_prune_leaf(tmp1)) { - free_xml(tmp1); + /* @COMPAT PCMK__XE_ROLE_REF was deprecated in Pacemaker 1.1.12 (needed for + * rolling upgrades) + */ + if (pcmk__strcase_any_of((const char *) node->name, + PCMK_XE_RESOURCE_REF, PCMK_XE_OBJ_REF, + PCMK_XE_ROLE, PCMK__XE_ROLE_REF, + NULL)) { + return false; } - tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top"); - if (suppress && (tmp1 != NULL) && can_prune_leaf(tmp1)) { - free_xml(tmp1); - } + for (xmlAttrPtr a = pcmk__xe_first_attr(node); a != NULL; a = a->next) { + const char *p_name = (const char *) a->name; - if ((added->children == NULL) && (removed->children == NULL)) { - free_xml(diff); - diff = NULL; + if (strcmp(p_name, PCMK_XA_ID) == 0) { + continue; + } + can_prune = false; } - return diff; -} - -static xmlNode * -subtract_xml_comment(xmlNode *parent, xmlNode *left, xmlNode *right, - gboolean *changed) -{ - CRM_CHECK(left != NULL, return NULL); - CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL); - - if ((right == NULL) || !pcmk__str_eq((const char *)left->content, - (const char *)right->content, - pcmk__str_casei)) { - xmlNode *deleted = NULL; - - deleted = add_node_copy(parent, left); - *changed = TRUE; + cIter = pcmk__xml_first_child(node); + while (cIter) { + xmlNode *child = cIter; - return deleted; + cIter = pcmk__xml_next(cIter); + if (can_prune_leaf_v1(child)) { + free_xml(child); + } else { + can_prune = false; + } } - - return NULL; + return can_prune; } +// @COMPAT Remove when v1 patchsets are removed xmlNode * -subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, - gboolean full, gboolean *changed, const char *marker) +pcmk__diff_v1_xml_object(xmlNode *old, xmlNode *new, bool suppress) { - gboolean dummy = FALSE; - xmlNode *diff = NULL; - xmlNode *right_child = NULL; - xmlNode *left_child = NULL; - xmlAttrPtr xIter = NULL; - - const char *id = NULL; - const char *name = NULL; - const char *value = NULL; - const char *right_val = NULL; - - if (changed == NULL) { - changed = &dummy; - } - - if (left == NULL) { - return NULL; - } - - if (left->type == XML_COMMENT_NODE) { - return subtract_xml_comment(parent, left, right, changed); - } - - id = ID(left); - name = (const char *) left->name; - if (right == NULL) { - xmlNode *deleted = NULL; - - crm_trace("Processing <%s " XML_ATTR_ID "=%s> (complete copy)", - name, id); - deleted = add_node_copy(parent, left); - crm_xml_add(deleted, XML_DIFF_MARKER, marker); - - *changed = TRUE; - return deleted; - } - - CRM_CHECK(name != NULL, return NULL); - CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL); - - // Check for XML_DIFF_MARKER in a child - value = crm_element_value(right, XML_DIFF_MARKER); - if ((value != NULL) && (strcmp(value, "removed:top") == 0)) { - crm_trace("We are the root of the deletion: %s.id=%s", name, id); - *changed = TRUE; - return NULL; - } - - // @TODO Avoiding creating the full hierarchy would save work here - diff = create_xml_node(parent, name); - - // Changes to child objects - for (left_child = pcmk__xml_first_child(left); left_child != NULL; - left_child = pcmk__xml_next(left_child)) { - gboolean child_changed = FALSE; - - right_child = pcmk__xml_match(right, left_child, false); - subtract_xml_object(diff, left_child, right_child, full, &child_changed, - marker); - if (child_changed) { - *changed = TRUE; - } - } - - if (!*changed) { - /* Nothing to do */ - - } else if (full) { - xmlAttrPtr pIter = NULL; - - for (pIter = pcmk__xe_first_attr(left); pIter != NULL; - pIter = pIter->next) { - const char *p_name = (const char *)pIter->name; - const char *p_value = pcmk__xml_attr_value(pIter); + xmlNode *tmp1 = NULL; + xmlNode *diff = pcmk__xe_create(NULL, PCMK_XE_DIFF); + xmlNode *removed = pcmk__xe_create(diff, PCMK__XE_DIFF_REMOVED); + xmlNode *added = pcmk__xe_create(diff, PCMK__XE_DIFF_ADDED); - xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value); - } + crm_xml_add(diff, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - // We have everything we need - goto done; + tmp1 = subtract_v1_xml_object(removed, old, new, false, NULL, + "removed:top"); + if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) { + free_xml(tmp1); } - // Changes to name/value pairs - for (xIter = pcmk__xe_first_attr(left); xIter != NULL; - xIter = xIter->next) { - const char *prop_name = (const char *) xIter->name; - xmlAttrPtr right_attr = NULL; - xml_node_private_t *nodepriv = NULL; - - if (strcmp(prop_name, XML_ATTR_ID) == 0) { - // id already obtained when present ~ this case, so just reuse - xmlSetProp(diff, (pcmkXmlStr) XML_ATTR_ID, (pcmkXmlStr) id); - continue; - } - - if (pcmk__xa_filterable(prop_name)) { - continue; - } - - right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name); - if (right_attr) { - nodepriv = right_attr->_private; - } - - right_val = crm_element_value(right, prop_name); - if ((right_val == NULL) || (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted))) { - /* new */ - *changed = TRUE; - if (full) { - xmlAttrPtr pIter = NULL; - - for (pIter = pcmk__xe_first_attr(left); pIter != NULL; - pIter = pIter->next) { - const char *p_name = (const char *) pIter->name; - const char *p_value = pcmk__xml_attr_value(pIter); - - xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value); - } - break; - - } else { - const char *left_value = pcmk__xml_attr_value(xIter); - - xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value); - crm_xml_add(diff, prop_name, left_value); - } - - } else { - /* Only now do we need the left value */ - const char *left_value = pcmk__xml_attr_value(xIter); - - if (strcmp(left_value, right_val) == 0) { - /* unchanged */ - - } else { - *changed = TRUE; - if (full) { - xmlAttrPtr pIter = NULL; - - crm_trace("Changes detected to %s in " - "<%s " XML_ATTR_ID "=%s>", prop_name, name, id); - for (pIter = pcmk__xe_first_attr(left); pIter != NULL; - pIter = pIter->next) { - const char *p_name = (const char *) pIter->name; - const char *p_value = pcmk__xml_attr_value(pIter); - - xmlSetProp(diff, (pcmkXmlStr) p_name, - (pcmkXmlStr) p_value); - } - break; - - } else { - crm_trace("Changes detected to %s (%s -> %s) in " - "<%s " XML_ATTR_ID "=%s>", - prop_name, left_value, right_val, name, id); - crm_xml_add(diff, prop_name, left_value); - } - } - } + tmp1 = subtract_v1_xml_object(added, new, old, true, NULL, "added:top"); + if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) { + free_xml(tmp1); } - if (!*changed) { + if ((added->children == NULL) && (removed->children == NULL)) { free_xml(diff); - return NULL; - - } else if (!full && (id != NULL)) { - crm_xml_add(diff, XML_ATTR_ID, id); + diff = NULL; } - done: + return diff; } @@ -1417,12 +1482,14 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml) { gboolean result = TRUE; int root_nodes_seen = 0; - const char *digest = crm_element_value(diff, XML_ATTR_DIGEST); - const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION); + const char *digest = crm_element_value(diff, PCMK__XA_DIGEST); + const char *version = crm_element_value(diff, PCMK_XA_CRM_FEATURE_SET); xmlNode *child_diff = NULL; - xmlNode *added = find_xml_node(diff, XML_TAG_DIFF_ADDED, FALSE); - xmlNode *removed = find_xml_node(diff, XML_TAG_DIFF_REMOVED, FALSE); + xmlNode *added = pcmk__xe_first_child(diff, PCMK__XE_DIFF_ADDED, NULL, + NULL); + xmlNode *removed = pcmk__xe_first_child(diff, PCMK__XE_DIFF_REMOVED, NULL, + NULL); CRM_CHECK(new_xml != NULL, return FALSE); @@ -1431,14 +1498,14 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml) child_diff = pcmk__xml_next(child_diff)) { CRM_CHECK(root_nodes_seen == 0, result = FALSE); if (root_nodes_seen == 0) { - *new_xml = subtract_xml_object(NULL, old_xml, child_diff, FALSE, - NULL, NULL); + *new_xml = subtract_v1_xml_object(NULL, old_xml, child_diff, false, + NULL, NULL); } root_nodes_seen++; } if (root_nodes_seen == 0) { - *new_xml = copy_xml(old_xml); + *new_xml = pcmk__xml_copy(NULL, old_xml); } else if (root_nodes_seen > 1) { crm_err("(-) Diffs cannot contain more than one change set... saw %d", @@ -1455,7 +1522,8 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml) child_diff = pcmk__xml_next(child_diff)) { CRM_CHECK(root_nodes_seen == 0, result = FALSE); if (root_nodes_seen == 0) { - pcmk__xml_update(NULL, *new_xml, child_diff, true); + pcmk__xml_update(NULL, *new_xml, child_diff, pcmk__xaf_none, + true); } root_nodes_seen++; } @@ -1469,7 +1537,7 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml) } else if (result && (digest != NULL)) { char *new_digest = NULL; - purge_diff_markers(*new_xml); // Purge now so diff is ok + purge_v1_diff_markers(*new_xml); // Purge now so diff is ok new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE, version); if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) { @@ -1493,11 +1561,36 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml) free(new_digest); } else if (result) { - purge_diff_markers(*new_xml); // Purge now so diff is ok + purge_v1_diff_markers(*new_xml); // Purge now so diff is ok } return result; } +void +purge_diff_markers(xmlNode *a_node) +{ + purge_v1_diff_markers(a_node); +} + +xmlNode * +diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress) +{ + return pcmk__diff_v1_xml_object(old, new, suppress); +} + +xmlNode * +subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, + gboolean full, gboolean *changed, const char *marker) +{ + return subtract_v1_xml_object(parent, left, right, full, changed, marker); +} + +gboolean +can_prune_leaf(xmlNode *xml_node) +{ + return can_prune_leaf_v1(xml_node); +} + // LCOV_EXCL_STOP // End deprecated API diff --git a/lib/common/patchset_display.c b/lib/common/patchset_display.c index 5cc0b52..1351c86 100644 --- a/lib/common/patchset_display.c +++ b/lib/common/patchset_display.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,7 +9,7 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "crmcommon_private.h" @@ -22,9 +22,9 @@ * * All header lines contain three integers separated by dots, of the form * <tt>{0}.{1}.{2}</tt>: - * * \p {0}: \p XML_ATTR_GENERATION_ADMIN - * * \p {1}: \p XML_ATTR_GENERATION - * * \p {2}: \p XML_ATTR_NUMUPDATES + * * \p {0}: \c PCMK_XA_ADMIN_EPOCH + * * \p {1}: \c PCMK_XA_EPOCH + * * \p {2}: \c PCMK_XA_NUM_UPDATES * * Lines containing \p "---" describe removals and end with the patch format * number. Lines containing \p "+++" describe additions and end with the patch @@ -48,7 +48,7 @@ xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset) if ((add[0] != del[0]) || (add[1] != del[1]) || (add[2] != del[2])) { const char *fmt = crm_element_value(patchset, PCMK_XA_FORMAT); - const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST); + const char *digest = crm_element_value(patchset, PCMK__XA_DIGEST); out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt); rc = out->info(out, "Diff: +++ %d.%d.%d %s", @@ -81,7 +81,7 @@ xml_show_patchset_v1_recursive(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options) { if ((data->children == NULL) - || (crm_element_value(data, XML_DIFF_MARKER) != NULL)) { + || (crm_element_value(data, PCMK__XA_CRM_DIFF_MARKER) != NULL)) { // Found a change; clear the pcmk__xml_fmt_diff_short option if set options &= ~pcmk__xml_fmt_diff_short; @@ -143,7 +143,7 @@ xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset, * However, v1 patchsets can only exist during rolling upgrades from * Pacemaker 1.1.11, so not worth worrying about. */ - removed = find_xml_node(patchset, XML_TAG_DIFF_REMOVED, FALSE); + removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED, NULL, NULL); for (child = pcmk__xml_first_child(removed); child != NULL; child = pcmk__xml_next(child)) { int temp_rc = xml_show_patchset_v1_recursive(out, "- ", child, 0, @@ -159,7 +159,7 @@ xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset, } is_first = true; - added = find_xml_node(patchset, XML_TAG_DIFF_ADDED, FALSE); + added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL, NULL); for (child = pcmk__xml_first_child(added); child != NULL; child = pcmk__xml_next(child)) { int temp_rc = xml_show_patchset_v1_recursive(out, "+ ", child, 0, @@ -197,16 +197,18 @@ xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset) int rc = xml_show_patchset_header(out, patchset); int temp_rc = pcmk_rc_no_output; - for (const xmlNode *change = pcmk__xml_first_child(patchset); - change != NULL; change = pcmk__xml_next(change)) { - const char *op = crm_element_value(change, XML_DIFF_OP); - const char *xpath = crm_element_value(change, XML_DIFF_PATH); + for (const xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL, + NULL); + change != NULL; change = pcmk__xe_next(change)) { + + const char *op = crm_element_value(change, PCMK_XA_OPERATION); + const char *xpath = crm_element_value(change, PCMK_XA_PATH); if (op == NULL) { continue; } - if (strcmp(op, "create") == 0) { + if (strcmp(op, PCMK_VALUE_CREATE) == 0) { char *prefix = crm_strdup_printf(PCMK__XML_PREFIX_CREATED " %s: ", xpath); @@ -226,30 +228,33 @@ xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset) rc = pcmk__output_select_rc(rc, temp_rc); free(prefix); - } else if (strcmp(op, "move") == 0) { - const char *position = crm_element_value(change, XML_DIFF_POSITION); + } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) { + const char *position = crm_element_value(change, PCMK_XE_POSITION); temp_rc = out->info(out, PCMK__XML_PREFIX_MOVED " %s moved to offset %s", xpath, position); rc = pcmk__output_select_rc(rc, temp_rc); - } else if (strcmp(op, "modify") == 0) { - xmlNode *clist = first_named_child(change, XML_DIFF_LIST); + } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) { + xmlNode *clist = pcmk__xe_first_child(change, PCMK_XE_CHANGE_LIST, + NULL, NULL); GString *buffer_set = NULL; GString *buffer_unset = NULL; - for (const xmlNode *child = pcmk__xml_first_child(clist); - child != NULL; child = pcmk__xml_next(child)) { - const char *name = crm_element_value(child, "name"); + for (const xmlNode *child = pcmk__xe_first_child(clist, NULL, NULL, + NULL); + child != NULL; child = pcmk__xe_next(child)) { + + const char *name = crm_element_value(child, PCMK_XA_NAME); - op = crm_element_value(child, XML_DIFF_OP); + op = crm_element_value(child, PCMK_XA_OPERATION); if (op == NULL) { continue; } if (strcmp(op, "set") == 0) { - const char *value = crm_element_value(child, "value"); + const char *value = crm_element_value(child, PCMK_XA_VALUE); pcmk__add_separated_word(&buffer_set, 256, "@", ", "); pcmk__g_strcat(buffer_set, name, "=", value, NULL); @@ -273,10 +278,10 @@ xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset) g_string_free(buffer_unset, TRUE); } - } else if (strcmp(op, "delete") == 0) { + } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) { int position = -1; - crm_element_value_int(change, XML_DIFF_POSITION, &position); + crm_element_value_int(change, PCMK_XE_POSITION, &position); if (position >= 0) { temp_rc = out->info(out, "-- %s (%d)", xpath, position); } else { @@ -301,7 +306,8 @@ xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset) * * \return Standard Pacemaker return code * - * \note \p args should contain only the XML patchset + * \note \p args should contain the following: + * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int @@ -340,7 +346,8 @@ xml_patchset_default(pcmk__output_t *out, va_list args) * * \return Standard Pacemaker return code * - * \note \p args should contain only the XML patchset + * \note \p args should contain the following: + * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int @@ -402,7 +409,8 @@ xml_patchset_log(pcmk__output_t *out, va_list args) * * \return Standard Pacemaker return code * - * \note \p args should contain only the XML patchset + * \note \p args should contain the following: + * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int @@ -411,10 +419,13 @@ xml_patchset_xml(pcmk__output_t *out, va_list args) const xmlNode *patchset = va_arg(args, const xmlNode *); if (patchset != NULL) { - char *buf = dump_xml_formatted_with_text(patchset); + GString *buf = g_string_sized_new(1024); + + pcmk__xml_string(patchset, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buf, + 0); - out->output_xml(out, "xml-patchset", buf); - free(buf); + out->output_xml(out, PCMK_XE_XML_PATCHSET, buf->str); + g_string_free(buf, TRUE); return pcmk_rc_ok; } crm_trace("Empty patch"); diff --git a/lib/common/probes.c b/lib/common/probes.c new file mode 100644 index 0000000..39a3905 --- /dev/null +++ b/lib/common/probes.c @@ -0,0 +1,84 @@ +/* + * Copyright 2004-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> // pcmk__str_eq(), etc. + +#include <stdio.h> // NULL +#include <stdbool.h> // bool, true, false +#include <glib.h> // guint +#include <libxml/tree.h> // xmlNode + +#include <crm/common/options.h> // PCMK_META_INTERVAL +#include <crm/common/xml.h> // PCMK_XA_OPERATION + +/*! + * \brief Check whether an action name and interval represent a probe + * + * \param[in] task Action name + * \param[in] interval_ms Action interval in milliseconds + * + * \return true if \p task is \c PCMK_ACTION_MONITOR and \p interval_ms is 0, + * otherwise false + */ +bool +pcmk_is_probe(const char *task, guint interval_ms) +{ + // @COMPAT This should be made inline at an API compatibility break + return (interval_ms == 0) + && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none); +} + +/*! + * \brief Check whether an action history entry represents a probe + * + * \param[in] xml XML of action history entry + * + * \return true if \p xml is for a probe action, otherwise false + */ +bool +pcmk_xe_is_probe(const xmlNode *xml) +{ + int interval_ms = 0; + + if (xml == NULL) { + return false; + } + + pcmk__scan_min_int(crm_element_value(xml, PCMK_META_INTERVAL), + &interval_ms, 0); + + return pcmk_is_probe(crm_element_value(xml, PCMK_XA_OPERATION), + interval_ms); +} + +/*! + * \brief Check whether an action history entry represents a maskable probe + * + * \param[in] xml XML of action history entry + * + * \return true if \p xml is for a failed probe action that should be treated as + * successful, otherwise false + */ +bool +pcmk_xe_mask_probe_failure(const xmlNode *xml) +{ + int exec_status = PCMK_EXEC_UNKNOWN; + int exit_status = PCMK_OCF_OK; + + if (!pcmk_xe_is_probe(xml)) { + return false; + } + + crm_element_value_int(xml, PCMK__XA_OP_STATUS, &exec_status); + crm_element_value_int(xml, PCMK__XA_RC_CODE, &exit_status); + + return (exit_status == PCMK_OCF_NOT_INSTALLED) + || (exit_status == PCMK_OCF_INVALID_PARAM) + || (exec_status == PCMK_EXEC_NOT_INSTALLED); +} diff --git a/lib/common/remote.c b/lib/common/remote.c index fe19296..9f8419b 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2023 the Pacemaker project contributors + * Copyright 2008-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -493,24 +493,25 @@ pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg) { int rc = pcmk_rc_ok; static uint64_t id = 0; - char *xml_text = NULL; + GString *xml_text = NULL; struct iovec iov[2]; struct remote_header_v0 *header; CRM_CHECK((remote != NULL) && (msg != NULL), return EINVAL); - xml_text = dump_xml_unformatted(msg); - CRM_CHECK(xml_text != NULL, return EINVAL); + xml_text = g_string_sized_new(1024); + pcmk__xml_string(msg, 0, xml_text, 0); + CRM_CHECK(xml_text->len > 0, + g_string_free(xml_text, TRUE); return EINVAL); - header = calloc(1, sizeof(struct remote_header_v0)); - CRM_ASSERT(header != NULL); + header = pcmk__assert_alloc(1, sizeof(struct remote_header_v0)); iov[0].iov_base = header; iov[0].iov_len = sizeof(struct remote_header_v0); - iov[1].iov_base = xml_text; - iov[1].iov_len = 1 + strlen(xml_text); + iov[1].iov_len = 1 + xml_text->len; + iov[1].iov_base = g_string_free(xml_text, FALSE); id++; header->id = id; @@ -527,7 +528,7 @@ pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg) } free(iov[0].iov_base); - free(iov[1].iov_base); + g_free((gchar *) iov[1].iov_base); return rc; } @@ -554,7 +555,8 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) if (header->payload_compressed) { int rc = 0; unsigned int size_u = 1 + header->payload_uncompressed; - char *uncompressed = calloc(1, header->payload_offset + size_u); + char *uncompressed = + pcmk__assert_alloc(1, header->payload_offset + size_u); crm_trace("Decompressing message data %d bytes into %d bytes", header->payload_compressed, size_u); @@ -592,7 +594,7 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) CRM_LOG_ASSERT(remote->buffer[sizeof(struct remote_header_v0) + header->payload_uncompressed - 1] == 0); - xml = string2xml(remote->buffer + header->payload_offset); + xml = pcmk__xml_parse(remote->buffer + header->payload_offset); if (xml == NULL && header->version > REMOTE_MSG_VERSION) { crm_warn("Couldn't parse v%d message, we only understand v%d", header->version, REMOTE_MSG_VERSION); @@ -978,7 +980,7 @@ connect_socket_retry(int sock, const struct sockaddr *addr, socklen_t addrlen, return rc; } - cb_data = calloc(1, sizeof(struct tcp_async_cb_data)); + cb_data = pcmk__assert_alloc(1, sizeof(struct tcp_async_cb_data)); cb_data->userdata = userdata; cb_data->callback = callback; cb_data->sock = sock; @@ -1206,6 +1208,9 @@ pcmk__accept_remote_connection(int ssock, int *csock) struct sockaddr_storage addr; socklen_t laddr = sizeof(addr); char addr_str[INET6_ADDRSTRLEN]; +#ifdef TCP_USER_TIMEOUT + long sbd_timeout = 0; +#endif /* accept the connection */ memset(&addr, 0, sizeof(addr)); @@ -1229,9 +1234,11 @@ pcmk__accept_remote_connection(int ssock, int *csock) } #ifdef TCP_USER_TIMEOUT - if (pcmk__get_sbd_timeout() > 0) { + sbd_timeout = pcmk__get_sbd_watchdog_timeout(); + if (sbd_timeout > 0) { // Time to fail and retry before watchdog - unsigned int optval = (unsigned int) pcmk__get_sbd_timeout() / 2; + long half = sbd_timeout / 2; + unsigned int optval = (half <= UINT_MAX)? half : UINT_MAX; rc = setsockopt(*csock, SOL_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)); diff --git a/lib/common/resources.c b/lib/common/resources.c new file mode 100644 index 0000000..46c038b --- /dev/null +++ b/lib/common/resources.c @@ -0,0 +1,67 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <stdbool.h> // bool, false + +#include <crm/common/scheduler.h> +#include <crm/common/scheduler_internal.h> + +/*! + * \internal + * \brief Get a resource's ID + * + * \param[in] rsc Resource to check + * + * \return ID of \p rsc (or NULL if \p rsc is NULL) + */ +const char * +pcmk_resource_id(const pcmk_resource_t *rsc) +{ + return (rsc == NULL)? NULL : rsc->id; +} + +/*! + * \internal + * \brief Check whether a resource is managed by the cluster + * + * \param[in] rsc Resource to check + * + * \return true if \p rsc is managed, otherwise false + */ +bool +pcmk_resource_is_managed(const pcmk_resource_t *rsc) +{ + return (rsc == NULL)? false : pcmk_is_set(rsc->flags, pcmk_rsc_managed); +} + +/*! + * \brief Get readable description of a multiply-active recovery type + * + * \param[in] recovery Recovery type + * + * \return Static string describing \p recovery + */ +const char * +pcmk__multiply_active_text(enum rsc_recovery_type recovery) +{ + switch (recovery) { + case pcmk_multiply_active_stop: + return "shutting it down"; + case pcmk_multiply_active_restart: + return "attempting recovery"; + case pcmk_multiply_active_block: + return "waiting for an administrator"; + case pcmk_multiply_active_unexpected: + return "stopping unexpected instances"; + } + return "Unknown"; +} diff --git a/lib/common/results.c b/lib/common/results.c index dde8b27..396ac0d 100644 --- a/lib/common/results.c +++ b/lib/common/results.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. * @@ -1091,9 +1091,9 @@ pcmk__copy_result(const pcmk__action_result_t *src, pcmk__action_result_t *dst) CRM_CHECK((src != NULL) && (dst != NULL), return); dst->exit_status = src->exit_status; dst->execution_status = src->execution_status; - pcmk__str_update(&dst->exit_reason, src->exit_reason); - pcmk__str_update(&dst->action_stdout, src->action_stdout); - pcmk__str_update(&dst->action_stderr, src->action_stderr); + dst->exit_reason = pcmk__str_copy(src->exit_reason); + dst->action_stdout = pcmk__str_copy(src->action_stdout); + dst->action_stderr = pcmk__str_copy(src->action_stderr); } // Deprecated functions kept only for backward API compatibility diff --git a/lib/common/roles.c b/lib/common/roles.c new file mode 100644 index 0000000..96aa72a --- /dev/null +++ b/lib/common/roles.c @@ -0,0 +1,88 @@ +/* + * Copyright 2004-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/scheduler.h> +#include <crm/common/scheduler_internal.h> + +/*! + * \brief Get readable description of a resource role + * + * \param[in] role Resource role + * + * \return Static string describing \p role, suitable for logging or display + */ +const char * +pcmk_role_text(enum rsc_role_e role) +{ + switch (role) { + case pcmk_role_stopped: + return PCMK_ROLE_STOPPED; + + case pcmk_role_started: + return PCMK_ROLE_STARTED; + + case pcmk_role_unpromoted: +#ifdef PCMK__COMPAT_2_0 + return PCMK__ROLE_UNPROMOTED_LEGACY; +#else + return PCMK_ROLE_UNPROMOTED; +#endif + + case pcmk_role_promoted: +#ifdef PCMK__COMPAT_2_0 + return PCMK__ROLE_PROMOTED_LEGACY; +#else + return PCMK_ROLE_PROMOTED; +#endif + + default: // pcmk_role_unknown + return PCMK__ROLE_UNKNOWN; + } +} + +/*! + * \brief Parse a resource role from a string role specification + * + * \param[in] role Role specification + * + * \return Resource role corresponding to \p role + */ +enum rsc_role_e +pcmk_parse_role(const char *role) +{ + if (pcmk__str_eq(role, PCMK__ROLE_UNKNOWN, + pcmk__str_casei|pcmk__str_null_matches)) { + return pcmk_role_unknown; + } else if (pcmk__str_eq(role, PCMK_ROLE_STOPPED, pcmk__str_casei)) { + return pcmk_role_stopped; + } else if (pcmk__str_eq(role, PCMK_ROLE_STARTED, pcmk__str_casei)) { + return pcmk_role_started; + } else if (pcmk__str_eq(role, PCMK__ROLE_UNPROMOTED_LEGACY, pcmk__str_casei)) { + pcmk__warn_once(pcmk__wo_slave_role, + "Support for the " PCMK__ROLE_UNPROMOTED_LEGACY + " role is deprecated and will be removed in a " + "future release. Use " PCMK_ROLE_UNPROMOTED + " instead."); + return pcmk_role_unpromoted; + } else if (pcmk__str_eq(role, PCMK_ROLE_UNPROMOTED, pcmk__str_casei)) { + return pcmk_role_unpromoted; + } else if (pcmk__str_eq(role, PCMK__ROLE_PROMOTED_LEGACY, pcmk__str_casei)) { + pcmk__warn_once(pcmk__wo_master_role, + "Support for the " PCMK__ROLE_PROMOTED_LEGACY + " role is deprecated and will be removed in a " + "future release. Use " PCMK_ROLE_PROMOTED + " instead."); + return pcmk_role_promoted; + } else if (pcmk__str_eq(role, PCMK_ROLE_PROMOTED, pcmk__str_casei)) { + return pcmk_role_promoted; + } + return pcmk_role_unknown; // Invalid role given +} diff --git a/lib/common/rules.c b/lib/common/rules.c new file mode 100644 index 0000000..32af835 --- /dev/null +++ b/lib/common/rules.c @@ -0,0 +1,1512 @@ +/* + * Copyright 2004-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL, size_t +#include <stdbool.h> // bool +#include <ctype.h> // isdigit() +#include <regex.h> // regmatch_t +#include <stdint.h> // uint32_t +#include <inttypes.h> // PRIu32 +#include <glib.h> // gboolean, FALSE +#include <libxml/tree.h> // xmlNode + +#include <crm/common/scheduler.h> + +#include <crm/common/iso8601_internal.h> +#include <crm/common/nvpair_internal.h> +#include <crm/common/scheduler_internal.h> +#include "crmcommon_private.h" + +/*! + * \internal + * \brief Get the condition type corresponding to given condition XML + * + * \param[in] condition Rule condition XML + * + * \return Condition type corresponding to \p condition + */ +enum expression_type +pcmk__condition_type(const xmlNode *condition) +{ + const char *name = NULL; + + // Expression types based on element name + + if (pcmk__xe_is(condition, PCMK_XE_DATE_EXPRESSION)) { + return pcmk__condition_datetime; + + } else if (pcmk__xe_is(condition, PCMK_XE_RSC_EXPRESSION)) { + return pcmk__condition_resource; + + } else if (pcmk__xe_is(condition, PCMK_XE_OP_EXPRESSION)) { + return pcmk__condition_operation; + + } else if (pcmk__xe_is(condition, PCMK_XE_RULE)) { + return pcmk__condition_rule; + + } else if (!pcmk__xe_is(condition, PCMK_XE_EXPRESSION)) { + return pcmk__condition_unknown; + } + + // Expression types based on node attribute name + + name = crm_element_value(condition, PCMK_XA_ATTRIBUTE); + + if (pcmk__str_any_of(name, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, + NULL)) { + return pcmk__condition_location; + } + + return pcmk__condition_attribute; +} + +/*! + * \internal + * \brief Get parent XML element's ID for logging purposes + * + * \param[in] xml XML of a subelement + * + * \return ID of \p xml's parent for logging purposes (guaranteed non-NULL) + */ +static const char * +loggable_parent_id(const xmlNode *xml) +{ + // Default if called without parent (likely for unit testing) + const char *parent_id = "implied"; + + if ((xml != NULL) && (xml->parent != NULL)) { + parent_id = pcmk__xe_id(xml->parent); + if (parent_id == NULL) { // Not possible with schema validation enabled + parent_id = "without ID"; + } + } + return parent_id; +} + +/*! + * \internal + * \brief Get the moon phase corresponding to a given date/time + * + * \param[in] now Date/time to get moon phase for + * + * \return Phase of the moon corresponding to \p now, where 0 is the new moon + * and 7 is the full moon + * \deprecated This feature has been deprecated since 2.1.6. + */ +static int +phase_of_the_moon(const crm_time_t *now) +{ + /* As per the nethack rules: + * - A moon period is 29.53058 days ~= 30 + * - A year is 365.2422 days + * - Number of days moon phase advances on first day of year compared to + * preceding year is (365.2422 - 12 * 29.53058) ~= 11 + * - Number of years until same phases fall on the same days of the month + * is 18.6 ~= 19 + * - Moon phase on first day of year (epact) ~= (11 * (year%19) + 29) % 30 + * (29 as initial condition) + * - Current phase in days = first day phase + days elapsed in year + * - 6 moons ~= 177 days ~= 8 reported phases * 22 (+ 11/22 for rounding) + */ + uint32_t epact, diy, goldn; + uint32_t y; + + crm_time_get_ordinal(now, &y, &diy); + goldn = (y % 19) + 1; + epact = (11 * goldn + 18) % 30; + if (((epact == 25) && (goldn > 11)) || (epact == 24)) { + epact++; + } + return (((((diy + epact) * 6) + 11) % 177) / 22) & 7; +} + +/*! + * \internal + * \brief Check an integer value against a range from a date specification + * + * \param[in] date_spec XML of PCMK_XE_DATE_SPEC element to check + * \param[in] id XML ID for logging purposes + * \param[in] attr Name of XML attribute with range to check against + * \param[in] value Value to compare against range + * + * \return Standard Pacemaker return code (specifically, pcmk_rc_before_range, + * pcmk_rc_after_range, or pcmk_rc_ok to indicate that result is either + * within range or undetermined) + * \note We return pcmk_rc_ok for an undetermined result so we can continue + * checking the next range attribute. + */ +static int +check_range(const xmlNode *date_spec, const char *id, const char *attr, + uint32_t value) +{ + int rc = pcmk_rc_ok; + const char *range = crm_element_value(date_spec, attr); + long long low, high; + + if (range == NULL) { // Attribute not present + goto bail; + } + + if (pcmk__parse_ll_range(range, &low, &high) != pcmk_rc_ok) { + // Invalid range + /* @COMPAT When we can break behavioral backward compatibility, treat + * the entire rule as not passing. + */ + pcmk__config_err("Ignoring " PCMK_XE_DATE_SPEC + " %s attribute %s because '%s' is not a valid range", + id, attr, range); + + } else if ((low != -1) && (value < low)) { + rc = pcmk_rc_before_range; + + } else if ((high != -1) && (value > high)) { + rc = pcmk_rc_after_range; + } + +bail: + crm_trace("Checked " PCMK_XE_DATE_SPEC " %s %s='%s' for %" PRIu32 ": %s", + id, attr, pcmk__s(range, ""), value, pcmk_rc_str(rc)); + return rc; +} + +/*! + * \internal + * \brief Evaluate a date specification for a given date/time + * + * \param[in] date_spec XML of PCMK_XE_DATE_SPEC element to evaluate + * \param[in] now Time to check + * + * \return Standard Pacemaker return code (specifically, EINVAL for NULL + * arguments, pcmk_rc_ok if time matches specification, or + * pcmk_rc_before_range, pcmk_rc_after_range, or pcmk_rc_op_unsatisfied + * as appropriate to how time relates to specification) + */ +int +pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now) +{ + const char *id = NULL; + const char *parent_id = loggable_parent_id(date_spec); + + // Range attributes that can be specified for a PCMK_XE_DATE_SPEC element + struct range { + const char *attr; + uint32_t value; + } ranges[] = { + { PCMK_XA_YEARS, 0U }, + { PCMK_XA_MONTHS, 0U }, + { PCMK_XA_MONTHDAYS, 0U }, + { PCMK_XA_HOURS, 0U }, + { PCMK_XA_MINUTES, 0U }, + { PCMK_XA_SECONDS, 0U }, + { PCMK_XA_YEARDAYS, 0U }, + { PCMK_XA_WEEKYEARS, 0U }, + { PCMK_XA_WEEKS, 0U }, + { PCMK_XA_WEEKDAYS, 0U }, + { PCMK__XA_MOON, 0U }, + }; + + if ((date_spec == NULL) || (now == NULL)) { + return EINVAL; + } + + // Get specification ID (for logging) + id = pcmk__xe_id(date_spec); + if (pcmk__str_empty(id)) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * fail the specification + */ + pcmk__config_warn(PCMK_XE_DATE_SPEC " subelement of " + PCMK_XE_DATE_EXPRESSION " %s has no " PCMK_XA_ID, + parent_id); + id = "without ID"; // for logging + } + + // Year, month, day + crm_time_get_gregorian(now, &(ranges[0].value), &(ranges[1].value), + &(ranges[2].value)); + + // Hour, minute, second + crm_time_get_timeofday(now, &(ranges[3].value), &(ranges[4].value), + &(ranges[5].value)); + + // Year (redundant) and day of year + crm_time_get_ordinal(now, &(ranges[0].value), &(ranges[6].value)); + + // Week year, week of week year, day of week + crm_time_get_isoweek(now, &(ranges[7].value), &(ranges[8].value), + &(ranges[9].value)); + + // Moon phase (deprecated) + ranges[10].value = phase_of_the_moon(now); + if (crm_element_value(date_spec, PCMK__XA_MOON) != NULL) { + pcmk__config_warn("Support for '" PCMK__XA_MOON "' in " + PCMK_XE_DATE_SPEC " elements (such as %s) is " + "deprecated and will be removed in a future release " + "of Pacemaker", id); + } + + for (int i = 0; i < PCMK__NELEM(ranges); ++i) { + int rc = check_range(date_spec, id, ranges[i].attr, ranges[i].value); + + if (rc != pcmk_rc_ok) { + return rc; + } + } + + // All specified ranges passed, or none were given (also considered a pass) + return pcmk_rc_ok; +} + +#define ADD_COMPONENT(component) do { \ + int sub_rc = pcmk__add_time_from_xml(*end, component, duration); \ + if (sub_rc != pcmk_rc_ok) { \ + /* @COMPAT return sub_rc when we can break compatibility */ \ + pcmk__config_warn("Ignoring %s in " PCMK_XE_DURATION " %s " \ + "because it is invalid", \ + pcmk__time_component_attr(component), id); \ + rc = sub_rc; \ + } \ + } while (0) + +/*! + * \internal + * \brief Given a duration and a start time, calculate the end time + * + * \param[in] duration XML of PCMK_XE_DURATION element + * \param[in] start Start time + * \param[out] end Where to store end time (\p *end must be NULL + * initially) + * + * \return Standard Pacemaker return code + * \note The caller is responsible for freeing \p *end using crm_time_free(). + */ +int +pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start, + crm_time_t **end) +{ + int rc = pcmk_rc_ok; + const char *id = NULL; + const char *parent_id = loggable_parent_id(duration); + + if ((start == NULL) || (duration == NULL) + || (end == NULL) || (*end != NULL)) { + return EINVAL; + } + + // Get duration ID (for logging) + id = pcmk__xe_id(duration); + if (pcmk__str_empty(id)) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error instead + */ + pcmk__config_warn(PCMK_XE_DURATION " subelement of " + PCMK_XE_DATE_EXPRESSION " %s has no " PCMK_XA_ID, + parent_id); + id = "without ID"; + } + + *end = pcmk_copy_time(start); + + ADD_COMPONENT(pcmk__time_years); + ADD_COMPONENT(pcmk__time_months); + ADD_COMPONENT(pcmk__time_weeks); + ADD_COMPONENT(pcmk__time_days); + ADD_COMPONENT(pcmk__time_hours); + ADD_COMPONENT(pcmk__time_minutes); + ADD_COMPONENT(pcmk__time_seconds); + + return rc; +} + +/*! + * \internal + * \brief Evaluate a range check for a given date/time + * + * \param[in] date_expression XML of PCMK_XE_DATE_EXPRESSION element + * \param[in] id Expression ID for logging purposes + * \param[in] now Date/time to compare + * \param[in,out] next_change If not NULL, set this to when the evaluation + * will change, if known and earlier than the + * original value + * + * \return Standard Pacemaker return code + */ +static int +evaluate_in_range(const xmlNode *date_expression, const char *id, + const crm_time_t *now, crm_time_t *next_change) +{ + crm_time_t *start = NULL; + crm_time_t *end = NULL; + + if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START, + &start) != pcmk_rc_ok) { + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Ignoring " PCMK_XA_START " in " + PCMK_XE_DATE_EXPRESSION " %s because it is invalid", + id); + } + + if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END, + &end) != pcmk_rc_ok) { + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Ignoring " PCMK_XA_END " in " + PCMK_XE_DATE_EXPRESSION " %s because it is invalid", + id); + } + + if ((start == NULL) && (end == NULL)) { + // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not " + "passing because in_range requires at least one of " + PCMK_XA_START " or " PCMK_XA_END, id); + return pcmk_rc_undetermined; + } + + if (end == NULL) { + xmlNode *duration = pcmk__xe_first_child(date_expression, + PCMK_XE_DURATION, NULL, NULL); + + if (duration != NULL) { + /* @COMPAT When we can break behavioral backward compatibility, + * return the result of this if not OK + */ + pcmk__unpack_duration(duration, start, &end); + } + } + + if ((start != NULL) && (crm_time_compare(now, start) < 0)) { + pcmk__set_time_if_earlier(next_change, start); + crm_time_free(start); + crm_time_free(end); + return pcmk_rc_before_range; + } + + if (end != NULL) { + if (crm_time_compare(now, end) > 0) { + crm_time_free(start); + crm_time_free(end); + return pcmk_rc_after_range; + } + + // Evaluation doesn't change until second after end + if (next_change != NULL) { + crm_time_add_seconds(end, 1); + pcmk__set_time_if_earlier(next_change, end); + } + } + + crm_time_free(start); + crm_time_free(end); + return pcmk_rc_within_range; +} + +/*! + * \internal + * \brief Evaluate a greater-than check for a given date/time + * + * \param[in] date_expression XML of PCMK_XE_DATE_EXPRESSION element + * \param[in] id Expression ID for logging purposes + * \param[in] now Date/time to compare + * \param[in,out] next_change If not NULL, set this to when the evaluation + * will change, if known and earlier than the + * original value + * + * \return Standard Pacemaker return code + */ +static int +evaluate_gt(const xmlNode *date_expression, const char *id, + const crm_time_t *now, crm_time_t *next_change) +{ + crm_time_t *start = NULL; + + if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START, + &start) != pcmk_rc_ok) { + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not " + "passing because " PCMK_XA_START " is invalid", + id); + return pcmk_rc_undetermined; + } + + if (start == NULL) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not " + "passing because " PCMK_VALUE_GT " requires " + PCMK_XA_START, id); + return pcmk_rc_undetermined; + } + + if (crm_time_compare(now, start) > 0) { + crm_time_free(start); + return pcmk_rc_within_range; + } + + // Evaluation doesn't change until second after start time + crm_time_add_seconds(start, 1); + pcmk__set_time_if_earlier(next_change, start); + crm_time_free(start); + return pcmk_rc_before_range; +} + +/*! + * \internal + * \brief Evaluate a less-than check for a given date/time + * + * \param[in] date_expression XML of PCMK_XE_DATE_EXPRESSION element + * \param[in] id Expression ID for logging purposes + * \param[in] now Date/time to compare + * \param[in,out] next_change If not NULL, set this to when the evaluation + * will change, if known and earlier than the + * original value + * + * \return Standard Pacemaker return code + */ +static int +evaluate_lt(const xmlNode *date_expression, const char *id, + const crm_time_t *now, crm_time_t *next_change) +{ + crm_time_t *end = NULL; + + if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END, + &end) != pcmk_rc_ok) { + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not " + "passing because " PCMK_XA_END " is invalid", id); + return pcmk_rc_undetermined; + } + + if (end == NULL) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not " + "passing because " PCMK_VALUE_GT " requires " + PCMK_XA_END, id); + return pcmk_rc_undetermined; + } + + if (crm_time_compare(now, end) < 0) { + pcmk__set_time_if_earlier(next_change, end); + crm_time_free(end); + return pcmk_rc_within_range; + } + + crm_time_free(end); + return pcmk_rc_after_range; +} + +/*! + * \internal + * \brief Evaluate a rule's date expression for a given date/time + * + * \param[in] date_expression XML of a PCMK_XE_DATE_EXPRESSION element + * \param[in] now Time to use for evaluation + * \param[in,out] next_change If not NULL, set this to when the evaluation + * will change, if known and earlier than the + * original value + * + * \return Standard Pacemaker return code (unlike most other evaluation + * functions, this can return either pcmk_rc_ok or pcmk_rc_within_range + * on success) + */ +int +pcmk__evaluate_date_expression(const xmlNode *date_expression, + const crm_time_t *now, crm_time_t *next_change) +{ + const char *id = NULL; + const char *op = NULL; + int rc = pcmk_rc_undetermined; + + if ((date_expression == NULL) || (now == NULL)) { + return EINVAL; + } + + // Get expression ID (for logging) + id = pcmk__xe_id(date_expression); + if (pcmk__str_empty(id)) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn(PCMK_XE_DATE_EXPRESSION " element has no " + PCMK_XA_ID); + id = "without ID"; // for logging + } + + op = crm_element_value(date_expression, PCMK_XA_OPERATION); + if (pcmk__str_eq(op, PCMK_VALUE_IN_RANGE, + pcmk__str_null_matches|pcmk__str_casei)) { + rc = evaluate_in_range(date_expression, id, now, next_change); + + } else if (pcmk__str_eq(op, PCMK_VALUE_DATE_SPEC, pcmk__str_casei)) { + xmlNode *date_spec = pcmk__xe_first_child(date_expression, + PCMK_XE_DATE_SPEC, NULL, + NULL); + + if (date_spec == NULL) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s " + "as not passing because " PCMK_VALUE_DATE_SPEC + " operations require a " PCMK_XE_DATE_SPEC + " subelement", id); + } else { + // @TODO set next_change appropriately + rc = pcmk__evaluate_date_spec(date_spec, now); + } + + } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) { + rc = evaluate_gt(date_expression, id, now, next_change); + + } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) { + rc = evaluate_lt(date_expression, id, now, next_change); + + } else { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION + " %s as not passing because '%s' is not a valid " + PCMK_XE_OPERATION, op); + } + + crm_trace(PCMK_XE_DATE_EXPRESSION " %s (%s): %s (%d)", + id, op, pcmk_rc_str(rc), rc); + return rc; +} + +/*! + * \internal + * \brief Go through submatches in a string, either counting how many bytes + * would be needed for the expansion, or performing the expansion, + * as requested + * + * \param[in] string String possibly containing submatch variables + * \param[in] match String that matched the regular expression + * \param[in] submatches Regular expression submatches (as set by regexec()) + * \param[in] nmatches Number of entries in \p submatches[] + * \param[out] expansion If not NULL, expand string here (must be + * pre-allocated to appropriate size) + * \param[out] nbytes If not NULL, set to size needed for expansion + * + * \return true if any expansion is needed, otherwise false + */ +static bool +process_submatches(const char *string, const char *match, + const regmatch_t submatches[], int nmatches, + char *expansion, size_t *nbytes) +{ + bool expanded = false; + const char *src = string; + + if (nbytes != NULL) { + *nbytes = 1; // Include space for terminator + } + + while (*src != '\0') { + int submatch = 0; + size_t match_len = 0; + + if ((src[0] != '%') || !isdigit(src[1])) { + /* src does not point to the first character of a %N sequence, + * so expand this character as-is + */ + if (expansion != NULL) { + *expansion++ = *src; + } + if (nbytes != NULL) { + ++(*nbytes); + } + ++src; + continue; + } + + submatch = src[1] - '0'; + src += 2; // Skip over %N sequence in source string + expanded = true; // Expansion will be different from source + + // Omit sequence from expansion unless it has a non-empty match + if ((nmatches <= submatch) // Not enough submatches + || (submatches[submatch].rm_so < 0) // Pattern did not match + || (submatches[submatch].rm_eo + <= submatches[submatch].rm_so)) { // Match was empty + continue; + } + + match_len = submatches[submatch].rm_eo - submatches[submatch].rm_so; + if (nbytes != NULL) { + *nbytes += match_len; + } + if (expansion != NULL) { + memcpy(expansion, match + submatches[submatch].rm_so, + match_len); + expansion += match_len; + } + } + + return expanded; +} + +/*! + * \internal + * \brief Expand any regular expression submatches (%0-%9) in a string + * + * \param[in] string String possibly containing submatch variables + * \param[in] match String that matched the regular expression + * \param[in] submatches Regular expression submatches (as set by regexec()) + * \param[in] nmatches Number of entries in \p submatches[] + * + * \return Newly allocated string identical to \p string with submatches + * expanded on success, or NULL if no expansions were needed + * \note The caller is responsible for freeing the result with free() + */ +char * +pcmk__replace_submatches(const char *string, const char *match, + const regmatch_t submatches[], int nmatches) +{ + size_t nbytes = 0; + char *result = NULL; + + if (pcmk__str_empty(string) || pcmk__str_empty(match)) { + return NULL; // Nothing to expand + } + + // Calculate how much space will be needed for expanded string + if (!process_submatches(string, match, submatches, nmatches, NULL, + &nbytes)) { + return NULL; // No expansions needed + } + + // Allocate enough space for expanded string + result = pcmk__assert_alloc(nbytes, sizeof(char)); + + // Expand submatches + (void) process_submatches(string, match, submatches, nmatches, result, + NULL); + return result; +} + +/*! + * \internal + * \brief Parse a comparison type from a string + * + * \param[in] op String with comparison type (valid values are + * \c PCMK_VALUE_DEFINED, \c PCMK_VALUE_NOT_DEFINED, + * \c PCMK_VALUE_EQ, \c PCMK_VALUE_NE, + * \c PCMK_VALUE_LT, \c PCMK_VALUE_LTE, + * \c PCMK_VALUE_GT, or \c PCMK_VALUE_GTE) + * + * \return Comparison type corresponding to \p op + */ +enum pcmk__comparison +pcmk__parse_comparison(const char *op) +{ + if (pcmk__str_eq(op, PCMK_VALUE_DEFINED, pcmk__str_casei)) { + return pcmk__comparison_defined; + + } else if (pcmk__str_eq(op, PCMK_VALUE_NOT_DEFINED, pcmk__str_casei)) { + return pcmk__comparison_undefined; + + } else if (pcmk__str_eq(op, PCMK_VALUE_EQ, pcmk__str_casei)) { + return pcmk__comparison_eq; + + } else if (pcmk__str_eq(op, PCMK_VALUE_NE, pcmk__str_casei)) { + return pcmk__comparison_ne; + + } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) { + return pcmk__comparison_lt; + + } else if (pcmk__str_eq(op, PCMK_VALUE_LTE, pcmk__str_casei)) { + return pcmk__comparison_lte; + + } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) { + return pcmk__comparison_gt; + + } else if (pcmk__str_eq(op, PCMK_VALUE_GTE, pcmk__str_casei)) { + return pcmk__comparison_gte; + } + + return pcmk__comparison_unknown; +} + +/*! + * \internal + * \brief Parse a value type from a string + * + * \param[in] type String with value type (valid values are NULL, + * \c PCMK_VALUE_STRING, \c PCMK_VALUE_INTEGER, + * \c PCMK_VALUE_NUMBER, and \c PCMK_VALUE_VERSION) + * \param[in] op Operation type (used only to select default) + * \param[in] value1 First value being compared (used only to select default) + * \param[in] value2 Second value being compared (used only to select default) + */ +enum pcmk__type +pcmk__parse_type(const char *type, enum pcmk__comparison op, + const char *value1, const char *value2) +{ + if (type == NULL) { + switch (op) { + case pcmk__comparison_lt: + case pcmk__comparison_lte: + case pcmk__comparison_gt: + case pcmk__comparison_gte: + if (((value1 != NULL) && (strchr(value1, '.') != NULL)) + || ((value2 != NULL) && (strchr(value2, '.') != NULL))) { + return pcmk__type_number; + } + return pcmk__type_integer; + + default: + return pcmk__type_string; + } + } + + if (pcmk__str_eq(type, PCMK_VALUE_STRING, pcmk__str_casei)) { + return pcmk__type_string; + + } else if (pcmk__str_eq(type, PCMK_VALUE_INTEGER, pcmk__str_casei)) { + return pcmk__type_integer; + + } else if (pcmk__str_eq(type, PCMK_VALUE_NUMBER, pcmk__str_casei)) { + return pcmk__type_number; + + } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_casei)) { + return pcmk__type_version; + } + + return pcmk__type_unknown; +} + +/*! + * \internal + * \brief Compare two strings according to a given type + * + * \param[in] value1 String with first value to compare + * \param[in] value2 String with second value to compare + * \param[in] type How to interpret the values + * + * \return Standard comparison result (a negative integer if \p value1 is + * lesser, 0 if the values are equal, and a positive integer if + * \p value1 is greater) + */ +int +pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type) +{ + // NULL compares as less than non-NULL + if (value2 == NULL) { + return (value1 == NULL)? 0 : 1; + } + if (value1 == NULL) { + return -1; + } + + switch (type) { + case pcmk__type_string: + return strcasecmp(value1, value2); + + case pcmk__type_integer: + { + long long integer1; + long long integer2; + + if ((pcmk__scan_ll(value1, &integer1, 0LL) != pcmk_rc_ok) + || (pcmk__scan_ll(value2, &integer2, 0LL) != pcmk_rc_ok)) { + crm_warn("Comparing '%s' and '%s' as strings because " + "invalid as integers", value1, value2); + return strcasecmp(value1, value2); + } + return (integer1 < integer2)? -1 : (integer1 > integer2)? 1 : 0; + } + break; + + case pcmk__type_number: + { + double num1; + double num2; + + if ((pcmk__scan_double(value1, &num1, NULL, NULL) != pcmk_rc_ok) + || (pcmk__scan_double(value2, &num2, NULL, + NULL) != pcmk_rc_ok)) { + crm_warn("Comparing '%s' and '%s' as strings because invalid as " + "numbers", value1, value2); + return strcasecmp(value1, value2); + } + return (num1 < num2)? -1 : (num1 > num2)? 1 : 0; + } + break; + + case pcmk__type_version: + return compare_version(value1, value2); + + default: // Invalid type + return 0; + } +} + +/*! + * \internal + * \brief Parse a reference value source from a string + * + * \param[in] source String indicating reference value source + * + * \return Reference value source corresponding to \p source + */ +enum pcmk__reference_source +pcmk__parse_source(const char *source) +{ + if (pcmk__str_eq(source, PCMK_VALUE_LITERAL, + pcmk__str_casei|pcmk__str_null_matches)) { + return pcmk__source_literal; + + } else if (pcmk__str_eq(source, PCMK_VALUE_PARAM, pcmk__str_casei)) { + return pcmk__source_instance_attrs; + + } else if (pcmk__str_eq(source, PCMK_VALUE_META, pcmk__str_casei)) { + return pcmk__source_meta_attrs; + + } else { + return pcmk__source_unknown; + } +} + +/*! + * \internal + * \brief Parse a boolean operator from a string + * + * \param[in] combine String indicating boolean operator + * + * \return Enumeration value corresponding to \p combine + */ +enum pcmk__combine +pcmk__parse_combine(const char *combine) +{ + if (pcmk__str_eq(combine, PCMK_VALUE_AND, + pcmk__str_null_matches|pcmk__str_casei)) { + return pcmk__combine_and; + + } else if (pcmk__str_eq(combine, PCMK_VALUE_OR, pcmk__str_casei)) { + return pcmk__combine_or; + + } else { + return pcmk__combine_unknown; + } +} + +/*! + * \internal + * \brief Get the result of a node attribute comparison for rule evaluation + * + * \param[in] actual Actual node attribute value + * \param[in] reference Node attribute value from rule (ignored for + * \p comparison of \c pcmk__comparison_defined or + * \c pcmk__comparison_undefined) + * \param[in] type How to interpret the values + * \param[in] comparison How to compare the values + * + * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok if the + * comparison passes, and some other value if it does not) + */ +static int +evaluate_attr_comparison(const char *actual, const char *reference, + enum pcmk__type type, enum pcmk__comparison comparison) +{ + int cmp = 0; + + switch (comparison) { + case pcmk__comparison_defined: + return (actual != NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied; + + case pcmk__comparison_undefined: + return (actual == NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied; + + default: + break; + } + + cmp = pcmk__cmp_by_type(actual, reference, type); + + switch (comparison) { + case pcmk__comparison_eq: + return (cmp == 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied; + + case pcmk__comparison_ne: + return (cmp != 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied; + + default: + break; + } + + if ((actual == NULL) || (reference == NULL)) { + return pcmk_rc_op_unsatisfied; // Comparison would be meaningless + } + + switch (comparison) { + case pcmk__comparison_lt: + return (cmp < 0)? pcmk_rc_ok : pcmk_rc_after_range; + + case pcmk__comparison_lte: + return (cmp <= 0)? pcmk_rc_ok : pcmk_rc_after_range; + + case pcmk__comparison_gt: + return (cmp > 0)? pcmk_rc_ok : pcmk_rc_before_range; + + case pcmk__comparison_gte: + return (cmp >= 0)? pcmk_rc_ok : pcmk_rc_before_range; + + default: // Not possible with schema validation enabled + return pcmk_rc_op_unsatisfied; + } +} + +/*! + * \internal + * \brief Get a reference value from a configured source + * + * \param[in] value Value given in rule expression + * \param[in] source Reference value source + * \param[in] rule_input Values used to evaluate rule criteria + */ +static const char * +value_from_source(const char *value, enum pcmk__reference_source source, + const pcmk_rule_input_t *rule_input) +{ + GHashTable *table = NULL; + + if (pcmk__str_empty(value)) { + /* @COMPAT When we can break backward compatibility, drop this block so + * empty strings are treated as such (there should never be an empty + * string as an instance attribute or meta-attribute name, so those will + * get NULL anyway, but it could matter for literal comparisons) + */ + return NULL; + } + + switch (source) { + case pcmk__source_literal: + return value; + + case pcmk__source_instance_attrs: + table = rule_input->rsc_params; + break; + + case pcmk__source_meta_attrs: + table = rule_input->rsc_meta; + break; + + default: + return NULL; // Not possible + } + + if (table == NULL) { + return NULL; + } + return (const char *) g_hash_table_lookup(table, value); +} + +/*! + * \internal + * \brief Evaluate a node attribute rule expression + * + * \param[in] expression XML of a rule's PCMK_XE_EXPRESSION subelement + * \param[in] rule_input Values used to evaluate rule criteria + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if the expression + * passes, some other value if it does not) + */ +int +pcmk__evaluate_attr_expression(const xmlNode *expression, + const pcmk_rule_input_t *rule_input) +{ + const char *id = NULL; + const char *op = NULL; + const char *attr = NULL; + const char *type_s = NULL; + const char *value = NULL; + const char *actual = NULL; + const char *source_s = NULL; + const char *reference = NULL; + char *expanded_attr = NULL; + int rc = pcmk_rc_ok; + + enum pcmk__type type = pcmk__type_unknown; + enum pcmk__reference_source source = pcmk__source_unknown; + enum pcmk__comparison comparison = pcmk__comparison_unknown; + + if ((expression == NULL) || (rule_input == NULL)) { + return EINVAL; + } + + // Get expression ID (for logging) + id = pcmk__xe_id(expression); + if (pcmk__str_empty(id)) { + /* @COMPAT When we can break behavioral backward compatibility, + * fail the expression + */ + pcmk__config_warn(PCMK_XE_EXPRESSION " element has no " PCMK_XA_ID); + id = "without ID"; // for logging + } + + /* Get name of node attribute to compare (expanding any %0-%9 to + * regular expression submatches) + */ + attr = crm_element_value(expression, PCMK_XA_ATTRIBUTE); + if (attr == NULL) { + pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not passing " + "because " PCMK_XA_ATTRIBUTE " was not specified", id); + return pcmk_rc_unpack_error; + } + expanded_attr = pcmk__replace_submatches(attr, rule_input->rsc_id, + rule_input->rsc_id_submatches, + rule_input->rsc_id_nmatches); + if (expanded_attr != NULL) { + attr = expanded_attr; + } + + // Get and validate operation + op = crm_element_value(expression, PCMK_XA_OPERATION); + comparison = pcmk__parse_comparison(op); + if (comparison == pcmk__comparison_unknown) { + // Not possible with schema validation enabled + if (op == NULL) { + pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not " + "passing because it has no " PCMK_XA_OPERATION, + id); + } else { + pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not " + "passing because '%s' is not a valid " + PCMK_XA_OPERATION, id, op); + } + rc = pcmk_rc_unpack_error; + goto done; + } + + // How reference value is obtained (literal, resource meta-attribute, etc.) + source_s = crm_element_value(expression, PCMK_XA_VALUE_SOURCE); + source = pcmk__parse_source(source_s); + if (source == pcmk__source_unknown) { + // Not possible with schema validation enabled + // @COMPAT Fail expression once we can break backward compatibility + pcmk__config_warn("Expression %s has invalid " PCMK_XA_VALUE_SOURCE + " value '%s', using default " + "('" PCMK_VALUE_LITERAL "')", id, source_s); + source = pcmk__source_literal; + } + + // Get and validate reference value + value = crm_element_value(expression, PCMK_XA_VALUE); + switch (comparison) { + case pcmk__comparison_defined: + case pcmk__comparison_undefined: + if (value != NULL) { + pcmk__config_warn("Ignoring " PCMK_XA_VALUE " in " + PCMK_XE_EXPRESSION " %s because it is unused " + "when " PCMK_XA_BOOLEAN_OP " is %s", id, op); + } + break; + + default: + if (value == NULL) { + pcmk__config_warn(PCMK_XE_EXPRESSION " %s has no " + PCMK_XA_VALUE, id); + } + break; + } + reference = value_from_source(value, source, rule_input); + + // Get actual value of node attribute + if (rule_input->node_attrs != NULL) { + actual = g_hash_table_lookup(rule_input->node_attrs, attr); + } + + // Get and validate value type (after expanding reference value) + type_s = crm_element_value(expression, PCMK_XA_TYPE); + type = pcmk__parse_type(type_s, comparison, actual, reference); + if (type == pcmk__type_unknown) { + /* Not possible with schema validation enabled + * + * @COMPAT When we can break behavioral backward compatibility, treat + * the expression as not passing. + */ + pcmk__config_warn("Non-empty node attribute values will be treated as " + "equal for " PCMK_XE_EXPRESSION " %s because '%s' " + "is not a valid type", id, type); + } + + rc = evaluate_attr_comparison(actual, reference, type, comparison); + switch (comparison) { + case pcmk__comparison_defined: + case pcmk__comparison_undefined: + crm_trace(PCMK_XE_EXPRESSION " %s result: %s (for attribute %s %s)", + id, pcmk_rc_str(rc), attr, op); + break; + + default: + crm_trace(PCMK_XE_EXPRESSION " %s result: " + "%s (attribute %s %s '%s' via %s source as %s type)", + id, pcmk_rc_str(rc), attr, op, pcmk__s(reference, ""), + pcmk__s(source_s, "default"), pcmk__s(type_s, "default")); + break; + } + +done: + free(expanded_attr); + return rc; +} + +/*! + * \internal + * \brief Evaluate a resource rule expression + * + * \param[in] rsc_expression XML of rule's \c PCMK_XE_RSC_EXPRESSION subelement + * \param[in] rule_input Values used to evaluate rule criteria + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if the expression + * passes, some other value if it does not) + */ +int +pcmk__evaluate_rsc_expression(const xmlNode *rsc_expression, + const pcmk_rule_input_t *rule_input) +{ + const char *id = NULL; + const char *standard = NULL; + const char *provider = NULL; + const char *type = NULL; + + if ((rsc_expression == NULL) || (rule_input == NULL)) { + return EINVAL; + } + + // Validate XML ID + id = pcmk__xe_id(rsc_expression); + if (pcmk__str_empty(id)) { + // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * fail the expression + */ + pcmk__config_warn(PCMK_XE_RSC_EXPRESSION " has no " PCMK_XA_ID); + id = "without ID"; // for logging + } + + // Compare resource standard + standard = crm_element_value(rsc_expression, PCMK_XA_CLASS); + if ((standard != NULL) + && !pcmk__str_eq(standard, rule_input->rsc_standard, pcmk__str_none)) { + crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because " + "actual standard '%s' doesn't match '%s'", + id, pcmk__s(rule_input->rsc_standard, ""), standard); + return pcmk_rc_op_unsatisfied; + } + + // Compare resource provider + provider = crm_element_value(rsc_expression, PCMK_XA_PROVIDER); + if ((provider != NULL) + && !pcmk__str_eq(provider, rule_input->rsc_provider, pcmk__str_none)) { + crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because " + "actual provider '%s' doesn't match '%s'", + id, pcmk__s(rule_input->rsc_provider, ""), provider); + return pcmk_rc_op_unsatisfied; + } + + // Compare resource agent type + type = crm_element_value(rsc_expression, PCMK_XA_TYPE); + if ((type != NULL) + && !pcmk__str_eq(type, rule_input->rsc_agent, pcmk__str_none)) { + crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because " + "actual agent '%s' doesn't match '%s'", + id, pcmk__s(rule_input->rsc_agent, ""), type); + return pcmk_rc_op_unsatisfied; + } + + crm_trace(PCMK_XE_RSC_EXPRESSION " %s is satisfied by %s%s%s:%s", + id, pcmk__s(standard, ""), + ((provider == NULL)? "" : ":"), pcmk__s(provider, ""), + pcmk__s(type, "")); + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Evaluate an operation rule expression + * + * \param[in] op_expression XML of a rule's \c PCMK_XE_OP_EXPRESSION subelement + * \param[in] rule_input Values used to evaluate rule criteria + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if the expression + * is satisfied, some other value if it is not) + */ +int +pcmk__evaluate_op_expression(const xmlNode *op_expression, + const pcmk_rule_input_t *rule_input) +{ + const char *id = NULL; + const char *name = NULL; + const char *interval_s = NULL; + guint interval_ms = 0U; + + if ((op_expression == NULL) || (rule_input == NULL)) { + return EINVAL; + } + + // Get operation expression ID (for logging) + id = pcmk__xe_id(op_expression); + if (pcmk__str_empty(id)) { // Not possible with schema validation enabled + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_op_unsatisfied + */ + pcmk__config_warn(PCMK_XE_OP_EXPRESSION " element has no " PCMK_XA_ID); + id = "without ID"; // for logging + } + + // Validate operation name + name = crm_element_value(op_expression, PCMK_XA_NAME); + if (name == NULL) { // Not possible with schema validation enabled + pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not " + "passing because it has no " PCMK_XA_NAME, id); + return pcmk_rc_unpack_error; + } + + // Validate operation interval + interval_s = crm_element_value(op_expression, PCMK_META_INTERVAL); + if (pcmk_parse_interval_spec(interval_s, &interval_ms) != pcmk_rc_ok) { + pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not " + "passing because '%s' is not a valid interval", + id, interval_s); + return pcmk_rc_unpack_error; + } + + // Compare operation name + if (!pcmk__str_eq(name, rule_input->op_name, pcmk__str_none)) { + crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because " + "actual name '%s' doesn't match '%s'", + id, pcmk__s(rule_input->op_name, ""), name); + return pcmk_rc_op_unsatisfied; + } + + // Compare operation interval (unspecified interval matches all) + if ((interval_s != NULL) && (interval_ms != rule_input->op_interval_ms)) { + crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because " + "actual interval %s doesn't match %s", + id, pcmk__readable_interval(rule_input->op_interval_ms), + pcmk__readable_interval(interval_ms)); + return pcmk_rc_op_unsatisfied; + } + + crm_trace(PCMK_XE_OP_EXPRESSION " %s is satisfied (name %s, interval %s)", + id, name, pcmk__readable_interval(rule_input->op_interval_ms)); + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Evaluate a rule condition + * + * \param[in,out] condition XML containing a rule condition (a subrule, or an + * expression of any type) + * \param[in] rule_input Values used to evaluate rule criteria + * \param[out] next_change If not NULL, set to when evaluation will change + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if the condition + * passes, some other value if it does not) + */ +int +pcmk__evaluate_condition(xmlNode *condition, + const pcmk_rule_input_t *rule_input, + crm_time_t *next_change) +{ + + if ((condition == NULL) || (rule_input == NULL)) { + return EINVAL; + } + + switch (pcmk__condition_type(condition)) { + case pcmk__condition_rule: + return pcmk_evaluate_rule(condition, rule_input, next_change); + + case pcmk__condition_attribute: + case pcmk__condition_location: + return pcmk__evaluate_attr_expression(condition, rule_input); + + case pcmk__condition_datetime: + { + int rc = pcmk__evaluate_date_expression(condition, + rule_input->now, + next_change); + + return (rc == pcmk_rc_within_range)? pcmk_rc_ok : rc; + } + + case pcmk__condition_resource: + return pcmk__evaluate_rsc_expression(condition, rule_input); + + case pcmk__condition_operation: + return pcmk__evaluate_op_expression(condition, rule_input); + + default: // Not possible with schema validation enabled + pcmk__config_err("Treating rule condition %s as not passing " + "because %s is not a valid condition type", + pcmk__s(pcmk__xe_id(condition), "without ID"), + (const char *) condition->name); + return pcmk_rc_unpack_error; + } +} + +/*! + * \brief Evaluate a single rule, including all its conditions + * + * \param[in,out] rule XML containing a rule definition or its id-ref + * \param[in] rule_input Values used to evaluate rule criteria + * \param[out] next_change If not NULL, set to when evaluation will change + * + * \return Standard Pacemaker return code (\c pcmk_rc_ok if the rule is + * satisfied, some other value if it is not) + */ +int +pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input, + crm_time_t *next_change) +{ + bool empty = true; + int rc = pcmk_rc_ok; + const char *id = NULL; + const char *value = NULL; + enum pcmk__combine combine = pcmk__combine_unknown; + + if ((rule == NULL) || (rule_input == NULL)) { + return EINVAL; + } + + rule = expand_idref(rule, NULL); + if (rule == NULL) { + // Not possible with schema validation enabled; message already logged + return pcmk_rc_unpack_error; + } + + // Validate XML ID + id = pcmk__xe_id(rule); + if (pcmk__str_empty(id)) { + /* @COMPAT When we can break behavioral backward compatibility, + * fail the rule + */ + pcmk__config_warn(PCMK_XE_RULE " has no " PCMK_XA_ID); + id = "without ID"; // for logging + } + + value = crm_element_value(rule, PCMK_XA_BOOLEAN_OP); + combine = pcmk__parse_combine(value); + switch (combine) { + case pcmk__combine_and: + // For "and", rc defaults to success (reset on failure below) + break; + + case pcmk__combine_or: + // For "or", rc defaults to failure (reset on success below) + rc = pcmk_rc_op_unsatisfied; + break; + + default: + /* @COMPAT When we can break behavioral backward compatibility, + * return pcmk_rc_unpack_error + */ + pcmk__config_warn("Rule %s has invalid " PCMK_XA_BOOLEAN_OP + " value '%s', using default '" PCMK_VALUE_AND "'", + pcmk__xe_id(rule), value); + combine = pcmk__combine_and; + break; + } + + // Evaluate each condition + for (xmlNode *condition = pcmk__xe_first_child(rule, NULL, NULL, NULL); + condition != NULL; condition = pcmk__xe_next(condition)) { + + empty = false; + if (pcmk__evaluate_condition(condition, rule_input, + next_change) == pcmk_rc_ok) { + if (combine == pcmk__combine_or) { + rc = pcmk_rc_ok; // Any pass is final for "or" + break; + } + } else if (combine == pcmk__combine_and) { + rc = pcmk_rc_op_unsatisfied; // Any failure is final for "and" + break; + } + } + + if (empty) { // Not possible with schema validation enabled + /* @COMPAT Currently, we don't actually ignore "or" rules because + * rc is initialized to failure above in that case. When we can break + * backward compatibility, reset rc to pcmk_rc_ok here. + */ + pcmk__config_warn("Ignoring rule %s because it contains no conditions", + id); + } + + crm_trace("Rule %s is %ssatisfied", id, ((rc == pcmk_rc_ok)? "" : "not ")); + return rc; +} + +/*! + * \internal + * \brief Evaluate all rules contained within an element + * + * \param[in,out] xml XML element possibly containing rule subelements + * \param[in] rule_input Values used to evaluate rule criteria + * \param[out] next_change If not NULL, set to when evaluation will change + * + * \return Standard Pacemaker return code (pcmk_rc_ok if there are no contained + * rules or any contained rule passes, otherwise the result of the last + * rule) + * \deprecated On code paths leading to this function, the schema allows + * multiple top-level rules only in the deprecated lifetime element + * of location constraints. The code also allows multiple top-level + * rules when unpacking attribute sets, but this is deprecated and + * already prevented by schema validation. This function can be + * dropped when support for those is dropped. + */ +int +pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, + crm_time_t *next_change) +{ + // If there are no rules, pass by default + int rc = pcmk_rc_ok; + bool have_rule = false; + + for (xmlNode *rule = pcmk__xe_first_child(xml, PCMK_XE_RULE, NULL, NULL); + rule != NULL; rule = pcmk__xe_next_same(rule)) { + + if (have_rule) { + pcmk__warn_once(pcmk__wo_multiple_rules, + "Support for multiple top-level rules is " + "deprecated (replace with a single rule containing " + "the existing rules with " PCMK_XA_BOOLEAN_OP + "set to " PCMK_VALUE_OR " instead)"); + } else { + have_rule = true; + } + + rc = pcmk_evaluate_rule(rule, rule_input, next_change); + if (rc == pcmk_rc_ok) { + break; + } + } + return rc; +} diff --git a/lib/common/scheduler.c b/lib/common/scheduler.c index 20e6fdf..dad3de9 100644 --- a/lib/common/scheduler.c +++ b/lib/common/scheduler.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,5 +10,100 @@ #include <crm_internal.h> #include <stdint.h> // uint32_t +#include <errno.h> // EINVAL +#include <glib.h> // gboolean, FALSE +#include <libxml/tree.h> // xmlNode + +#include <crm/common/scheduler.h> uint32_t pcmk__warnings = 0; + +gboolean was_processing_error = FALSE; +gboolean was_processing_warning = FALSE; + +/*! + * \internal + * \brief Get the Designated Controller node from scheduler data + * + * \param[in] scheduler Scheduler data + * + * \return Designated Controller node from scheduler data, or NULL if none + */ +pcmk_node_t * +pcmk_get_dc(const pcmk_scheduler_t *scheduler) +{ + return (scheduler == NULL)? NULL : scheduler->dc_node; +} + +/*! + * \internal + * \brief Get the no quorum policy from scheduler data + * + * \param[in] scheduler Scheduler data + * + * \return No quorum policy from scheduler data + */ +enum pe_quorum_policy +pcmk_get_no_quorum_policy(const pcmk_scheduler_t *scheduler) +{ + if (scheduler == NULL) { + return pcmk_no_quorum_stop; // The default + } + return scheduler->no_quorum_policy; +} + +/*! + * \internal + * \brief Set CIB XML as scheduler input in scheduler data + * + * \param[out] scheduler Scheduler data + * \param[in] cib CIB XML to set as scheduler input + * + * \return Standard Pacemaker return code (EINVAL if \p scheduler is NULL, + * otherwise pcmk_rc_ok) + * \note This will not free any previously set scheduler CIB. + */ +int +pcmk_set_scheduler_cib(pcmk_scheduler_t *scheduler, xmlNode *cib) +{ + if (scheduler == NULL) { + return EINVAL; + } + scheduler->input = cib; + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Check whether cluster has quorum + * + * \param[in] scheduler Scheduler data + * + * \return true if cluster has quorum, otherwise false + */ +bool +pcmk_has_quorum(const pcmk_scheduler_t *scheduler) +{ + if (scheduler == NULL) { + return false; + } + return pcmk_is_set(scheduler->flags, pcmk_sched_quorate); +} + +/*! + * \brief Find a node by name in scheduler data + * + * \param[in] scheduler Scheduler data + * \param[in] node_name Name of node to find + * + * \return Node from scheduler data that matches \p node_name if any, + * otherwise NULL + */ +pcmk_node_t * +pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name) +{ + if ((scheduler == NULL) || (node_name == NULL)) { + return NULL; + } + return pcmk__find_node_in_list(scheduler->nodes, node_name); +} diff --git a/lib/common/schemas.c b/lib/common/schemas.c index b3c09eb..16f2ccb 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.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,19 +22,13 @@ #include <libxslt/security.h> #include <libxslt/xsltutils.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> /* PCMK__XML_LOG_BASE */ -typedef struct { - unsigned char v[2]; -} schema_version_t; +#include "crmcommon_private.h" #define SCHEMA_ZERO { .v = { 0, 0 } } -#define schema_scanf(s, prefix, version, suffix) \ - sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1])) - #define schema_strdup_printf(prefix, version, suffix) \ crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1]) @@ -44,31 +38,11 @@ typedef struct { xmlRelaxNGParserCtxtPtr parser; } relaxng_ctx_cache_t; -enum schema_validator_e { - schema_validator_none, - schema_validator_rng -}; - -struct schema_s { - char *name; - char *transform; - void *cache; - enum schema_validator_e validator; - int after_transform; - schema_version_t version; - char *transform_enter; - bool transform_onleave; -}; - -static struct schema_s *known_schemas = NULL; -static int xml_schema_max = 0; +static GList *known_schemas = NULL; +static bool initialized = false; static bool silent_logging = FALSE; -static void -xml_log(int priority, const char *fmt, ...) -G_GNUC_PRINTF(2, 3); - -static void +static void G_GNUC_PRINTF(2, 3) xml_log(int priority, const char *fmt, ...) { va_list ap; @@ -84,50 +58,114 @@ xml_log(int priority, const char *fmt, ...) static int xml_latest_schema_index(void) { - // @COMPAT: pacemaker-next is deprecated since 2.1.5 - return xml_schema_max - 3; // index from 0, ignore "pacemaker-next"/"none" + /* This function assumes that crm_schema_init() has been called beforehand, + * so we have at least three schemas (one real schema, the "pacemaker-next" + * schema, and the "none" schema). + * + * @COMPAT: pacemaker-next is deprecated since 2.1.5 and none since 2.1.8. + * Update this when we drop those. + */ + return g_list_length(known_schemas) - 3; } -static int -xml_minimum_schema_index(void) +/*! + * \internal + * \brief Return the schema entry of the highest-versioned schema + * + * \return Schema entry of highest-versioned schema (or NULL on error) + */ +static GList * +get_highest_schema(void) { - static int best = 0; - if (best == 0) { - int lpc = 0; - - best = xml_latest_schema_index(); - for (lpc = best; lpc > 0; lpc--) { - if (known_schemas[lpc].version.v[0] - < known_schemas[best].version.v[0]) { - return best; - } else { - best = lpc; - } - } - best = xml_latest_schema_index(); - } - return best; + /* The highest numerically versioned schema is the one before pacemaker-next + * + * @COMPAT pacemaker-next is deprecated since 2.1.5 + */ + GList *entry = pcmk__get_schema("pacemaker-next"); + + CRM_ASSERT((entry != NULL) && (entry->prev != NULL)); + return entry->prev; } +/*! + * \internal + * \brief Return the name of the highest-versioned schema + * + * \return Name of highest-versioned schema (or NULL on error) + */ const char * -xml_latest_schema(void) +pcmk__highest_schema_name(void) { - return get_schema_name(xml_latest_schema_index()); + GList *entry = get_highest_schema(); + + return ((pcmk__schema_t *)(entry->data))->name; } -static inline bool -version_from_filename(const char *filename, schema_version_t *version) +/*! + * \internal + * \brief Find first entry of highest major schema version series + * + * \return Schema entry of first schema with highest major version + */ +GList * +pcmk__find_x_0_schema(void) { - int rc = schema_scanf(filename, "pacemaker-", *version, ".rng"); +#if defined(PCMK__UNIT_TESTING) + /* If we're unit testing, this can't be static because it'll stick + * around from one test run to the next. It needs to be cleared out + * every time. + */ + GList *x_0_entry = NULL; +#else + static GList *x_0_entry = NULL; +#endif + + pcmk__schema_t *highest_schema = NULL; + + if (x_0_entry != NULL) { + return x_0_entry; + } + x_0_entry = get_highest_schema(); + highest_schema = x_0_entry->data; + + for (GList *iter = x_0_entry->prev; iter != NULL; iter = iter->prev) { + pcmk__schema_t *schema = iter->data; + + /* We've found a schema in an older major version series. Return + * the index of the first one in the same major version series as + * the highest schema. + */ + if (schema->version.v[0] < highest_schema->version.v[0]) { + x_0_entry = iter->next; + break; + } + + /* We're out of list to examine. This probably means there was only + * one major version series, so return the first schema entry. + */ + if (iter->prev == NULL) { + x_0_entry = known_schemas->data; + break; + } + } + return x_0_entry; +} - return (rc == 2); +static inline bool +version_from_filename(const char *filename, pcmk__schema_version_t *version) +{ + if (pcmk__ends_with(filename, ".rng")) { + return sscanf(filename, "pacemaker-%hhu.%hhu.rng", &(version->v[0]), &(version->v[1])) == 2; + } else { + return sscanf(filename, "pacemaker-%hhu.%hhu", &(version->v[0]), &(version->v[1])) == 2; + } } static int schema_filter(const struct dirent *a) { int rc = 0; - schema_version_t version = SCHEMA_ZERO; + pcmk__schema_version_t version = SCHEMA_ZERO; if (strstr(a->d_name, "pacemaker-") != a->d_name) { /* crm_trace("%s - wrong prefix", a->d_name); */ @@ -147,17 +185,8 @@ schema_filter(const struct dirent *a) } static int -schema_sort(const struct dirent **a, const struct dirent **b) +schema_cmp(pcmk__schema_version_t a_version, pcmk__schema_version_t b_version) { - schema_version_t a_version = SCHEMA_ZERO; - schema_version_t b_version = SCHEMA_ZERO; - - if (!version_from_filename(a[0]->d_name, &a_version) - || !version_from_filename(b[0]->d_name, &b_version)) { - // Shouldn't be possible, but makes static analysis happy - return 0; - } - for (int i = 0; i < 2; ++i) { if (a_version.v[i] < b_version.v[i]) { return -1; @@ -168,6 +197,21 @@ schema_sort(const struct dirent **a, const struct dirent **b) return 0; } +static int +schema_cmp_directory(const struct dirent **a, const struct dirent **b) +{ + pcmk__schema_version_t a_version = SCHEMA_ZERO; + pcmk__schema_version_t b_version = SCHEMA_ZERO; + + if (!version_from_filename(a[0]->d_name, &a_version) + || !version_from_filename(b[0]->d_name, &b_version)) { + // Shouldn't be possible, but makes static analysis happy + return 0; + } + + return schema_cmp(a_version, b_version); +} + /*! * \internal * \brief Add given schema + auxiliary data to internal bookkeeping. @@ -176,63 +220,35 @@ schema_sort(const struct dirent **a, const struct dirent **b) * through \c add_schema_by_version. */ static void -add_schema(enum schema_validator_e validator, const schema_version_t *version, +add_schema(enum pcmk__schema_validator validator, const pcmk__schema_version_t *version, const char *name, const char *transform, - const char *transform_enter, bool transform_onleave, - int after_transform) + const char *transform_enter, bool transform_onleave) { - int last = xml_schema_max; - bool have_version = FALSE; + pcmk__schema_t *schema = NULL; - xml_schema_max++; - known_schemas = pcmk__realloc(known_schemas, - xml_schema_max * sizeof(struct schema_s)); - CRM_ASSERT(known_schemas != NULL); - memset(known_schemas+last, 0, sizeof(struct schema_s)); - known_schemas[last].validator = validator; - known_schemas[last].after_transform = after_transform; + schema = pcmk__assert_alloc(1, sizeof(pcmk__schema_t)); - for (int i = 0; i < 2; ++i) { - known_schemas[last].version.v[i] = version->v[i]; - if (version->v[i]) { - have_version = TRUE; - } - } - if (have_version) { - known_schemas[last].name = schema_strdup_printf("pacemaker-", *version, ""); + schema->validator = validator; + schema->version.v[0] = version->v[0]; + schema->version.v[1] = version->v[1]; + schema->transform_onleave = transform_onleave; + // schema->schema_index is set after all schemas are loaded and sorted + + if (version->v[0] || version->v[1]) { + schema->name = schema_strdup_printf("pacemaker-", *version, ""); } else { - CRM_ASSERT(name); - schema_scanf(name, "%*[^-]-", known_schemas[last].version, ""); - known_schemas[last].name = strdup(name); + schema->name = pcmk__str_copy(name); } if (transform) { - known_schemas[last].transform = strdup(transform); + schema->transform = pcmk__str_copy(transform); } + if (transform_enter) { - known_schemas[last].transform_enter = strdup(transform_enter); - } - known_schemas[last].transform_onleave = transform_onleave; - if (after_transform == 0) { - after_transform = xml_schema_max; /* upgrade is a one-way */ + schema->transform_enter = pcmk__str_copy(transform_enter); } - known_schemas[last].after_transform = after_transform; - if (known_schemas[last].after_transform < 0) { - crm_debug("Added supported schema %d: %s", - last, known_schemas[last].name); - - } else if (known_schemas[last].transform) { - crm_debug("Added supported schema %d: %s (upgrades to %d with %s.xsl)", - last, known_schemas[last].name, - known_schemas[last].after_transform, - known_schemas[last].transform); - - } else { - crm_debug("Added supported schema %d: %s (upgrades to %d)", - last, known_schemas[last].name, - known_schemas[last].after_transform); - } + known_schemas = g_list_prepend(known_schemas, schema); } /*! @@ -264,8 +280,7 @@ add_schema(enum schema_validator_e validator, const schema_version_t *version, * . name convention: (see "upgrade-enter") */ static int -add_schema_by_version(const schema_version_t *version, int next, - bool transform_expected) +add_schema_by_version(const pcmk__schema_version_t *version, bool transform_expected) { bool transform_onleave = FALSE; int rc = pcmk_rc_ok; @@ -321,12 +336,11 @@ add_schema_by_version(const schema_version_t *version, int next, free(xslt); free(transform_upgrade); transform_upgrade = NULL; - next = -1; rc = ENOENT; } - add_schema(schema_validator_rng, version, NULL, - transform_upgrade, transform_enter, transform_onleave, next); + add_schema(pcmk__schema_validator_rng, version, NULL, + transform_upgrade, transform_enter, transform_onleave); free(transform_upgrade); free(transform_enter); @@ -366,6 +380,85 @@ wrap_libxslt(bool finalize) } } +void +pcmk__load_schemas_from_dir(const char *dir) +{ + int lpc, max; + struct dirent **namelist = NULL; + + max = scandir(dir, &namelist, schema_filter, schema_cmp_directory); + if (max < 0) { + crm_warn("Could not load schemas from %s: %s", dir, strerror(errno)); + return; + } + + for (lpc = 0; lpc < max; lpc++) { + bool transform_expected = false; + pcmk__schema_version_t version = SCHEMA_ZERO; + + if (!version_from_filename(namelist[lpc]->d_name, &version)) { + // Shouldn't be possible, but makes static analysis happy + crm_warn("Skipping schema '%s': could not parse version", + namelist[lpc]->d_name); + continue; + } + if ((lpc + 1) < max) { + pcmk__schema_version_t next_version = SCHEMA_ZERO; + + if (version_from_filename(namelist[lpc+1]->d_name, &next_version) + && (version.v[0] < next_version.v[0])) { + transform_expected = true; + } + } + + if (add_schema_by_version(&version, transform_expected) != pcmk_rc_ok) { + break; + } + } + + for (lpc = 0; lpc < max; lpc++) { + free(namelist[lpc]); + } + + free(namelist); +} + +static gint +schema_sort_GCompareFunc(gconstpointer a, gconstpointer b) +{ + const pcmk__schema_t *schema_a = a; + const pcmk__schema_t *schema_b = b; + + // @COMPAT pacemaker-next is deprecated since 2.1.5 and none since 2.1.8 + if (pcmk__str_eq(schema_a->name, "pacemaker-next", pcmk__str_none)) { + if (pcmk__str_eq(schema_b->name, PCMK_VALUE_NONE, pcmk__str_none)) { + return -1; + } else { + return 1; + } + } else if (pcmk__str_eq(schema_a->name, PCMK_VALUE_NONE, pcmk__str_none)) { + return 1; + } else if (pcmk__str_eq(schema_b->name, "pacemaker-next", pcmk__str_none)) { + return -1; + } else { + return schema_cmp(schema_a->version, schema_b->version); + } +} + +/*! + * \internal + * \brief Sort the list of known schemas such that all pacemaker-X.Y are in + * version order, then pacemaker-next, then none + * + * This function should be called whenever additional schemas are loaded using + * pcmk__load_schemas_from_dir(), after the initial sets in crm_schema_init(). + */ +void +pcmk__sort_schemas(void) +{ + known_schemas = g_list_sort(known_schemas, schema_sort_GCompareFunc); +} + /*! * \internal * \brief Load pacemaker schemas into cache @@ -376,79 +469,67 @@ wrap_libxslt(bool finalize) void crm_schema_init(void) { - int lpc, max; - char *base = pcmk__xml_artefact_root(pcmk__xml_artefact_ns_legacy_rng); - struct dirent **namelist = NULL; - const schema_version_t zero = SCHEMA_ZERO; + if (!initialized) { + const char *remote_schema_dir = pcmk__remote_schema_dir(); + char *base = pcmk__xml_artefact_root(pcmk__xml_artefact_ns_legacy_rng); + const pcmk__schema_version_t zero = SCHEMA_ZERO; + int schema_index = 0; - wrap_libxslt(false); + initialized = true; - max = scandir(base, &namelist, schema_filter, schema_sort); - if (max < 0) { - crm_notice("scandir(%s) failed: %s (%d)", base, strerror(errno), errno); - free(base); + wrap_libxslt(false); - } else { + pcmk__load_schemas_from_dir(base); + pcmk__load_schemas_from_dir(remote_schema_dir); free(base); - for (lpc = 0; lpc < max; lpc++) { - bool transform_expected = FALSE; - int next = 0; - schema_version_t version = SCHEMA_ZERO; - - if (!version_from_filename(namelist[lpc]->d_name, &version)) { - // Shouldn't be possible, but makes static analysis happy - crm_err("Skipping schema '%s': could not parse version", - namelist[lpc]->d_name); - continue; - } - if ((lpc + 1) < max) { - schema_version_t next_version = SCHEMA_ZERO; - if (version_from_filename(namelist[lpc+1]->d_name, &next_version) - && (version.v[0] < next_version.v[0])) { - transform_expected = TRUE; - } + // @COMPAT: Deprecated since 2.1.5 + add_schema(pcmk__schema_validator_rng, &zero, "pacemaker-next", NULL, + NULL, FALSE); + // @COMPAT Deprecated since 2.1.8 + add_schema(pcmk__schema_validator_none, &zero, PCMK_VALUE_NONE, NULL, + NULL, FALSE); + + /* add_schema() prepends items to the list, so in the simple case, this + * just reverses the list. However if there were any remote schemas, + * sorting is necessary. + */ + pcmk__sort_schemas(); + + // Now set the schema indexes and log the final result + for (GList *iter = known_schemas; iter != NULL; iter = iter->next) { + pcmk__schema_t *schema = iter->data; + + if (schema->transform == NULL) { + crm_debug("Loaded schema %d: %s", schema_index, schema->name); } else { - next = -1; - } - if (add_schema_by_version(&version, next, transform_expected) - == ENOENT) { - break; + crm_debug("Loaded schema %d: %s (upgrades with %s.xsl)", + schema_index, schema->name, schema->transform); } + schema->schema_index = schema_index++; } - - for (lpc = 0; lpc < max; lpc++) { - free(namelist[lpc]); - } - free(namelist); } - - // @COMPAT: Deprecated since 2.1.5 - add_schema(schema_validator_rng, &zero, "pacemaker-next", - NULL, NULL, FALSE, -1); - - add_schema(schema_validator_none, &zero, PCMK__VALUE_NONE, - NULL, NULL, FALSE, -1); } -static gboolean -validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context, const char *relaxng_file, +static bool +validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, + void *error_handler_context, const char *relaxng_file, relaxng_ctx_cache_t **cached_ctx) { int rc = 0; - gboolean valid = TRUE; + bool valid = true; relaxng_ctx_cache_t *ctx = NULL; - CRM_CHECK(doc != NULL, return FALSE); - CRM_CHECK(relaxng_file != NULL, return FALSE); + CRM_CHECK(doc != NULL, return false); + CRM_CHECK(relaxng_file != NULL, return false); if (cached_ctx && *cached_ctx) { ctx = *cached_ctx; } else { crm_debug("Creating RNG parser context"); - ctx = calloc(1, sizeof(relaxng_ctx_cache_t)); + ctx = pcmk__assert_alloc(1, sizeof(relaxng_ctx_cache_t)); ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file); CRM_CHECK(ctx->parser != NULL, goto cleanup); @@ -488,7 +569,7 @@ validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, rc = xmlRelaxNGValidateDoc(ctx->valid, doc); if (rc > 0) { - valid = FALSE; + valid = false; } else if (rc < 0) { crm_err("Internal libxml error during validation"); @@ -515,6 +596,45 @@ validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, return valid; } +static void +free_schema(gpointer data) +{ + pcmk__schema_t *schema = data; + relaxng_ctx_cache_t *ctx = NULL; + + switch (schema->validator) { + case pcmk__schema_validator_none: // not cached + break; + + case pcmk__schema_validator_rng: // cached + ctx = (relaxng_ctx_cache_t *) schema->cache; + if (ctx == NULL) { + break; + } + + if (ctx->parser != NULL) { + xmlRelaxNGFreeParserCtxt(ctx->parser); + } + + if (ctx->valid != NULL) { + xmlRelaxNGFreeValidCtxt(ctx->valid); + } + + if (ctx->rng != NULL) { + xmlRelaxNGFree(ctx->rng); + } + + free(ctx); + schema->cache = NULL; + break; + } + + free(schema->name); + free(schema->transform); + free(schema->transform_enter); + free(schema); +} + /*! * \internal * \brief Clean up global memory associated with XML schemas @@ -522,62 +642,86 @@ validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, void crm_schema_cleanup(void) { - int lpc; - relaxng_ctx_cache_t *ctx = NULL; + if (known_schemas != NULL) { + g_list_free_full(known_schemas, free_schema); + known_schemas = NULL; + } + initialized = false; - for (lpc = 0; lpc < xml_schema_max; lpc++) { + wrap_libxslt(true); +} - switch (known_schemas[lpc].validator) { - case schema_validator_none: // not cached - break; - case schema_validator_rng: // cached - ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache; - if (ctx == NULL) { - break; - } - if (ctx->parser != NULL) { - xmlRelaxNGFreeParserCtxt(ctx->parser); - } - if (ctx->valid != NULL) { - xmlRelaxNGFreeValidCtxt(ctx->valid); - } - if (ctx->rng != NULL) { - xmlRelaxNGFree(ctx->rng); - } - free(ctx); - known_schemas[lpc].cache = NULL; - break; +/*! + * \internal + * \brief Get schema list entry corresponding to a schema name + * + * \param[in] name Name of schema to get + * + * \return Schema list entry corresponding to \p name, or NULL if unknown + */ +GList * +pcmk__get_schema(const char *name) +{ + // @COMPAT Not specifying a schema name is deprecated since 2.1.8 + if (name == NULL) { + name = PCMK_VALUE_NONE; + } + for (GList *iter = known_schemas; iter != NULL; iter = iter->next) { + pcmk__schema_t *schema = iter->data; + + if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) { + return iter; } - free(known_schemas[lpc].name); - free(known_schemas[lpc].transform); - free(known_schemas[lpc].transform_enter); } - free(known_schemas); - known_schemas = NULL; + return NULL; +} - wrap_libxslt(true); +/*! + * \internal + * \brief Compare two schema version numbers given the schema names + * + * \param[in] schema1 Name of first schema to compare + * \param[in] schema2 Name of second schema to compare + * + * \return Standard comparison result (negative integer if \p schema1 has the + * lower version number, positive integer if \p schema1 has the higher + * version number, of 0 if the version numbers are equal) + */ +int +pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name) +{ + GList *entry1 = pcmk__get_schema(schema1_name); + GList *entry2 = pcmk__get_schema(schema2_name); + + if (entry1 == NULL) { + return (entry2 == NULL)? 0 : -1; + + } else if (entry2 == NULL) { + return 1; + + } else { + pcmk__schema_t *schema1 = entry1->data; + pcmk__schema_t *schema2 = entry2->data; + + return schema1->schema_index - schema2->schema_index; + } } -static gboolean -validate_with(xmlNode *xml, int method, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context) +static bool +validate_with(xmlNode *xml, pcmk__schema_t *schema, + xmlRelaxNGValidityErrorFunc error_handler, + void *error_handler_context) { - gboolean valid = FALSE; + bool valid = false; char *file = NULL; - struct schema_s *schema = NULL; relaxng_ctx_cache_t **cache = NULL; - if (method < 0) { - return FALSE; - } - - schema = &(known_schemas[method]); - if (schema->validator == schema_validator_none) { - return TRUE; + if (schema == NULL) { + return false; } - if (pcmk__str_eq(schema->name, "pacemaker-next", pcmk__str_none)) { - crm_warn("The pacemaker-next schema is deprecated and will be removed " - "in a future release."); + if (schema->validator == pcmk__schema_validator_none) { + return true; } file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng, @@ -586,13 +730,12 @@ validate_with(xmlNode *xml, int method, xmlRelaxNGValidityErrorFunc error_handle crm_trace("Validating with %s (type=%d)", pcmk__s(file, "missing schema"), schema->validator); switch (schema->validator) { - case schema_validator_rng: + case pcmk__schema_validator_rng: cache = (relaxng_ctx_cache_t **) &(schema->cache); valid = validate_with_relaxng(xml->doc, error_handler, error_handler_context, file, cache); break; default: - crm_err("Unknown validator type: %d", - known_schemas[method].validator); + crm_err("Unknown validator type: %d", schema->validator); break; } @@ -601,124 +744,74 @@ validate_with(xmlNode *xml, int method, xmlRelaxNGValidityErrorFunc error_handle } static bool -validate_with_silent(xmlNode *xml, int method) +validate_with_silent(xmlNode *xml, pcmk__schema_t *schema) { bool rc, sl_backup = silent_logging; silent_logging = TRUE; - rc = validate_with(xml, method, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); + rc = validate_with(xml, schema, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); silent_logging = sl_backup; return rc; } -static void -dump_file(const char *filename) +bool +pcmk__validate_xml(xmlNode *xml_blob, const char *validation, + xmlRelaxNGValidityErrorFunc error_handler, + void *error_handler_context) { + GList *entry = NULL; + pcmk__schema_t *schema = NULL; - FILE *fp = NULL; - int ch, line = 0; - - CRM_CHECK(filename != NULL, return); - - fp = fopen(filename, "r"); - if (fp == NULL) { - crm_perror(LOG_ERR, "Could not open %s for reading", filename); - return; - } - - fprintf(stderr, "%4d ", ++line); - do { - ch = getc(fp); - if (ch == EOF) { - putc('\n', stderr); - break; - } else if (ch == '\n') { - fprintf(stderr, "\n%4d ", ++line); - } else { - putc(ch, stderr); - } - } while (1); - - fclose(fp); -} - -gboolean -validate_xml_verbose(const xmlNode *xml_blob) -{ - int fd = 0; - xmlDoc *doc = NULL; - xmlNode *xml = NULL; - gboolean rc = FALSE; - char *filename = NULL; - - filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir()); - - umask(S_IWGRP | S_IWOTH | S_IROTH); - fd = mkstemp(filename); - write_xml_fd(xml_blob, filename, fd, FALSE); - - dump_file(filename); - - doc = xmlReadFile(filename, NULL, 0); - xml = xmlDocGetRootElement(doc); - rc = validate_xml(xml, NULL, FALSE); - free_xml(xml); - - unlink(filename); - free(filename); - - return rc; -} - -gboolean -validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs) -{ - return pcmk__validate_xml(xml_blob, validation, to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL, GUINT_TO_POINTER(LOG_ERR)); -} - -gboolean -pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context) -{ - int version = 0; - - CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return FALSE); + CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return false); if (validation == NULL) { - validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION); + validation = crm_element_value(xml_blob, PCMK_XA_VALIDATE_WITH); } + pcmk__warn_if_schema_deprecated(validation); + // @COMPAT Not specifying a schema name is deprecated since 2.1.8 if (validation == NULL) { - int lpc = 0; - bool valid = FALSE; - - for (lpc = 0; lpc < xml_schema_max; lpc++) { - if (validate_with(xml_blob, lpc, NULL, NULL)) { - valid = TRUE; - crm_xml_add(xml_blob, XML_ATTR_VALIDATION, - known_schemas[lpc].name); - crm_info("XML validated against %s", known_schemas[lpc].name); - if(known_schemas[lpc].after_transform == 0) { - break; - } + bool valid = false; + + for (entry = known_schemas; entry != NULL; entry = entry->next) { + schema = entry->data; + if (validate_with(xml_blob, schema, NULL, NULL)) { + valid = true; + crm_xml_add(xml_blob, PCMK_XA_VALIDATE_WITH, schema->name); + crm_info("XML validated against %s", schema->name); } } - return valid; } - version = get_schema_version(validation); - if (strcmp(validation, PCMK__VALUE_NONE) == 0) { - return TRUE; - } else if (version < xml_schema_max) { - return validate_with(xml_blob, version, error_handler, error_handler_context); + entry = pcmk__get_schema(validation); + if (entry == NULL) { + pcmk__config_err("Cannot validate CIB with " PCMK_XA_VALIDATE_WITH + " set to an unknown schema such as '%s' (manually" + " edit to use a known schema)", + validation); + return false; } - crm_err("Unknown validator: %s", validation); - return FALSE; + schema = entry->data; + return validate_with(xml_blob, schema, error_handler, + error_handler_context); } -static void -cib_upgrade_err(void *ctx, const char *fmt, ...) -G_GNUC_PRINTF(2, 3); +/*! + * \internal + * \brief Validate XML using its configured schema (and send errors to logs) + * + * \param[in] xml XML to validate + * + * \return true if XML validates, otherwise false + */ +bool +pcmk__configured_schema_validates(xmlNode *xml) +{ + return pcmk__validate_xml(xml, NULL, + (xmlRelaxNGValidityErrorFunc) xml_log, + GUINT_TO_POINTER(LOG_ERR)); +} /* With this arrangement, an attempt to identify the message severity as explicitly signalled directly from XSLT is performed in rather @@ -743,7 +836,7 @@ G_GNUC_PRINTF(2, 3); (suspicious, likely internal errors or some runaways) is LOG_WARNING. */ -static void +static void G_GNUC_PRINTF(2, 3) cib_upgrade_err(void *ctx, const char *fmt, ...) { va_list ap, aq; @@ -858,8 +951,20 @@ cib_upgrade_err(void *ctx, const char *fmt, ...) va_end(ap); } +/*! + * \internal + * \brief Apply a single XSL transformation to given XML + * + * \param[in] xml XML to transform + * \param[in] transform XSL name + * \param[in] to_logs If false, certain validation errors will be sent to + * stderr rather than logged + * + * \return Transformed XML on success, otherwise NULL + */ static xmlNode * -apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs) +apply_transformation(const xmlNode *xml, const char *transform, + gboolean to_logs) { char *xform = NULL; xmlNode *out = NULL; @@ -898,52 +1003,79 @@ apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs) /*! * \internal - * \brief Possibly full enter->upgrade->leave trip per internal bookkeeping. + * \brief Perform all transformations needed to upgrade XML to next schema + * + * A schema upgrade can require up to three XSL transformations: an "enter" + * transform, the main upgrade transform, and a "leave" transform. Perform + * all needed transforms to upgrade given XML to the next schema. + * + * \param[in] original_xml XML to transform + * \param[in] schema_index Index of schema that successfully validates + * \p original_xml + * \param[in] to_logs If false, certain validation errors will be sent to + * stderr rather than logged * - * \note Only emits warnings about enter/leave phases in case of issues. + * \return XML result of schema transforms if successful, otherwise NULL */ static xmlNode * -apply_upgrade(xmlNode *xml, const struct schema_s *schema, gboolean to_logs) +apply_upgrade(const xmlNode *original_xml, int schema_index, gboolean to_logs) { - bool transform_onleave = schema->transform_onleave; + pcmk__schema_t *schema = g_list_nth_data(known_schemas, schema_index); + pcmk__schema_t *upgraded_schema = g_list_nth_data(known_schemas, + schema_index + 1); + bool transform_onleave = false; char *transform_leave; - xmlNode *upgrade = NULL, - *final = NULL; + const xmlNode *xml = original_xml; + xmlNode *upgrade = NULL; + xmlNode *final = NULL; + xmlRelaxNGValidityErrorFunc error_handler = NULL; - if (schema->transform_enter) { - crm_debug("Upgrading %s-style configuration, pre-upgrade phase with %s.xsl", - schema->name, schema->transform_enter); + CRM_ASSERT((schema != NULL) && (upgraded_schema != NULL)); + + if (to_logs) { + error_handler = (xmlRelaxNGValidityErrorFunc) xml_log; + } + + transform_onleave = schema->transform_onleave; + if (schema->transform_enter != NULL) { + crm_debug("Upgrading schema from %s to %s: " + "applying pre-upgrade XSL transform %s", + schema->name, upgraded_schema->name, schema->transform_enter); upgrade = apply_transformation(xml, schema->transform_enter, to_logs); if (upgrade == NULL) { - crm_warn("Upgrade-enter transformation %s.xsl failed", + crm_warn("Pre-upgrade XSL transform %s failed, " + "will skip post-upgrade transform", schema->transform_enter); transform_onleave = FALSE; + } else { + xml = upgrade; } } - if (upgrade == NULL) { - upgrade = xml; - } - crm_debug("Upgrading %s-style configuration, main phase with %s.xsl", - schema->name, schema->transform); - final = apply_transformation(upgrade, schema->transform, to_logs); + + crm_debug("Upgrading schema from %s to %s: " + "applying upgrade XSL transform %s", + schema->name, upgraded_schema->name, schema->transform); + final = apply_transformation(xml, schema->transform, to_logs); if (upgrade != xml) { free_xml(upgrade); upgrade = NULL; } - if (final != NULL && transform_onleave) { + if ((final != NULL) && transform_onleave) { upgrade = final; /* following condition ensured in add_schema_by_version */ CRM_ASSERT(schema->transform_enter != NULL); transform_leave = strdup(schema->transform_enter); /* enter -> leave */ memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1); - crm_debug("Upgrading %s-style configuration, post-upgrade phase with %s.xsl", - schema->name, transform_leave); + crm_debug("Upgrading schema from %s to %s: " + "applying post-upgrade XSL transform %s", + schema->name, upgraded_schema->name, transform_leave); final = apply_transformation(upgrade, transform_leave, to_logs); if (final == NULL) { - crm_warn("Upgrade-leave transformation %s.xsl failed", transform_leave); + crm_warn("Ignoring failure of post-upgrade XSL transform %s", + transform_leave); final = upgrade; } else { free_xml(upgrade); @@ -951,290 +1083,661 @@ apply_upgrade(xmlNode *xml, const struct schema_s *schema, gboolean to_logs) free(transform_leave); } - return final; -} + if (final == NULL) { + return NULL; + } -const char * -get_schema_name(int version) -{ - if (version < 0 || version >= xml_schema_max) { - return "unknown"; + // Ensure result validates with its new schema + if (!validate_with(final, upgraded_schema, error_handler, + GUINT_TO_POINTER(LOG_ERR))) { + crm_err("Schema upgrade from %s to %s failed: " + "XSL transform %s produced an invalid configuration", + schema->name, upgraded_schema->name, schema->transform); + crm_log_xml_debug(final, "bad-transform-result"); + free_xml(final); + return NULL; } - return known_schemas[version].name; + + crm_info("Schema upgrade from %s to %s succeeded", + schema->name, upgraded_schema->name); + return final; } -int -get_schema_version(const char *name) +/*! + * \internal + * \brief Get the schema list entry corresponding to XML configuration + * + * \param[in] xml CIB XML to check + * + * \return List entry of schema configured in \p xml + */ +static GList * +get_configured_schema(const xmlNode *xml) { - int lpc = 0; + const char *schema_name = crm_element_value(xml, PCMK_XA_VALIDATE_WITH); - if (name == NULL) { - name = PCMK__VALUE_NONE; + pcmk__warn_if_schema_deprecated(schema_name); + if (schema_name == NULL) { + return NULL; } - for (; lpc < xml_schema_max; lpc++) { - if (pcmk__str_eq(name, known_schemas[lpc].name, pcmk__str_casei)) { - return lpc; - } - } - return -1; + return pcmk__get_schema(schema_name); } -/* set which validation to use */ +/*! + * \brief Update CIB XML to latest schema that validates it + * + * \param[in,out] xml XML to update (may be freed and replaced + * after being transformed) + * \param[in] max_schema_name If not NULL, do not update \p xml to any + * schema later than this one + * \param[in] transform If false, do not update \p xml to any schema + * that requires an XSL transform + * \param[in] to_logs If false, certain validation errors will be + * sent to stderr rather than logged + * + * \return Standard Pacemaker return code + */ int -update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, - gboolean to_logs) +pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, + bool to_logs) { - xmlNode *xml = NULL; - char *value = NULL; int max_stable_schemas = xml_latest_schema_index(); - int lpc = 0, match = -1, rc = pcmk_ok; - int next = -1; /* -1 denotes "inactive" value */ + int max_schema_index = 0; + int rc = pcmk_rc_ok; + GList *entry = NULL; + pcmk__schema_t *best_schema = NULL; + pcmk__schema_t *original_schema = NULL; xmlRelaxNGValidityErrorFunc error_handler = to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL; - CRM_CHECK(best != NULL, return -EINVAL); - *best = 0; - - CRM_CHECK((xml_blob != NULL) && (*xml_blob != NULL) - && ((*xml_blob)->doc != NULL), - return -EINVAL); - - xml = *xml_blob; - value = crm_element_value_copy(xml, XML_ATTR_VALIDATION); + CRM_CHECK((xml != NULL) && (*xml != NULL) && ((*xml)->doc != NULL), + return EINVAL); - if (value != NULL) { - match = get_schema_version(value); + if (max_schema_name != NULL) { + GList *max_entry = pcmk__get_schema(max_schema_name); - lpc = match; - if (lpc >= 0 && transform == FALSE) { - *best = lpc++; + if (max_entry != NULL) { + pcmk__schema_t *max_schema = max_entry->data; - } else if (lpc < 0) { - crm_debug("Unknown validation schema"); - lpc = 0; + max_schema_index = max_schema->schema_index; } } + if ((max_schema_index < 1) || (max_schema_index > max_stable_schemas)) { + max_schema_index = max_stable_schemas; + } - if (match >= max_stable_schemas) { - /* nothing to do */ - free(value); - *best = match; - return pcmk_ok; + entry = get_configured_schema(*xml); + if (entry == NULL) { + // @COMPAT Not specifying a schema name is deprecated since 2.1.8 + entry = known_schemas; + } else { + original_schema = entry->data; + if (original_schema->schema_index >= max_schema_index) { + return pcmk_rc_ok; + } } - while (lpc <= max_stable_schemas) { - crm_debug("Testing '%s' validation (%d of %d)", - known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>", - lpc, max_stable_schemas); + for (; entry != NULL; entry = entry->next) { + pcmk__schema_t *current_schema = entry->data; + xmlNode *upgrade = NULL; - if (validate_with(xml, lpc, error_handler, GUINT_TO_POINTER(LOG_ERR)) == FALSE) { - if (next != -1) { - crm_info("Configuration not valid for schema: %s", - known_schemas[lpc].name); - next = -1; - } else { - crm_trace("%s validation failed", - known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>"); - } - if (*best) { + if (current_schema->schema_index > max_schema_index) { + break; + } + + if (!validate_with(*xml, current_schema, error_handler, + GUINT_TO_POINTER(LOG_ERR))) { + crm_debug("Schema %s does not validate", current_schema->name); + if (best_schema != NULL) { /* we've satisfied the validation, no need to check further */ break; } - rc = -pcmk_err_schema_validation; - - } else { - if (next != -1) { - crm_debug("Configuration valid for schema: %s", - known_schemas[next].name); - next = -1; - } - rc = pcmk_ok; + rc = pcmk_rc_schema_validation; + continue; // Try again with the next higher schema } - if (rc == pcmk_ok) { - *best = lpc; + crm_debug("Schema %s validates", current_schema->name); + rc = pcmk_rc_ok; + best_schema = current_schema; + if (current_schema->schema_index == max_schema_index) { + break; // No further transformations possible } - if (rc == pcmk_ok && transform) { - xmlNode *upgrade = NULL; - next = known_schemas[lpc].after_transform; - - if (next <= lpc) { - /* There is no next version, or next would regress */ - crm_trace("Stopping at %s", known_schemas[lpc].name); - break; - - } else if (max > 0 && (lpc == max || next > max)) { - crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)", - known_schemas[lpc].name, lpc, next, max); - break; - - } else if (known_schemas[lpc].transform == NULL - /* possibly avoid transforming when readily valid - (in general more restricted when crossing the major - version boundary, as X.0 "transitional" version is - expected to be more strict than it's successors that - may re-allow constructs from previous major line) */ - || validate_with_silent(xml, next)) { - crm_debug("%s-style configuration is also valid for %s", - known_schemas[lpc].name, known_schemas[next].name); - - lpc = next; - - } else { - crm_debug("Upgrading %s-style configuration to %s with %s.xsl", - known_schemas[lpc].name, known_schemas[next].name, - known_schemas[lpc].transform); - - upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs); - if (upgrade == NULL) { - crm_err("Transformation %s.xsl failed", - known_schemas[lpc].transform); - rc = -pcmk_err_transform_failed; - - } else if (validate_with(upgrade, next, error_handler, GUINT_TO_POINTER(LOG_ERR))) { - crm_info("Transformation %s.xsl successful", - known_schemas[lpc].transform); - lpc = next; - *best = next; - free_xml(xml); - xml = upgrade; - rc = pcmk_ok; - - } else { - crm_err("Transformation %s.xsl did not produce a valid configuration", - known_schemas[lpc].transform); - crm_log_xml_info(upgrade, "transform:bad"); - free_xml(upgrade); - rc = -pcmk_err_schema_validation; - } - next = -1; - } + if (!transform || (current_schema->transform == NULL) + || validate_with_silent(*xml, entry->next->data)) { + /* The next schema either doesn't require a transform or validates + * successfully even without the transform. Skip the transform and + * try the next schema with the same XML. + */ + continue; } - if (transform == FALSE || rc != pcmk_ok) { - /* we need some progress! */ - lpc++; + upgrade = apply_upgrade(*xml, current_schema->schema_index, to_logs); + if (upgrade == NULL) { + /* The transform failed, so this schema can't be used. Later + * schemas are unlikely to validate, but try anyway until we + * run out of options. + */ + rc = pcmk_rc_transform_failed; + } else { + best_schema = current_schema; + free_xml(*xml); + *xml = upgrade; } } - if (*best > match && *best) { - crm_info("%s the configuration from %s to %s", - transform?"Transformed":"Upgraded", - value ? value : "<none>", known_schemas[*best].name); - crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name); + if (best_schema != NULL) { + if ((original_schema == NULL) + || (best_schema->schema_index > original_schema->schema_index)) { + crm_info("%s the configuration schema to %s", + (transform? "Transformed" : "Upgraded"), + best_schema->name); + crm_xml_add(*xml, PCMK_XA_VALIDATE_WITH, best_schema->name); + } } - - *xml_blob = xml; - free(value); return rc; } -gboolean -cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs) +/*! + * \brief Update XML from its configured schema to the latest major series + * + * \param[in,out] xml XML to update + * \param[in] to_logs If false, certain validation errors will be + * sent to stderr rather than logged + * + * \return Standard Pacemaker return code + */ +int +pcmk_update_configured_schema(xmlNode **xml, bool to_logs) { - gboolean rc = TRUE; - const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION); - char *const orig_value = strdup(value == NULL ? "(none)" : value); + int rc = pcmk_rc_ok; + char *original_schema_name = NULL; + + // @COMPAT Not specifying a schema name is deprecated since 2.1.8 + const char *effective_original_name = "the first"; + + int orig_version = -1; + pcmk__schema_t *x_0_schema = pcmk__find_x_0_schema()->data; + GList *entry = NULL; + + CRM_CHECK(xml != NULL, return EINVAL); - int version = get_schema_version(value); - int orig_version = version; - int min_version = xml_minimum_schema_index(); + original_schema_name = crm_element_value_copy(*xml, PCMK_XA_VALIDATE_WITH); + pcmk__warn_if_schema_deprecated(original_schema_name); + entry = pcmk__get_schema(original_schema_name); + if (entry != NULL) { + pcmk__schema_t *original_schema = entry->data; - if (version < min_version) { + effective_original_name = original_schema->name; + orig_version = original_schema->schema_index; + } + + if (orig_version < x_0_schema->schema_index) { // Current configuration schema is not acceptable, try to update xmlNode *converted = NULL; + const char *new_schema_name = NULL; + pcmk__schema_t *schema = NULL; + + entry = NULL; + converted = pcmk__xml_copy(NULL, *xml); + if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) { + new_schema_name = crm_element_value(converted, + PCMK_XA_VALIDATE_WITH); + entry = pcmk__get_schema(new_schema_name); + } + schema = (entry == NULL)? NULL : entry->data; - converted = copy_xml(*xml); - update_validation(&converted, &version, 0, TRUE, to_logs); - - value = crm_element_value(converted, XML_ATTR_VALIDATION); - if (version < min_version) { + if ((schema == NULL) + || (schema->schema_index < x_0_schema->schema_index)) { // Updated configuration schema is still not acceptable - if (version < orig_version || orig_version == -1) { + if ((orig_version == -1) || (schema == NULL) + || (schema->schema_index < orig_version)) { // We couldn't validate any schema at all if (to_logs) { pcmk__config_err("Cannot upgrade configuration (claiming " - "schema %s) to at least %s because it " + "%s schema) to at least %s because it " "does not validate with any schema from " - "%s to %s", - orig_value, - get_schema_name(min_version), - get_schema_name(orig_version), - xml_latest_schema()); + "%s to the latest", + pcmk__s(original_schema_name, "no"), + x_0_schema->name, effective_original_name); } else { fprintf(stderr, "Cannot upgrade configuration (claiming " - "schema %s) to at least %s because it " + "%s schema) to at least %s because it " "does not validate with any schema from " - "%s to %s\n", - orig_value, - get_schema_name(min_version), - get_schema_name(orig_version), - xml_latest_schema()); + "%s to the latest\n", + pcmk__s(original_schema_name, "no"), + x_0_schema->name, effective_original_name); } } else { // We updated configuration successfully, but still too low if (to_logs) { pcmk__config_err("Cannot upgrade configuration (claiming " - "schema %s) to at least %s because it " + "%s schema) to at least %s because it " "would not upgrade past %s", - orig_value, - get_schema_name(min_version), - pcmk__s(value, "unspecified version")); + pcmk__s(original_schema_name, "no"), + x_0_schema->name, + pcmk__s(new_schema_name, "unspecified version")); } else { fprintf(stderr, "Cannot upgrade configuration (claiming " - "schema %s) to at least %s because it " + "%s schema) to at least %s because it " "would not upgrade past %s\n", - orig_value, - get_schema_name(min_version), - pcmk__s(value, "unspecified version")); + pcmk__s(original_schema_name, "no"), + x_0_schema->name, + pcmk__s(new_schema_name, "unspecified version")); } } free_xml(converted); converted = NULL; - rc = FALSE; + rc = pcmk_rc_transform_failed; } else { // Updated configuration schema is acceptable free_xml(*xml); *xml = converted; - if (version < xml_latest_schema_index()) { + if (schema->schema_index < xml_latest_schema_index()) { if (to_logs) { - pcmk__config_warn("Configuration with schema %s was " + pcmk__config_warn("Configuration with %s schema was " "internally upgraded to acceptable (but " "not most recent) %s", - orig_value, get_schema_name(version)); - } - } else { - if (to_logs) { - crm_info("Configuration with schema %s was internally " - "upgraded to latest version %s", - orig_value, get_schema_name(version)); + pcmk__s(original_schema_name, "no"), + schema->name); } + } else if (to_logs) { + crm_info("Configuration with %s schema was internally " + "upgraded to latest version %s", + pcmk__s(original_schema_name, "no"), + schema->name); } } - } else if (version >= get_schema_version(PCMK__VALUE_NONE)) { - // Schema validation is disabled - if (to_logs) { - pcmk__config_warn("Schema validation of configuration is disabled " - "(enabling is encouraged and prevents common " - "misconfigurations)"); + } else { + // @COMPAT the none schema is deprecated since 2.1.8 + pcmk__schema_t *none_schema = NULL; + + entry = pcmk__get_schema(PCMK_VALUE_NONE); + CRM_ASSERT((entry != NULL) && (entry->data != NULL)); + + none_schema = entry->data; + if (!to_logs && (orig_version >= none_schema->schema_index)) { + fprintf(stderr, "Schema validation of configuration is " + "disabled (support for " PCMK_XA_VALIDATE_WITH + " set to \"" PCMK_VALUE_NONE "\" is deprecated" + " and will be removed in a future release)\n"); + } + } - } else { - fprintf(stderr, "Schema validation of configuration is disabled " - "(enabling is encouraged and prevents common " - "misconfigurations)\n"); + free(original_schema_name); + return rc; +} + +/*! + * \internal + * \brief Return a list of all schema files and any associated XSLT files + * later than the given one + * \brief Return a list of all schema versions later than the given one + * + * \param[in] schema The schema to compare against (for example, + * "pacemaker-3.1.rng" or "pacemaker-3.1") + * + * \note The caller is responsible for freeing both the returned list and + * the elements of the list + */ +GList * +pcmk__schema_files_later_than(const char *name) +{ + GList *lst = NULL; + pcmk__schema_version_t ver; + + if (!version_from_filename(name, &ver)) { + return lst; + } + + for (GList *iter = g_list_nth(known_schemas, xml_latest_schema_index()); + iter != NULL; iter = iter->prev) { + pcmk__schema_t *schema = iter->data; + char *s = NULL; + + if (schema_cmp(ver, schema->version) != -1) { + continue; + } + + s = crm_strdup_printf("%s.rng", schema->name); + lst = g_list_prepend(lst, s); + + if (schema->transform != NULL) { + char *xform = crm_strdup_printf("%s.xsl", schema->transform); + lst = g_list_prepend(lst, xform); + } + + if (schema->transform_enter != NULL) { + char *enter = crm_strdup_printf("%s.xsl", schema->transform_enter); + + lst = g_list_prepend(lst, enter); + + if (schema->transform_onleave) { + int last_dash = strrchr(enter, '-') - enter; + char *leave = crm_strdup_printf("%.*s-leave.xsl", last_dash, enter); + + lst = g_list_prepend(lst, leave); + } } } - if (best_version) { - *best_version = version; + return lst; +} + +static void +append_href(xmlNode *xml, void *user_data) +{ + GList **list = user_data; + char *href = crm_element_value_copy(xml, "href"); + + if (href == NULL) { + return; } + *list = g_list_prepend(*list, href); +} + +static void +external_refs_in_schema(GList **list, const char *contents) +{ + /* local-name()= is needed to ignore the xmlns= setting at the top of + * the XML file. Otherwise, the xpath query will always return nothing. + */ + const char *search = "//*[local-name()='externalRef'] | //*[local-name()='include']"; + xmlNode *xml = pcmk__xml_parse(contents); - free(orig_value); + crm_foreach_xpath_result(xml, search, append_href, list); + free_xml(xml); +} + +static int +read_file_contents(const char *file, char **contents) +{ + int rc = pcmk_rc_ok; + char *path = NULL; + + if (pcmk__ends_with(file, ".rng")) { + path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng, file); + } else { + path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt, file); + } + + rc = pcmk__file_contents(path, contents); + + free(path); return rc; } + +static void +add_schema_file_to_xml(xmlNode *parent, const char *file, GList **already_included) +{ + char *contents = NULL; + char *path = NULL; + xmlNode *file_node = NULL; + GList *includes = NULL; + int rc = pcmk_rc_ok; + + /* If we already included this file, don't do so again. */ + if (g_list_find_custom(*already_included, file, (GCompareFunc) strcmp) != NULL) { + return; + } + + /* Ensure whatever file we were given has a suffix we know about. If not, + * just assume it's an RNG file. + */ + if (!pcmk__ends_with(file, ".rng") && !pcmk__ends_with(file, ".xsl")) { + path = crm_strdup_printf("%s.rng", file); + } else { + path = pcmk__str_copy(file); + } + + rc = read_file_contents(path, &contents); + if (rc != pcmk_rc_ok || contents == NULL) { + crm_warn("Could not read schema file %s: %s", file, pcmk_rc_str(rc)); + free(path); + return; + } + + /* Create a new <file path="..."> node with the contents of the file + * as a CDATA block underneath it. + */ + file_node = pcmk__xe_create(parent, PCMK_XA_FILE); + crm_xml_add(file_node, PCMK_XA_PATH, path); + *already_included = g_list_prepend(*already_included, path); + + xmlAddChild(file_node, xmlNewCDataBlock(parent->doc, (pcmkXmlStr) contents, + strlen(contents))); + + /* Scan the file for any <externalRef> or <include> nodes and build up + * a list of the files they reference. + */ + external_refs_in_schema(&includes, contents); + + /* For each referenced file, recurse to add it (and potentially anything it + * references, ...) to the XML. + */ + for (GList *iter = includes; iter != NULL; iter = iter->next) { + add_schema_file_to_xml(parent, iter->data, already_included); + } + + free(contents); + g_list_free_full(includes, free); +} + +/*! + * \internal + * \brief Add an XML schema file and all the files it references as children + * of a given XML node + * + * \param[in,out] parent The parent XML node + * \param[in] name The schema version to compare against + * (for example, "pacemaker-3.1" or "pacemaker-3.1.rng") + * \param[in,out] already_included A list of names that have already been added + * to the parent node. + * + * \note The caller is responsible for freeing both the returned list and + * the elements of the list + */ +void +pcmk__build_schema_xml_node(xmlNode *parent, const char *name, GList **already_included) +{ + xmlNode *schema_node = pcmk__xe_create(parent, PCMK__XA_SCHEMA); + + crm_xml_add(schema_node, PCMK_XA_VERSION, name); + add_schema_file_to_xml(schema_node, name, already_included); + + if (schema_node->children == NULL) { + // Not needed if empty. May happen if name was invalid, for example. + free_xml(schema_node); + } +} + +/*! + * \internal + * \brief Return the directory containing any extra schema files that a + * Pacemaker Remote node fetched from the cluster + */ +const char * +pcmk__remote_schema_dir(void) +{ + const char *dir = pcmk__env_option(PCMK__ENV_REMOTE_SCHEMA_DIRECTORY); + + if (pcmk__str_empty(dir)) { + return PCMK__REMOTE_SCHEMA_DIR; + } + + return dir; +} + +/*! + * \internal + * \brief Warn if a given validation schema is deprecated + * + * \param[in] Schema name to check + */ +void +pcmk__warn_if_schema_deprecated(const char *schema) +{ + if ((schema == NULL) || + pcmk__strcase_any_of(schema, "pacemaker-next", PCMK_VALUE_NONE, NULL)) { + pcmk__config_warn("Support for " PCMK_XA_VALIDATE_WITH "='%s' is " + "deprecated and will be removed in a future release " + "without the possibility of upgrades (manually edit " + "to use a supported schema)", pcmk__s(schema, "")); + } +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/common/xml_compat.h> + +const char * +xml_latest_schema(void) +{ + return pcmk__highest_schema_name(); +} + +const char * +get_schema_name(int version) +{ + pcmk__schema_t *schema = g_list_nth_data(known_schemas, version); + + return (schema != NULL)? schema->name : "unknown"; +} + +int +get_schema_version(const char *name) +{ + int lpc = 0; + + if (name == NULL) { + name = PCMK_VALUE_NONE; + } + + for (GList *iter = known_schemas; iter != NULL; iter = iter->next) { + pcmk__schema_t *schema = iter->data; + + if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) { + return lpc; + } + + lpc++; + } + + return -1; +} + +int +update_validation(xmlNode **xml, int *best, int max, gboolean transform, + gboolean to_logs) +{ + int rc = pcmk__update_schema(xml, get_schema_name(max), transform, to_logs); + + if ((best != NULL) && (xml != NULL) && (rc == pcmk_rc_ok)) { + const char *schema_name = crm_element_value(*xml, + PCMK_XA_VALIDATE_WITH); + GList *schema_entry = pcmk__get_schema(schema_name); + + if (schema_entry != NULL) { + *best = ((pcmk__schema_t *)(schema_entry->data))->schema_index; + } + } + + return pcmk_rc2legacy(rc); +} + +gboolean +validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs) +{ + bool rc = pcmk__validate_xml(xml_blob, validation, + to_logs? (xmlRelaxNGValidityErrorFunc) xml_log : NULL, + GUINT_TO_POINTER(LOG_ERR)); + return rc? TRUE : FALSE; +} + +static void +dump_file(const char *filename) +{ + + FILE *fp = NULL; + int ch, line = 0; + + CRM_CHECK(filename != NULL, return); + + fp = fopen(filename, "r"); + if (fp == NULL) { + crm_perror(LOG_ERR, "Could not open %s for reading", filename); + return; + } + + fprintf(stderr, "%4d ", ++line); + do { + ch = getc(fp); + if (ch == EOF) { + putc('\n', stderr); + break; + } else if (ch == '\n') { + fprintf(stderr, "\n%4d ", ++line); + } else { + putc(ch, stderr); + } + } while (1); + + fclose(fp); +} + +gboolean +validate_xml_verbose(const xmlNode *xml_blob) +{ + int fd = 0; + xmlDoc *doc = NULL; + xmlNode *xml = NULL; + gboolean rc = FALSE; + char *filename = NULL; + + filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir()); + + umask(S_IWGRP | S_IWOTH | S_IROTH); + fd = mkstemp(filename); + pcmk__xml_write_fd(xml_blob, filename, fd, false, NULL); + + dump_file(filename); + + doc = xmlReadFile(filename, NULL, 0); + xml = xmlDocGetRootElement(doc); + rc = pcmk__validate_xml(xml, NULL, NULL, NULL); + free_xml(xml); + + unlink(filename); + free(filename); + + return rc? TRUE : FALSE; +} + +gboolean +cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs) +{ + int rc = pcmk_update_configured_schema(xml, to_logs); + + if (best_version != NULL) { + const char *name = crm_element_value(*xml, PCMK_XA_VALIDATE_WITH); + + if (name == NULL) { + *best_version = -1; + } else { + GList *entry = pcmk__get_schema(name); + pcmk__schema_t *schema = (entry == NULL)? NULL : entry->data; + + *best_version = (schema == NULL)? -1 : schema->schema_index; + } + } + return (rc == pcmk_rc_ok)? TRUE: FALSE; +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/common/scores.c b/lib/common/scores.c index 63c314e..39e224e 100644 --- a/lib/common/scores.c +++ b/lib/common/scores.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. * @@ -39,29 +39,29 @@ char2score(const char *score) return 0; } else if (pcmk_str_is_minus_infinity(score)) { - return -CRM_SCORE_INFINITY; + return -PCMK_SCORE_INFINITY; } else if (pcmk_str_is_infinity(score)) { - return CRM_SCORE_INFINITY; + return PCMK_SCORE_INFINITY; - } else if (pcmk__str_eq(score, PCMK__VALUE_RED, pcmk__str_casei)) { + } else if (pcmk__str_eq(score, PCMK_VALUE_RED, pcmk__str_casei)) { return pcmk__score_red; - } else if (pcmk__str_eq(score, PCMK__VALUE_YELLOW, pcmk__str_casei)) { + } else if (pcmk__str_eq(score, PCMK_VALUE_YELLOW, pcmk__str_casei)) { return pcmk__score_yellow; - } else if (pcmk__str_eq(score, PCMK__VALUE_GREEN, pcmk__str_casei)) { + } else if (pcmk__str_eq(score, PCMK_VALUE_GREEN, pcmk__str_casei)) { return pcmk__score_green; } else { long long score_ll; pcmk__scan_ll(score, &score_ll, 0LL); - if (score_ll > CRM_SCORE_INFINITY) { - return CRM_SCORE_INFINITY; + if (score_ll > PCMK_SCORE_INFINITY) { + return PCMK_SCORE_INFINITY; - } else if (score_ll < -CRM_SCORE_INFINITY) { - return -CRM_SCORE_INFINITY; + } else if (score_ll < -PCMK_SCORE_INFINITY) { + return -PCMK_SCORE_INFINITY; } else { return (int) score_ll; @@ -86,13 +86,13 @@ const char * pcmk_readable_score(int score) { // The longest possible result is "-INFINITY" - static char score_s[sizeof(CRM_MINUS_INFINITY_S)]; + static char score_s[sizeof(PCMK_VALUE_MINUS_INFINITY)]; - if (score >= CRM_SCORE_INFINITY) { - strcpy(score_s, CRM_INFINITY_S); + if (score >= PCMK_SCORE_INFINITY) { + strcpy(score_s, PCMK_VALUE_INFINITY); - } else if (score <= -CRM_SCORE_INFINITY) { - strcpy(score_s, CRM_MINUS_INFINITY_S); + } else if (score <= -PCMK_SCORE_INFINITY) { + strcpy(score_s, PCMK_VALUE_MINUS_INFINITY); } else { // Range is limited to +/-1000000, so no chance of overflow @@ -115,25 +115,25 @@ pcmk_readable_score(int score) int pcmk__add_scores(int score1, int score2) { - /* As long as CRM_SCORE_INFINITY is less than half of the maximum integer, + /* As long as PCMK_SCORE_INFINITY is less than half of the maximum integer, * we can ignore the possibility of integer overflow. */ int result = score1 + score2; // First handle the cases where one or both is infinite - if ((score1 <= -CRM_SCORE_INFINITY) || (score2 <= -CRM_SCORE_INFINITY)) { - return -CRM_SCORE_INFINITY; + if ((score1 <= -PCMK_SCORE_INFINITY) || (score2 <= -PCMK_SCORE_INFINITY)) { + return -PCMK_SCORE_INFINITY; } - if ((score1 >= CRM_SCORE_INFINITY) || (score2 >= CRM_SCORE_INFINITY)) { - return CRM_SCORE_INFINITY; + if ((score1 >= PCMK_SCORE_INFINITY) || (score2 >= PCMK_SCORE_INFINITY)) { + return PCMK_SCORE_INFINITY; } // Bound result to infinity. - if (result >= CRM_SCORE_INFINITY) { - return CRM_SCORE_INFINITY; + if (result >= PCMK_SCORE_INFINITY) { + return PCMK_SCORE_INFINITY; } - if (result <= -CRM_SCORE_INFINITY) { - return -CRM_SCORE_INFINITY; + if (result <= -PCMK_SCORE_INFINITY) { + return -PCMK_SCORE_INFINITY; } return result; @@ -142,21 +142,18 @@ pcmk__add_scores(int score1, int score2) // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START -#include <crm/common/util_compat.h> +#include <crm/common/scores_compat.h> char * score2char(int score) { - char *result = strdup(pcmk_readable_score(score)); - - CRM_ASSERT(result != NULL); - return result; + return pcmk__str_copy(pcmk_readable_score(score)); } char * score2char_stack(int score, char *buf, size_t len) { - CRM_CHECK((buf != NULL) && (len >= sizeof(CRM_MINUS_INFINITY_S)), + CRM_CHECK((buf != NULL) && (len >= sizeof(PCMK_VALUE_MINUS_INFINITY)), return NULL); strcpy(buf, pcmk_readable_score(score)); return buf; diff --git a/lib/common/strings.c b/lib/common/strings.c index d9d2fda..acf174d 100644 --- a/lib/common/strings.c +++ b/lib/common/strings.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. * @@ -342,74 +342,146 @@ pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, return pcmk_rc_ok; } -#ifndef NUMCHARS -# define NUMCHARS "0123456789." -#endif - -#ifndef WHITESPACE -# define WHITESPACE " \t\n\r\f" -#endif - /*! * \brief Parse a time+units string and return milliseconds equivalent * - * \param[in] input String with a number and optional unit (optionally - * with whitespace before and/or after the number). If - * missing, the unit defaults to seconds. + * \param[in] input String with a nonnegative number and optional unit + * (optionally with whitespace before and/or after the + * number). If missing, the unit defaults to seconds. * * \return Milliseconds corresponding to string expression, or - * PCMK__PARSE_INT_DEFAULT on error + * \c PCMK__PARSE_INT_DEFAULT on error */ long long crm_get_msec(const char *input) { - const char *num_start = NULL; - const char *units; + char *units = NULL; // Do not free; will point to part of input long long multiplier = 1000; long long divisor = 1; long long msec = PCMK__PARSE_INT_DEFAULT; - size_t num_len = 0; - char *end_text = NULL; if (input == NULL) { return PCMK__PARSE_INT_DEFAULT; } - num_start = input + strspn(input, WHITESPACE); - num_len = strspn(num_start, NUMCHARS); - if (num_len < 1) { + // Skip initial whitespace + while (isspace(*input)) { + input++; + } + + // Reject negative and unparsable inputs + scan_ll(input, &msec, -1, &units); + if (msec < 0) { return PCMK__PARSE_INT_DEFAULT; } - units = num_start + num_len; - units += strspn(units, WHITESPACE); - if (!strncasecmp(units, "ms", 2) || !strncasecmp(units, "msec", 4)) { + /* If the number is a decimal, scan_ll() reads only the integer part. Skip + * any remaining digits or decimal characters. + * + * @COMPAT Well-formed and malformed decimals are both accepted inputs. For + * example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" and + * parsed successfully. At a compatibility break, decide if this is still + * desired. + */ + while (isdigit(*units) || (*units == '.')) { + units++; + } + + // Skip any additional whitespace after the number + while (isspace(*units)) { + units++; + } + + /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the + * second strncasecmp() in each case is redundant. + */ + if ((*units == '\0') + || (strncasecmp(units, "s", 1) == 0) + || (strncasecmp(units, "sec", 3) == 0)) { + multiplier = 1000; + divisor = 1; + + } else if ((strncasecmp(units, "ms", 2) == 0) + || (strncasecmp(units, "msec", 4) == 0)) { multiplier = 1; divisor = 1; - } else if (!strncasecmp(units, "us", 2) || !strncasecmp(units, "usec", 4)) { + + } else if ((strncasecmp(units, "us", 2) == 0) + || (strncasecmp(units, "usec", 4) == 0)) { multiplier = 1; divisor = 1000; - } else if (!strncasecmp(units, "s", 1) || !strncasecmp(units, "sec", 3)) { - multiplier = 1000; - divisor = 1; - } else if (!strncasecmp(units, "m", 1) || !strncasecmp(units, "min", 3)) { + + } else if ((strncasecmp(units, "m", 1) == 0) + || (strncasecmp(units, "min", 3) == 0)) { multiplier = 60 * 1000; divisor = 1; - } else if (!strncasecmp(units, "h", 1) || !strncasecmp(units, "hr", 2)) { + + } else if ((strncasecmp(units, "h", 1) == 0) + || (strncasecmp(units, "hr", 2) == 0)) { multiplier = 60 * 60 * 1000; divisor = 1; - } else if ((*units != '\0') && (*units != '\n') && (*units != '\r')) { + + } else { + // Invalid units return PCMK__PARSE_INT_DEFAULT; } - scan_ll(num_start, &msec, PCMK__PARSE_INT_DEFAULT, &end_text); + // Apply units, capping at LLONG_MAX if (msec > (LLONG_MAX / multiplier)) { - // Arithmetics overflow while multiplier/divisor mutually exclusive return LLONG_MAX; } - msec *= multiplier; - msec /= divisor; - return msec; + return (msec * multiplier) / divisor; +} + +/*! + * \brief Parse milliseconds from a Pacemaker interval specification + * + * \param[in] input Pacemaker time interval specification (a bare number + * of seconds; a number with a unit, optionally with + * whitespace before and/or after the number; or an ISO + * 8601 duration) + * \param[out] result_ms Where to store milliseconds equivalent of \p input on + * success (limited to the range of an unsigned integer), + * or 0 if \p input is \c NULL or invalid + * + * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok if + * \p input is valid or \c NULL, and \c EINVAL otherwise) + */ +int +pcmk_parse_interval_spec(const char *input, guint *result_ms) +{ + long long msec = PCMK__PARSE_INT_DEFAULT; + int rc = pcmk_rc_ok; + + if (input == NULL) { + msec = 0; + goto done; + } + + if (input[0] == 'P') { + crm_time_t *period_s = crm_time_parse_duration(input); + + if (period_s != NULL) { + msec = 1000 * crm_time_get_seconds(period_s); + crm_time_free(period_s); + } + + } else { + msec = crm_get_msec(input); + } + + if (msec == PCMK__PARSE_INT_DEFAULT) { + crm_warn("Using 0 instead of invalid interval specification '%s'", + input); + msec = 0; + rc = EINVAL; + } + +done: + if (result_ms != NULL) { + *result_ms = (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec; + } + return rc; } gboolean @@ -425,17 +497,20 @@ crm_str_to_boolean(const char *s, int *ret) { if (s == NULL) { return -1; + } - } else if (strcasecmp(s, "true") == 0 - || strcasecmp(s, "on") == 0 - || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) { - *ret = TRUE; + if (pcmk__strcase_any_of(s, PCMK_VALUE_TRUE, "on", "yes", "y", "1", NULL)) { + if (ret != NULL) { + *ret = TRUE; + } return 1; + } - } else if (strcasecmp(s, "false") == 0 - || strcasecmp(s, "off") == 0 - || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) { - *ret = FALSE; + if (pcmk__strcase_any_of(s, PCMK_VALUE_FALSE, "off", "no", "n", "0", + NULL)) { + if (ret != NULL) { + *ret = FALSE; + } return 1; } return -1; @@ -612,6 +687,24 @@ pcmk__strkey_table(GDestroyNotify key_destroy_func, key_destroy_func, value_destroy_func); } +/*! + * \internal + * \brief Insert string copies into a hash table as key and value + * + * \param[in,out] table Hash table to add to + * \param[in] name String to add a copy of as key + * \param[in] value String to add a copy of as value + * + * \note This asserts on invalid arguments or memory allocation failure. + */ +void +pcmk__insert_dup(GHashTable *table, const char *name, const char *value) +{ + CRM_ASSERT((table != NULL) && (name != NULL)); + + g_hash_table_insert(table, pcmk__str_copy(name), pcmk__str_copy(value)); +} + /* used with hash tables where case does not matter */ static gboolean pcmk__strcase_equal(gconstpointer a, gconstpointer b) @@ -654,7 +747,8 @@ static void copy_str_table_entry(gpointer key, gpointer value, gpointer user_data) { if (key && value && user_data) { - g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value)); + pcmk__insert_dup((GHashTable *) user_data, + (const char *) key, (const char *) value); } } @@ -759,8 +853,7 @@ pcmk__compress(const char *data, unsigned int length, unsigned int max, clock_gettime(CLOCK_MONOTONIC, &before_t); #endif - compressed = calloc((size_t) max, sizeof(char)); - CRM_ASSERT(compressed); + compressed = pcmk__assert_alloc((size_t) max, sizeof(char)); *result_len = max; rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, @@ -967,44 +1060,6 @@ pcmk__str_any_of(const char *s, ...) /*! * \internal - * \brief Check whether a character is in any of a list of strings - * - * \param[in] ch Character (ASCII) to search for - * \param[in] ... Strings to search. Final argument must be - * \c NULL. - * - * \return \c true if any of \p ... contain \p ch, \c false otherwise - * \note \p ... must contain at least one argument (\c NULL). - */ -bool -pcmk__char_in_any_str(int ch, ...) -{ - bool rc = false; - va_list ap; - - /* - * Passing a char to va_start() can generate compiler warnings, - * so ch is declared as an int. - */ - va_start(ap, ch); - - while (1) { - const char *ele = va_arg(ap, const char *); - - if (ele == NULL) { - break; - } else if (strchr(ele, ch) != NULL) { - rc = true; - break; - } - } - - va_end(ap); - return rc; -} - -/*! - * \internal * \brief Sort strings, with numeric portions sorted numerically * * Sort two strings case-insensitively like strcasecmp(), but with any numeric @@ -1178,6 +1233,35 @@ pcmk__strcmp(const char *s1, const char *s2, uint32_t flags) /*! * \internal + * \brief Copy a string, asserting on failure + * + * \param[in] file File where \p function is located + * \param[in] function Calling function + * \param[in] line Line within \p file + * \param[in] str String to copy (can be \c NULL) + * + * \return Newly allocated copy of \p str, or \c NULL if \p str is \c NULL + * + * \note The caller is responsible for freeing the return value using \c free(). + */ +char * +pcmk__str_copy_as(const char *file, const char *function, uint32_t line, + const char *str) +{ + if (str != NULL) { + char *result = strdup(str); + + if (result == NULL) { + crm_abort(file, function, line, "Out of memory", FALSE, TRUE); + crm_exit(CRM_EX_OSERR); + } + return result; + } + return NULL; +} + +/*! + * \internal * \brief Update a dynamically allocated string with a new value * * Given a dynamically allocated string and a new value for it, if the string @@ -1194,12 +1278,7 @@ pcmk__str_update(char **str, const char *value) { if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) { free(*str); - if (value == NULL) { - *str = NULL; - } else { - *str = strdup(value); - CRM_ASSERT(*str != NULL); - } + *str = pcmk__str_copy(value); } } diff --git a/lib/common/tests/Makefile.am b/lib/common/tests/Makefile.am index c0407e5..bde6605 100644 --- a/lib/common/tests/Makefile.am +++ b/lib/common/tests/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -17,10 +17,16 @@ SUBDIRS = \ io \ iso8601 \ lists \ + nodes \ nvpair \ options \ output \ + probes \ + resources \ results \ + rules \ + scheduler \ + schemas \ scores \ strings \ utils \ diff --git a/lib/common/tests/acl/xml_acl_denied_test.c b/lib/common/tests/acl/xml_acl_denied_test.c index faf2a39..7c5457e 100644 --- a/lib/common/tests/acl/xml_acl_denied_test.c +++ b/lib/common/tests/acl/xml_acl_denied_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ static void is_xml_acl_denied_without_node(void **state) { - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); assert_false(xml_acl_denied(test_xml)); test_xml->doc->_private = NULL; @@ -35,10 +35,10 @@ is_xml_acl_denied_with_node(void **state) { xml_doc_private_t *docpriv; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); // allocate memory for _private, which is NULL by default - test_xml->doc->_private = calloc(1, sizeof(xml_doc_private_t)); + test_xml->doc->_private = pcmk__assert_alloc(1, sizeof(xml_doc_private_t)); assert_false(xml_acl_denied(test_xml)); @@ -56,6 +56,6 @@ is_xml_acl_denied_with_node(void **state) assert_true(xml_acl_denied(test_xml)); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(is_xml_acl_denied_without_node), cmocka_unit_test(is_xml_acl_denied_with_node)) diff --git a/lib/common/tests/acl/xml_acl_enabled_test.c b/lib/common/tests/acl/xml_acl_enabled_test.c index 28665f4..97e361e 100644 --- a/lib/common/tests/acl/xml_acl_enabled_test.c +++ b/lib/common/tests/acl/xml_acl_enabled_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ static void is_xml_acl_enabled_without_node(void **state) { - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); assert_false(xml_acl_enabled(test_xml)); test_xml->doc->_private = NULL; @@ -35,10 +35,10 @@ is_xml_acl_enabled_with_node(void **state) { xml_doc_private_t *docpriv; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); // allocate memory for _private, which is NULL by default - test_xml->doc->_private = calloc(1, sizeof(xml_doc_private_t)); + test_xml->doc->_private = pcmk__assert_alloc(1, sizeof(xml_doc_private_t)); assert_false(xml_acl_enabled(test_xml)); @@ -56,6 +56,6 @@ is_xml_acl_enabled_with_node(void **state) assert_true(xml_acl_enabled(test_xml)); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(is_xml_acl_enabled_without_node), cmocka_unit_test(is_xml_acl_enabled_with_node)) diff --git a/lib/common/tests/actions/Makefile.am b/lib/common/tests/actions/Makefile.am index 6890b84..0dfe521 100644 --- a/lib/common/tests/actions/Makefile.am +++ b/lib/common/tests/actions/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,12 +11,6 @@ include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = copy_in_properties_test \ - expand_plus_plus_test \ - fix_plus_plus_recursive_test \ - parse_op_key_test \ - pcmk_is_probe_test \ - pcmk_xe_is_probe_test \ - pcmk_xe_mask_probe_failure_test +check_PROGRAMS = parse_op_key_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/actions/copy_in_properties_test.c b/lib/common/tests/actions/copy_in_properties_test.c deleted file mode 100644 index 7882551..0000000 --- a/lib/common/tests/actions/copy_in_properties_test.c +++ /dev/null @@ -1,62 +0,0 @@ - /* - * Copyright 2022 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -#include <glib.h> - -static void -target_is_NULL(void **state) -{ - xmlNode *test_xml_1 = create_xml_node(NULL, "test_xml_1"); - xmlNode *test_xml_2 = NULL; - - pcmk__xe_set_props(test_xml_1, "test_prop", "test_value", NULL); - - copy_in_properties(test_xml_2, test_xml_1); - - assert_ptr_equal(test_xml_2, NULL); -} - -static void -src_is_NULL(void **state) -{ - xmlNode *test_xml_1 = NULL; - xmlNode *test_xml_2 = create_xml_node(NULL, "test_xml_2"); - - copy_in_properties(test_xml_2, test_xml_1); - - assert_ptr_equal(test_xml_2->properties, NULL); -} - -static void -copying_is_successful(void **state) -{ - const char *xml_1_value; - const char *xml_2_value; - - xmlNode *test_xml_1 = create_xml_node(NULL, "test_xml_1"); - xmlNode *test_xml_2 = create_xml_node(NULL, "test_xml_2"); - - pcmk__xe_set_props(test_xml_1, "test_prop", "test_value", NULL); - - copy_in_properties(test_xml_2, test_xml_1); - - xml_1_value = crm_element_value(test_xml_1, "test_prop"); - xml_2_value = crm_element_value(test_xml_2, "test_prop"); - - assert_string_equal(xml_1_value, xml_2_value); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(target_is_NULL), - cmocka_unit_test(src_is_NULL), - cmocka_unit_test(copying_is_successful)) diff --git a/lib/common/tests/actions/expand_plus_plus_test.c b/lib/common/tests/actions/expand_plus_plus_test.c deleted file mode 100644 index 41471f9..0000000 --- a/lib/common/tests/actions/expand_plus_plus_test.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2022 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -#include <glib.h> - -static void -value_is_name_plus_plus(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_name_plus_equals_integer(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "7"); -} - -// NULL input - -static void -target_is_NULL(void **state) -{ - - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(NULL, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -name_is_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, NULL, "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -value_is_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", NULL); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -// the value input doesn't start with the name input - -static void -value_is_wrong_name(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "Y++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "Y++"); -} - -static void -value_is_only_an_integer(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "2"); -} - -// non-integers - -static void -variable_is_initialized_to_be_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", NULL); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "X++"); -} - -static void -variable_is_initialized_to_be_non_numeric(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "hello"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "1"); -} - -static void -variable_is_initialized_to_be_non_numeric_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "hello"); - expand_plus_plus(test_xml, "X", "X+=2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "2"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.01"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.50"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing_3(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.99"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_non_numeric(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=hello"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -value_is_numeric_and_decimal_point_containing(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=2.01"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "7"); -} - -static void -value_is_numeric_and_decimal_point_containing_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=1.50"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_numeric_and_decimal_point_containing_3(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=1.99"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -// undefined input - -static void -name_is_undefined(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "Y", "5"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "X++"); -} - -// large input - -static void -assignment_result_is_too_large(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=100000000000"); - new_value = crm_element_value(test_xml, "X"); - printf("assignment result is too large %s\n", new_value); - assert_string_equal(new_value, "1000000"); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(value_is_name_plus_plus), - cmocka_unit_test(value_is_name_plus_equals_integer), - cmocka_unit_test(target_is_NULL), - cmocka_unit_test(name_is_NULL), - cmocka_unit_test(value_is_NULL), - cmocka_unit_test(value_is_wrong_name), - cmocka_unit_test(value_is_only_an_integer), - cmocka_unit_test(variable_is_initialized_to_be_NULL), - cmocka_unit_test(variable_is_initialized_to_be_non_numeric), - cmocka_unit_test(variable_is_initialized_to_be_non_numeric_2), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_2), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_3), - cmocka_unit_test(value_is_non_numeric), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing_2), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing_3), - cmocka_unit_test(name_is_undefined), - cmocka_unit_test(assignment_result_is_too_large)) diff --git a/lib/common/tests/actions/fix_plus_plus_recursive_test.c b/lib/common/tests/actions/fix_plus_plus_recursive_test.c deleted file mode 100644 index b3c7cc2..0000000 --- a/lib/common/tests/actions/fix_plus_plus_recursive_test.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2022 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -#include <glib.h> - -static void -element_nodes(void **state) -{ - const char *new_value_root; - const char *new_value_child; - const char *new_value_grandchild; - - xmlNode *test_xml_root = create_xml_node(NULL, "test_xml_root"); - xmlNode *test_xml_child = create_xml_node(test_xml_root, "test_xml_child"); - xmlNode *test_xml_grandchild = create_xml_node(test_xml_child, "test_xml_grandchild"); - xmlNode *test_xml_text = pcmk_create_xml_text_node(test_xml_root, "text_xml_text", "content"); - xmlNode *test_xml_comment = string2xml("<!-- a comment -->"); - - crm_xml_add(test_xml_root, "X", "5"); - crm_xml_add(test_xml_child, "X", "X++"); - crm_xml_add(test_xml_grandchild, "X", "X+=2"); - crm_xml_add(test_xml_text, "X", "X++"); - - fix_plus_plus_recursive(test_xml_root); - fix_plus_plus_recursive(test_xml_comment); - - new_value_root = crm_element_value(test_xml_root, "X"); - new_value_child = crm_element_value(test_xml_child, "X"); - new_value_grandchild = crm_element_value(test_xml_grandchild, "X"); - - assert_string_equal(new_value_root, "5"); - assert_string_equal(new_value_child, "1"); - assert_string_equal(new_value_grandchild, "2"); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(element_nodes)) diff --git a/lib/common/tests/actions/pcmk_xe_is_probe_test.c b/lib/common/tests/actions/pcmk_xe_is_probe_test.c deleted file mode 100644 index 62b21d9..0000000 --- a/lib/common/tests/actions/pcmk_xe_is_probe_test.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -static void -op_is_probe_test(void **state) -{ - xmlNode *node = NULL; - - assert_false(pcmk_xe_is_probe(NULL)); - - node = string2xml("<lrm_rsc_op/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation_key=\"blah\" interval=\"30s\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"30s\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"start\" interval=\"0\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\"/>"); - assert_true(pcmk_xe_is_probe(node)); - free_xml(node); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(op_is_probe_test)) diff --git a/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c b/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c deleted file mode 100644 index 9e38019..0000000 --- a/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2021 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -static void -op_is_not_probe_test(void **state) { - xmlNode *node = NULL; - - /* Not worth testing this thoroughly since it's just a duplicate of whether - * pcmk_op_is_probe works or not. - */ - - node = string2xml("<lrm_rsc_op operation=\"start\" interval=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -static void -op_does_not_have_right_values_test(void **state) { - xmlNode *node = NULL; - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -static void -check_values_test(void **state) { - xmlNode *node = NULL; - - /* PCMK_EXEC_NOT_SUPPORTED */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"3\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"3\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_DONE */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"0\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"0\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_NOT_INSTALLED */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"7\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"7\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"4\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"4\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR_HARD */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"5\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"5\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR_FATAL */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"6\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"6\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(op_is_not_probe_test), - cmocka_unit_test(op_does_not_have_right_values_test), - cmocka_unit_test(check_values_test)) diff --git a/lib/common/tests/health/pcmk__parse_health_strategy_test.c b/lib/common/tests/health/pcmk__parse_health_strategy_test.c index 28cc702..197ad1f 100644 --- a/lib/common/tests/health/pcmk__parse_health_strategy_test.c +++ b/lib/common/tests/health/pcmk__parse_health_strategy_test.c @@ -16,7 +16,7 @@ valid(void **state) { assert_int_equal(pcmk__parse_health_strategy(NULL), pcmk__health_strategy_none); - assert_int_equal(pcmk__parse_health_strategy("none"), + assert_int_equal(pcmk__parse_health_strategy(PCMK_VALUE_NONE), pcmk__health_strategy_none); assert_int_equal(pcmk__parse_health_strategy("NONE"), diff --git a/lib/common/tests/health/pcmk__validate_health_strategy_test.c b/lib/common/tests/health/pcmk__validate_health_strategy_test.c index c7c60aa..e706185 100644 --- a/lib/common/tests/health/pcmk__validate_health_strategy_test.c +++ b/lib/common/tests/health/pcmk__validate_health_strategy_test.c @@ -15,7 +15,7 @@ static void valid_strategy(void **state) { - assert_true(pcmk__validate_health_strategy("none")); + assert_true(pcmk__validate_health_strategy(PCMK_VALUE_NONE)); assert_true(pcmk__validate_health_strategy("None")); assert_true(pcmk__validate_health_strategy("NONE")); assert_true(pcmk__validate_health_strategy("NoNe")); diff --git a/lib/common/tests/io/pcmk__full_path_test.c b/lib/common/tests/io/pcmk__full_path_test.c index dbbd71b..2f514aa 100644 --- a/lib/common/tests/io/pcmk__full_path_test.c +++ b/lib/common/tests/io/pcmk__full_path_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,8 +18,13 @@ function_asserts(void **state) { pcmk__assert_asserts(pcmk__full_path(NULL, "/dir")); pcmk__assert_asserts(pcmk__full_path("file", NULL)); +} - pcmk__assert_asserts( +static void +function_exits(void **state) +{ + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_strdup = true; // strdup() will return NULL expect_string(__wrap_strdup, s, "/full/path"); @@ -49,4 +54,5 @@ full_path(void **state) PCMK__UNIT_TEST(NULL, NULL, cmocka_unit_test(function_asserts), + cmocka_unit_test(function_exits), cmocka_unit_test(full_path)) diff --git a/lib/common/tests/iso8601/Makefile.am b/lib/common/tests/iso8601/Makefile.am index 5187aec..581115a 100644 --- a/lib/common/tests/iso8601/Makefile.am +++ b/lib/common/tests/iso8601/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2022 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,6 +11,8 @@ include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pcmk__readable_interval_test +check_PROGRAMS = pcmk__add_time_from_xml_test \ + pcmk__readable_interval_test \ + pcmk__set_time_if_earlier_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c b/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c new file mode 100644 index 0000000..60a71c0 --- /dev/null +++ b/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c @@ -0,0 +1,243 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <libxml/tree.h> // xmlNode + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/iso8601_internal.h> +#include <crm/common/xml.h> +#include "../../crmcommon_private.h" + +#define ALL_VALID "<duration id=\"duration1\" years=\"1\" months=\"2\" " \ + "weeks=\"3\" days=\"-1\" hours=\"1\" minutes=\"1\" " \ + "seconds=\"1\" />" + +#define YEARS_INVALID "<duration id=\"duration1\" years=\"not-a-number\" />" + +#define YEARS_TOO_BIG "<duration id=\"duration1\" years=\"2222222222\" />" + +#define YEARS_TOO_SMALL "<duration id=\"duration1\" years=\"-2222222222\" />" + +static void +null_time_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(NULL, pcmk__time_years, xml), + EINVAL); + free_xml(xml); +} + +static void +null_xml_ok(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, NULL), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); +} + +static void +invalid_component(void **state) +{ + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(NULL, pcmk__time_unknown, xml), + EINVAL); + free_xml(xml); +} + +static void +missing_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = pcmk__xml_parse(YEARS_INVALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_months, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +invalid_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = pcmk__xml_parse(YEARS_INVALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), + pcmk_rc_unpack_error); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +out_of_range_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = NULL; + + xml = pcmk__xml_parse(YEARS_TOO_BIG); + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), ERANGE); + assert_int_equal(crm_time_compare(t, reference), 0); + free_xml(xml); + + xml = pcmk__xml_parse(YEARS_TOO_SMALL); + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), ERANGE); + assert_int_equal(crm_time_compare(t, reference), 0); + free_xml(xml); + + crm_time_free(t); + crm_time_free(reference); +} + +static void +add_years(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2025-01-01 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_months(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-03-01 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_months, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_weeks(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-22 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_weeks, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_days(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2023-12-31 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_days, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_hours(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 16:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_hours, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_minutes(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 15:01:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_minutes, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_seconds(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 15:00:01"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_seconds, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_time_invalid), + cmocka_unit_test(null_xml_ok), + cmocka_unit_test(invalid_component), + cmocka_unit_test(missing_attr), + cmocka_unit_test(invalid_attr), + cmocka_unit_test(out_of_range_attr), + cmocka_unit_test(add_years), + cmocka_unit_test(add_months), + cmocka_unit_test(add_weeks), + cmocka_unit_test(add_days), + cmocka_unit_test(add_hours), + cmocka_unit_test(add_minutes), + cmocka_unit_test(add_seconds)); diff --git a/lib/common/tests/iso8601/pcmk__readable_interval_test.c b/lib/common/tests/iso8601/pcmk__readable_interval_test.c index 43b5541..d354975 100644 --- a/lib/common/tests/iso8601/pcmk__readable_interval_test.c +++ b/lib/common/tests/iso8601/pcmk__readable_interval_test.c @@ -17,9 +17,11 @@ static void readable_interval(void **state) { assert_string_equal(pcmk__readable_interval(0), "0s"); + assert_string_equal(pcmk__readable_interval(503), "503ms"); + assert_string_equal(pcmk__readable_interval(3333), "3.333s"); assert_string_equal(pcmk__readable_interval(30000), "30s"); + assert_string_equal(pcmk__readable_interval(61000), "1m1s"); assert_string_equal(pcmk__readable_interval(150000), "2m30s"); - assert_string_equal(pcmk__readable_interval(3333), "3.333s"); assert_string_equal(pcmk__readable_interval(UINT_MAX), "49d17h2m47.295s"); } diff --git a/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c b/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c new file mode 100644 index 0000000..c4bf014 --- /dev/null +++ b/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c @@ -0,0 +1,80 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include "../../crmcommon_private.h" + +static void +null_ok(void **state) +{ + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + crm_time_t *target_copy = pcmk_copy_time(target); + + // Should do nothing (just checking it doesn't assert or crash) + pcmk__set_time_if_earlier(NULL, NULL); + pcmk__set_time_if_earlier(NULL, target); + + // Shouldn't assert, crash, or change target + pcmk__set_time_if_earlier(target, NULL); + assert_int_equal(crm_time_compare(target, target_copy), 0); + + crm_time_free(target); + crm_time_free(target_copy); +} + +static void +target_undefined(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:29:59 +01:00"); + crm_time_t *target = crm_time_new_undefined(); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, source), 0); + + crm_time_free(source); + crm_time_free(target); +} + +static void +source_earlier(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:29:59 +01:00"); + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, source), 0); + + crm_time_free(source); + crm_time_free(target); +} + +static void +source_later(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:31:00 +01:00"); + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + crm_time_t *target_copy = pcmk_copy_time(target); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, target_copy), 0); + + crm_time_free(source); + crm_time_free(target); + crm_time_free(target_copy); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_ok), + cmocka_unit_test(target_undefined), + cmocka_unit_test(source_earlier), + cmocka_unit_test(source_later)) diff --git a/lib/common/tests/nodes/Makefile.am b/lib/common/tests/nodes/Makefile.am new file mode 100644 index 0000000..f52c615 --- /dev/null +++ b/lib/common/tests/nodes/Makefile.am @@ -0,0 +1,23 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk__find_node_in_list_test \ + pcmk_foreach_active_resource_test \ + pcmk_node_is_clean_test \ + pcmk_node_is_in_maintenance_test \ + pcmk_node_is_online_test \ + pcmk_node_is_pending_test \ + pcmk_node_is_shutting_down_test \ + pcmk__xe_add_node_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/nodes/pcmk__find_node_in_list_test.c b/lib/common/tests/nodes/pcmk__find_node_in_list_test.c new file mode 100644 index 0000000..7726bf5 --- /dev/null +++ b/lib/common/tests/nodes/pcmk__find_node_in_list_test.c @@ -0,0 +1,53 @@ +/* + * Copyright 2022-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> +#include <crm/pengine/internal.h> + +static void +empty_list(void **state) +{ + assert_null(pcmk__find_node_in_list(NULL, NULL)); + assert_null(pcmk__find_node_in_list(NULL, "cluster1")); +} + +static void +non_null_list(void **state) +{ + GList *nodes = NULL; + + pcmk_node_t *a = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *b = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + + a->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); + a->details->uname = "cluster1"; + b->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); + b->details->uname = "cluster2"; + + nodes = g_list_append(nodes, a); + nodes = g_list_append(nodes, b); + + assert_ptr_equal(a, pcmk__find_node_in_list(nodes, "cluster1")); + assert_null(pcmk__find_node_in_list(nodes, "cluster10")); + assert_null(pcmk__find_node_in_list(nodes, "nodecluster1")); + assert_ptr_equal(b, pcmk__find_node_in_list(nodes, "CLUSTER2")); + assert_null(pcmk__find_node_in_list(nodes, "xyz")); + + free(a->details); + free(a); + free(b->details); + free(b); + g_list_free(nodes); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(empty_list), + cmocka_unit_test(non_null_list)) diff --git a/lib/common/tests/nodes/pcmk__xe_add_node_test.c b/lib/common/tests/nodes/pcmk__xe_add_node_test.c new file mode 100644 index 0000000..dd77527 --- /dev/null +++ b/lib/common/tests/nodes/pcmk__xe_add_node_test.c @@ -0,0 +1,71 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +static void +bad_input(void **state) { + xmlNode *node = NULL; + + pcmk__assert_asserts(pcmk__xe_add_node(NULL, NULL, 0)); + + node = pcmk__xe_create(NULL, "test"); + + pcmk__xe_add_node(node, NULL, 0); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST)); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST_ID)); + + pcmk__xe_add_node(node, NULL, -100); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST)); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST_ID)); + + free_xml(node); +} + +static void +expected_input(void **state) { + xmlNode *node = pcmk__xe_create(NULL, "test"); + int i; + + pcmk__xe_add_node(node, "somenode", 47); + assert_string_equal("somenode", + crm_element_value(node, PCMK__XA_ATTR_HOST)); + assert_int_equal(pcmk_rc_ok, + crm_element_value_int(node, PCMK__XA_ATTR_HOST_ID, &i)); + assert_int_equal(i, 47); + + free_xml(node); +} + +static void +repeated_use(void **state) { + xmlNode *node = pcmk__xe_create(NULL, "test"); + int i; + + /* Later calls override settings from earlier calls. */ + pcmk__xe_add_node(node, "nodeA", 1); + pcmk__xe_add_node(node, "nodeB", 2); + pcmk__xe_add_node(node, "nodeC", 3); + + assert_string_equal("nodeC", crm_element_value(node, PCMK__XA_ATTR_HOST)); + assert_int_equal(pcmk_rc_ok, + crm_element_value_int(node, PCMK__XA_ATTR_HOST_ID, &i)); + assert_int_equal(i, 3); + + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(bad_input), + cmocka_unit_test(expected_input), + cmocka_unit_test(repeated_use)) diff --git a/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c b/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c new file mode 100644 index 0000000..2402789 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c @@ -0,0 +1,149 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // GList, TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static int counter = 1; +static int return_false = -1; + +static char rsc1_id[] = "rsc1"; +static char rsc2_id[] = "rsc2"; +static char rsc3_id[] = "rsc3"; + +static pcmk_resource_t rsc1 = { + .id = rsc1_id, +}; +static pcmk_resource_t rsc2 = { + .id = rsc2_id, +}; +static pcmk_resource_t rsc3 = { + .id = rsc3_id, +}; + +static bool +fn(pcmk_resource_t *rsc, void *user_data) +{ + char *expected_id = crm_strdup_printf("rsc%d", counter); + + assert_string_equal(rsc->id, expected_id); + free(expected_id); + + return counter++ != return_false; +} + +static void +null_args(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + counter = 1; + + // These just test that it doesn't crash + pcmk_foreach_active_resource(NULL, NULL, NULL); + pcmk_foreach_active_resource(&node, NULL, NULL); + + pcmk_foreach_active_resource(NULL, fn, NULL); + assert_int_equal(counter, 1); +} + +static void +list_of_0(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 1); +} + +static void +list_of_1(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 2); + + g_list_free(shared.running_rsc); +} + +static void +list_of_3(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc2); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc3); + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 4); + + g_list_free(shared.running_rsc); +} + +static void +list_of_3_return_false(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc2); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc3); + + counter = 1; + return_false = 2; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 3); + + g_list_free(shared.running_rsc); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_args), + cmocka_unit_test(list_of_0), + cmocka_unit_test(list_of_1), + cmocka_unit_test(list_of_3), + cmocka_unit_test(list_of_3_return_false)) diff --git a/lib/common/tests/nodes/pcmk_node_is_clean_test.c b/lib/common/tests/nodes/pcmk_node_is_clean_test.c new file mode 100644 index 0000000..0534633 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_clean_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_unclean(void **state) +{ + assert_false(pcmk_node_is_clean(NULL)); +} + +static void +node_is_clean(void **state) +{ + struct pe_node_shared_s shared = { + .unclean = FALSE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_clean(&node)); +} + +static void +node_is_unclean(void **state) +{ + struct pe_node_shared_s shared = { + .unclean = TRUE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_clean(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_unclean), + cmocka_unit_test(node_is_clean), + cmocka_unit_test(node_is_unclean)) diff --git a/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c b/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c new file mode 100644 index 0000000..45a3b6f --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_in_maintenance(void **state) +{ + assert_false(pcmk_node_is_in_maintenance(NULL)); +} + +static void +node_is_in_maintenance(void **state) +{ + struct pe_node_shared_s shared = { + .maintenance = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_in_maintenance(&node)); +} + +static void +node_is_not_in_maintenance(void **state) +{ + struct pe_node_shared_s shared = { + .maintenance = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_in_maintenance(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_in_maintenance), + cmocka_unit_test(node_is_in_maintenance), + cmocka_unit_test(node_is_not_in_maintenance)) diff --git a/lib/common/tests/nodes/pcmk_node_is_online_test.c b/lib/common/tests/nodes/pcmk_node_is_online_test.c new file mode 100644 index 0000000..d22e3b4 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_online_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_offline(void **state) +{ + assert_false(pcmk_node_is_online(NULL)); +} + +static void +node_is_online(void **state) +{ + struct pe_node_shared_s shared = { + .online = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_online(&node)); +} + +static void +node_is_offline(void **state) +{ + struct pe_node_shared_s shared = { + .online = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_online(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_offline), + cmocka_unit_test(node_is_online), + cmocka_unit_test(node_is_offline)) diff --git a/lib/common/tests/nodes/pcmk_node_is_pending_test.c b/lib/common/tests/nodes/pcmk_node_is_pending_test.c new file mode 100644 index 0000000..9f2abca --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_pending_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_pending(void **state) +{ + assert_false(pcmk_node_is_pending(NULL)); +} + +static void +node_is_pending(void **state) +{ + struct pe_node_shared_s shared = { + .pending = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_pending(&node)); +} + +static void +node_is_not_pending(void **state) +{ + struct pe_node_shared_s shared = { + .pending = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_pending(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_pending), + cmocka_unit_test(node_is_pending), + cmocka_unit_test(node_is_not_pending)) diff --git a/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c b/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c new file mode 100644 index 0000000..b6054b0 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_shutting_down(void **state) +{ + assert_false(pcmk_node_is_shutting_down(NULL)); +} + +static void +node_is_shutting_down(void **state) +{ + struct pe_node_shared_s shared = { + .shutdown = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_shutting_down(&node)); +} + +static void +node_is_not_shutting_down(void **state) +{ + struct pe_node_shared_s shared = { + .shutdown = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_shutting_down(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_shutting_down), + cmocka_unit_test(node_is_shutting_down), + cmocka_unit_test(node_is_not_shutting_down)) diff --git a/lib/common/tests/nvpair/Makefile.am b/lib/common/tests/nvpair/Makefile.am index 7f406bd..9f762d4 100644 --- a/lib/common/tests/nvpair/Makefile.am +++ b/lib/common/tests/nvpair/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2021-2023 the Pacemaker project contributors +# Copyright 2021-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,7 +11,10 @@ include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pcmk__xe_attr_is_true_test \ +check_PROGRAMS = crm_meta_name_test \ + crm_meta_value_test \ + pcmk__xe_attr_is_true_test \ + pcmk__xe_get_datetime_test \ pcmk__xe_get_bool_attr_test \ pcmk__xe_set_bool_attr_test diff --git a/lib/common/tests/utils/crm_meta_name_test.c b/lib/common/tests/nvpair/crm_meta_name_test.c index 06fecc5..7c6c32b 100644 --- a/lib/common/tests/utils/crm_meta_name_test.c +++ b/lib/common/tests/nvpair/crm_meta_name_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,12 +10,12 @@ #include <crm_internal.h> #include <crm/common/unittest_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> static void empty_params(void **state) { - assert_null(crm_meta_name(NULL)); + pcmk__assert_asserts(crm_meta_name(NULL)); } static void @@ -23,11 +23,11 @@ standard_usage(void **state) { char *s = NULL; - s = crm_meta_name(XML_RSC_ATTR_NOTIFY); + s = crm_meta_name(PCMK_META_NOTIFY); assert_string_equal(s, "CRM_meta_notify"); free(s); - s = crm_meta_name(XML_RSC_ATTR_STICKINESS); + s = crm_meta_name(PCMK_META_RESOURCE_STICKINESS); assert_string_equal(s, "CRM_meta_resource_stickiness"); free(s); diff --git a/lib/common/tests/utils/crm_meta_value_test.c b/lib/common/tests/nvpair/crm_meta_value_test.c index 0bde5c6..ffe9619 100644 --- a/lib/common/tests/utils/crm_meta_value_test.c +++ b/lib/common/tests/nvpair/crm_meta_value_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,7 +10,7 @@ #include <crm_internal.h> #include <crm/common/unittest_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <glib.h> @@ -30,8 +30,8 @@ key_not_in_table(void **state) { GHashTable *tbl = pcmk__strkey_table(free, free); - assert_null(crm_meta_value(tbl, XML_RSC_ATTR_NOTIFY)); - assert_null(crm_meta_value(tbl, XML_RSC_ATTR_STICKINESS)); + assert_null(crm_meta_value(tbl, PCMK_META_NOTIFY)); + assert_null(crm_meta_value(tbl, PCMK_META_RESOURCE_STICKINESS)); g_hash_table_destroy(tbl); } @@ -41,11 +41,13 @@ key_in_table(void **state) { GHashTable *tbl = pcmk__strkey_table(free, free); - g_hash_table_insert(tbl, crm_meta_name(XML_RSC_ATTR_NOTIFY), strdup("1")); - g_hash_table_insert(tbl, crm_meta_name(XML_RSC_ATTR_STICKINESS), strdup("2")); + g_hash_table_insert(tbl, crm_meta_name(PCMK_META_NOTIFY), strdup("1")); + g_hash_table_insert(tbl, crm_meta_name(PCMK_META_RESOURCE_STICKINESS), + strdup("2")); - assert_string_equal(crm_meta_value(tbl, XML_RSC_ATTR_NOTIFY), "1"); - assert_string_equal(crm_meta_value(tbl, XML_RSC_ATTR_STICKINESS), "2"); + assert_string_equal(crm_meta_value(tbl, PCMK_META_NOTIFY), "1"); + assert_string_equal(crm_meta_value(tbl, PCMK_META_RESOURCE_STICKINESS), + "2"); g_hash_table_destroy(tbl); } diff --git a/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c b/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c index 3723707..84187da 100644 --- a/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ static void empty_input(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); assert_false(pcmk__xe_attr_is_true(NULL, NULL)); assert_false(pcmk__xe_attr_is_true(NULL, "whatever")); @@ -27,7 +27,7 @@ empty_input(void **state) static void attr_missing(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); assert_false(pcmk__xe_attr_is_true(node, "c")); free_xml(node); @@ -36,7 +36,7 @@ attr_missing(void **state) static void attr_present(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); assert_true(pcmk__xe_attr_is_true(node, "a")); assert_false(pcmk__xe_attr_is_true(node, "b")); @@ -44,7 +44,7 @@ attr_present(void **state) free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(empty_input), cmocka_unit_test(attr_missing), cmocka_unit_test(attr_present)) diff --git a/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c b/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c index 500d8a6..4823f6a 100644 --- a/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ static void empty_input(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); bool value; assert_int_equal(pcmk__xe_get_bool_attr(NULL, NULL, &value), ENODATA); @@ -29,7 +29,7 @@ empty_input(void **state) static void attr_missing(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); bool value; assert_int_equal(pcmk__xe_get_bool_attr(node, "c", &value), ENODATA); @@ -39,7 +39,8 @@ attr_missing(void **state) static void attr_present(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\" c=\"blah\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\" " + "c=\"blah\"/>"); bool value; value = false; @@ -53,7 +54,7 @@ attr_present(void **state) free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(empty_input), cmocka_unit_test(attr_missing), cmocka_unit_test(attr_present)) diff --git a/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c b/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c new file mode 100644 index 0000000..6da1e23 --- /dev/null +++ b/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c @@ -0,0 +1,108 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <errno.h> +#include <libxml/tree.h> + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/xml.h> +#include <crm/common/nvpair_internal.h> + +#define REFERENCE_ISO8601 "2024-001" +#define ATTR_PRESENT "start" +#define ATTR_MISSING "end" +#define REFERENCE_XML "<date_expression id=\"id1\" " \ + ATTR_PRESENT "=\"" REFERENCE_ISO8601 "\"" \ + " operation=\"gt\">" +#define BAD_XML "<date_expression id=\"id1\" " \ + ATTR_PRESENT "=\"not_a_time\"" \ + " operation=\"gt\">" + +static void +null_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, NULL, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, NULL, &t), EINVAL); + assert_null(t); + assert_int_equal(pcmk__xe_get_datetime(NULL, ATTR_PRESENT, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(NULL, ATTR_PRESENT, &t), EINVAL); + assert_null(t); + assert_int_equal(pcmk__xe_get_datetime(NULL, NULL, &t), EINVAL); + assert_null(t); + + free_xml(xml); +} + +static void +nonnull_time_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = crm_time_new_undefined(); + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), EINVAL); + + crm_time_free(t); + free_xml(xml); +} + +static void +attr_missing(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_MISSING, &t), pcmk_rc_ok); + assert_null(t); + + free_xml(xml); +} + +static void +attr_valid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + crm_time_t *reference = crm_time_new(REFERENCE_ISO8601); + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +attr_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(BAD_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), + pcmk_rc_unpack_error); + assert_null(t); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(nonnull_time_invalid), + cmocka_unit_test(attr_missing), + cmocka_unit_test(attr_valid), + cmocka_unit_test(attr_invalid)) diff --git a/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c b/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c index e1fb9c4..dda2878 100644 --- a/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -11,21 +11,21 @@ #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> static void set_attr(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); pcmk__xe_set_bool_attr(node, "a", true); pcmk__xe_set_bool_attr(node, "b", false); - assert_string_equal(crm_element_value(node, "a"), XML_BOOLEAN_TRUE); - assert_string_equal(crm_element_value(node, "b"), XML_BOOLEAN_FALSE); + assert_string_equal(crm_element_value(node, "a"), PCMK_VALUE_TRUE); + assert_string_equal(crm_element_value(node, "b"), PCMK_VALUE_FALSE); free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(set_attr)) diff --git a/lib/common/tests/probes/Makefile.am b/lib/common/tests/probes/Makefile.am new file mode 100644 index 0000000..f5a3fb4 --- /dev/null +++ b/lib/common/tests/probes/Makefile.am @@ -0,0 +1,18 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk_is_probe_test \ + pcmk_xe_is_probe_test \ + pcmk_xe_mask_probe_failure_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/actions/pcmk_is_probe_test.c b/lib/common/tests/probes/pcmk_is_probe_test.c index 4a65e3f..835aac1 100644 --- a/lib/common/tests/actions/pcmk_is_probe_test.c +++ b/lib/common/tests/probes/pcmk_is_probe_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * diff --git a/lib/common/tests/probes/pcmk_xe_is_probe_test.c b/lib/common/tests/probes/pcmk_xe_is_probe_test.c new file mode 100644 index 0000000..e42476a --- /dev/null +++ b/lib/common/tests/probes/pcmk_xe_is_probe_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2021-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +static void +op_is_probe_test(void **state) +{ + xmlNode *node = NULL; + + assert_false(pcmk_xe_is_probe(NULL)); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP "/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK__XA_OPERATION_KEY "=\"blah\" " + PCMK_META_INTERVAL "=\"30s\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"30s\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_START "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_true(pcmk_xe_is_probe(node)); + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(op_is_probe_test)) diff --git a/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c b/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c new file mode 100644 index 0000000..ad378bf --- /dev/null +++ b/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c @@ -0,0 +1,333 @@ +/* + * Copyright 2021-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +static void +op_is_not_probe_test(void **state) { + xmlNode *node = NULL; + + /* Not worth testing this thoroughly since it's just a duplicate of whether + * pcmk_op_is_probe works or not. + */ + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_START "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_mask_probe_failure(node)); + free_xml(node); +} + +static void +op_does_not_have_right_values_test(void **state) { + xmlNode *node = NULL; + char *s = NULL; + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_mask_probe_failure(node)); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"\"/>", + PCMK_OCF_OK); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); +} + +static void +check_values_test(void **state) { + xmlNode *node = NULL; + char *s = NULL; + + /* PCMK_EXEC_NOT_SUPPORTED */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_NOT_SUPPORTED); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_NOT_SUPPORTED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_DONE */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_NOT_INSTALLED */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_NOT_INSTALLED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_NOT_INSTALLED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR_HARD */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR_FATAL */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(op_is_not_probe_test), + cmocka_unit_test(op_does_not_have_right_values_test), + cmocka_unit_test(check_values_test)) diff --git a/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c b/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c index 2bae541..52fe006 100644 --- a/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c +++ b/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -21,7 +21,7 @@ static void no_exe_file(void **state) { size_t len = PATH_MAX; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; @@ -43,7 +43,7 @@ static void contents_too_long(void **state) { size_t len = 10; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; @@ -66,7 +66,7 @@ static void contents_ok(void **state) { size_t len = PATH_MAX; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; diff --git a/lib/common/tests/resources/Makefile.am b/lib/common/tests/resources/Makefile.am new file mode 100644 index 0000000..91e29a6 --- /dev/null +++ b/lib/common/tests/resources/Makefile.am @@ -0,0 +1,17 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk_resource_id_test \ + pcmk_resource_is_managed_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/resources/pcmk_resource_id_test.c b/lib/common/tests/resources/pcmk_resource_id_test.c new file mode 100644 index 0000000..5676d2a --- /dev/null +++ b/lib/common/tests/resources/pcmk_resource_id_test.c @@ -0,0 +1,36 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL + +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static void +null_resource(void **state) +{ + assert_null(pcmk_resource_id(NULL)); +} + +static void +resource_with_id(void **state) +{ + char rsc1_id[] = "rsc1"; + pcmk_resource_t rsc1 = { + .id = rsc1_id, + }; + + assert_string_equal(pcmk_resource_id(&rsc1), "rsc1"); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_resource), + cmocka_unit_test(resource_with_id)) diff --git a/lib/common/tests/resources/pcmk_resource_is_managed_test.c b/lib/common/tests/resources/pcmk_resource_is_managed_test.c new file mode 100644 index 0000000..958bdc2 --- /dev/null +++ b/lib/common/tests/resources/pcmk_resource_is_managed_test.c @@ -0,0 +1,46 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL + +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static void +null_resource(void **state) +{ + assert_false(pcmk_resource_is_managed(NULL)); +} + +static void +resource_is_managed(void **state) +{ + pcmk_resource_t rsc1 = { + .flags = pcmk_rsc_managed, + }; + + assert_true(pcmk_resource_is_managed(&rsc1)); +} + +static void +resource_is_not_managed(void **state) +{ + pcmk_resource_t rsc1 = { + .flags = 0, + }; + + assert_false(pcmk_resource_is_managed(&rsc1)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_resource), + cmocka_unit_test(resource_is_managed), + cmocka_unit_test(resource_is_not_managed)) diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am new file mode 100644 index 0000000..4163037 --- /dev/null +++ b/lib/common/tests/rules/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2020-2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk__cmp_by_type_test \ + pcmk__evaluate_attr_expression_test \ + pcmk__evaluate_date_expression_test \ + pcmk__evaluate_date_spec_test \ + pcmk__evaluate_condition_test \ + pcmk__evaluate_op_expression_test \ + pcmk__evaluate_rsc_expression_test \ + pcmk__parse_combine_test \ + pcmk__parse_comparison_test \ + pcmk__parse_source_test \ + pcmk__parse_type_test \ + pcmk__replace_submatches_test \ + pcmk__unpack_duration_test \ + pcmk_evaluate_rule_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/rules/pcmk__cmp_by_type_test.c b/lib/common/tests/rules/pcmk__cmp_by_type_test.c new file mode 100644 index 0000000..cf468f1 --- /dev/null +++ b/lib/common/tests/rules/pcmk__cmp_by_type_test.c @@ -0,0 +1,102 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <limits.h> // INT_MIN, INT_MAX + +#include <crm/common/util.h> // crm_strdup_printf() +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +null_compares_lesser(void **state) +{ + assert_int_equal(pcmk__cmp_by_type(NULL, NULL, pcmk__type_string), 0); + assert_true(pcmk__cmp_by_type("0", NULL, pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type(NULL, "0", pcmk__type_number) < 0); +} + +static void +invalid_compares_equal(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("0", "1", pcmk__type_unknown), 0); + assert_int_equal(pcmk__cmp_by_type("hi", "bye", pcmk__type_unknown), 0); + assert_int_equal(pcmk__cmp_by_type("-1.0", "2.0", pcmk__type_unknown), 0); +} + +static void +compare_string_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("bye", "bye", pcmk__type_string), 0); + assert_int_equal(pcmk__cmp_by_type("bye", "BYE", pcmk__type_string), 0); + assert_true(pcmk__cmp_by_type("bye", "hello", pcmk__type_string) < 0); + assert_true(pcmk__cmp_by_type("bye", "HELLO", pcmk__type_string) < 0); + assert_true(pcmk__cmp_by_type("bye", "boo", pcmk__type_string) > 0); + assert_true(pcmk__cmp_by_type("bye", "Boo", pcmk__type_string) > 0); +} + +static void +compare_integer_type(void **state) +{ + char *int_min = crm_strdup_printf("%d", INT_MIN); + char *int_max = crm_strdup_printf("%d", INT_MAX); + + assert_int_equal(pcmk__cmp_by_type("0", "0", pcmk__type_integer), 0); + assert_true(pcmk__cmp_by_type("0", "1", pcmk__type_integer) < 0); + assert_true(pcmk__cmp_by_type("1", "0", pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type(int_min, int_max, pcmk__type_integer) < 0); + assert_true(pcmk__cmp_by_type(int_max, int_min, pcmk__type_integer) > 0); + free(int_min); + free(int_max); + + // Non-integers compare as strings + assert_int_equal(pcmk__cmp_by_type("0", "x", pcmk__type_integer), + pcmk__cmp_by_type("0", "x", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "0", pcmk__type_integer), + pcmk__cmp_by_type("x", "0", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "X", pcmk__type_integer), + pcmk__cmp_by_type("x", "X", pcmk__type_string)); +} + +static void +compare_number_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("0", "0.0", pcmk__type_number), 0); + assert_true(pcmk__cmp_by_type("0.345", "0.5", pcmk__type_number) < 0); + assert_true(pcmk__cmp_by_type("5", "3.1", pcmk__type_number) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_number) > 0); + + // Non-numbers compare as strings + assert_int_equal(pcmk__cmp_by_type("0.0", "x", pcmk__type_number), + pcmk__cmp_by_type("0.0", "x", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "0.0", pcmk__type_number), + pcmk__cmp_by_type("x", "0.0", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "X", pcmk__type_number), + pcmk__cmp_by_type("x", "X", pcmk__type_string)); +} + +static void +compare_version_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("1.0", "1.0", pcmk__type_version), 0); + assert_true(pcmk__cmp_by_type("1.0.0", "1.0.1", pcmk__type_version) < 0); + assert_true(pcmk__cmp_by_type("5.0", "3.1.15", pcmk__type_version) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_version) > 0); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_compares_lesser), + cmocka_unit_test(invalid_compares_equal), + cmocka_unit_test(compare_string_type), + cmocka_unit_test(compare_integer_type), + cmocka_unit_test(compare_number_type), + cmocka_unit_test(compare_version_type)) diff --git a/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c new file mode 100644 index 0000000..d28cb11 --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c @@ -0,0 +1,831 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +#define MATCHED_STRING "server-north" + +static const regmatch_t submatches[] = { + { .rm_so = 0, .rm_eo = 12 }, // %0 = Entire string + { .rm_so = 7, .rm_eo = 12 }, // %1 = "north" +}; + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate attribute expressions + + // Used to replace submatches in attribute name + .rsc_id = MATCHED_STRING, + .rsc_id_submatches = submatches, + .rsc_id_nmatches = 2, + + // Used when source is instance attributes + .rsc_params = NULL, + + // Used when source is meta-attributes + .rsc_meta = NULL, + + // Used to get actual value of node attribute + .node_attrs = NULL, +}; + +static int +setup(void **state) +{ + rule_input.rsc_params = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.rsc_params, "foo-param", "bar"); + pcmk__insert_dup(rule_input.rsc_params, "myparam", "different"); + + rule_input.rsc_meta = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.rsc_meta, "foo-meta", "bar"); + pcmk__insert_dup(rule_input.rsc_params, "mymeta", "different"); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, "foo", "bar"); + pcmk__insert_dup(rule_input.node_attrs, "num", "10"); + pcmk__insert_dup(rule_input.node_attrs, "ver", "3.5.0"); + pcmk__insert_dup(rule_input.node_attrs, "prefer-north", "100"); + + return 0; +} + +static int +teardown(void **state) +{ + g_hash_table_destroy(rule_input.rsc_params); + g_hash_table_destroy(rule_input.rsc_meta); + g_hash_table_destroy(rule_input.node_attrs); + return 0; +} + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Node attribute expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_attr_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_attr_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_SOURCE_LITERAL_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_SOURCE_LITERAL_PASSES); + + assert_int_equal(pcmk__evaluate_attr_expression(NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_attr_expression(xml, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_attr_expression(NULL, &rule_input), EINVAL); + + free_xml(xml); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_EXPRESSION " " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_attr_expression(EXPR_ID_MISSING, pcmk_rc_ok); +} + + +/* + * Test PCMK_XA_ATTRIBUTE + */ + +#define EXPR_ATTR_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +attr_missing(void **state) +{ + assert_attr_expression(EXPR_ATTR_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_ATTR_SUBMATCH_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='prefer-%1' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +attr_with_submatch_passes(void **state) +{ + assert_attr_expression(EXPR_ATTR_SUBMATCH_PASSES, pcmk_rc_ok); +} + +#define EXPR_ATTR_SUBMATCH_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='undefined-%1' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +attr_with_submatch_fails(void **state) +{ + assert_attr_expression(EXPR_ATTR_SUBMATCH_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_VALUE_SOURCE + */ + +#define EXPR_SOURCE_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +source_missing(void **state) +{ + // Defaults to literal + assert_attr_expression(EXPR_SOURCE_MISSING, pcmk_rc_ok); +} + +#define EXPR_SOURCE_INVALID \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='not-a-source' />" + +static void +source_invalid(void **state) +{ + // Currently treated as literal + assert_attr_expression(EXPR_SOURCE_INVALID, pcmk_rc_ok); +} + +static void +source_literal_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_LITERAL_VALUE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='wrong-value' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +source_literal_value_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_VALUE_FAILS, + pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_LITERAL_ATTR_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='not-an-attribute' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +source_literal_attr_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_ATTR_FAILS, + pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_PARAM_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='not-a-param' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_missing(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_MISSING, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_PARAM_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='foo-param' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_PARAM_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='myparam' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_META_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='not-a-meta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_missing(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_MISSING, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_META_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='foo-meta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_META_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='mymeta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_TYPE + */ + +#define EXPR_TYPE_DEFAULT_NUMBER \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2.5' />" + +static void +type_default_number(void **state) +{ + // Defaults to number for "gt" if either value contains a decimal point + assert_attr_expression(EXPR_TYPE_DEFAULT_NUMBER, pcmk_rc_ok); +} + +#define EXPR_TYPE_DEFAULT_INT \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +type_default_int(void **state) +{ + // Defaults to integer for "gt" if neither value contains a decimal point + assert_attr_expression(EXPR_TYPE_DEFAULT_INT, pcmk_rc_ok); +} + +#define EXPR_TYPE_STRING_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +type_string_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_STRING_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_STRING_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bat' />" + +static void +type_string_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_STRING_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_INTEGER_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +type_integer_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_INTEGER_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='11' />" + +static void +type_integer_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_INTEGER_TRUNCATION \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.5' />" + +static void +type_integer_truncation(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_TRUNCATION, pcmk_rc_ok); +} + +#define EXPR_TYPE_NUMBER_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.0' />" + +static void +type_number_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_NUMBER_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_NUMBER_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.1' />" + +static void +type_number_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_NUMBER_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_VERSION_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='3.4.9' />" + +static void +type_version_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_VERSION_EQUALITY \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='3.5' />" + +static void +type_version_equality(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_EQUALITY, pcmk_rc_ok); +} + +#define EXPR_TYPE_VERSION_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='4.0' />" + +static void +type_version_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_FAILS, pcmk_rc_before_range); +} + +/* + * Test PCMK_XA_OPERATION + */ + +#define EXPR_OP_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_missing(void **state) +{ + assert_attr_expression(EXPR_OP_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_OP_INVALID \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='not-an-operation' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_invalid(void **state) +{ + assert_attr_expression(EXPR_OP_INVALID, pcmk_rc_unpack_error); +} + +#define EXPR_OP_LT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_lt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LT_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +op_lt_fails(void **state) +{ + assert_attr_expression(EXPR_OP_LT_FAILS, pcmk_rc_after_range); +} + +#define EXPR_OP_GT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +op_gt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GT_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_gt_fails(void **state) +{ + assert_attr_expression(EXPR_OP_GT_FAILS, pcmk_rc_before_range); +} + +#define EXPR_OP_LTE_LT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_lte_lt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_LT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LTE_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_lte_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LTE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='9' />" + +static void +op_lte_fails(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_FAILS, pcmk_rc_after_range); +} + +#define EXPR_OP_GTE_GT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='1' />" + +static void +op_gte_gt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_GT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GTE_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_gte_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GTE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='11' />" + +static void +op_gte_fails(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_FAILS, pcmk_rc_before_range); +} + +// This also tests that string is used if values aren't parseable as numbers +#define EXPR_OP_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +op_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_EQ_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_eq_fails(void **state) +{ + assert_attr_expression(EXPR_OP_EQ_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_NE_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NE "' " \ + PCMK_XA_VALUE "='bat' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +op_ne_passes(void **state) +{ + assert_attr_expression(EXPR_OP_NE_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_NE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_ne_fails(void **state) +{ + assert_attr_expression(EXPR_OP_NE_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_DEFINED_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_passes(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_DEFINED_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='boo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_fails(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_DEFINED_WITH_VALUE \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_with_value(void **state) +{ + // Ill-formed but currently accepted + assert_attr_expression(EXPR_OP_DEFINED_WITH_VALUE, pcmk_rc_ok); +} + +#define EXPR_OP_UNDEFINED_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='boo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NOT_DEFINED "' />" + +static void +op_undefined_passes(void **state) +{ + assert_attr_expression(EXPR_OP_UNDEFINED_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_UNDEFINED_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NOT_DEFINED "' />" + +static void +op_undefined_fails(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_VALUE + */ + +#define EXPR_VALUE_MISSING_DEFINED_OK \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +value_missing_defined_ok(void **state) +{ + assert_attr_expression(EXPR_VALUE_MISSING_DEFINED_OK, pcmk_rc_ok); +} + +#define EXPR_VALUE_MISSING_EQ_OK \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='not-an-attr' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' />" + +static void +value_missing_eq_ok(void **state) +{ + // Currently treated as NULL reference value + assert_attr_expression(EXPR_VALUE_MISSING_EQ_OK, pcmk_rc_ok); +} + + +#define expr_test(f) cmocka_unit_test_setup_teardown(f, setup, teardown) + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + expr_test(id_missing), + expr_test(attr_missing), + expr_test(attr_with_submatch_passes), + expr_test(attr_with_submatch_fails), + expr_test(source_missing), + expr_test(source_invalid), + expr_test(source_literal_passes), + expr_test(source_literal_value_fails), + expr_test(source_literal_attr_fails), + expr_test(source_params_missing), + expr_test(source_params_passes), + expr_test(source_params_fails), + expr_test(source_meta_missing), + expr_test(source_meta_passes), + expr_test(source_meta_fails), + expr_test(type_default_number), + expr_test(type_default_int), + expr_test(type_string_passes), + expr_test(type_string_fails), + expr_test(type_integer_passes), + expr_test(type_integer_fails), + expr_test(type_integer_truncation), + expr_test(type_number_passes), + expr_test(type_number_fails), + expr_test(type_version_passes), + expr_test(type_version_equality), + expr_test(type_version_fails), + expr_test(op_missing), + expr_test(op_invalid), + expr_test(op_lt_passes), + expr_test(op_lt_fails), + expr_test(op_gt_passes), + expr_test(op_gt_fails), + expr_test(op_lte_lt_passes), + expr_test(op_lte_eq_passes), + expr_test(op_lte_fails), + expr_test(op_gte_gt_passes), + expr_test(op_gte_eq_passes), + expr_test(op_gte_fails), + expr_test(op_eq_passes), + expr_test(op_eq_fails), + expr_test(op_ne_passes), + expr_test(op_ne_fails), + expr_test(op_defined_passes), + expr_test(op_defined_fails), + expr_test(op_defined_with_value), + expr_test(op_undefined_passes), + expr_test(op_undefined_fails), + expr_test(value_missing_defined_ok), + expr_test(value_missing_eq_ok)) diff --git a/lib/common/tests/rules/pcmk__evaluate_condition_test.c b/lib/common/tests/rules/pcmk__evaluate_condition_test.c new file mode 100644 index 0000000..bcb13a0 --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_condition_test.c @@ -0,0 +1,197 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + + +/* + * Test invalid arguments + */ + +#define EXPR_ATTRIBUTE \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk__evaluate_condition(NULL, NULL, next_change), EINVAL); + + xml = pcmk__xml_parse(EXPR_ATTRIBUTE); + assert_int_equal(pcmk__evaluate_condition(xml, NULL, next_change), EINVAL); + free_xml(xml); + + assert_int_equal(pcmk__evaluate_condition(NULL, &rule_input, next_change), + EINVAL); + + crm_time_free(next_change); +} + + +#define EXPR_INVALID "<not_an_expression " PCMK_XA_ID "='e' />" + +static void +invalid_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_INVALID); + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, next_change), + pcmk_rc_unpack_error); + + crm_time_free(next_change); + free_xml(xml); +} + + +/* Each expression type function already has unit tests, so we just need to test + * that they are called correctly (essentially, one of each one's own tests). + */ + +static void +attribute_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_ATTRIBUTE); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, "foo", "bar"); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + + g_hash_table_destroy(rule_input.node_attrs); + rule_input.node_attrs = NULL; + free_xml(xml); +} + +#define EXPR_LOCATION \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='node1' />" + +static void +location_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_LOCATION); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, CRM_ATTR_UNAME, "node1"); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + + g_hash_table_destroy(rule_input.node_attrs); + rule_input.node_attrs = NULL; + free_xml(xml); +} + +#define EXPR_DATE \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +date_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_DATE); + crm_time_t *now = crm_time_new("2024-02-01 11:59:59"); + crm_time_t *next_change = crm_time_new("2024-02-01 14:00:00"); + crm_time_t *reference = crm_time_new("2024-02-01 12:00:00"); + + rule_input.now = now; + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, next_change), + pcmk_rc_before_range); + assert_int_equal(crm_time_compare(next_change, reference), 0); + rule_input.now = NULL; + + crm_time_free(reference); + crm_time_free(next_change); + crm_time_free(now); +} + +#define EXPR_RESOURCE \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +resource_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_RESOURCE); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_OP \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +op_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_OP); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_SUBRULE \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' /> />" + +static void +subrule(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_SUBRULE); + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(invalid_expression), + cmocka_unit_test(attribute_expression), + cmocka_unit_test(location_expression), + cmocka_unit_test(date_expression), + cmocka_unit_test(resource_expression), + cmocka_unit_test(op_expression), + cmocka_unit_test(subrule)) diff --git a/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c new file mode 100644 index 0000000..df8dcbf --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c @@ -0,0 +1,684 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/*! + * \internal + * \brief Run one test, comparing return value and output argument + * + * \param[in] xml Date expression XML + * \param[in] now_s Time to evaluate expression with (as string) + * \param[in] next_change_s If this and \p reference_s are not NULL, initialize + * next change time with this time (as string), + * and assert that its value after evaluation is the + * reference + * \param[in] reference_s If not NULL, time (as string) that next change + * should be after expression evaluation + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_date_expression(const xmlNode *xml, const char *now_s, + const char *next_change_s, const char *reference_s, + int reference_rc) +{ + crm_time_t *now = NULL; + crm_time_t *next_change = NULL; + bool check_next_change = (next_change_s != NULL) && (reference_s != NULL); + + if (check_next_change) { + next_change = crm_time_new(next_change_s); + } + + now = crm_time_new(now_s); + assert_int_equal(pcmk__evaluate_date_expression(xml, now, next_change), + reference_rc); + crm_time_free(now); + + if (check_next_change) { + crm_time_t *reference = crm_time_new(reference_s); + + assert_int_equal(crm_time_compare(next_change, reference), 0); + crm_time_free(reference); + crm_time_free(next_change); + } +} + +#define EXPR_LT_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +null_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + crm_time_t *t = crm_time_new("2024-02-01"); + + assert_int_equal(pcmk__evaluate_date_expression(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_expression(xml, NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_expression(NULL, t, NULL), EINVAL); + + crm_time_free(t); + free_xml(xml); +} + +static void +null_next_change_ok(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_within_range); + free_xml(xml); +} + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + xmlNodePtr xml = pcmk__xml_parse(EXPR_ID_MISSING); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_within_range); + free_xml(xml); +} + +#define EXPR_OP_INVALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='not-a-choice' />" + +static void +op_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_OP_INVALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_LT_MISSING_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' />" + +static void +lt_missing_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_MISSING_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_LT_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='not-a-datetime' />" + +static void +lt_invalid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_INVALID_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +static void +lt_valid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + + // Now and next change are both before end + assert_date_expression(xml, "2023-01-01 05:00:00", "2024-02-01 10:00:00", + "2024-02-01 10:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:00", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 20:00:00", + "2024-02-01 20:00:00", pcmk_rc_after_range); + + // Now and next change are both after end + assert_date_expression(xml, "2024-03-01 12:00:00", "2024-02-01 20:00:00", + "2024-02-01 20:00:00", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_GT_MISSING_START \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' />" + +static void +gt_missing_start(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_MISSING_START); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_GT_INVALID_START \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_START "='not-a-datetime' />" + +static void +gt_invalid_start(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_INVALID_START); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_GT_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' />" + +static void +gt_valid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_VALID); + + // Now and next change are both before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:01", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 12:00:01", pcmk_rc_before_range); + + // Now is one second after start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:01", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // t is after start, next change is after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' />" + +static void +range_missing(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_MISSING); + crm_time_t *t = crm_time_new("2024-01-01"); + + assert_int_equal(pcmk__evaluate_date_expression(xml, t, NULL), + pcmk_rc_undetermined); + + crm_time_free(t); + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_invalid_start_invalid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_INVALID_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' />" + +static void +range_invalid_start_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_ONLY); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' />" + +static void +range_valid_start_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_ONLY); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_END_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_invalid_end_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_END_ONLY); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_VALID_END_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_valid_end_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_END_ONLY); + + // Now and next change are before end + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_valid_start_invalid_end(void **state) +{ + // Currently treated same as start without end + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_INVALID_END); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_VALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_invalid_start_valid_end(void **state) +{ + // Currently treated same as end without start + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_VALID_END); + + // Now and next change are before end + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_VALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_valid_start_valid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_VALID_END); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_INVALID_DURATION \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='not-a-number' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_invalid_duration(void **state) +{ + // Currently treated same as end equals start + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_INVALID_DURATION); + + // Now and next change are before start + assert_date_expression(xml, "2024-02-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 12:00:01", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-02-01 12:00:01", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_VALID_DURATION \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='3' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_valid_duration(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_VALID_DURATION); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_DURATION_MISSING_ID \ + "<" PCMK_XE_DATE_EXPRESSION " " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='3' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_duration_missing_id(void **state) +{ + // Currently acceptable + xmlNodePtr xml = NULL; + + xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_DURATION_MISSING_ID); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_SPEC_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "' />" + +static void +spec_missing(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_MISSING); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_SPEC_INVALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " PCMK_XA_ID "='s' " \ + PCMK_XA_MONTHS "='not-a-number'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_invalid(void **state) +{ + // Currently treated as date_spec with no ranges (which passes) + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_INVALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_SPEC_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " PCMK_XA_ID "='s' " \ + PCMK_XA_MONTHS "='2'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_valid(void **state) +{ + // date_spec does not currently support next_change + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_VALID); + + // Now is just before spec start + assert_date_expression(xml, "2024-01-01 23:59:59", NULL, NULL, + pcmk_rc_before_range); + + // Now matches spec start + assert_date_expression(xml, "2024-02-01 00:00:00", NULL, NULL, pcmk_rc_ok); + + // Now is within spec range + assert_date_expression(xml, "2024-02-22 22:22:22", NULL, NULL, pcmk_rc_ok); + + // Now matches spec end + assert_date_expression(xml, "2024-02-29 23:59:59", NULL, NULL, pcmk_rc_ok); + + // Now is just past spec end + assert_date_expression(xml, "2024-03-01 00:00:00", NULL, NULL, + pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_SPEC_MISSING_ID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " \ + PCMK_XA_MONTHS "='2'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_missing_id(void **state) +{ + // Currently acceptable; date_spec does not currently support next_change + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_MISSING_ID); + + // Now is just before spec start + assert_date_expression(xml, "2024-01-01 23:59:59", NULL, NULL, + pcmk_rc_before_range); + + // Now matches spec start + assert_date_expression(xml, "2024-02-01 00:00:00", NULL, NULL, pcmk_rc_ok); + + // Now is within spec range + assert_date_expression(xml, "2024-02-22 22:22:22", NULL, NULL, pcmk_rc_ok); + + // Now matches spec end + assert_date_expression(xml, "2024-02-29 23:59:59", NULL, NULL, pcmk_rc_ok); + + // Now is just past spec end + assert_date_expression(xml, "2024-03-01 00:00:00", NULL, NULL, + pcmk_rc_after_range); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(null_next_change_ok), + cmocka_unit_test(id_missing), + cmocka_unit_test(op_invalid), + cmocka_unit_test(lt_missing_end), + cmocka_unit_test(lt_invalid_end), + cmocka_unit_test(lt_valid), + cmocka_unit_test(gt_missing_start), + cmocka_unit_test(gt_invalid_start), + cmocka_unit_test(gt_valid), + cmocka_unit_test(range_missing), + cmocka_unit_test(range_invalid_start_invalid_end), + cmocka_unit_test(range_invalid_start_only), + cmocka_unit_test(range_valid_start_only), + cmocka_unit_test(range_invalid_end_only), + cmocka_unit_test(range_valid_end_only), + cmocka_unit_test(range_valid_start_invalid_end), + cmocka_unit_test(range_invalid_start_valid_end), + cmocka_unit_test(range_valid_start_valid_end), + cmocka_unit_test(range_valid_start_invalid_duration), + cmocka_unit_test(range_valid_start_valid_duration), + cmocka_unit_test(range_valid_start_duration_missing_id), + cmocka_unit_test(spec_missing), + cmocka_unit_test(spec_invalid), + cmocka_unit_test(spec_valid), + cmocka_unit_test(spec_missing_id)) diff --git a/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c b/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c new file mode 100644 index 0000000..6048adf --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c @@ -0,0 +1,231 @@ +/* + * Copyright 2020-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <errno.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +run_one_test(const char *t, const char *x, int expected) +{ + crm_time_t *tm = crm_time_new(t); + xmlNodePtr xml = pcmk__xml_parse(x); + + assert_int_equal(pcmk__evaluate_date_spec(xml, tm), expected); + + crm_time_free(tm); + free_xml(xml); +} + +static void +null_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse("<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2019'/>"); + crm_time_t *tm = crm_time_new(NULL); + + assert_int_equal(pcmk__evaluate_date_spec(NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_spec(xml, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_spec(NULL, tm), EINVAL); + + crm_time_free(tm); + free_xml(xml); +} + +static void +spec_id_missing(void **state) +{ + // Currently acceptable + run_one_test("2020-01-01", "<date_spec years='2020'/>", pcmk_rc_ok); +} + +static void +invalid_range(void **state) +{ + // Currently acceptable + run_one_test("2020-01-01", "<date_spec years='not-a-year' months='1'/>", + pcmk_rc_ok); +} + +static void +time_satisfies_year_spec(void **state) +{ + run_one_test("2020-01-01", + "<date_spec " PCMK_XA_ID "='spec' years='2020'/>", + pcmk_rc_ok); +} + +static void +time_after_year_spec(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2019'/>", + pcmk_rc_after_range); +} + +static void +time_satisfies_year_range(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2030'/>", + pcmk_rc_ok); +} + +static void +time_before_year_range(void **state) +{ + run_one_test("2000-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2030'/>", + pcmk_rc_before_range); +} + +static void +time_after_year_range(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2015'/>", + pcmk_rc_after_range); +} + +static void +range_without_start_year_passes(void **state) +{ + run_one_test("2010-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='-2020'/>", + pcmk_rc_ok); +} + +static void +range_without_end_year_passes(void **state) +{ + run_one_test("2010-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2000-'/>", + pcmk_rc_ok); + run_one_test("2000-10-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2000-'/>", + pcmk_rc_ok); +} + +static void +yeardays_satisfies(void **state) +{ + run_one_test("2020-01-30", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='30'/>", + pcmk_rc_ok); +} + +static void +time_after_yeardays_spec(void **state) +{ + run_one_test("2020-02-15", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='40'/>", + pcmk_rc_after_range); +} + +static void +yeardays_feb_29_satisfies(void **state) +{ + run_one_test("2016-02-29", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='60'/>", + pcmk_rc_ok); +} + +static void +exact_ymd_satisfies(void **state) +{ + run_one_test("2001-12-31", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='12' " + PCMK_XA_MONTHDAYS "='31'/>", + pcmk_rc_ok); +} + +static void +range_in_month_satisfies(void **state) +{ + run_one_test("2001-06-10", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='6' " + PCMK_XA_MONTHDAYS "='1-10'/>", + pcmk_rc_ok); +} + +static void +exact_ymd_after_range(void **state) +{ + run_one_test("2001-12-31", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='12' " + PCMK_XA_MONTHDAYS "='30'/>", + pcmk_rc_after_range); +} + +static void +time_after_monthdays_range(void **state) +{ + run_one_test("2001-06-10", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='6' " + PCMK_XA_MONTHDAYS "='11-15'/>", + pcmk_rc_before_range); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(spec_id_missing), + cmocka_unit_test(invalid_range), + cmocka_unit_test(time_satisfies_year_spec), + cmocka_unit_test(time_after_year_spec), + cmocka_unit_test(time_satisfies_year_range), + cmocka_unit_test(time_before_year_range), + cmocka_unit_test(time_after_year_range), + cmocka_unit_test(range_without_start_year_passes), + cmocka_unit_test(range_without_end_year_passes), + cmocka_unit_test(yeardays_satisfies), + cmocka_unit_test(time_after_yeardays_spec), + cmocka_unit_test(yeardays_feb_29_satisfies), + cmocka_unit_test(exact_ymd_satisfies), + cmocka_unit_test(range_in_month_satisfies), + cmocka_unit_test(exact_ymd_after_range), + cmocka_unit_test(time_after_monthdays_range)) diff --git a/lib/common/tests/rules/pcmk__evaluate_op_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_op_expression_test.c new file mode 100644 index 0000000..d1cb35f --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_op_expression_test.c @@ -0,0 +1,207 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate operation expressions + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Operation expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_op_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_op_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_FAIL_BOTH \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_START "' " \ + PCMK_XA_INTERVAL "='0' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk__evaluate_op_expression(NULL, NULL), EINVAL); + + xml = pcmk__xml_parse(EXPR_FAIL_BOTH); + assert_int_equal(pcmk__evaluate_op_expression(xml, NULL), EINVAL); + free_xml(xml); + + assert_op_expression(NULL, EINVAL); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_OP_EXPRESSION " " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +#define EXPR_ID_EMPTY \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_op_expression(EXPR_ID_MISSING, pcmk_rc_ok); + assert_op_expression(EXPR_ID_EMPTY, pcmk_rc_ok); +} + + +/* + * Test PCMK_XA_NAME + */ + +#define EXPR_NAME_MISSING \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +name_missing(void **state) +{ + assert_op_expression(EXPR_NAME_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_MATCH_BOTH \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +#define EXPR_EMPTY_NAME \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='' " PCMK_XA_INTERVAL "='10s' />" + +static void +input_name_missing(void **state) +{ + rule_input.op_name = NULL; + assert_op_expression(EXPR_MATCH_BOTH, pcmk_rc_op_unsatisfied); + assert_op_expression(EXPR_EMPTY_NAME, pcmk_rc_op_unsatisfied); + rule_input.op_name = PCMK_ACTION_MONITOR; +} + +#define EXPR_FAIL_NAME \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_START "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +fail_name(void **state) +{ + assert_op_expression(EXPR_FAIL_NAME, pcmk_rc_op_unsatisfied); + + // An empty name is meaningless but accepted, so not an unpack error + assert_op_expression(EXPR_EMPTY_NAME, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_INTERVAL + */ + +#define EXPR_EMPTY_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='' />" + +#define EXPR_INVALID_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='not-an-interval' />" + +static void +invalid_interval(void **state) +{ + assert_op_expression(EXPR_EMPTY_INTERVAL, pcmk_rc_unpack_error); + assert_op_expression(EXPR_INVALID_INTERVAL, pcmk_rc_unpack_error); +} + +#define EXPR_DEFAULT_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' />" + +static void +default_interval(void **state) +{ + assert_op_expression(EXPR_DEFAULT_INTERVAL, pcmk_rc_ok); +} + +#define EXPR_FAIL_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='9s' />" + +static void +fail_interval(void **state) +{ + assert_op_expression(EXPR_FAIL_INTERVAL, pcmk_rc_op_unsatisfied); +} + + +static void +match_both(void **state) +{ + assert_op_expression(EXPR_MATCH_BOTH, pcmk_rc_ok); +} + +static void +fail_both(void **state) +{ + assert_op_expression(EXPR_FAIL_BOTH, pcmk_rc_op_unsatisfied); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(name_missing), + cmocka_unit_test(input_name_missing), + cmocka_unit_test(fail_name), + cmocka_unit_test(invalid_interval), + cmocka_unit_test(default_interval), + cmocka_unit_test(fail_interval), + cmocka_unit_test(match_both), + cmocka_unit_test(fail_both)) diff --git a/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c new file mode 100644 index 0000000..c3a164e --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c @@ -0,0 +1,227 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate resource expressions + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", +}; + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Resource expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_rsc_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_rsc_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_ALL_MATCH \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk__evaluate_rsc_expression(NULL, NULL), EINVAL); + + xml = pcmk__xml_parse(EXPR_ALL_MATCH); + assert_int_equal(pcmk__evaluate_rsc_expression(xml, NULL), EINVAL); + free_xml(xml); + + assert_rsc_expression(NULL, EINVAL); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_RSC_EXPRESSION " " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +#define EXPR_ID_EMPTY \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_rsc_expression(EXPR_ID_MISSING, pcmk_rc_ok); + assert_rsc_expression(EXPR_ID_EMPTY, pcmk_rc_ok); +} + + +/* + * Test standard, provider, and agent + */ + +#define EXPR_FAIL_STANDARD \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_LSB "' />" + +#define EXPR_EMPTY_STANDARD \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='' />" + +static void +fail_standard(void **state) +{ + assert_rsc_expression(EXPR_FAIL_STANDARD, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_STANDARD, pcmk_rc_op_unsatisfied); + + rule_input.rsc_standard = NULL; + assert_rsc_expression(EXPR_FAIL_STANDARD, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_STANDARD, pcmk_rc_op_unsatisfied); + rule_input.rsc_standard = PCMK_RESOURCE_CLASS_OCF; +} + +#define EXPR_FAIL_PROVIDER \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='pacemaker' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +#define EXPR_EMPTY_PROVIDER \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='' " PCMK_XA_TYPE "='IPaddr2' />" + +static void +fail_provider(void **state) +{ + assert_rsc_expression(EXPR_FAIL_PROVIDER, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_PROVIDER, pcmk_rc_op_unsatisfied); + + rule_input.rsc_provider = NULL; + assert_rsc_expression(EXPR_FAIL_PROVIDER, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_PROVIDER, pcmk_rc_op_unsatisfied); + rule_input.rsc_provider = "heartbeat"; +} + +#define EXPR_FAIL_AGENT \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr3' />" + +#define EXPR_EMPTY_AGENT \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " PCMK_XA_TYPE "='' />" + +static void +fail_agent(void **state) +{ + assert_rsc_expression(EXPR_FAIL_AGENT, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_AGENT, pcmk_rc_op_unsatisfied); + + rule_input.rsc_agent = NULL; + assert_rsc_expression(EXPR_FAIL_AGENT, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_AGENT, pcmk_rc_op_unsatisfied); + rule_input.rsc_agent = "IPaddr2"; +} + +#define EXPR_NO_STANDARD_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +no_standard_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_STANDARD_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_PROVIDER_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +no_provider_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_PROVIDER_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_AGENT_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' />" + +static void +no_agent_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_AGENT_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_CRITERIA_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' />" + +static void +no_criteria_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_CRITERIA_MATCHES, pcmk_rc_ok); +} + +static void +all_match(void **state) +{ + assert_rsc_expression(EXPR_ALL_MATCH, pcmk_rc_ok); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(fail_standard), + cmocka_unit_test(fail_provider), + cmocka_unit_test(fail_agent), + cmocka_unit_test(no_standard_matches), + cmocka_unit_test(no_provider_matches), + cmocka_unit_test(no_agent_matches), + cmocka_unit_test(no_criteria_matches), + cmocka_unit_test(all_match)) diff --git a/lib/common/tests/rules/pcmk__parse_combine_test.c b/lib/common/tests/rules/pcmk__parse_combine_test.c new file mode 100644 index 0000000..afebcf8 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_combine_test.c @@ -0,0 +1,52 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +static void +default_and(void **state) +{ + assert_int_equal(pcmk__parse_combine(NULL), pcmk__combine_and); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_combine(""), pcmk__combine_unknown); + assert_int_equal(pcmk__parse_combine(" "), pcmk__combine_unknown); + assert_int_equal(pcmk__parse_combine("but"), pcmk__combine_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_combine(PCMK_VALUE_AND), pcmk__combine_and); + assert_int_equal(pcmk__parse_combine(PCMK_VALUE_OR), pcmk__combine_or); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_combine("And"), + pcmk__combine_and); + + assert_int_equal(pcmk__parse_combine("OR"), + pcmk__combine_or); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(default_and), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_comparison_test.c b/lib/common/tests/rules/pcmk__parse_comparison_test.c new file mode 100644 index 0000000..a995596 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_comparison_test.c @@ -0,0 +1,72 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +null_unknown(void **state) +{ + assert_int_equal(pcmk__parse_comparison(NULL), pcmk__comparison_unknown); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_comparison("nope"), pcmk__comparison_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_DEFINED), + pcmk__comparison_defined); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_NOT_DEFINED), + pcmk__comparison_undefined); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_EQ), + pcmk__comparison_eq); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_NE), + pcmk__comparison_ne); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_LT), + pcmk__comparison_lt); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_LTE), + pcmk__comparison_lte); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_GT), + pcmk__comparison_gt); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_GTE), + pcmk__comparison_gte); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_comparison("DEFINED"), + pcmk__comparison_defined); + + assert_int_equal(pcmk__parse_comparison("Not_Defined"), + pcmk__comparison_undefined); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_unknown), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_source_test.c b/lib/common/tests/rules/pcmk__parse_source_test.c new file mode 100644 index 0000000..9cf9b32 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_source_test.c @@ -0,0 +1,62 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +default_literal(void **state) +{ + assert_int_equal(pcmk__parse_source(NULL), pcmk__source_literal); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_source(""), pcmk__source_unknown); + assert_int_equal(pcmk__parse_source(" "), pcmk__source_unknown); + assert_int_equal(pcmk__parse_source("params"), pcmk__source_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_source(PCMK_VALUE_LITERAL), + pcmk__source_literal); + + assert_int_equal(pcmk__parse_source(PCMK_VALUE_PARAM), + pcmk__source_instance_attrs); + + assert_int_equal(pcmk__parse_source(PCMK_VALUE_META), + pcmk__source_meta_attrs); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_source("LITERAL"), + pcmk__source_literal); + + assert_int_equal(pcmk__parse_source("Param"), + pcmk__source_instance_attrs); + + assert_int_equal(pcmk__parse_source("MeTa"), + pcmk__source_meta_attrs); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(default_literal), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_type_test.c b/lib/common/tests/rules/pcmk__parse_type_test.c new file mode 100644 index 0000000..96f02c8 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_type_test.c @@ -0,0 +1,127 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_type("nope", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_type(PCMK_VALUE_STRING, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_INTEGER, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_NUMBER, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_VERSION, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_version); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_type("STRING", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type("Integer", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_integer); +} + +static void +default_number(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lt, "1.0", "2.5"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lte, "1.", "2"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gt, "1", ".5"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1.0", "2"), + pcmk__type_number); +} + +static void +default_integer(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lt, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lte, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gt, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, NULL, NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1", NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, NULL, "2.5"), + pcmk__type_number); +} + +static void +default_string(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_defined, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_undefined, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_eq, NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_ne, NULL, NULL), + pcmk__type_string); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive), + cmocka_unit_test(default_number), + cmocka_unit_test(default_integer), + cmocka_unit_test(default_string)) diff --git a/lib/common/tests/rules/pcmk__replace_submatches_test.c b/lib/common/tests/rules/pcmk__replace_submatches_test.c new file mode 100644 index 0000000..d404fcc --- /dev/null +++ b/lib/common/tests/rules/pcmk__replace_submatches_test.c @@ -0,0 +1,81 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <regex.h> // regmatch_t + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +// An example matched string with submatches +static const char *match = "this is a string"; +static const regmatch_t submatches[] = { + { .rm_so = 0, .rm_eo = 16 }, // %0 = entire string + { .rm_so = 5, .rm_eo = 7 }, // %1 = "is" + { .rm_so = 9, .rm_eo = 9 }, // %2 = empty match +}; +static const int nmatches = 3; + +static void +assert_submatch(const char *string, const char *reference) +{ + char *expanded = NULL; + + expanded = pcmk__replace_submatches(string, match, submatches, nmatches); + if ((expanded == NULL) || (reference == NULL)) { + assert_null(expanded); + assert_null(reference); + } else { + assert_int_equal(strcmp(expanded, reference), 0); + } + free(expanded); +} + +static void +no_source(void **state) +{ + assert_null(pcmk__replace_submatches(NULL, NULL, NULL, 0)); + assert_submatch(NULL, NULL); + assert_submatch("", NULL); +} + +static void +source_has_no_variables(void **state) +{ + assert_null(pcmk__replace_submatches("this has no submatch variables", + match, submatches, nmatches)); + assert_null(pcmk__replace_submatches("this ends in a %", + match, submatches, nmatches)); + assert_null(pcmk__replace_submatches("%this starts with one", + match, submatches, nmatches)); +} + +static void +without_matches(void **state) +{ + assert_submatch("this has an empty submatch %2", + "this has an empty submatch "); + assert_submatch("this has a nonexistent submatch %3", + "this has a nonexistent submatch "); +} + +static void +with_matches(void **state) +{ + assert_submatch("%0", match); // %0 matches entire string + assert_submatch("this %1", "this is"); + assert_submatch("%1 this %ok", "is this %ok"); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(no_source), + cmocka_unit_test(source_has_no_variables), + cmocka_unit_test(without_matches), + cmocka_unit_test(with_matches)) diff --git a/lib/common/tests/rules/pcmk__unpack_duration_test.c b/lib/common/tests/rules/pcmk__unpack_duration_test.c new file mode 100644 index 0000000..e82546c --- /dev/null +++ b/lib/common/tests/rules/pcmk__unpack_duration_test.c @@ -0,0 +1,120 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <glib.h> + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/xml.h> +#include "../../crmcommon_private.h" + +#define MONTHS_TO_SECONDS "months=\"2\" weeks=\"3\" days=\"-1\" " \ + "hours=\"1\" minutes=\"1\" seconds=\"1\" />" + +#define ALL_VALID "<duration id=\"duration1\" years=\"1\" " MONTHS_TO_SECONDS + +#define NO_ID "<duration years=\"1\" " MONTHS_TO_SECONDS + +#define YEARS_INVALID "<duration id=\"duration1\" years=\"not-a-number\" " \ + MONTHS_TO_SECONDS + +static void +null_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + + assert_int_equal(pcmk__unpack_duration(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, NULL, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, start, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, NULL, &end), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, start, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, start, &end), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, NULL, &end), EINVAL); + + crm_time_free(start); + free_xml(duration); +} + +static void +nonnull_end_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = crm_time_new("2024-01-01 15:00:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), EINVAL); + + crm_time_free(start); + crm_time_free(end); + free_xml(duration); +} + +static void +no_id(void **state) +{ + xmlNode *duration = pcmk__xml_parse(NO_ID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2025-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), pcmk_rc_ok); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +static void +years_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(YEARS_INVALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2024-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), + pcmk_rc_unpack_error); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +static void +all_valid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2025-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), pcmk_rc_ok); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(nonnull_end_invalid), + cmocka_unit_test(no_id), + cmocka_unit_test(years_invalid), + cmocka_unit_test(all_valid)) diff --git a/lib/common/tests/rules/pcmk_evaluate_rule_test.c b/lib/common/tests/rules/pcmk_evaluate_rule_test.c new file mode 100644 index 0000000..6b6f9eb --- /dev/null +++ b/lib/common/tests/rules/pcmk_evaluate_rule_test.c @@ -0,0 +1,379 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + + +/* + * Test invalid arguments + */ + +#define RULE_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' > " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk_evaluate_rule(NULL, NULL, next_change), + EINVAL); + + xml = pcmk__xml_parse(RULE_OP); + assert_int_equal(pcmk_evaluate_rule(xml, NULL, next_change), EINVAL); + free_xml(xml); + + assert_int_equal(pcmk_evaluate_rule(NULL, &rule_input, next_change), + EINVAL); + + crm_time_free(next_change); +} + +#define RULE_OP_MISSING_ID \ + "<" PCMK_XE_RULE "> " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +id_missing(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_OP_MISSING_ID); + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, next_change), + pcmk_rc_ok); + + crm_time_free(next_change); + free_xml(xml); +} + +#define RULE_IDREF_PARENT "<" PCMK_XE_CIB ">" RULE_OP "</" PCMK_XE_CIB ">" + +static void +good_idref(void **state) +{ + xmlNode *parent_xml = pcmk__xml_parse(RULE_IDREF_PARENT); + xmlNode *rule_xml = pcmk__xe_create(parent_xml, PCMK_XE_RULE); + crm_time_t *next_change = crm_time_new_undefined(); + + crm_xml_add(rule_xml, PCMK_XA_ID_REF, "r"); + assert_int_equal(pcmk_evaluate_rule(rule_xml, &rule_input, next_change), + pcmk_rc_ok); + + crm_time_free(next_change); + free_xml(parent_xml); +} + +static void +bad_idref(void **state) +{ + xmlNode *parent_xml = pcmk__xml_parse(RULE_IDREF_PARENT); + xmlNode *rule_xml = pcmk__xe_create(parent_xml, PCMK_XE_RULE); + crm_time_t *next_change = crm_time_new_undefined(); + + crm_xml_add(rule_xml, PCMK_XA_ID_REF, "x"); + assert_int_equal(pcmk_evaluate_rule(rule_xml, &rule_input, next_change), + pcmk_rc_unpack_error); + + crm_time_free(next_change); + free_xml(parent_xml); +} + +#define RULE_EMPTY "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' />" + +static void +empty_default(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_EMPTY_AND \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' />" + +static void +empty_and(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY_AND); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_EMPTY_OR \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' />" + +static void +empty_or(void **state) +{ + // Currently treated as unsatisfied + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY_OR); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_DEFAULT_BOOLEAN_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +default_boolean_op(void **state) +{ + // Defaults to PCMK_VALUE_AND + xmlNode *xml = pcmk__xml_parse(RULE_DEFAULT_BOOLEAN_OP); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_INVALID_BOOLEAN_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='not-an-op' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +invalid_boolean_op(void **state) +{ + // Currently defaults to PCMK_VALUE_AND + xmlNode *xml = pcmk__xml_parse(RULE_INVALID_BOOLEAN_OP); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_AND_PASSES \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPaddr2' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_PASSES); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_LONELY_AND \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPaddr2' />" \ + "</" PCMK_XE_RULE ">" + +static void +lonely_and_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_LONELY_AND); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_AND_ONE_FAILS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_one_fails(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_ONE_FAILS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_AND_TWO_FAIL \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='9s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_two_fail(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_TWO_FAIL); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_OR_ONE_PASSES \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_one_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_ONE_PASSES); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_OR_TWO_PASS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPAddr2' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_two_pass(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_TWO_PASS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_LONELY_OR \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +lonely_or_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_LONELY_OR); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_OR_FAILS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='20s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_fails(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_FAILS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(good_idref), + cmocka_unit_test(bad_idref), + cmocka_unit_test(empty_default), + cmocka_unit_test(empty_and), + cmocka_unit_test(empty_or), + cmocka_unit_test(default_boolean_op), + cmocka_unit_test(invalid_boolean_op), + cmocka_unit_test(and_passes), + cmocka_unit_test(lonely_and_passes), + cmocka_unit_test(and_one_fails), + cmocka_unit_test(and_two_fail), + cmocka_unit_test(or_one_passes), + cmocka_unit_test(or_two_pass), + cmocka_unit_test(lonely_or_passes), + cmocka_unit_test(or_fails)) diff --git a/lib/common/tests/scheduler/Makefile.am b/lib/common/tests/scheduler/Makefile.am new file mode 100644 index 0000000..6d5f4f8 --- /dev/null +++ b/lib/common/tests/scheduler/Makefile.am @@ -0,0 +1,19 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +# Add "_test" to the end of all test program names to simplify .gitignore. +check_PROGRAMS = pcmk_get_dc_test \ + pcmk_get_no_quorum_policy_test \ + pcmk_has_quorum_test \ + pcmk_set_scheduler_cib_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/scheduler/pcmk_get_dc_test.c b/lib/common/tests/scheduler/pcmk_get_dc_test.c new file mode 100644 index 0000000..5d9d459 --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_get_dc_test.c @@ -0,0 +1,47 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_null(pcmk_get_dc(NULL)); +} + +static void +null_dc(void **state) +{ + pcmk_scheduler_t scheduler = { + .dc_node = NULL, + }; + + assert_null(pcmk_get_dc(&scheduler)); +} + +static void +valid_dc(void **state) +{ + pcmk_node_t dc = { + .weight = 1, + }; + pcmk_scheduler_t scheduler = { + .dc_node = &dc, + }; + + assert_ptr_equal(&dc, pcmk_get_dc(&scheduler)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(null_dc), + cmocka_unit_test(valid_dc)) diff --git a/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c b/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c new file mode 100644 index 0000000..61c97e6 --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c @@ -0,0 +1,34 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_int_equal(pcmk_get_no_quorum_policy(NULL), pcmk_no_quorum_stop); +} + +static void +valid_no_quorum_policy(void **state) +{ + pcmk_scheduler_t scheduler = { + .no_quorum_policy = pcmk_no_quorum_fence, + }; + + assert_int_equal(pcmk_get_no_quorum_policy(&scheduler), + pcmk_no_quorum_fence); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(valid_no_quorum_policy)) diff --git a/lib/common/tests/scheduler/pcmk_has_quorum_test.c b/lib/common/tests/scheduler/pcmk_has_quorum_test.c new file mode 100644 index 0000000..51903df --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_has_quorum_test.c @@ -0,0 +1,36 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_false(pcmk_has_quorum(NULL)); +} + +static void +valid_scheduler(void **state) +{ + pcmk_scheduler_t scheduler = { + .flags = pcmk_sched_quorate, + }; + + assert_true(pcmk_has_quorum(&scheduler)); + + scheduler.flags = pcmk_sched_none; + assert_false(pcmk_has_quorum(&scheduler)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(valid_scheduler)) diff --git a/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c b/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c new file mode 100644 index 0000000..71e690b --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c @@ -0,0 +1,71 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + xmlNode *cib = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk_set_scheduler_cib(NULL, NULL), EINVAL); + assert_int_equal(pcmk_set_scheduler_cib(NULL, cib), EINVAL); + + free_xml(cib); +} + +static void +null_cib(void **state) +{ + pcmk_scheduler_t scheduler = { + .input = NULL, + }; + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, NULL), pcmk_rc_ok); + assert_null(scheduler.input); +} + +static void +previous_cib_null(void **state) +{ + pcmk_scheduler_t scheduler = { + .input = NULL, + }; + xmlNode *cib = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, cib), pcmk_rc_ok); + assert_ptr_equal(scheduler.input, cib); + + free_xml(cib); +} + +static void +previous_cib_nonnull(void **state) +{ + xmlNode *old_cib = pcmk__xe_create(NULL, "old"); + xmlNode *new_cib = pcmk__xe_create(NULL, "new"); + pcmk_scheduler_t scheduler = { + .input = old_cib, + }; + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, new_cib), pcmk_rc_ok); + assert_ptr_equal(scheduler.input, new_cib); + + free_xml(old_cib); + free_xml(new_cib); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(null_cib), + cmocka_unit_test(previous_cib_null), + cmocka_unit_test(previous_cib_nonnull)) diff --git a/lib/common/tests/schemas/Makefile.am b/lib/common/tests/schemas/Makefile.am new file mode 100644 index 0000000..ba0f805 --- /dev/null +++ b/lib/common/tests/schemas/Makefile.am @@ -0,0 +1,88 @@ +# +# Copyright 2023-2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +CFLAGS += -DPCMK__TEST_SCHEMA_DIR='"$(abs_builddir)/schemas"' + +# Add "_test" to the end of all test program names to simplify .gitignore. + +# These tests share a schema subdirectory +SHARED_SCHEMA_TESTS = pcmk__cmp_schemas_by_name_test \ + crm_schema_init_test \ + pcmk__build_schema_xml_node_test \ + pcmk__get_schema_test \ + pcmk__schema_files_later_than_test + +# This test has its own schema directory +FIND_X_0_SCHEMA_TEST = pcmk__find_x_0_schema_test + +check_PROGRAMS = $(SHARED_SCHEMA_TESTS) $(FIND_X_0_SCHEMA_TEST) + +TESTS = $(check_PROGRAMS) + +$(SHARED_SCHEMA_TESTS): setup-schema-dir + +$(FIND_X_0_SCHEMA_TEST): setup-find_x_0-schema-dir + +# Set up a temporary schemas/ directory containing only some of the full set of +# pacemaker schema files. This lets us know exactly how many schemas are present, +# allowing us to write tests without having to make changes when new schemas are +# added. +# +# This directory contains the following: +# +# * pacemaker-next.rng - Used to verify that this sorts before all versions +# * upgrade-*.xsl - Required by various schema versions +# * pacemaker-[0-9]*.rng - We're only pulling in 15 schemas, which is enough +# to get everything through pacemaker-3.0.rng. This +# includes 2.10, needed so we can check that versions +# are compared as numbers instead of strings. +# * other RNG files - This catches everything except the pacemaker-*rng +# files. These files are included by the top-level +# pacemaker-*rng files, so we need them for tests. +# This will glob more than we need, but the extra ones +# won't get in the way. + +LINK_FILES = $(abs_top_builddir)/xml/pacemaker-next.rng \ + $(abs_top_builddir)/xml/upgrade-*.xsl +ROOT_RNGS = $(shell ls -1v $(abs_top_builddir)/xml/pacemaker-[0-9]*.rng | head -15) +INCLUDED_RNGS = $(shell ls -1 $(top_srcdir)/xml/*.rng | grep -v pacemaker-[0-9]) + +# Most tests share a common, read-only schema directory +.PHONY: setup-schema-dir +setup-schema-dir: + $(MKDIR_P) schemas + ( cd schemas ; \ + ln -sf $(LINK_FILES) . ; \ + for f in $(ROOT_RNGS); do \ + ln -sf $$f $$(basename $$f); \ + done ; \ + for f in $(INCLUDED_RNGS); do \ + ln -sf ../$$f $$(basename $$f); \ + done ) + +# pcmk__find_x_0_schema_test moves schema files around, so it needs its +# own directory, otherwise other tests run in parallel could fail. +.PHONY: setup-find_x_0-schema-dir +setup-find_x_0-schema-dir: + $(MKDIR_P) schemas/find_x_0 + ( cd schemas/find_x_0 ; \ + ln -sf $(LINK_FILES) . ; \ + for f in $(ROOT_RNGS); do \ + ln -sf $$f $$(basename $$f); \ + done ; \ + for f in $(INCLUDED_RNGS); do \ + ln -sf ../$$f $$(basename $$f); \ + done ) + +.PHONY: clean-local +clean-local: + -rm -rf schemas diff --git a/lib/common/tests/schemas/crm_schema_init_test.c b/lib/common/tests/schemas/crm_schema_init_test.c new file mode 100644 index 0000000..8da79cb --- /dev/null +++ b/lib/common/tests/schemas/crm_schema_init_test.c @@ -0,0 +1,152 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <ftw.h> +#include <unistd.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static char *remote_schema_dir = NULL; + +static int +symlink_schema(const char *tmpdir, const char *target_file, const char *link_file) +{ + int rc = 0; + char *oldpath = NULL; + char *newpath = NULL; + + oldpath = crm_strdup_printf("%s/%s", PCMK__TEST_SCHEMA_DIR, target_file); + newpath = crm_strdup_printf("%s/%s", tmpdir, link_file); + + rc = symlink(oldpath, newpath); + + free(oldpath); + free(newpath); + return rc; +} + +static int +rm_files(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) +{ + return remove(pathname); +} + +static int +rmtree(const char *dir) +{ + return nftw(dir, rm_files, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS); +} + +static int +setup(void **state) +{ + char *dir = NULL; + + /* Create a directory to hold additional schema files. These don't need + * to be anything special - we can just copy existing schemas but give + * them new names. + */ + dir = crm_strdup_printf("%s/test-schemas.XXXXXX", pcmk__get_tmpdir()); + remote_schema_dir = mkdtemp(dir); + + if (remote_schema_dir == NULL) { + free(dir); + return -1; + } + + /* Add new files to simulate a remote node not being up-to-date. We can't + * add a new major version here without also creating an XSL transform, and + * we can't add an older version (like 1.1 or 2.11 or something) because + * remotes will only ever ask for stuff newer than their newest. + */ + if (symlink_schema(dir, "pacemaker-3.0.rng", "pacemaker-3.1.rng") != 0) { + rmdir(dir); + free(dir); + return -1; + } + + if (symlink_schema(dir, "pacemaker-3.0.rng", "pacemaker-3.2.rng") != 0) { + rmdir(dir); + free(dir); + return -1; + } + + setenv("PCMK_remote_schema_directory", remote_schema_dir, 1); + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + + /* Do not call crm_schema_init here because that is the function we're + * testing. It needs to be called in each unit test. However, we can + * call crm_schema_cleanup in teardown(). + */ + + return 0; +} + +static int +teardown(void **state) +{ + int rc = 0; + char *f = NULL; + + crm_schema_cleanup(); + unsetenv("PCMK_remote_schema_directory"); + unsetenv("PCMK_schema_directory"); + + rc = rmtree(remote_schema_dir); + + free(remote_schema_dir); + free(f); + return rc; +} + +static void +assert_schema(const char *schema_name, int schema_index) +{ + GList *entry = NULL; + pcmk__schema_t *schema = NULL; + + entry = pcmk__get_schema(schema_name); + assert_non_null(entry); + + schema = entry->data; + assert_non_null(schema); + + assert_int_equal(schema_index, schema->schema_index); +} + +static void +extra_schema_files(void **state) +{ + crm_schema_init(); + + /* Just iterate through the list of schemas and make sure everything + * (including the new schemas we loaded from a second directory) is in + * the right order. + */ + assert_schema("pacemaker-1.0", 0); + assert_schema("pacemaker-1.2", 1); + assert_schema("pacemaker-2.0", 3); + assert_schema("pacemaker-3.0", 14); + assert_schema("pacemaker-3.1", 15); + assert_schema("pacemaker-3.2", 16); + + // @COMPAT pacemaker-next is deprecated since 2.1.5 + assert_schema("pacemaker-next", 17); + + // @COMPAT none is deprecated since 2.1.8 + assert_schema(PCMK_VALUE_NONE, 18); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(extra_schema_files)); diff --git a/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c b/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c new file mode 100644 index 0000000..e4454e2 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c @@ -0,0 +1,158 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/lists_internal.h> + +#include <glib.h> + +const char *rngs1[] = { "pacemaker-3.0.rng", "status-1.0.rng", "alerts-2.10.rng", + "nvset-2.9.rng", "score.rng", "rule-2.9.rng", + "tags-1.3.rng", "acls-2.0.rng", "fencing-2.4.rng", + "constraints-3.0.rng", "resources-3.0.rng", "nvset-3.0.rng", + "nodes-3.0.rng", "options-3.0.rng", NULL }; + +const char *rngs2[] = { "pacemaker-2.0.rng", "status-1.0.rng", "tags-1.3.rng", + "acls-2.0.rng", "fencing-1.2.rng", "constraints-1.2.rng", + "rule.rng", "score.rng", "resources-1.3.rng", + "nvset-1.3.rng", "nodes-1.3.rng", "options-1.0.rng", + "nvset.rng", "cib-1.2.rng", NULL }; + +const char *rngs3[] = { "pacemaker-2.1.rng", "constraints-2.1.rng", NULL }; + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + pcmk__xml_test_setup_group(state); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +invalid_name(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + + pcmk__build_schema_xml_node(parent, "pacemaker-9.0", &already_included); + assert_null(parent->children); + assert_null(already_included); + free_xml(parent); +} + +static void +single_schema(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + xmlNode *schema_node = NULL; + xmlNode *file_node = NULL; + int i = 0; + + pcmk__build_schema_xml_node(parent, "pacemaker-3.0", &already_included); + + assert_non_null(already_included); + assert_non_null(parent->children); + + /* Test that the result looks like this: + * + * <schemas> + * <schema version="pacemaker-3.0"> + * <file path="pacemaker-3.0.rng">CDATA</file> + * <file path="status-1.0.rng">CDATA</file> + * ... + * </schema> + * </schemas> + */ + schema_node = pcmk__xe_first_child(parent, NULL, NULL, NULL); + assert_string_equal("pacemaker-3.0", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + while (file_node != NULL && rngs1[i] != NULL) { + assert_string_equal(rngs1[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + g_list_free_full(already_included, free); + free_xml(parent); +} + +static void +multiple_schemas(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + xmlNode *schema_node = NULL; + xmlNode *file_node = NULL; + int i = 0; + + pcmk__build_schema_xml_node(parent, "pacemaker-2.0", &already_included); + pcmk__build_schema_xml_node(parent, "pacemaker-2.1", &already_included); + + assert_non_null(already_included); + assert_non_null(parent->children); + + /* Like single_schema, but make sure files aren't included multiple times + * when the function is called repeatedly. + */ + schema_node = pcmk__xe_first_child(parent, NULL, NULL, NULL); + assert_string_equal("pacemaker-2.0", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + while (file_node != NULL && rngs2[i] != NULL) { + assert_string_equal(rngs2[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + schema_node = pcmk__xe_next(schema_node); + assert_string_equal("pacemaker-2.1", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + i = 0; + + while (file_node != NULL && rngs3[i] != NULL) { + assert_string_equal(rngs3[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + g_list_free_full(already_included, free); + free_xml(parent); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(invalid_name), + cmocka_unit_test(single_schema), + cmocka_unit_test(multiple_schemas)) diff --git a/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c b/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c new file mode 100644 index 0000000..19ec743 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c @@ -0,0 +1,121 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +// NULL schema name defaults to the "none" schema +// @COMPAT none is deprecated since 2.1.8 + +static void +unknown_is_lesser(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.1", + "pacemaker-0.2") == 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.1", + "pacemaker-1.0") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.0", + "pacemaker-0.1") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.1", NULL) < 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, "pacemaker-0.0") > 0); + + /* @COMPAT pacemaker-next is deprecated since 2.1.5, + * and pacemaker-0.6 and pacemaker-0.7 since 2.1.8 + */ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.6", + "pacemaker-next") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-0.7") > 0); +} + +// @COMPAT none is deprecated since 2.1.8 +static void +none_is_greater(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name(NULL, NULL) == 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, PCMK_VALUE_NONE) == 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, NULL) == 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + PCMK_VALUE_NONE) == 0); + + assert_true(pcmk__cmp_schemas_by_name("pacemaker-3.0", + PCMK_VALUE_NONE) < 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + "pacemaker-1.0") > 0); + + // @COMPAT pacemaker-next is deprecated since 2.1.5 + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + PCMK_VALUE_NONE) < 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + "pacemaker-next") > 0); +} + +// @COMPAT pacemaker-next is deprecated since 2.1.5 +// @COMPAT none is deprecated since 2.1.8 +static void +next_is_before_none(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-next") == 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, "pacemaker-next") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", NULL) < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-3.0", + "pacemaker-next") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-1.0") > 0); +} + +static void +known_numeric(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.0", + "pacemaker-1.0") == 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.2", + "pacemaker-1.0") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.2", + "pacemaker-2.0") < 0); +} + +static void +case_insensitive(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("Pacemaker-1.0", + "pacemaker-1.0") == 0); + assert_true(pcmk__cmp_schemas_by_name("PACEMAKER-1.2", + "pacemaker-1.0") > 0); + assert_true(pcmk__cmp_schemas_by_name("PaceMaker-1.2", + "pacemaker-2.0") < 0); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(unknown_is_lesser), + cmocka_unit_test(none_is_greater), + cmocka_unit_test(next_is_before_none), + cmocka_unit_test(known_numeric), + cmocka_unit_test(case_insensitive)); diff --git a/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c b/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c new file mode 100644 index 0000000..25ba0f3 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c @@ -0,0 +1,100 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> // NULL, rename() +#include <stdlib.h> // setenv(), unsetenv() +#include <glib.h> + +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +#define SCHEMA_PREFIX PCMK__TEST_SCHEMA_DIR "/find_x_0/pacemaker-" + +static int +setup(void **state) +{ + // Use a unique schema directory so we can move files around + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR "/find_x_0", 1); + return 0; +} + +static int +teardown(void **state) +{ + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +assert_schema_0(int schema_index, const char *schema_name) +{ + GList *entry = NULL; + pcmk__schema_t *schema = NULL; + + entry = pcmk__find_x_0_schema(); + assert_non_null(entry); + + schema = entry->data; + assert_non_null(schema); + + assert_int_equal(schema->schema_index, schema_index); + assert_string_equal(schema->name, schema_name); +} + +static void +last_is_0(void **state) +{ + /* This loads all the schemas normally linked for unit testing, so we have + * many 1.x and 2.x schemas and a single pacemaker-3.0 schema at index 14. + */ + crm_schema_init(); + assert_schema_0(14, "pacemaker-3.0"); + crm_schema_cleanup(); +} + +static void +last_is_not_0(void **state) +{ + /* Disable the pacemaker-3.0 schema, so we now should get pacemaker-2.0 at + * index 3. + */ + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.rng", + SCHEMA_PREFIX "3.0.bak")); + crm_schema_init(); + assert_schema_0(3, "pacemaker-2.0"); + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.bak", + SCHEMA_PREFIX "3.0.rng")); + crm_schema_cleanup(); +} + +static void +schema_0_missing(void **state) +{ + /* Disable the pacemaker-3.0 and pacemaker-2.0 schemas, so we now should get + * pacemaker-2.1 at index 3. + */ + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.rng", + SCHEMA_PREFIX "3.0.bak")); + assert_int_equal(0, rename(SCHEMA_PREFIX "2.0.rng", + SCHEMA_PREFIX "2.0.bak")); + crm_schema_init(); + assert_schema_0(3, "pacemaker-2.1"); + assert_int_equal(0, rename(SCHEMA_PREFIX "2.0.bak", + SCHEMA_PREFIX "2.0.rng")); + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.bak", + SCHEMA_PREFIX "3.0.rng")); + crm_schema_cleanup(); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(last_is_0), + cmocka_unit_test(last_is_not_0), + cmocka_unit_test(schema_0_missing)) diff --git a/lib/common/tests/schemas/pcmk__get_schema_test.c b/lib/common/tests/schemas/pcmk__get_schema_test.c new file mode 100644 index 0000000..6513dfc --- /dev/null +++ b/lib/common/tests/schemas/pcmk__get_schema_test.c @@ -0,0 +1,81 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +assert_schema(const char *name, int expected_index) +{ + GList *schema_entry = NULL; + pcmk__schema_t *schema = NULL; + + schema_entry = pcmk__get_schema(name); + assert_non_null(schema_entry); + + schema = schema_entry->data; + assert_non_null(schema); + + assert_int_equal(schema->schema_index, expected_index); +} + +static void +unknown_schema(void **state) +{ + assert_null(pcmk__get_schema("")); + assert_null(pcmk__get_schema("blahblah")); + assert_null(pcmk__get_schema("pacemaker-2.47")); + assert_null(pcmk__get_schema("pacemaker-47.0")); +} + +static void +known_schema(void **state) +{ + // @COMPAT none is deprecated since 2.1.8 + assert_schema(NULL, 16); // defaults to "none" + + assert_schema("pacemaker-1.0", 0); + assert_schema("pacemaker-1.2", 1); + assert_schema("pacemaker-2.0", 3); + assert_schema("pacemaker-2.5", 8); + assert_schema("pacemaker-3.0", 14); +} + +static void +case_insensitive(void **state) +{ + assert_schema("PACEMAKER-1.0", 0); + assert_schema("pAcEmAkEr-2.0", 3); + assert_schema("paceMAKER-3.0", 14); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(unknown_schema), + cmocka_unit_test(known_schema), + cmocka_unit_test(case_insensitive)); diff --git a/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c b/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c new file mode 100644 index 0000000..68744ef --- /dev/null +++ b/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c @@ -0,0 +1,106 @@ +/* + * Copyright 2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> +#include <crm/common/lists_internal.h> + +#include <glib.h> + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +invalid_name(void **state) +{ + assert_null(pcmk__schema_files_later_than("xyz")); + assert_null(pcmk__schema_files_later_than("pacemaker-")); +} + +static void +valid_name(void **state) +{ + GList *schemas = NULL; + + schemas = pcmk__schema_files_later_than("pacemaker-1.0"); + assert_int_equal(g_list_length(schemas), 18); + /* There is no "pacemaker-1.1". */ + assert_string_equal("pacemaker-1.2.rng", g_list_nth_data(schemas, 0)); + assert_string_equal("upgrade-1.3.xsl", g_list_nth_data(schemas, 1)); + assert_string_equal("pacemaker-1.3.rng", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.0.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-2.1.rng", g_list_nth_data(schemas, 4)); + assert_string_equal("pacemaker-2.2.rng", g_list_nth_data(schemas, 5)); + assert_string_equal("pacemaker-2.3.rng", g_list_nth_data(schemas, 6)); + assert_string_equal("pacemaker-2.4.rng", g_list_nth_data(schemas, 7)); + assert_string_equal("pacemaker-2.5.rng", g_list_nth_data(schemas, 8)); + assert_string_equal("pacemaker-2.6.rng", g_list_nth_data(schemas, 9)); + assert_string_equal("pacemaker-2.7.rng", g_list_nth_data(schemas, 10)); + assert_string_equal("pacemaker-2.8.rng", g_list_nth_data(schemas, 11)); + assert_string_equal("pacemaker-2.9.rng", g_list_nth_data(schemas, 12)); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 13)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 14)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 15)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 16)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 17)); + g_list_free_full(schemas, free); + + /* Adding .rng to the end of the schema we're requesting is also valid. */ + schemas = pcmk__schema_files_later_than("pacemaker-2.0.rng"); + assert_int_equal(g_list_length(schemas), 14); + assert_string_equal("pacemaker-2.1.rng", g_list_nth_data(schemas, 0)); + assert_string_equal("pacemaker-2.2.rng", g_list_nth_data(schemas, 1)); + assert_string_equal("pacemaker-2.3.rng", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.4.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-2.5.rng", g_list_nth_data(schemas, 4)); + assert_string_equal("pacemaker-2.6.rng", g_list_nth_data(schemas, 5)); + assert_string_equal("pacemaker-2.7.rng", g_list_nth_data(schemas, 6)); + assert_string_equal("pacemaker-2.8.rng", g_list_nth_data(schemas, 7)); + assert_string_equal("pacemaker-2.9.rng", g_list_nth_data(schemas, 8)); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 9)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 10)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 11)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 12)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 13)); + g_list_free_full(schemas, free); + + /* Check that "pacemaker-2.10" counts as later than "pacemaker-2.9". */ + schemas = pcmk__schema_files_later_than("pacemaker-2.9"); + assert_int_equal(g_list_length(schemas), 5); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 0)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 1)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 4)); + g_list_free_full(schemas, free); + + /* And then something way in the future that will never apply due to our + * special schema directory. + */ + schemas = pcmk__schema_files_later_than("pacemaker-9.0"); + assert_null(schemas); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(invalid_name), + cmocka_unit_test(valid_name)) diff --git a/lib/common/tests/scores/char2score_test.c b/lib/common/tests/scores/char2score_test.c index fbba12a..5d7252f 100644 --- a/lib/common/tests/scores/char2score_test.c +++ b/lib/common/tests/scores/char2score_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -32,9 +32,9 @@ bad_input(void **state) static void special_values(void **state) { - assert_int_equal(char2score("-INFINITY"), -CRM_SCORE_INFINITY); - assert_int_equal(char2score("INFINITY"), CRM_SCORE_INFINITY); - assert_int_equal(char2score("+INFINITY"), CRM_SCORE_INFINITY); + assert_int_equal(char2score("-INFINITY"), -PCMK_SCORE_INFINITY); + assert_int_equal(char2score("INFINITY"), PCMK_SCORE_INFINITY); + assert_int_equal(char2score("+INFINITY"), PCMK_SCORE_INFINITY); pcmk__score_red = 10; pcmk__score_green = 20; @@ -56,8 +56,10 @@ special_values(void **state) static void outside_limits(void **state) { - assert_int_equal(char2score(B(CRM_SCORE_INFINITY) "00"), CRM_SCORE_INFINITY); - assert_int_equal(char2score("-" B(CRM_SCORE_INFINITY) "00"), -CRM_SCORE_INFINITY); + assert_int_equal(char2score(B(PCMK_SCORE_INFINITY) "00"), + PCMK_SCORE_INFINITY); + assert_int_equal(char2score("-" B(PCMK_SCORE_INFINITY) "00"), + -PCMK_SCORE_INFINITY); } static void diff --git a/lib/common/tests/scores/pcmk__add_scores_test.c b/lib/common/tests/scores/pcmk__add_scores_test.c index 1309659..952cf97 100644 --- a/lib/common/tests/scores/pcmk__add_scores_test.c +++ b/lib/common/tests/scores/pcmk__add_scores_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,48 +14,71 @@ static void score1_minus_inf(void **state) { - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, -1), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, 0), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, 1), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, + -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, -1), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, 0), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, 1), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, + PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); } static void score2_minus_inf(void **state) { - assert_int_equal(pcmk__add_scores(-1, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(0, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(1, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-1, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(0, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(1, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, + -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); } static void score1_pos_inf(void **state) { - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, -1), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, 0), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, 1), CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, -1), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, 0), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, 1), + PCMK_SCORE_INFINITY); } static void score2_pos_inf(void **state) { - assert_int_equal(pcmk__add_scores(-1, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(0, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(1, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-1, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(0, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(1, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); } static void result_infinite(void **state) { - assert_int_equal(pcmk__add_scores(INT_MAX, INT_MAX), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(INT_MIN, INT_MIN), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(2000000, 50), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY/2, CRM_SCORE_INFINITY/2), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY/2, -CRM_SCORE_INFINITY/2), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-4000000, 50), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(INT_MAX, INT_MAX), PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(INT_MIN, INT_MIN), -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(2000000, 50), PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY/2, + PCMK_SCORE_INFINITY/2), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY/2, + -PCMK_SCORE_INFINITY/2), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-4000000, 50), -PCMK_SCORE_INFINITY); } static void diff --git a/lib/common/tests/scores/pcmk_readable_score_test.c b/lib/common/tests/scores/pcmk_readable_score_test.c index ae24159..c3d66f6 100644 --- a/lib/common/tests/scores/pcmk_readable_score_test.c +++ b/lib/common/tests/scores/pcmk_readable_score_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,10 +14,10 @@ static void outside_limits(void **state) { - assert_string_equal(pcmk_readable_score(CRM_SCORE_INFINITY * 2), - CRM_INFINITY_S); - assert_string_equal(pcmk_readable_score(-CRM_SCORE_INFINITY * 2), - CRM_MINUS_INFINITY_S); + assert_string_equal(pcmk_readable_score(PCMK_SCORE_INFINITY * 2), + PCMK_VALUE_INFINITY); + assert_string_equal(pcmk_readable_score(-PCMK_SCORE_INFINITY * 2), + PCMK_VALUE_MINUS_INFINITY); } static void diff --git a/lib/common/tests/strings/Makefile.am b/lib/common/tests/strings/Makefile.am index e66af0d..439b6bf 100644 --- a/lib/common/tests/strings/Makefile.am +++ b/lib/common/tests/strings/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -16,7 +16,6 @@ check_PROGRAMS = crm_get_msec_test \ crm_str_to_boolean_test \ pcmk__add_word_test \ pcmk__btoa_test \ - pcmk__char_in_any_str_test \ pcmk__compress_test \ pcmk__ends_with_test \ pcmk__g_strcat_test \ diff --git a/lib/common/tests/strings/crm_get_msec_test.c b/lib/common/tests/strings/crm_get_msec_test.c index 5da548b..14b87cf 100644 --- a/lib/common/tests/strings/crm_get_msec_test.c +++ b/lib/common/tests/strings/crm_get_msec_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,6 +19,11 @@ bad_input(void **state) { assert_int_equal(crm_get_msec("100xs"), PCMK__PARSE_INT_DEFAULT); assert_int_equal(crm_get_msec(" 100 xs "), PCMK__PARSE_INT_DEFAULT); assert_int_equal(crm_get_msec("-100ms"), PCMK__PARSE_INT_DEFAULT); + + assert_int_equal(crm_get_msec("3.xs"), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec(" 3. xs "), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec("3.14xs"), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec(" 3.14 xs "), PCMK__PARSE_INT_DEFAULT); } static void @@ -28,6 +33,7 @@ good_input(void **state) { assert_int_equal(crm_get_msec("\t100\n"), 100000); assert_int_equal(crm_get_msec("100ms"), 100); + assert_int_equal(crm_get_msec(" 100 ms "), 100); assert_int_equal(crm_get_msec("100 MSEC"), 100); assert_int_equal(crm_get_msec("1000US"), 1); assert_int_equal(crm_get_msec("1000usec"), 1); @@ -37,6 +43,28 @@ good_input(void **state) { assert_int_equal(crm_get_msec("13 min"), 780000); assert_int_equal(crm_get_msec("2\th"), 7200000); assert_int_equal(crm_get_msec("1 hr"), 3600000); + + assert_int_equal(crm_get_msec("3."), 3000); + assert_int_equal(crm_get_msec(" 3. ms "), 3); + assert_int_equal(crm_get_msec("3.14"), 3000); + assert_int_equal(crm_get_msec(" 3.14 ms "), 3); + + // Questionable + assert_int_equal(crm_get_msec("3.14."), 3000); + assert_int_equal(crm_get_msec(" 3.14. ms "), 3); + assert_int_equal(crm_get_msec("3.14.159"), 3000); + assert_int_equal(crm_get_msec(" 3.14.159 "), 3000); + assert_int_equal(crm_get_msec("3.14.159ms"), 3); + assert_int_equal(crm_get_msec(" 3.14.159 ms "), 3); + + // Questionable + assert_int_equal(crm_get_msec(" 100 mshr "), 100); + assert_int_equal(crm_get_msec(" 100 ms hr "), 100); + assert_int_equal(crm_get_msec(" 100 sasdf "), 100000); + assert_int_equal(crm_get_msec(" 100 s asdf "), 100000); + assert_int_equal(crm_get_msec(" 3.14 shour "), 3000); + assert_int_equal(crm_get_msec(" 3.14 s hour "), 3000); + assert_int_equal(crm_get_msec(" 3.14 ms!@#$ "), 3); } static void diff --git a/lib/common/tests/strings/crm_str_to_boolean_test.c b/lib/common/tests/strings/crm_str_to_boolean_test.c index 3bd2e5d..1b0ba45 100644 --- a/lib/common/tests/strings/crm_str_to_boolean_test.c +++ b/lib/common/tests/strings/crm_str_to_boolean_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -40,6 +40,13 @@ is_true(void **state) { assert_true(ret); assert_int_equal(crm_str_to_boolean("1", &ret), 1); assert_true(ret); + + // Ensure it still validates the string with a NULL result argument + assert_int_equal(crm_str_to_boolean("true", NULL), 1); + assert_int_equal(crm_str_to_boolean("on", NULL), 1); + assert_int_equal(crm_str_to_boolean("yes", NULL), 1); + assert_int_equal(crm_str_to_boolean("y", NULL), 1); + assert_int_equal(crm_str_to_boolean("1", NULL), 1); } static void @@ -73,6 +80,13 @@ is_false(void **state) { assert_false(ret); assert_int_equal(crm_str_to_boolean("0", &ret), 1); assert_false(ret); + + // Ensure it still validates the string with a NULL result argument + assert_int_equal(crm_str_to_boolean("false", NULL), 1); + assert_int_equal(crm_str_to_boolean("off", NULL), 1); + assert_int_equal(crm_str_to_boolean("no", NULL), 1); + assert_int_equal(crm_str_to_boolean("n", NULL), 1); + assert_int_equal(crm_str_to_boolean("0", NULL), 1); } static void diff --git a/lib/common/tests/strings/pcmk__char_in_any_str_test.c b/lib/common/tests/strings/pcmk__char_in_any_str_test.c deleted file mode 100644 index e70dfb4..0000000 --- a/lib/common/tests/strings/pcmk__char_in_any_str_test.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2020-2021 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -static void -empty_list(void **state) -{ - assert_false(pcmk__char_in_any_str('x', NULL)); - assert_false(pcmk__char_in_any_str('\0', NULL)); -} - -static void -null_char(void **state) -{ - assert_true(pcmk__char_in_any_str('\0', "xxx", "yyy", NULL)); - assert_true(pcmk__char_in_any_str('\0', "", NULL)); -} - -static void -in_list(void **state) -{ - assert_true(pcmk__char_in_any_str('x', "aaa", "bbb", "xxx", NULL)); -} - -static void -not_in_list(void **state) -{ - assert_false(pcmk__char_in_any_str('x', "aaa", "bbb", NULL)); - assert_false(pcmk__char_in_any_str('A', "aaa", "bbb", NULL)); - assert_false(pcmk__char_in_any_str('x', "", NULL)); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(empty_list), - cmocka_unit_test(null_char), - cmocka_unit_test(in_list), - cmocka_unit_test(not_in_list)) diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c index 7b59d9d..813bcdb 100644 --- a/lib/common/tests/strings/pcmk__compress_test.c +++ b/lib/common/tests/strings/pcmk__compress_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,7 +20,7 @@ const char *SIMPLE_COMPRESSED = "BZh41AY&SYO\x1ai"; static void simple_compress(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok); @@ -30,7 +30,7 @@ simple_compress(void **state) static void max_too_small(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), EFBIG); @@ -38,10 +38,11 @@ max_too_small(void **state) static void calloc_fails(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; - pcmk__assert_asserts( + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_calloc = true; // calloc() will return NULL expect_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601)); diff --git a/lib/common/tests/strings/pcmk__str_update_test.c b/lib/common/tests/strings/pcmk__str_update_test.c index 571031d..4a44fba 100644 --- a/lib/common/tests/strings/pcmk__str_update_test.c +++ b/lib/common/tests/strings/pcmk__str_update_test.c @@ -59,7 +59,8 @@ strdup_fails(void **state) { str = strdup("hello"); - pcmk__assert_asserts( + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_strdup = true; // strdup() will return NULL expect_string(__wrap_strdup, s, "world"); diff --git a/lib/common/tests/utils/Makefile.am b/lib/common/tests/utils/Makefile.am index f028ce4..fb9d5c3 100644 --- a/lib/common/tests/utils/Makefile.am +++ b/lib/common/tests/utils/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -12,8 +12,6 @@ include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. check_PROGRAMS = compare_version_test \ - crm_meta_name_test \ - crm_meta_value_test \ crm_user_lookup_test \ pcmk_daemon_user_test \ pcmk_str_is_infinity_test \ @@ -21,10 +19,7 @@ check_PROGRAMS = compare_version_test \ pcmk__fail_attr_name_test \ pcmk__failcount_name_test \ pcmk__getpid_s_test \ - pcmk__lastfailure_name_test - -if WRAPPABLE_UNAME -check_PROGRAMS += pcmk_hostname_test -endif + pcmk__lastfailure_name_test \ + pcmk__realloc_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/utils/compare_version_test.c b/lib/common/tests/utils/compare_version_test.c index 35ebb63..d191f4a 100644 --- a/lib/common/tests/utils/compare_version_test.c +++ b/lib/common/tests/utils/compare_version_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -46,6 +46,9 @@ shorter_versions(void **state) { assert_int_equal(compare_version("1.0", "1.0.1"), -1); assert_int_equal(compare_version("1.0.1", "1.0"), 1); + assert_int_equal(compare_version("1.0", "1"), 0); + assert_int_equal(compare_version("1", "1.2"), -1); + assert_int_equal(compare_version("1.2", "1"), 1); } PCMK__UNIT_TEST(NULL, NULL, diff --git a/lib/common/tests/utils/pcmk__realloc_test.c b/lib/common/tests/utils/pcmk__realloc_test.c new file mode 100644 index 0000000..62d51df --- /dev/null +++ b/lib/common/tests/utils/pcmk__realloc_test.c @@ -0,0 +1,69 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +#include "mock_private.h" + +static void +bad_size(void **state) +{ + char *ptr = NULL; + + pcmk__assert_asserts(pcmk__realloc(ptr, 0)); +} + +static void +realloc_fails(void **state) +{ + char *ptr = NULL; + + pcmk__assert_aborts( + { + pcmk__mock_realloc = true; // realloc() will return NULL + expect_any(__wrap_realloc, ptr); + expect_value(__wrap_realloc, size, 1000); + pcmk__realloc(ptr, 1000); + pcmk__mock_realloc = false; // Use real realloc() + } + ); +} + +static void +realloc_succeeds(void **state) +{ + char *ptr = NULL; + + /* We can't really test that the resulting pointer is the size we asked + * for - it might be larger if that's what the memory allocator decides + * to do. And anyway, testing realloc isn't really the point. All we + * want to do here is make sure the function works when given good input. + */ + + /* Allocate new memory */ + ptr = pcmk__realloc(ptr, 1000); + assert_non_null(ptr); + + /* Grow previously allocated memory */ + ptr = pcmk__realloc(ptr, 2000); + assert_non_null(ptr); + + /* Shrink previously allocated memory */ + ptr = pcmk__realloc(ptr, 500); + assert_non_null(ptr); + + free(ptr); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(bad_size), + cmocka_unit_test(realloc_fails), + cmocka_unit_test(realloc_succeeds)) diff --git a/lib/common/tests/utils/pcmk_hostname_test.c b/lib/common/tests/utils/pcmk_hostname_test.c deleted file mode 100644 index 7329486..0000000 --- a/lib/common/tests/utils/pcmk_hostname_test.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2021 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> - -#include "mock_private.h" - -#include <sys/utsname.h> - -static void -uname_succeeded_test(void **state) -{ - char *retval; - - // Set uname() return value and buf parameter node name - pcmk__mock_uname = true; - - expect_any(__wrap_uname, buf); - will_return(__wrap_uname, 0); - will_return(__wrap_uname, "somename"); - - retval = pcmk_hostname(); - assert_non_null(retval); - assert_string_equal("somename", retval); - - free(retval); - - pcmk__mock_uname = false; -} - -static void -uname_failed_test(void **state) -{ - // Set uname() return value and buf parameter node name - pcmk__mock_uname = true; - - expect_any(__wrap_uname, buf); - will_return(__wrap_uname, -1); - will_return(__wrap_uname, NULL); - - assert_null(pcmk_hostname()); - - pcmk__mock_uname = false; -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(uname_succeeded_test), - cmocka_unit_test(uname_failed_test)) diff --git a/lib/common/tests/xml/Makefile.am b/lib/common/tests/xml/Makefile.am index 465c950..9ed1620 100644 --- a/lib/common/tests/xml/Makefile.am +++ b/lib/common/tests/xml/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2022-2023 the Pacemaker project contributors +# Copyright 2022-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,7 +11,12 @@ include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pcmk__xe_foreach_child_test \ - pcmk__xe_match_test +check_PROGRAMS = crm_xml_init_test \ + pcmk__xe_copy_attrs_test \ + pcmk__xe_first_child_test \ + pcmk__xe_foreach_child_test \ + pcmk__xe_set_score_test \ + pcmk__xml_escape_test \ + pcmk__xml_needs_escape_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/xml/crm_xml_init_test.c b/lib/common/tests/xml/crm_xml_init_test.c new file mode 100644 index 0000000..1f78728 --- /dev/null +++ b/lib/common/tests/xml/crm_xml_init_test.c @@ -0,0 +1,230 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +#include "crmcommon_private.h" + +/* Copied from lib/common/xml.c */ +#define XML_DOC_PRIVATE_MAGIC 0x81726354UL +#define XML_NODE_PRIVATE_MAGIC 0x54637281UL + +static int +setup(void **state) { + crm_xml_init(); + return 0; +} + +static int +teardown(void **state) { + crm_xml_cleanup(); + return 0; +} + +static void +buffer_scheme_test(void **state) { + assert_int_equal(XML_BUFFER_ALLOC_DOUBLEIT, xmlGetBufferAllocationScheme()); +} + +/* These functions also serve as unit tests of the static new_private_data + * function. We can't test free_private_data because libxml will call that as + * part of freeing everything else. By the time we'd get back into a unit test + * where we could check that private members are NULL, the structure containing + * the private data would have been freed. + * + * This could probably be tested with a lot of function mocking, but that + * doesn't seem worth it. + */ + +static void +create_document_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + + /* Double check things */ + assert_non_null(doc); + assert_int_equal(doc->type, XML_DOCUMENT_NODE); + + /* Check that the private data is initialized correctly */ + docpriv = doc->_private; + assert_non_null(docpriv); + assert_int_equal(docpriv->check, XML_DOC_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeDoc(doc); +} + +static void +create_element_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_ELEMENT_NODE); + + /* Check that the private data is initialized correctly */ + priv = node->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_attr_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL); + xmlAttrPtr attr = xmlNewProp(node, (pcmkXmlStr) PCMK_XA_NAME, + (pcmkXmlStr) "dummy-value"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(attr); + assert_int_equal(attr->type, XML_ATTRIBUTE_NODE); + + /* Check that the private data is initialized correctly */ + priv = attr->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_comment_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocComment(doc, (pcmkXmlStr) "blahblah"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_COMMENT_NODE); + + /* Check that the private data is initialized correctly */ + priv = node->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_text_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocText(doc, (pcmkXmlStr) "blahblah"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_TEXT_NODE); + + /* Check that no private data was created */ + priv = node->_private; + assert_null(priv); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_dtd_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlDtdPtr dtd = xmlNewDtd(doc, (pcmkXmlStr) PCMK_XA_NAME, + (pcmkXmlStr) "externalId", + (pcmkXmlStr) "systemId"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(dtd); + assert_int_equal(dtd->type, XML_DTD_NODE); + + /* Check that no private data was created */ + priv = dtd->_private; + assert_null(priv); + + /* Clean up */ + /* If you call xmlFreeDtd before xmlFreeDoc, you get a segfault */ + xmlFreeDoc(doc); +} + +static void +create_cdata_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewCDataBlock(doc, (pcmkXmlStr) "blahblah", 8); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_CDATA_SECTION_NODE); + + /* Check that no private data was created */ + priv = node->_private; + assert_null(priv); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(buffer_scheme_test), + cmocka_unit_test(create_document_node), + cmocka_unit_test(create_element_node), + cmocka_unit_test(create_attr_node), + cmocka_unit_test(create_comment_node), + cmocka_unit_test(create_text_node), + cmocka_unit_test(create_dtd_node), + cmocka_unit_test(create_cdata_node)); diff --git a/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c new file mode 100644 index 0000000..146317c --- /dev/null +++ b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c @@ -0,0 +1,188 @@ + /* + * Copyright 2022-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +#include <glib.h> + +static void +null_args(void **state) +{ + // This test dumps core via CRM_CHECK() + xmlNode *xml = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk__xe_copy_attrs(NULL, NULL, pcmk__xaf_none), EINVAL); + assert_int_equal(pcmk__xe_copy_attrs(NULL, xml, pcmk__xaf_none), EINVAL); + assert_int_equal(pcmk__xe_copy_attrs(xml, NULL, pcmk__xaf_none), EINVAL); + assert_ptr_equal(xml->properties, NULL); + + free_xml(xml); +} + +static void +no_source_attrs(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + // Ensure copying from empty source doesn't create target properties + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_ptr_equal(target->properties, NULL); + + // Ensure copying from empty source doesn't delete target attributes + crm_xml_add(target, "attr", "value"); + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "attr"), "value"); + + free_xml(src); + free_xml(target); +} + +static void +copy_one(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "value"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + free_xml(src); + free_xml(target); +} + +static void +copy_multiple(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + pcmk__xe_set_props(src, + "attr1", "value1", + "attr2", "value2", + "attr3", "value3", + NULL); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr1"), + crm_element_value(target, "attr1")); + assert_string_equal(crm_element_value(src, "attr2"), + crm_element_value(target, "attr2")); + assert_string_equal(crm_element_value(src, "attr3"), + crm_element_value(target, "attr3")); + + free_xml(src); + free_xml(target); +} + +static void +overwrite(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "src_value"); + crm_xml_add(target, "attr", "target_value"); + + // Overwrite enabled by default + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + free_xml(src); + free_xml(target); +} + +static void +no_overwrite(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "src_value"); + crm_xml_add(target, "attr", "target_value"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite), + pcmk_rc_ok); + assert_string_not_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + // no_overwrite doesn't prevent copy if there's no conflict + pcmk__xe_remove_attr(target, "attr"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + free_xml(src); + free_xml(target); +} + +static void +score_update(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++"); + crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2"); + crm_xml_add(target, "plus_plus_attr", "1"); + crm_xml_add(target, "plus_two_attr", "1"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_score_update), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "plus_plus_attr"), "2"); + assert_string_equal(crm_element_value(target, "plus_two_attr"), "3"); + + free_xml(src); + free_xml(target); +} + +static void +no_score_update(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++"); + crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2"); + crm_xml_add(target, "plus_plus_attr", "1"); + crm_xml_add(target, "plus_two_attr", "1"); + + // Score update disabled by default + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "plus_plus_attr"), + "plus_plus_attr++"); + assert_string_equal(crm_element_value(target, "plus_two_attr"), + "plus_two_attr+=2"); + + free_xml(src); + free_xml(target); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_args), + cmocka_unit_test(no_source_attrs), + cmocka_unit_test(copy_one), + cmocka_unit_test(copy_multiple), + cmocka_unit_test(overwrite), + cmocka_unit_test(no_overwrite), + cmocka_unit_test(score_update), + cmocka_unit_test(no_score_update)); diff --git a/lib/common/tests/xml/pcmk__xe_match_test.c b/lib/common/tests/xml/pcmk__xe_first_child_test.c index be2c949..64b90b0 100644 --- a/lib/common/tests/xml/pcmk__xe_match_test.c +++ b/lib/common/tests/xml/pcmk__xe_first_child_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,97 +9,97 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> const char *str1 = "<xml>\n" " <!-- This is an A node -->\n" - " <nodeA attrA=\"123\" " XML_ATTR_ID "=\"1\">\n" + " <nodeA attrA=\"123\" " PCMK_XA_ID "=\"1\">\n" " content\n" " </nodeA>\n" " <!-- This is an A node -->\n" - " <nodeA attrA=\"456\" " XML_ATTR_ID "=\"2\">\n" + " <nodeA attrA=\"456\" " PCMK_XA_ID "=\"2\">\n" " content\n" " </nodeA>\n" " <!-- This is an A node -->\n" - " <nodeA attrB=\"XYZ\" " XML_ATTR_ID "=\"3\">\n" + " <nodeA attrB=\"XYZ\" " PCMK_XA_ID "=\"3\">\n" " content\n" " </nodeA>\n" " <!-- This is a B node -->\n" - " <nodeB attrA=\"123\" " XML_ATTR_ID "=\"4\">\n" + " <nodeB attrA=\"123\" " PCMK_XA_ID "=\"4\">\n" " content\n" " </nodeA>\n" " <!-- This is a B node -->\n" - " <nodeB attrB=\"ABC\" " XML_ATTR_ID "=\"5\">\n" + " <nodeB attrB=\"ABC\" " PCMK_XA_ID "=\"5\">\n" " content\n" " </nodeA>\n" "</xml>"; static void bad_input(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); - assert_null(pcmk__xe_match(NULL, NULL, NULL, NULL)); - assert_null(pcmk__xe_match(NULL, NULL, NULL, "attrX")); + assert_null(pcmk__xe_first_child(NULL, NULL, NULL, NULL)); + assert_null(pcmk__xe_first_child(NULL, NULL, NULL, "attrX")); free_xml(xml); } static void not_found(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* No node with an attrX attribute */ - assert_null(pcmk__xe_match(xml, NULL, "attrX", NULL)); + assert_null(pcmk__xe_first_child(xml, NULL, "attrX", NULL)); /* No nodeX node */ - assert_null(pcmk__xe_match(xml, "nodeX", NULL, NULL)); + assert_null(pcmk__xe_first_child(xml, "nodeX", NULL, NULL)); /* No nodeA node with attrX */ - assert_null(pcmk__xe_match(xml, "nodeA", "attrX", NULL)); + assert_null(pcmk__xe_first_child(xml, "nodeA", "attrX", NULL)); /* No nodeA node with attrA=XYZ */ - assert_null(pcmk__xe_match(xml, "nodeA", "attrA", "XYZ")); + assert_null(pcmk__xe_first_child(xml, "nodeA", "attrA", "XYZ")); free_xml(xml); } static void find_attrB(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); xmlNode *result = NULL; /* Find the first node with attrB */ - result = pcmk__xe_match(xml, NULL, "attrB", NULL); + result = pcmk__xe_first_child(xml, NULL, "attrB", NULL); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "3"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "3"); /* Find the first nodeB with attrB */ - result = pcmk__xe_match(xml, "nodeB", "attrB", NULL); + result = pcmk__xe_first_child(xml, "nodeB", "attrB", NULL); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "5"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "5"); free_xml(xml); } static void find_attrA_matching(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); xmlNode *result = NULL; /* Find attrA=456 */ - result = pcmk__xe_match(xml, NULL, "attrA", "456"); + result = pcmk__xe_first_child(xml, NULL, "attrA", "456"); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "2"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "2"); /* Find a nodeB with attrA=123 */ - result = pcmk__xe_match(xml, "nodeB", "attrA", "123"); + result = pcmk__xe_first_child(xml, "nodeB", "attrA", "123"); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "4"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "4"); free_xml(xml); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(bad_input), cmocka_unit_test(not_found), cmocka_unit_test(find_attrB), diff --git a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c index ffb9171..a833dde 100644 --- a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c +++ b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -36,7 +36,7 @@ const char *str1 = static void bad_input(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); pcmk__assert_asserts(pcmk__xe_foreach_child(xml, NULL, NULL, NULL)); @@ -45,7 +45,7 @@ bad_input(void **state) { static void name_given_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -58,7 +58,7 @@ name_given_test(void **state) { static void no_name_given_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -71,7 +71,7 @@ no_name_given_test(void **state) { static void name_doesnt_exist_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); pcmk__xe_foreach_child(xml, "xxx", compare_name_handler, NULL); free_xml(xml); } @@ -100,7 +100,7 @@ const char *str2 = static void multiple_levels_test(void **state) { - xmlNode *xml = string2xml(str2); + xmlNode *xml = pcmk__xml_parse(str2); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -112,7 +112,7 @@ multiple_levels_test(void **state) { static void multiple_levels_no_name_test(void **state) { - xmlNode *xml = string2xml(str2); + xmlNode *xml = pcmk__xml_parse(str2); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -147,7 +147,7 @@ static int any_of_handler(xmlNode *xml, void *userdata) { static void any_of_test(void **state) { - xmlNode *xml = string2xml(str3); + xmlNode *xml = pcmk__xml_parse(str3); /* The handler should be called once for every <nodeX> node. */ expect_function_call(any_of_handler); @@ -190,7 +190,7 @@ static int stops_on_third_handler(xmlNode *xml, void *userdata) { static void one_of_test(void **state) { - xmlNode *xml = string2xml(str3); + xmlNode *xml = pcmk__xml_parse(str3); /* The handler should be called once. */ expect_function_call(stops_on_first_handler); @@ -205,7 +205,7 @@ one_of_test(void **state) { free_xml(xml); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(bad_input), cmocka_unit_test(name_given_test), cmocka_unit_test(no_name_given_test), diff --git a/lib/common/tests/xml/pcmk__xe_set_score_test.c b/lib/common/tests/xml/pcmk__xe_set_score_test.c new file mode 100644 index 0000000..deb85b0 --- /dev/null +++ b/lib/common/tests/xml/pcmk__xe_set_score_test.c @@ -0,0 +1,188 @@ +/* + * Copyright 2022-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +#include <glib.h> + +#include "crmcommon_private.h" // pcmk__xe_set_score() + +/*! + * \internal + * \brief Update an XML attribute value and check it against a reference value + * + * The attribute name is hard-coded as \c "X". + * + * \param[in] initial Initial value + * \param[in] new Value to set + * \param[in] reference_val Expected attribute value after update + * \param[in] reference_rc Expected return code from \c pcmk__xe_set_score() + */ +static void +assert_set_score(const char *initial, const char *new, + const char *reference_val, int reference_rc) +{ + const char *name = "X"; + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); + + crm_xml_add(test_xml, name, initial); + assert_int_equal(pcmk__xe_set_score(test_xml, name, new), reference_rc); + assert_string_equal(crm_element_value(test_xml, name), reference_val); + + free_xml(test_xml); +} + +static void +value_is_name_plus_plus(void **state) +{ + assert_set_score("5", "X++", "6", pcmk_rc_ok); +} + +static void +value_is_name_plus_equals_integer(void **state) +{ + assert_set_score("5", "X+=2", "7", pcmk_rc_ok); +} + +// NULL input + +static void +target_is_NULL(void **state) +{ + // Dumps core via CRM_CHECK() + assert_int_equal(pcmk__xe_set_score(NULL, "X", "X++"), EINVAL); +} + +static void +name_is_NULL(void **state) +{ + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); + + crm_xml_add(test_xml, "X", "5"); + + // Dumps core via CRM_CHECK() + assert_int_equal(pcmk__xe_set_score(test_xml, NULL, "X++"), EINVAL); + assert_string_equal(crm_element_value(test_xml, "X"), "5"); + + free_xml(test_xml); +} + +static void +value_is_NULL(void **state) +{ + assert_set_score("5", NULL, "5", pcmk_rc_ok); +} + +// the value input doesn't start with the name input + +static void +value_is_wrong_name(void **state) +{ + assert_set_score("5", "Y++", "Y++", pcmk_rc_ok); +} + +static void +value_is_only_an_integer(void **state) +{ + assert_set_score("5", "2", "2", pcmk_rc_ok); +} + +// non-integers + +static void +variable_is_initialized_to_be_non_numeric(void **state) +{ + assert_set_score("hello", "X++", "1", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_non_numeric_2(void **state) +{ + assert_set_score("hello", "X+=2", "2", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing(void **state) +{ + assert_set_score("5.01", "X++", "6", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing_2(void **state) +{ + assert_set_score("5.50", "X++", "6", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing_3(void **state) +{ + assert_set_score("5.99", "X++", "6", pcmk_rc_ok); +} + +static void +value_is_non_numeric(void **state) +{ + assert_set_score("5", "X+=hello", "5", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing(void **state) +{ + assert_set_score("5", "X+=2.01", "7", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing_2(void **state) +{ + assert_set_score("5", "X+=1.50", "6", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing_3(void **state) +{ + assert_set_score("5", "X+=1.99", "6", pcmk_rc_ok); +} + +// undefined input + +static void +name_is_undefined(void **state) +{ + assert_set_score(NULL, "X++", "X++", pcmk_rc_ok); +} + +// large input + +static void +assignment_result_is_too_large(void **state) +{ + assert_set_score("5", "X+=100000000000", "1000000", pcmk_rc_ok); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(value_is_name_plus_plus), + cmocka_unit_test(value_is_name_plus_equals_integer), + cmocka_unit_test(target_is_NULL), + cmocka_unit_test(name_is_NULL), + cmocka_unit_test(value_is_NULL), + cmocka_unit_test(value_is_wrong_name), + cmocka_unit_test(value_is_only_an_integer), + cmocka_unit_test(variable_is_initialized_to_be_non_numeric), + cmocka_unit_test(variable_is_initialized_to_be_non_numeric_2), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_2), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_3), + cmocka_unit_test(value_is_non_numeric), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing_2), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing_3), + cmocka_unit_test(name_is_undefined), + cmocka_unit_test(assignment_result_is_too_large)) diff --git a/lib/common/tests/xml/pcmk__xml_escape_test.c b/lib/common/tests/xml/pcmk__xml_escape_test.c new file mode 100644 index 0000000..8c6fd21 --- /dev/null +++ b/lib/common/tests/xml/pcmk__xml_escape_test.c @@ -0,0 +1,213 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +#include "crmcommon_private.h" + +static void +assert_escape(const char *str, const char *reference, + enum pcmk__xml_escape_type type) +{ + gchar *buf = pcmk__xml_escape(str, type); + + assert_string_equal(buf, reference); + g_free(buf); +} + +static void +null_empty(void **state) +{ + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_text)); + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr)); + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr_pretty)); + + assert_escape("", "", pcmk__xml_escape_text); + assert_escape("", "", pcmk__xml_escape_attr); + assert_escape("", "", pcmk__xml_escape_attr_pretty); +} + +static void +invalid_type(void **state) +{ + const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1; + + // Easier to ignore invalid type for NULL or empty string + assert_null(pcmk__xml_escape(NULL, type)); + assert_escape("", "", type); + + // Otherwise, assert if we somehow passed an invalid type + pcmk__assert_asserts(pcmk__xml_escape("he<>llo", type)); +} + +static void +escape_unchanged(void **state) +{ + // No escaped characters (note: this string includes single quote at end) + const char *unchanged = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "`~!@#$%^*()-_=+/|\\[]{}?.,'"; + + assert_escape(unchanged, unchanged, pcmk__xml_escape_text); + assert_escape(unchanged, unchanged, pcmk__xml_escape_attr); + assert_escape(unchanged, unchanged, pcmk__xml_escape_attr_pretty); +} + +// Ensure special characters get escaped at start, middle, and end + +static void +escape_left_angle(void **state) +{ + const char *l_angle = "<abc<def<"; + const char *l_angle_esc = PCMK__XML_ENTITY_LT "abc" + PCMK__XML_ENTITY_LT "def" PCMK__XML_ENTITY_LT; + + assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_text); + assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_attr); + assert_escape(l_angle, l_angle, pcmk__xml_escape_attr_pretty); +} + +static void +escape_right_angle(void **state) +{ + const char *r_angle = ">abc>def>"; + const char *r_angle_esc = PCMK__XML_ENTITY_GT "abc" + PCMK__XML_ENTITY_GT "def" PCMK__XML_ENTITY_GT; + + assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_text); + assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_attr); + assert_escape(r_angle, r_angle, pcmk__xml_escape_attr_pretty); +} + +static void +escape_ampersand(void **state) +{ + const char *ampersand = "&abc&def&"; + const char *ampersand_esc = PCMK__XML_ENTITY_AMP "abc" + PCMK__XML_ENTITY_AMP "def" PCMK__XML_ENTITY_AMP; + + assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_text); + assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_attr); + assert_escape(ampersand, ampersand, pcmk__xml_escape_attr_pretty); +} + +static void +escape_double_quote(void **state) +{ + const char *double_quote = "\"abc\"def\""; + const char *double_quote_esc_ref = PCMK__XML_ENTITY_QUOT "abc" + PCMK__XML_ENTITY_QUOT "def" + PCMK__XML_ENTITY_QUOT; + const char *double_quote_esc_backslash = "\\\"abc\\\"def\\\""; + + assert_escape(double_quote, double_quote, pcmk__xml_escape_text); + assert_escape(double_quote, double_quote_esc_ref, pcmk__xml_escape_attr); + assert_escape(double_quote, double_quote_esc_backslash, + pcmk__xml_escape_attr_pretty); +} + +static void +escape_newline(void **state) +{ + const char *newline = "\nabc\ndef\n"; + const char *newline_esc_ref = "
abc
def
"; + const char *newline_esc_backslash = "\\nabc\\ndef\\n"; + + assert_escape(newline, newline, pcmk__xml_escape_text); + assert_escape(newline, newline_esc_ref, pcmk__xml_escape_attr); + assert_escape(newline, newline_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_tab(void **state) +{ + const char *tab = "\tabc\tdef\t"; + const char *tab_esc_ref = "	abc	def	"; + const char *tab_esc_backslash = "\\tabc\\tdef\\t"; + + assert_escape(tab, tab, pcmk__xml_escape_text); + assert_escape(tab, tab_esc_ref, pcmk__xml_escape_attr); + assert_escape(tab, tab_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_carriage_return(void **state) +{ + const char *cr = "\rabc\rdef\r"; + const char *cr_esc_ref = "
abc
def
"; + const char *cr_esc_backslash = "\\rabc\\rdef\\r"; + + assert_escape(cr, cr_esc_ref, pcmk__xml_escape_text); + assert_escape(cr, cr_esc_ref, pcmk__xml_escape_attr); + assert_escape(cr, cr_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_nonprinting(void **state) +{ + const char *nonprinting = "\a\x7F\x1B"; + const char *nonprinting_esc = ""; + + assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_text); + assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_attr); + assert_escape(nonprinting, nonprinting, pcmk__xml_escape_attr_pretty); +} + +static void +escape_utf8(void **state) +{ + /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide + * and should not be escaped. + */ + const char *chinese = "仅高级使用"; + const char *two_byte = "abc""\xCF\xA6""d<ef"; + const char *two_byte_esc = "abc""\xCF\xA6""d" PCMK__XML_ENTITY_LT "ef"; + + const char *three_byte = "abc""\xEF\x98\x98""d<ef"; + const char *three_byte_esc = "abc""\xEF\x98\x98""d" + PCMK__XML_ENTITY_LT "ef"; + + const char *four_byte = "abc""\xF0\x94\x81\x90""d<ef"; + const char *four_byte_esc = "abc""\xF0\x94\x81\x90""d" + PCMK__XML_ENTITY_LT "ef"; + + assert_escape(chinese, chinese, pcmk__xml_escape_text); + assert_escape(chinese, chinese, pcmk__xml_escape_attr); + assert_escape(chinese, chinese, pcmk__xml_escape_attr_pretty); + + assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_text); + assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_attr); + assert_escape(two_byte, two_byte, pcmk__xml_escape_attr_pretty); + + assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_text); + assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_attr); + assert_escape(three_byte, three_byte, pcmk__xml_escape_attr_pretty); + + assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_text); + assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_attr); + assert_escape(four_byte, four_byte, pcmk__xml_escape_attr_pretty); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_empty), + cmocka_unit_test(invalid_type), + cmocka_unit_test(escape_unchanged), + cmocka_unit_test(escape_left_angle), + cmocka_unit_test(escape_right_angle), + cmocka_unit_test(escape_ampersand), + cmocka_unit_test(escape_double_quote), + cmocka_unit_test(escape_newline), + cmocka_unit_test(escape_tab), + cmocka_unit_test(escape_carriage_return), + cmocka_unit_test(escape_nonprinting), + cmocka_unit_test(escape_utf8)); diff --git a/lib/common/tests/xml/pcmk__xml_needs_escape_test.c b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c new file mode 100644 index 0000000..612f61b --- /dev/null +++ b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c @@ -0,0 +1,337 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +#include "crmcommon_private.h" + +static void +null_empty(void **state) +{ + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr_pretty)); +} + +static void +invalid_type(void **state) +{ + const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1; + + // Easier to ignore invalid type for NULL or empty string + assert_false(pcmk__xml_needs_escape(NULL, type)); + assert_false(pcmk__xml_needs_escape("", type)); + + // Otherwise, assert if we somehow passed an invalid type + pcmk__assert_asserts(pcmk__xml_needs_escape("he<>llo", type)); +} + +static void +escape_unchanged(void **state) +{ + // No escaped characters (note: this string includes single quote at end) + const char *unchanged = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "`~!@#$%^*()-_=+/|\\[]{}?.,'"; + + assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(unchanged, + pcmk__xml_escape_attr_pretty)); +} + +// Ensure special characters get escaped at start, middle, and end + +static void +escape_left_angle(void **state) +{ + const char *l_angle_left = "<abcdef"; + const char *l_angle_mid = "abc<def"; + const char *l_angle_right = "abcdef<"; + + assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(l_angle_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(l_angle_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(l_angle_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_right_angle(void **state) +{ + const char *r_angle_left = ">abcdef"; + const char *r_angle_mid = "abc>def"; + const char *r_angle_right = "abcdef>"; + + assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(r_angle_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(r_angle_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(r_angle_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_ampersand(void **state) +{ + const char *ampersand_left = "&abcdef"; + const char *ampersand_mid = "abc&def"; + const char *ampersand_right = "abcdef&"; + + assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(ampersand_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(ampersand_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(ampersand_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_double_quote(void **state) +{ + const char *double_quote_left = "\"abcdef"; + const char *double_quote_mid = "abc\"def"; + const char *double_quote_right = "abcdef\""; + + assert_false(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_newline(void **state) +{ + const char *newline_left = "\nabcdef"; + const char *newline_mid = "abc\ndef"; + const char *newline_right = "abcdef\n"; + + assert_false(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(newline_left, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(newline_mid, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(newline_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_tab(void **state) +{ + const char *tab_left = "\tabcdef"; + const char *tab_mid = "abc\tdef"; + const char *tab_right = "abcdef\t"; + + assert_false(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(tab_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_carriage_return(void **state) +{ + const char *cr_left = "\rabcdef"; + const char *cr_mid = "abc\rdef"; + const char *cr_right = "abcdef\r"; + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr_pretty)); +} + +static void +escape_nonprinting(void **state) +{ + const char *alert_left = "\aabcdef"; + const char *alert_mid = "abc\adef"; + const char *alert_right = "abcdef\a"; + + const char *delete_left = "\x7F""abcdef"; + const char *delete_mid = "abc\x7F""def"; + const char *delete_right = "abcdef\x7F"; + + const char *nonprinting_all = "\a\x7F\x1B"; + + assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(alert_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(alert_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(alert_right, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(delete_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(delete_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(delete_right, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(nonprinting_all, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_utf8(void **state) +{ + /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide + * and should not be escaped. + */ + const char *chinese = "仅高级使用"; + const char *two_byte = "abc""\xCF\xA6""def"; + const char *two_byte_special = "abc""\xCF\xA6""d<ef"; + const char *three_byte = "abc""\xEF\x98\x98""def"; + const char *three_byte_special = "abc""\xEF\x98\x98""d<ef"; + const char *four_byte = "abc""\xF0\x94\x81\x90""def"; + const char *four_byte_special = "abc""\xF0\x94\x81\x90""d<ef"; + + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(two_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(three_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(four_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_attr_pretty)); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_empty), + cmocka_unit_test(invalid_type), + cmocka_unit_test(escape_unchanged), + cmocka_unit_test(escape_left_angle), + cmocka_unit_test(escape_right_angle), + cmocka_unit_test(escape_ampersand), + cmocka_unit_test(escape_double_quote), + cmocka_unit_test(escape_newline), + cmocka_unit_test(escape_tab), + cmocka_unit_test(escape_carriage_return), + cmocka_unit_test(escape_nonprinting), + cmocka_unit_test(escape_utf8)); diff --git a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c index 3922b34..86500c2 100644 --- a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c +++ b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,14 +9,14 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> static void empty_input(void **state) { - assert_null(pcmk__xpath_node_id(NULL, "lrm")); - assert_null(pcmk__xpath_node_id("", "lrm")); + assert_null(pcmk__xpath_node_id(NULL, PCMK__XE_LRM)); + assert_null(pcmk__xpath_node_id("", PCMK__XE_LRM)); assert_null(pcmk__xpath_node_id("/blah/blah", NULL)); assert_null(pcmk__xpath_node_id("/blah/blah", "")); assert_null(pcmk__xpath_node_id(NULL, NULL)); @@ -24,30 +24,31 @@ empty_input(void **state) { static void no_quotes(void **state) { - const char *xpath = "/some/xpath/lrm[@" XML_ATTR_ID "=xyz]"; - pcmk__assert_asserts(pcmk__xpath_node_id(xpath, "lrm")); + const char *xpath = "/some/xpath/" PCMK__XE_LRM "[@" PCMK_XA_ID "=xyz]"; + pcmk__assert_asserts(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); } static void not_present(void **state) { - const char *xpath = "/some/xpath/string[@" XML_ATTR_ID "='xyz']"; - assert_null(pcmk__xpath_node_id(xpath, "lrm")); + const char *xpath = "/some/xpath/string[@" PCMK_XA_ID "='xyz']"; + assert_null(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); - xpath = "/some/xpath/containing[@" XML_ATTR_ID "='lrm']"; - assert_null(pcmk__xpath_node_id(xpath, "lrm")); + xpath = "/some/xpath/containing[@" PCMK_XA_ID "='" PCMK__XE_LRM "']"; + assert_null(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); } static void present(void **state) { char *s = NULL; - const char *xpath = "/some/xpath/containing/lrm[@" XML_ATTR_ID "='xyz']"; + const char *xpath = "/some/xpath/containing" + "/" PCMK__XE_LRM "[@" PCMK_XA_ID "='xyz']"; - s = pcmk__xpath_node_id(xpath, "lrm"); + s = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); assert_int_equal(strcmp(s, "xyz"), 0); free(s); - xpath = "/some/other/lrm[@" XML_ATTR_ID "='xyz']/xpath"; - s = pcmk__xpath_node_id(xpath, "lrm"); + xpath = "/some/other/" PCMK__XE_LRM "[@" PCMK_XA_ID "='xyz']/xpath"; + s = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); assert_int_equal(strcmp(s, "xyz"), 0); free(s); } diff --git a/lib/common/unittest.c b/lib/common/unittest.c new file mode 100644 index 0000000..0e63915 --- /dev/null +++ b/lib/common/unittest.c @@ -0,0 +1,128 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/common/unittest_internal.h> + +#include <stdlib.h> +#include <unistd.h> + +// LCOV_EXCL_START + +void +pcmk__assert_validates(xmlNode *xml) +{ + const char *schema_dir = NULL; + char *cmd = NULL; + gchar *out = NULL; + gchar *err = NULL; + gint status; + GError *gerr = NULL; + char *xmllint_input = crm_strdup_printf("%s/test-xmllint.XXXXXX", + pcmk__get_tmpdir()); + int fd; + int rc; + + fd = mkstemp(xmllint_input); + if (fd < 0) { + fail_msg("Could not create temp file: %s", strerror(errno)); + } + + rc = pcmk__xml2fd(fd, xml); + if (rc != pcmk_rc_ok) { + unlink(xmllint_input); + fail_msg("Could not write temp file: %s", pcmk_rc_str(rc)); + } + + close(fd); + + /* This should be set as part of AM_TESTS_ENVIRONMENT in Makefile.am. */ + schema_dir = getenv("PCMK_schema_directory"); + if (schema_dir == NULL) { + unlink(xmllint_input); + fail_msg("PCMK_schema_directory is not set in test environment"); + } + + cmd = crm_strdup_printf("xmllint --relaxng %s/api/api-result.rng %s", + schema_dir, xmllint_input); + + if (!g_spawn_command_line_sync(cmd, &out, &err, &status, &gerr)) { + unlink(xmllint_input); + fail_msg("Error occurred when performing validation: %s", gerr->message); + } + + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + unlink(xmllint_input); + fail_msg("XML validation failed: %s\n%s\n", out, err); + } + + free(cmd); + g_free(out); + g_free(err); + unlink(xmllint_input); + free(xmllint_input); +} + +int +pcmk__xml_test_setup_group(void **state) +{ + /* This needs to be run before we run unit tests that manipulate XML. + * Without it, document private data won't get created, which can cause + * segmentation faults or assertions in functions related to change + * tracking and ACLs. There's no harm in doing this before all tests. + */ + crm_xml_init(); + return 0; +} + +char * +pcmk__cib_test_copy_cib(const char *in_file) +{ + char *in_path = crm_strdup_printf("%s/%s", getenv("PCMK_CTS_CLI_DIR"), in_file); + char *out_path = NULL; + char *contents = NULL; + int fd; + + /* Copy the CIB over to a temp location so we can modify it. */ + out_path = crm_strdup_printf("%s/test-cib.XXXXXX", pcmk__get_tmpdir()); + + fd = mkstemp(out_path); + if (fd < 0) { + free(out_path); + return NULL; + } + + if (pcmk__file_contents(in_path, &contents) != pcmk_rc_ok) { + free(out_path); + close(fd); + return NULL; + } + + if (pcmk__write_sync(fd, contents) != pcmk_rc_ok) { + free(out_path); + free(in_path); + free(contents); + close(fd); + return NULL; + } + + setenv("CIB_file", out_path, 1); + return out_path; +} + +void +pcmk__cib_test_cleanup(char *out_path) +{ + unlink(out_path); + free(out_path); + unsetenv("CIB_file"); +} + +// LCOV_EXCL_STOP diff --git a/lib/common/utils.c b/lib/common/utils.c index e5b9ef0..e8d343e 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 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,6 @@ #include <crm/crm.h> #include <crm/services.h> -#include <crm/msg_xml.h> #include <crm/cib/internal.h> #include <crm/common/xml.h> #include <crm/common/util.h> @@ -257,46 +256,6 @@ compare_version(const char *version1, const char *version2) } /*! - * \brief Parse milliseconds from a Pacemaker interval specification - * - * \param[in] input Pacemaker time interval specification (a bare number of - * seconds, a number with a unit optionally with whitespace - * before and/or after the number, or an ISO 8601 duration) - * - * \return Milliseconds equivalent of given specification on success (limited - * to the range of an unsigned integer), 0 if input is NULL, - * or 0 (and set errno to EINVAL) on error - */ -guint -crm_parse_interval_spec(const char *input) -{ - long long msec = -1; - - errno = 0; - if (input == NULL) { - return 0; - - } else if (input[0] == 'P') { - crm_time_t *period_s = crm_time_parse_duration(input); - - if (period_s) { - msec = 1000 * crm_time_get_seconds(period_s); - crm_time_free(period_s); - } - - } else { - msec = crm_get_msec(input); - } - - if (msec < 0) { - crm_warn("Using 0 instead of '%s'", input); - errno = EINVAL; - return 0; - } - return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec; -} - -/*! * \internal * \brief Log a failed assertion * @@ -464,43 +423,6 @@ pcmk__daemonize(const char *name, const char *pidfile) pcmk__open_devnull(O_WRONLY); // stderr (fd 2) } -char * -crm_meta_name(const char *field) -{ - int lpc = 0; - int max = 0; - char *crm_name = NULL; - - CRM_CHECK(field != NULL, return NULL); - crm_name = crm_strdup_printf(CRM_META "_%s", field); - - /* Massage the names so they can be used as shell variables */ - max = strlen(crm_name); - for (; lpc < max; lpc++) { - switch (crm_name[lpc]) { - case '-': - crm_name[lpc] = '_'; - break; - } - } - return crm_name; -} - -const char * -crm_meta_value(GHashTable * hash, const char *field) -{ - char *key = NULL; - const char *value = NULL; - - key = crm_meta_name(field); - if (key) { - value = g_hash_table_lookup(hash, key); - free(key); - } - - return value; -} - #ifdef HAVE_UUID_UUID_H # include <uuid/uuid.h> #endif @@ -511,7 +433,7 @@ crm_generate_uuid(void) unsigned char uuid[16]; char *buffer = malloc(37); /* Including NUL byte */ - CRM_ASSERT(buffer != NULL); + pcmk__mem_assert(buffer); uuid_generate(uuid); uuid_unparse(uuid, buffer); return buffer; @@ -526,27 +448,15 @@ crm_gnutls_global_init(void) } #endif -/*! - * \brief Get the local hostname - * - * \return Newly allocated string with name, or NULL (and set errno) on error - */ -char * -pcmk_hostname(void) -{ - struct utsname hostinfo; - - return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename); -} - bool pcmk_str_is_infinity(const char *s) { - return pcmk__str_any_of(s, CRM_INFINITY_S, CRM_PLUS_INFINITY_S, NULL); + return pcmk__str_any_of(s, PCMK_VALUE_INFINITY, PCMK_VALUE_PLUS_INFINITY, + NULL); } bool pcmk_str_is_minus_infinity(const char *s) { - return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none); + return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none); } /*! @@ -592,3 +502,48 @@ pcmk__sleep_ms(unsigned int ms) } #endif } + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/common/util_compat.h> + +guint +crm_parse_interval_spec(const char *input) +{ + long long msec = -1; + + errno = 0; + if (input == NULL) { + return 0; + + } else if (input[0] == 'P') { + crm_time_t *period_s = crm_time_parse_duration(input); + + if (period_s) { + msec = 1000 * crm_time_get_seconds(period_s); + crm_time_free(period_s); + } + + } else { + msec = crm_get_msec(input); + } + + if (msec < 0) { + crm_warn("Using 0 instead of '%s'", input); + errno = EINVAL; + return 0; + } + return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec; +} + +char * +pcmk_hostname(void) +{ + struct utsname hostinfo; + + return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename); +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/common/watchdog.c b/lib/common/watchdog.c index e569214..b7921fe 100644 --- a/lib/common/watchdog.c +++ b/lib/common/watchdog.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -112,10 +112,10 @@ panic_local(void) if(ppid > 1) { /* child daemon */ - exit(CRM_EX_PANIC); + crm_exit(CRM_EX_PANIC); } else { /* pacemakerd or orphan child */ - exit(CRM_EX_FATAL); + crm_exit(CRM_EX_FATAL); } } @@ -141,10 +141,10 @@ panic_sbd(void) if(ppid > 1) { /* child daemon */ - exit(CRM_EX_PANIC); + crm_exit(CRM_EX_PANIC); } else { /* pacemakerd or orphan child */ - exit(CRM_EX_FATAL); + crm_exit(CRM_EX_FATAL); } } @@ -232,7 +232,7 @@ pcmk__locate_sbd(void) } long -pcmk__get_sbd_timeout(void) +pcmk__get_sbd_watchdog_timeout(void) { static long sbd_timeout = -2; @@ -266,44 +266,53 @@ pcmk__get_sbd_sync_resource_startup(void) } long -pcmk__auto_watchdog_timeout(void) +pcmk__auto_stonith_watchdog_timeout(void) { - long sbd_timeout = pcmk__get_sbd_timeout(); + long sbd_timeout = pcmk__get_sbd_watchdog_timeout(); return (sbd_timeout <= 0)? 0 : (2 * sbd_timeout); } bool -pcmk__valid_sbd_timeout(const char *value) +pcmk__valid_stonith_watchdog_timeout(const char *value) { + /* @COMPAT At a compatibility break, accept either negative values or a + * specific string like "auto" (but not both) to mean "auto-calculate the + * timeout." Reject other values that aren't parsable as timeouts. + */ long st_timeout = value? crm_get_msec(value) : 0; if (st_timeout < 0) { - st_timeout = pcmk__auto_watchdog_timeout(); - crm_debug("Using calculated value %ld for stonith-watchdog-timeout (%s)", + st_timeout = pcmk__auto_stonith_watchdog_timeout(); + crm_debug("Using calculated value %ld for " + PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " (%s)", st_timeout, value); } if (st_timeout == 0) { - crm_debug("Watchdog may be enabled but stonith-watchdog-timeout is disabled (%s)", + crm_debug("Watchdog may be enabled but " + PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " is disabled (%s)", value? value : "default"); } else if (pcmk__locate_sbd() == 0) { - crm_emerg("Shutting down: stonith-watchdog-timeout configured (%s) " - "but SBD not active", (value? value : "auto")); + crm_emerg("Shutting down: " PCMK_OPT_STONITH_WATCHDOG_TIMEOUT + " configured (%s) but SBD not active", + pcmk__s(value, "auto")); crm_exit(CRM_EX_FATAL); return false; } else { - long sbd_timeout = pcmk__get_sbd_timeout(); + long sbd_timeout = pcmk__get_sbd_watchdog_timeout(); if (st_timeout < sbd_timeout) { - crm_emerg("Shutting down: stonith-watchdog-timeout (%s) too short " - "(must be >%ldms)", value, sbd_timeout); + crm_emerg("Shutting down: " PCMK_OPT_STONITH_WATCHDOG_TIMEOUT + " (%s) too short (must be >%ldms)", + value, sbd_timeout); crm_exit(CRM_EX_FATAL); return false; } - crm_info("Watchdog configured with stonith-watchdog-timeout %s and SBD timeout %ldms", + crm_info("Watchdog configured with " PCMK_OPT_STONITH_WATCHDOG_TIMEOUT + " %s and SBD timeout %ldms", value, sbd_timeout); } return true; diff --git a/lib/common/xml.c b/lib/common/xml.c index 53ebff7..73d6025 100644 --- a/lib/common/xml.c +++ b/lib/common/xml.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,41 +9,52 @@ #include <crm_internal.h> +#include <stdarg.h> +#include <stdint.h> // uint32_t #include <stdio.h> -#include <sys/types.h> -#include <unistd.h> -#include <time.h> -#include <string.h> #include <stdlib.h> -#include <stdarg.h> -#include <bzlib.h> +#include <string.h> +#include <sys/stat.h> // stat(), S_ISREG, etc. +#include <sys/types.h> #include <libxml/parser.h> #include <libxml/tree.h> -#include <libxml/xmlIO.h> /* xmlAllocOutputBuffer */ #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> -#include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc. +#include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc. #include "crmcommon_private.h" -// Define this as 1 in development to get insanely verbose trace messages -#ifndef XML_PARSER_DEBUG -#define XML_PARSER_DEBUG 0 -#endif - -/* @TODO XML_PARSE_RECOVER allows some XML errors to be silently worked around - * by libxml2, which is potentially ambiguous and dangerous. We should drop it - * when we can break backward compatibility with configurations that might be - * relying on it (i.e. pacemaker 3.0.0). +/*! + * \internal + * \brief Apply a function to each XML node in a tree (pre-order, depth-first) * - * It might be a good idea to have a transitional period where we first try - * parsing without XML_PARSE_RECOVER, and if that fails, try parsing again with - * it, logging a warning if it succeeds. + * \param[in,out] xml XML tree to traverse + * \param[in,out] fn Function to call for each node (returns \c true to + * continue traversing the tree or \c false to stop) + * \param[in,out] user_data Argument to \p fn + * + * \return \c false if any \p fn call returned \c false, or \c true otherwise + * + * \note This function is recursive. */ -#define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER (XML_PARSE_NOBLANKS) -#define PCMK__XML_PARSE_OPTS_WITH_RECOVER (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER) +bool +pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *), + void *user_data) +{ + if (!fn(xml, user_data)) { + return false; + } + + for (xml = pcmk__xml_first_child(xml); xml != NULL; + xml = pcmk__xml_next(xml)) { + + if (!pcmk__xml_tree_foreach(xml, fn, user_data)) { + return false; + } + } + return true; +} bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy) @@ -93,43 +104,72 @@ pcmk__mark_xml_node_dirty(xmlNode *xml) set_parent_flag(xml, pcmk__xf_dirty); } -// Clear flags on XML node and its children -static void -reset_xml_node_flags(xmlNode *xml) +/*! + * \internal + * \brief Clear flags on an XML node + * + * \param[in,out] xml XML node whose flags to reset + * \param[in,out] user_data Ignored + * + * \return \c true (to continue traversing the tree) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +reset_xml_node_flags(xmlNode *xml, void *user_data) { - xmlNode *cIter = NULL; xml_node_private_t *nodepriv = xml->_private; - if (nodepriv) { - nodepriv->flags = 0; + if (nodepriv != NULL) { + nodepriv->flags = pcmk__xf_none; } + return true; +} - for (cIter = pcmk__xml_first_child(xml); cIter != NULL; - cIter = pcmk__xml_next(cIter)) { - reset_xml_node_flags(cIter); +/*! + * \internal + * \brief Set the \c pcmk__xf_dirty and \c pcmk__xf_created flags on an XML node + * + * \param[in,out] xml Node whose flags to set + * \param[in] user_data Ignored + * + * \return \c true (to continue traversing the tree) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +mark_xml_dirty_created(xmlNode *xml, void *user_data) +{ + xml_node_private_t *nodepriv = xml->_private; + + if (nodepriv != NULL) { + pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created); } + return true; } -// Set xpf_created flag on XML node and any children +/*! + * \internal + * \brief Mark an XML tree as dirty and created, and mark its parents dirty + * + * Also mark the document dirty. + * + * \param[in,out] xml Tree to mark as dirty and created + */ void -pcmk__mark_xml_created(xmlNode *xml) +pcmk__xml_mark_created(xmlNode *xml) { - xmlNode *cIter = NULL; - xml_node_private_t *nodepriv = NULL; - CRM_ASSERT(xml != NULL); - nodepriv = xml->_private; - if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) { - if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) { - pcmk__set_xml_flags(nodepriv, pcmk__xf_created); - pcmk__mark_xml_node_dirty(xml); - } - for (cIter = pcmk__xml_first_child(xml); cIter != NULL; - cIter = pcmk__xml_next(cIter)) { - pcmk__mark_xml_created(cIter); - } + if (!pcmk__tracking_xml_changes(xml, false)) { + // Tracking is disabled for entire document + return; } + + // Mark all parents and document dirty + pcmk__mark_xml_node_dirty(xml); + + pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL); } #define XML_DOC_PRIVATE_MAGIC 0x81726354UL @@ -142,7 +182,7 @@ free_deleted_object(void *data) if(data) { pcmk__deleted_xml_t *deleted_obj = data; - free(deleted_obj->path); + g_free(deleted_obj->path); free(deleted_obj); } } @@ -220,9 +260,9 @@ new_private_data(xmlNode *node) { switch (node->type) { case XML_DOCUMENT_NODE: { - xml_doc_private_t *docpriv = NULL; - docpriv = calloc(1, sizeof(xml_doc_private_t)); - CRM_ASSERT(docpriv != NULL); + xml_doc_private_t *docpriv = + pcmk__assert_alloc(1, sizeof(xml_doc_private_t)); + docpriv->check = XML_DOC_PRIVATE_MAGIC; /* Flags will be reset if necessary when tracking is enabled */ pcmk__set_xml_flags(docpriv, pcmk__xf_dirty|pcmk__xf_created); @@ -232,9 +272,9 @@ new_private_data(xmlNode *node) case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: case XML_COMMENT_NODE: { - xml_node_private_t *nodepriv = NULL; - nodepriv = calloc(1, sizeof(xml_node_private_t)); - CRM_ASSERT(nodepriv != NULL); + xml_node_private_t *nodepriv = + pcmk__assert_alloc(1, sizeof(xml_node_private_t)); + nodepriv->check = XML_NODE_PRIVATE_MAGIC; /* Flags will be reset if necessary when tracking is enabled */ pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created); @@ -314,21 +354,23 @@ pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set) return position; } -// Remove all attributes marked as deleted from an XML node -static void -accept_attr_deletions(xmlNode *xml) +/*! + * \internal + * \brief Remove all attributes marked as deleted from an XML node + * + * \param[in,out] xml XML node whose deleted attributes to remove + * \param[in,out] user_data Ignored + * + * \return \c true (to continue traversing the tree) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +accept_attr_deletions(xmlNode *xml, void *user_data) { - // Clear XML node's flags - ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none; - - // Remove this XML node's attributes that were marked as deleted + reset_xml_node_flags(xml, NULL); pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL); - - // Recursively do the same for this XML node's children - for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL; - cIter = pcmk__xml_next(cIter)) { - accept_attr_deletions(cIter); - } + return true; } /*! @@ -348,10 +390,11 @@ pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact) return pcmk__xc_match(haystack, needle, exact); } else { - const char *id = ID(needle); - const char *attr = (id == NULL)? NULL : XML_ATTR_ID; + const char *id = pcmk__xe_id(needle); + const char *attr = (id == NULL)? NULL : PCMK_XA_ID; - return pcmk__xe_match(haystack, (const char *) needle->name, attr, id); + return pcmk__xe_first_child(haystack, (const char *) needle->name, attr, + id); } } @@ -377,207 +420,263 @@ xml_accept_changes(xmlNode * xml) } docpriv->flags = pcmk__xf_none; - accept_attr_deletions(top); -} - -xmlNode * -find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find) -{ - xmlNode *a_child = NULL; - const char *name = (root == NULL)? "<NULL>" : (const char *) root->name; - - if (search_path == NULL) { - crm_warn("Will never find <NULL>"); - return NULL; - } - - for (a_child = pcmk__xml_first_child(root); a_child != NULL; - a_child = pcmk__xml_next(a_child)) { - if (strcmp((const char *)a_child->name, search_path) == 0) { - return a_child; - } - } - - if (must_find) { - crm_warn("Could not find %s in %s.", search_path, name); - } else if (root != NULL) { - crm_trace("Could not find %s in %s.", search_path, name); - } else { - crm_trace("Could not find %s in <NULL>.", search_path); - } - - return NULL; + pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL); } -#define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \ - (v), pcmk__str_none) - /*! * \internal * \brief Find first XML child element matching given criteria * - * \param[in] parent XML element to search - * \param[in] node_name If not NULL, only match children of this type - * \param[in] attr_n If not NULL, only match children with an attribute + * \param[in] parent XML element to search (can be \c NULL) + * \param[in] node_name If not \c NULL, only match children of this type + * \param[in] attr_n If not \c NULL, only match children with an attribute * of this name. * \param[in] attr_v If \p attr_n and this are not NULL, only match children * with an attribute named \p attr_n and this value * - * \return Matching XML child element, or NULL if none found + * \return Matching XML child element, or \c NULL if none found */ xmlNode * -pcmk__xe_match(const xmlNode *parent, const char *node_name, - const char *attr_n, const char *attr_v) +pcmk__xe_first_child(const xmlNode *parent, const char *node_name, + const char *attr_n, const char *attr_v) { - CRM_CHECK(parent != NULL, return NULL); - CRM_CHECK(attr_v == NULL || attr_n != NULL, return NULL); + xmlNode *child = NULL; + const char *parent_name = "<null>"; - for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL; - child = pcmk__xml_next(child)) { - if (pcmk__str_eq(node_name, (const char *) (child->name), - pcmk__str_null_matches) - && ((attr_n == NULL) || - (attr_v == NULL && xmlHasProp(child, (pcmkXmlStr) attr_n)) || - (attr_v != NULL && attr_matches(child, attr_n, attr_v)))) { - return child; + CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL); + + if (parent != NULL) { + child = parent->children; + while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) { + child = child->next; } + + parent_name = (const char *) parent->name; } - crm_trace("XML child node <%s%s%s%s%s> not found in %s", - (node_name? node_name : "(any)"), - (attr_n? " " : ""), - (attr_n? attr_n : ""), - (attr_n? "=" : ""), - (attr_n? attr_v : ""), - (const char *) parent->name); - return NULL; -} -void -copy_in_properties(xmlNode *target, const xmlNode *src) -{ - if (src == NULL) { - crm_warn("No node to copy properties from"); + for (; child != NULL; child = pcmk__xe_next(child)) { + const char *value = NULL; - } else if (target == NULL) { - crm_err("No node to copy properties into"); + if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) { + // Node name mismatch + continue; + } + if (attr_n == NULL) { + // No attribute match needed + return child; + } - } else { - for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) { - const char *p_name = (const char *) a->name; - const char *p_value = pcmk__xml_attr_value(a); + value = crm_element_value(child, attr_n); - expand_plus_plus(target, p_name, p_value); - if (xml_acl_denied(target)) { - crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name); - return; - } + if ((attr_v == NULL) && (value != NULL)) { + // attr_v == NULL: Attribute attr_n must be set (to any value) + return child; + } + if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) { + // attr_v != NULL: Attribute attr_n must be set to value attr_v + return child; } } - return; + if (node_name == NULL) { + node_name = "(any)"; // For logging + } + if (attr_n != NULL) { + crm_trace("XML child node <%s %s=%s> not found in %s", + node_name, attr_n, attr_v, parent_name); + } else { + crm_trace("XML child node <%s> not found in %s", + node_name, parent_name); + } + return NULL; } /*! - * \brief Parse integer assignment statements on this node and all its child - * nodes + * \internal + * \brief Set an XML attribute, expanding \c ++ and \c += where appropriate + * + * If \p target already has an attribute named \p name set to an integer value + * and \p value is an addition assignment expression on \p name, then expand + * \p value to an integer and set attribute \p name to the expanded value in + * \p target. + * + * Otherwise, set attribute \p name on \p target using the literal \p value. + * + * The original attribute value in \p target and the number in an assignment + * expression in \p value are parsed and added as scores (that is, their values + * are capped at \c INFINITY and \c -INFINITY). For more details, refer to + * \c char2score(). * - * \param[in,out] target Root XML node to be processed + * For example, suppose \p target has an attribute named \c "X" with value + * \c "5", and that \p name is \c "X". + * * If \p value is \c "X++", the new value of \c "X" in \p target is \c "6". + * * If \p value is \c "X+=3", the new value of \c "X" in \p target is \c "8". + * * If \p value is \c "val", the new value of \c "X" in \p target is \c "val". + * * If \p value is \c "Y++", the new value of \c "X" in \p target is \c "Y++". * - * \note This function is recursive + * \param[in,out] target XML node whose attribute to set + * \param[in] name Name of the attribute to set + * \param[in] value New value of attribute to set + * + * \return Standard Pacemaker return code (specifically, \c EINVAL on invalid + * argument, or \c pcmk_rc_ok otherwise) */ -void -fix_plus_plus_recursive(xmlNode *target) +int +pcmk__xe_set_score(xmlNode *target, const char *name, const char *value) { - /* TODO: Remove recursion and use xpath searches for value++ */ - xmlNode *child = NULL; + const char *old_value = NULL; - for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) { - const char *p_name = (const char *) a->name; - const char *p_value = pcmk__xml_attr_value(a); + CRM_CHECK((target != NULL) && (name != NULL), return EINVAL); - expand_plus_plus(target, p_name, p_value); + if (value == NULL) { + return pcmk_rc_ok; } - for (child = pcmk__xml_first_child(target); child != NULL; - child = pcmk__xml_next(child)) { - fix_plus_plus_recursive(child); + + old_value = crm_element_value(target, name); + + // If no previous value, skip to default case and set the value unexpanded. + if (old_value != NULL) { + const char *n = name; + const char *v = value; + + // Stop at first character that differs between name and value + for (; (*n == *v) && (*n != '\0'); n++, v++); + + // If value begins with name followed by a "++" or "+=" + if ((*n == '\0') + && (*v++ == '+') + && ((*v == '+') || (*v == '='))) { + + // If we're expanding ourselves, no previous value was set; use 0 + int old_value_i = (old_value != value)? char2score(old_value) : 0; + + /* value="X++": new value of X is old_value + 1 + * value="X+=Y": new value of X is old_value + Y (for some number Y) + */ + int add = (*v == '+')? 1 : char2score(++v); + + crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add)); + return pcmk_rc_ok; + } + } + + // Default case: set the attribute unexpanded (with value treated literally) + if (old_value != value) { + crm_xml_add(target, name, value); } + return pcmk_rc_ok; } /*! - * \brief Update current XML attribute value per parsed integer assignment - statement - * - * \param[in,out] target an XML node, containing a XML attribute that is - * initialized to some numeric value, to be processed - * \param[in] name name of the XML attribute, e.g. X, whose value - * should be updated - * \param[in] value assignment statement, e.g. "X++" or - * "X+=5", to be applied to the initialized value. - * - * \note The original XML attribute value is treated as 0 if non-numeric and - * truncated to be an integer if decimal-point-containing. - * \note The final XML attribute value is truncated to not exceed 1000000. - * \note Undefined behavior if unexpected input. + * \internal + * \brief Copy XML attributes from a source element to a target element + * + * This is similar to \c xmlCopyPropList() except that attributes are marked + * as dirty for change tracking purposes. + * + * \param[in,out] target XML element to receive copied attributes from \p src + * \param[in] src XML element whose attributes to copy to \p target + * \param[in] flags Group of <tt>enum pcmk__xa_flags</tt> + * + * \return Standard Pacemaker return code */ -void -expand_plus_plus(xmlNode * target, const char *name, const char *value) +int +pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags) { - int offset = 1; - int name_len = 0; - int int_value = 0; - int value_len = 0; + CRM_CHECK((src != NULL) && (target != NULL), return EINVAL); - const char *old_value = NULL; + for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL; + attr = attr->next) { - if (target == NULL || value == NULL || name == NULL) { - return; - } + const char *name = (const char *) attr->name; + const char *value = pcmk__xml_attr_value(attr); - old_value = crm_element_value(target, name); - - if (old_value == NULL) { - /* if no previous value, set unexpanded */ - goto set_unexpanded; + if (pcmk_is_set(flags, pcmk__xaf_no_overwrite) + && (crm_element_value(target, name) != NULL)) { + continue; + } - } else if (strstr(value, name) != value) { - goto set_unexpanded; + if (pcmk_is_set(flags, pcmk__xaf_score_update)) { + pcmk__xe_set_score(target, name, value); + } else { + crm_xml_add(target, name, value); + } } - name_len = strlen(name); - value_len = strlen(value); - if (value_len < (name_len + 2) - || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) { - goto set_unexpanded; - } + return pcmk_rc_ok; +} - /* if we are expanding ourselves, - * then no previous value was set and leave int_value as 0 - */ - if (old_value != value) { - int_value = char2score(old_value); +/*! + * \internal + * \brief Remove an XML attribute from an element + * + * \param[in,out] element XML element that owns \p attr + * \param[in,out] attr XML attribute to remove from \p element + * + * \return Standard Pacemaker return code (\c EPERM if ACLs prevent removal of + * attributes from \p element, or \c pcmk_rc_ok otherwise) + */ +static int +remove_xe_attr(xmlNode *element, xmlAttr *attr) +{ + if (attr == NULL) { + return pcmk_rc_ok; } - if (value[name_len + 1] != '+') { - const char *offset_s = value + (name_len + 2); + if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) { + // ACLs apply to element, not to particular attributes + crm_trace("ACLs prevent removal of attributes from %s element", + (const char *) element->name); + return EPERM; + } - offset = char2score(offset_s); + if (pcmk__tracking_xml_changes(element, false)) { + // Leave in place (marked for removal) until after diff is calculated + set_parent_flag(element, pcmk__xf_dirty); + pcmk__set_xml_flags((xml_node_private_t *) attr->_private, + pcmk__xf_deleted); + } else { + xmlRemoveProp(attr); } - int_value += offset; + return pcmk_rc_ok; +} - if (int_value > INFINITY) { - int_value = (int)INFINITY; +/*! + * \internal + * \brief Remove a named attribute from an XML element + * + * \param[in,out] element XML element to remove an attribute from + * \param[in] name Name of attribute to remove + */ +void +pcmk__xe_remove_attr(xmlNode *element, const char *name) +{ + if (name != NULL) { + remove_xe_attr(element, xmlHasProp(element, (pcmkXmlStr) name)); } +} - crm_xml_add_int(target, name, int_value); - return; +/*! + * \internal + * \brief Remove a named attribute from an XML element + * + * This is a wrapper for \c pcmk__xe_remove_attr() for use with + * \c pcmk__xml_tree_foreach(). + * + * \param[in,out] xml XML element to remove an attribute from + * \param[in] user_data Name of attribute to remove + * + * \return \c true (to continue traversing the tree) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +bool +pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data) +{ + const char *name = user_data; - set_unexpanded: - if (old_value == value) { - /* the old value is already set, nothing to do */ - return; - } - crm_xml_add(target, name, value); - return; + pcmk__xe_remove_attr(xml, name); + return true; } /*! @@ -599,102 +698,92 @@ pcmk__xe_remove_matching_attrs(xmlNode *element, for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) { next = a->next; // Grab now because attribute might get removed if ((match == NULL) || match(a, user_data)) { - if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) { - crm_trace("ACLs prevent removal of attributes (%s and " - "possibly others) from %s element", - (const char *) a->name, (const char *) element->name); - return; // ACLs apply to element, not particular attributes - } - - if (pcmk__tracking_xml_changes(element, false)) { - // Leave (marked for removal) until after diff is calculated - set_parent_flag(element, pcmk__xf_dirty); - pcmk__set_xml_flags((xml_node_private_t *) a->_private, - pcmk__xf_deleted); - } else { - xmlRemoveProp(a); + if (remove_xe_attr(element, a) != pcmk_rc_ok) { + return; } } } } +/*! + * \internal + * \brief Create a new XML element under a given parent + * + * \param[in,out] parent XML element that will be the new element's parent + * (\c NULL to create a new XML document with the new + * node as root) + * \param[in] name Name of new element + * + * \return Newly created XML element (guaranteed not to be \c NULL) + */ xmlNode * -add_node_copy(xmlNode * parent, xmlNode * src_node) -{ - xmlNode *child = NULL; - - CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL); - - child = xmlDocCopyNode(src_node, parent->doc, 1); - if (child == NULL) { - return NULL; - } - xmlAddChild(parent, child); - pcmk__mark_xml_created(child); - return child; -} - -xmlNode * -create_xml_node(xmlNode * parent, const char *name) +pcmk__xe_create(xmlNode *parent, const char *name) { - xmlDoc *doc = NULL; xmlNode *node = NULL; - if (pcmk__str_empty(name)) { - CRM_CHECK(name != NULL && name[0] == 0, return NULL); - return NULL; - } + CRM_ASSERT(!pcmk__str_empty(name)); if (parent == NULL) { - doc = xmlNewDoc((pcmkXmlStr) "1.0"); - if (doc == NULL) { - return NULL; - } + xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION); + + pcmk__mem_assert(doc); node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL); - if (node == NULL) { - xmlFreeDoc(doc); - return NULL; - } + pcmk__mem_assert(node); + xmlDocSetRootElement(doc, node); } else { node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL); - if (node == NULL) { - return NULL; - } + pcmk__mem_assert(node); } - pcmk__mark_xml_created(node); + + pcmk__xml_mark_created(node); return node; } -xmlNode * -pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content) +/*! + * \internal + * \brief Set a formatted string as an XML node's content + * + * \param[in,out] node Node whose content to set + * \param[in] format <tt>printf(3)</tt>-style format string + * \param[in] ... Arguments for \p format + * + * \note This function escapes special characters. \c xmlNodeSetContent() does + * not. + */ +G_GNUC_PRINTF(2, 3) +void +pcmk__xe_set_content(xmlNode *node, const char *format, ...) { - xmlNode *node = create_xml_node(parent, name); - if (node != NULL) { - xmlNodeSetContent(node, (pcmkXmlStr) content); - } + const char *content = NULL; + char *buf = NULL; - return node; -} + if (strchr(format, '%') == NULL) { + // Nothing to format + content = format; -xmlNode * -pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id, - const char *class_name, const char *text) -{ - xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text); + } else { + va_list ap; - if (class_name != NULL) { - crm_xml_add(node, "class", class_name); - } + va_start(ap, format); - if (id != NULL) { - crm_xml_add(node, "id", id); - } + if (pcmk__str_eq(format, "%s", pcmk__str_none)) { + // No need to make a copy + content = va_arg(ap, const char *); - return node; + } else { + CRM_ASSERT(vasprintf(&buf, format, ap) >= 0); + content = buf; + } + va_end(ap); + } + + xmlNodeSetContent(node, (pcmkXmlStr) content); + free(buf); + } } /*! @@ -710,72 +799,67 @@ pcmk_free_xml_subtree(xmlNode *xml) } static void -free_xml_with_position(xmlNode * child, int position) +free_xml_with_position(xmlNode *child, int position) { - if (child != NULL) { - xmlNode *top = NULL; - xmlDoc *doc = child->doc; - xml_node_private_t *nodepriv = child->_private; - xml_doc_private_t *docpriv = NULL; - - if (doc != NULL) { - top = xmlDocGetRootElement(doc); - } + xmlDoc *doc = NULL; + xml_node_private_t *nodepriv = NULL; - if (doc != NULL && top == child) { - /* Free everything */ - xmlFreeDoc(doc); + if (child == NULL) { + return; + } + doc = child->doc; + nodepriv = child->_private; - } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) { - GString *xpath = NULL; + if ((doc != NULL) && (xmlDocGetRootElement(doc) == child)) { + // Free everything + xmlFreeDoc(doc); + return; + } - pcmk__if_tracing({}, return); - xpath = pcmk__element_xpath(child); - qb_log_from_external_source(__func__, __FILE__, - "Cannot remove %s %x", LOG_TRACE, - __LINE__, 0, (const char *) xpath->str, - nodepriv->flags); - g_string_free(xpath, TRUE); - return; + if (!pcmk__check_acl(child, NULL, pcmk__xf_acl_write)) { + GString *xpath = NULL; - } else { - if (doc && pcmk__tracking_xml_changes(child, FALSE) - && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) { - - GString *xpath = pcmk__element_xpath(child); + pcmk__if_tracing({}, return); + xpath = pcmk__element_xpath(child); + qb_log_from_external_source(__func__, __FILE__, + "Cannot remove %s %x", LOG_TRACE, + __LINE__, 0, xpath->str, nodepriv->flags); + g_string_free(xpath, TRUE); + return; + } - if (xpath != NULL) { - pcmk__deleted_xml_t *deleted_obj = NULL; + if ((doc != NULL) && pcmk__tracking_xml_changes(child, false) + && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) { - crm_trace("Deleting %s %p from %p", - (const char *) xpath->str, child, doc); + xml_doc_private_t *docpriv = doc->_private; + GString *xpath = pcmk__element_xpath(child); - deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t)); - deleted_obj->path = strdup((const char *) xpath->str); + if (xpath != NULL) { + pcmk__deleted_xml_t *deleted_obj = NULL; - CRM_ASSERT(deleted_obj->path != NULL); - g_string_free(xpath, TRUE); + crm_trace("Deleting %s %p from %p", xpath->str, child, doc); - deleted_obj->position = -1; - /* Record the "position" only for XML comments for now */ - if (child->type == XML_COMMENT_NODE) { - if (position >= 0) { - deleted_obj->position = position; + deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t)); + deleted_obj->path = g_string_free(xpath, FALSE); + deleted_obj->position = -1; - } else { - deleted_obj->position = pcmk__xml_position(child, - pcmk__xf_skip); - } - } + // Record the position only for XML comments for now + if (child->type == XML_COMMENT_NODE) { + if (position >= 0) { + deleted_obj->position = position; - docpriv = doc->_private; - docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, deleted_obj); - pcmk__set_xml_doc_flag(child, pcmk__xf_dirty); + } else { + deleted_obj->position = pcmk__xml_position(child, + pcmk__xf_skip); } } - pcmk_free_xml_subtree(child); + + docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, + deleted_obj); + pcmk__set_xml_doc_flag(child, pcmk__xf_dirty); } } + pcmk_free_xml_subtree(child); } @@ -785,171 +869,48 @@ free_xml(xmlNode * child) free_xml_with_position(child, -1); } +/*! + * \internal + * \brief Make a deep copy of an XML node under a given parent + * + * \param[in,out] parent XML element that will be the copy's parent (\c NULL + * to create a new XML document with the copy as root) + * \param[in] src XML node to copy + * + * \return Deep copy of \p src, or \c NULL if \p src is \c NULL + */ xmlNode * -copy_xml(xmlNode * src) -{ - xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0"); - xmlNode *copy = xmlDocCopyNode(src, doc, 1); - - CRM_ASSERT(copy != NULL); - xmlDocSetRootElement(doc, copy); - return copy; -} - -xmlNode * -string2xml(const char *input) -{ - xmlNode *xml = NULL; - xmlDocPtr output = NULL; - xmlParserCtxtPtr ctxt = NULL; - const xmlError *last_error = NULL; - - if (input == NULL) { - crm_err("Can't parse NULL input"); - return NULL; - } - - /* create a parser context */ - ctxt = xmlNewParserCtxt(); - CRM_CHECK(ctxt != NULL, return NULL); - - xmlCtxtResetLastError(ctxt); - xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err); - output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); - - if (output == NULL) { - output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS_WITH_RECOVER); - if (output) { - crm_warn("Successfully recovered from XML errors " - "(note: a future release will treat this as a fatal failure)"); - } - } - - if (output) { - xml = xmlDocGetRootElement(output); - } - last_error = xmlCtxtGetLastError(ctxt); - if (last_error && last_error->code != XML_ERR_OK) { - /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */ - /* - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors - */ - crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s", - last_error->domain, last_error->level, last_error->code, last_error->message); - - if (last_error->code == XML_ERR_DOCUMENT_EMPTY) { - CRM_LOG_ASSERT("Cannot parse an empty string"); - - } else if (last_error->code != XML_ERR_DOCUMENT_END) { - crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input), - input); - if (xml != NULL) { - crm_log_xml_err(xml, "Partial"); - } - - } else { - int len = strlen(input); - int lpc = 0; - - while(lpc < len) { - crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc); - lpc += 80; - } - - CRM_LOG_ASSERT("String parsing error"); - } - } - - xmlFreeParserCtxt(ctxt); - return xml; -} - -xmlNode * -stdin2xml(void) -{ - size_t data_length = 0; - size_t read_chars = 0; - - char *xml_buffer = NULL; - xmlNode *xml_obj = NULL; - - do { - xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE); - read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE, - stdin); - data_length += read_chars; - } while (read_chars == PCMK__BUFFER_SIZE); - - if (data_length == 0) { - crm_warn("No XML supplied on stdin"); - free(xml_buffer); - return NULL; - } - - xml_buffer[data_length] = '\0'; - xml_obj = string2xml(xml_buffer); - free(xml_buffer); - - crm_log_xml_trace(xml_obj, "Created fragment"); - return xml_obj; -} - -static char * -decompress_file(const char *filename) +pcmk__xml_copy(xmlNode *parent, xmlNode *src) { - char *buffer = NULL; - int rc = 0; - size_t length = 0, read_len = 0; - BZFILE *bz_file = NULL; - FILE *input = fopen(filename, "r"); + xmlNode *copy = NULL; - if (input == NULL) { - crm_perror(LOG_ERR, "Could not open %s for reading", filename); + if (src == NULL) { return NULL; } - bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); - rc = pcmk__bzlib2rc(rc); - - if (rc != pcmk_rc_ok) { - crm_err("Could not prepare to read compressed %s: %s " - CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc); - BZ2_bzReadClose(&rc, bz_file); - fclose(input); - return NULL; - } + if (parent == NULL) { + xmlDoc *doc = NULL; - rc = BZ_OK; - // cppcheck seems not to understand the abort-logic in pcmk__realloc - // cppcheck-suppress memleak - while (rc == BZ_OK) { - buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1); - read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE); + // The copy will be the root element of a new document + CRM_ASSERT(src->type == XML_ELEMENT_NODE); - crm_trace("Read %ld bytes from file: %d", (long)read_len, rc); + doc = xmlNewDoc(PCMK__XML_VERSION); + pcmk__mem_assert(doc); - if (rc == BZ_OK || rc == BZ_STREAM_END) { - length += read_len; - } - } + copy = xmlDocCopyNode(src, doc, 1); + pcmk__mem_assert(copy); - buffer[length] = '\0'; + xmlDocSetRootElement(doc, copy); - rc = pcmk__bzlib2rc(rc); + } else { + copy = xmlDocCopyNode(src, parent->doc, 1); + pcmk__mem_assert(copy); - if (rc != pcmk_rc_ok) { - crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d", - filename, pcmk_rc_str(rc), rc); - free(buffer); - buffer = NULL; + xmlAddChild(parent, copy); } - BZ2_bzReadClose(&rc, bz_file); - fclose(input); - return buffer; + pcmk__xml_mark_created(copy); + return copy; } /*! @@ -986,97 +947,6 @@ pcmk__strip_xml_text(xmlNode *xml) } } -xmlNode * -filename2xml(const char *filename) -{ - xmlNode *xml = NULL; - xmlDocPtr output = NULL; - bool uncompressed = true; - xmlParserCtxtPtr ctxt = NULL; - const xmlError *last_error = NULL; - - /* create a parser context */ - ctxt = xmlNewParserCtxt(); - CRM_CHECK(ctxt != NULL, return NULL); - - xmlCtxtResetLastError(ctxt); - xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err); - - if (filename) { - uncompressed = !pcmk__ends_with_ext(filename, ".bz2"); - } - - if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) { - /* STDIN_FILENO == fileno(stdin) */ - output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, - PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); - - if (output == NULL) { - output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, - PCMK__XML_PARSE_OPTS_WITH_RECOVER); - if (output) { - crm_warn("Successfully recovered from XML errors " - "(note: a future release will treat this as a fatal failure)"); - } - } - - } else if (uncompressed) { - output = xmlCtxtReadFile(ctxt, filename, NULL, - PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); - - if (output == NULL) { - output = xmlCtxtReadFile(ctxt, filename, NULL, - PCMK__XML_PARSE_OPTS_WITH_RECOVER); - if (output) { - crm_warn("Successfully recovered from XML errors " - "(note: a future release will treat this as a fatal failure)"); - } - } - - } else { - char *input = decompress_file(filename); - - output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); - - if (output == NULL) { - output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL, - PCMK__XML_PARSE_OPTS_WITH_RECOVER); - if (output) { - crm_warn("Successfully recovered from XML errors " - "(note: a future release will treat this as a fatal failure)"); - } - } - - free(input); - } - - if (output && (xml = xmlDocGetRootElement(output))) { - pcmk__strip_xml_text(xml); - } - - last_error = xmlCtxtGetLastError(ctxt); - if (last_error && last_error->code != XML_ERR_OK) { - /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */ - /* - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors - */ - crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s", - last_error->domain, last_error->level, last_error->code, last_error->message); - - if (last_error && last_error->code != XML_ERR_OK) { - crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename); - if (xml != NULL) { - crm_log_xml_err(xml, "Partial"); - } - } - } - - xmlFreeParserCtxt(ctxt); - return xml; -} - /*! * \internal * \brief Add a "last written" attribute to an XML element, set to current time @@ -1091,7 +961,7 @@ pcmk__xe_add_last_written(xmlNode *xe) char *now_s = pcmk__epoch2str(NULL, 0); const char *result = NULL; - result = crm_xml_add(xe, XML_CIB_ATTR_WRITTEN, + result = crm_xml_add(xe, PCMK_XA_CIB_LAST_WRITTEN, pcmk__s(now_s, "Could not determine current time")); free(now_s); return result; @@ -1138,598 +1008,202 @@ crm_xml_set_id(xmlNode *xml, const char *format, ...) CRM_ASSERT(len > 0); crm_xml_sanitize_id(id); - crm_xml_add(xml, XML_ATTR_ID, id); + crm_xml_add(xml, PCMK_XA_ID, id); free(id); } /*! * \internal - * \brief Write XML to a file stream + * \brief Check whether a string has XML special characters that must be escaped * - * \param[in] xml XML to write - * \param[in] filename Name of file being written (for logging only) - * \param[in,out] stream Open file stream corresponding to filename - * \param[in] compress Whether to compress XML before writing - * \param[out] nbytes Number of bytes written + * See \c pcmk__xml_escape() and \c pcmk__xml_escape_type for more details. * - * \return Standard Pacemaker return code - */ -static int -write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream, - bool compress, unsigned int *nbytes) -{ - int rc = pcmk_rc_ok; - char *buffer = NULL; - - *nbytes = 0; - crm_log_xml_trace(xml, "writing"); - - buffer = dump_xml_formatted(xml); - CRM_CHECK(buffer && strlen(buffer), - crm_log_xml_warn(xml, "formatting failed"); - rc = pcmk_rc_error; - goto bail); - - if (compress) { - unsigned int in = 0; - BZFILE *bz_file = NULL; - - rc = BZ_OK; - bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30); - rc = pcmk__bzlib2rc(rc); - - if (rc != pcmk_rc_ok) { - crm_warn("Not compressing %s: could not prepare file stream: %s " - CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc); - } else { - BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer)); - rc = pcmk__bzlib2rc(rc); - - if (rc != pcmk_rc_ok) { - crm_warn("Not compressing %s: could not compress data: %s " - CRM_XS " rc=%d errno=%d", - filename, pcmk_rc_str(rc), rc, errno); - } - } - - if (rc == pcmk_rc_ok) { - BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes); - rc = pcmk__bzlib2rc(rc); - - if (rc != pcmk_rc_ok) { - crm_warn("Not compressing %s: could not write compressed data: %s " - CRM_XS " rc=%d errno=%d", - filename, pcmk_rc_str(rc), rc, errno); - *nbytes = 0; // retry without compression - } else { - crm_trace("Compressed XML for %s from %u bytes to %u", - filename, in, *nbytes); - } - } - rc = pcmk_rc_ok; // Either true, or we'll retry without compression - } - - if (*nbytes == 0) { - rc = fprintf(stream, "%s", buffer); - if (rc < 0) { - rc = errno; - crm_perror(LOG_ERR, "writing %s", filename); - } else { - *nbytes = (unsigned int) rc; - rc = pcmk_rc_ok; - } - } - - bail: - - if (fflush(stream) != 0) { - rc = errno; - crm_perror(LOG_ERR, "flushing %s", filename); - } - - /* Don't report error if the file does not support synchronization */ - if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) { - rc = errno; - crm_perror(LOG_ERR, "synchronizing %s", filename); - } - - fclose(stream); - - crm_trace("Saved %d bytes to %s as XML", *nbytes, filename); - free(buffer); - - return rc; -} - -/*! - * \brief Write XML to a file descriptor - * - * \param[in] xml XML to write - * \param[in] filename Name of file being written (for logging only) - * \param[in] fd Open file descriptor corresponding to filename - * \param[in] compress Whether to compress XML before writing + * \param[in] text String to check + * \param[in] type Type of escaping * - * \return Number of bytes written on success, -errno otherwise + * \return \c true if \p text has special characters that need to be escaped, or + * \c false otherwise */ -int -write_xml_fd(const xmlNode *xml, const char *filename, int fd, - gboolean compress) -{ - FILE *stream = NULL; - unsigned int nbytes = 0; - int rc = pcmk_rc_ok; - - CRM_CHECK((xml != NULL) && (fd > 0), return -EINVAL); - stream = fdopen(fd, "w"); - if (stream == NULL) { - return -errno; - } - rc = write_xml_stream(xml, filename, stream, compress, &nbytes); - if (rc != pcmk_rc_ok) { - return pcmk_rc2legacy(rc); - } - return (int) nbytes; -} - -/*! - * \brief Write XML to a file - * - * \param[in] xml XML to write - * \param[in] filename Name of file to write - * \param[in] compress Whether to compress XML before writing - * - * \return Number of bytes written on success, -errno otherwise - */ -int -write_xml_file(const xmlNode *xml, const char *filename, gboolean compress) +bool +pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type) { - FILE *stream = NULL; - unsigned int nbytes = 0; - int rc = pcmk_rc_ok; + if (text == NULL) { + return false; + } + + while (*text != '\0') { + switch (type) { + case pcmk__xml_escape_text: + switch (*text) { + case '<': + case '>': + case '&': + return true; + case '\n': + case '\t': + break; + default: + if (g_ascii_iscntrl(*text)) { + return true; + } + break; + } + break; - CRM_CHECK((xml != NULL) && (filename != NULL), return -EINVAL); - stream = fopen(filename, "w"); - if (stream == NULL) { - return -errno; - } - rc = write_xml_stream(xml, filename, stream, compress, &nbytes); - if (rc != pcmk_rc_ok) { - return pcmk_rc2legacy(rc); - } - return (int) nbytes; -} + case pcmk__xml_escape_attr: + switch (*text) { + case '<': + case '>': + case '&': + case '"': + return true; + default: + if (g_ascii_iscntrl(*text)) { + return true; + } + break; + } + break; -// Replace a portion of a dynamically allocated string (reallocating memory) -static char * -replace_text(char *text, int start, size_t *length, const char *replace) -{ - size_t offset = strlen(replace) - 1; // We have space for 1 char already + case pcmk__xml_escape_attr_pretty: + switch (*text) { + case '\n': + case '\r': + case '\t': + case '"': + return true; + default: + break; + } + break; - *length += offset; - text = pcmk__realloc(text, *length); + default: // Invalid enum value + CRM_ASSERT(false); + break; + } - for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) { - text[lpc] = text[lpc - offset]; + text = g_utf8_next_char(text); } - - memcpy(text + start, replace, offset + 1); - return text; + return false; } /*! + * \internal * \brief Replace special characters with their XML escape sequences * * \param[in] text Text to escape + * \param[in] type Type of escaping * * \return Newly allocated string equivalent to \p text but with special - * characters replaced with XML escape sequences (or NULL if \p text - * is NULL) + * characters replaced with XML escape sequences (or \c NULL if \p text + * is \c NULL). If \p text is not \c NULL, the return value is + * guaranteed not to be \c NULL. + * + * \note There are libxml functions that purport to do this: + * \c xmlEncodeEntitiesReentrant() and \c xmlEncodeSpecialChars(). + * However, their escaping is incomplete. See: + * https://discourse.gnome.org/t/intended-use-of-xmlencodeentitiesreentrant-vs-xmlencodespecialchars/19252 + * \note The caller is responsible for freeing the return value using + * \c g_free(). */ -char * -crm_xml_escape(const char *text) +gchar * +pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type) { - size_t length; - char *copy; - - /* - * When xmlCtxtReadDoc() parses < and friends in a - * value, it converts them to their human readable - * form. - * - * If one uses xmlNodeDump() to convert it back to a - * string, all is well, because special characters are - * converted back to their escape sequences. - * - * However xmlNodeDump() is randomly dog slow, even with the same - * input. So we need to replicate the escaping in our custom - * version so that the result can be re-parsed by xmlCtxtReadDoc() - * when necessary. - */ + GString *copy = NULL; if (text == NULL) { return NULL; } + copy = g_string_sized_new(strlen(text)); - length = 1 + strlen(text); - copy = strdup(text); - CRM_ASSERT(copy != NULL); - for (size_t index = 0; index < length; index++) { - if(copy[index] & 0x80 && copy[index+1] & 0x80){ - index++; - break; - } - switch (copy[index]) { - case 0: - break; - case '<': - copy = replace_text(copy, index, &length, "<"); - break; - case '>': - copy = replace_text(copy, index, &length, ">"); - break; - case '"': - copy = replace_text(copy, index, &length, """); - break; - case '\'': - copy = replace_text(copy, index, &length, "'"); - break; - case '&': - copy = replace_text(copy, index, &length, "&"); - break; - case '\t': - /* Might as well just expand to a few spaces... */ - copy = replace_text(copy, index, &length, " "); - break; - case '\n': - copy = replace_text(copy, index, &length, "\\n"); - break; - case '\r': - copy = replace_text(copy, index, &length, "\\r"); - break; - default: - /* Check for and replace non-printing characters with their octal equivalent */ - if(copy[index] < ' ' || copy[index] > '~') { - char *replace = crm_strdup_printf("\\%.3o", copy[index]); + while (*text != '\0') { + // Don't escape any non-ASCII characters + if ((*text & 0x80) != 0) { + size_t bytes = g_utf8_next_char(text) - text; - copy = replace_text(copy, index, &length, replace); - free(replace); - } - } - } - return copy; -} - -/*! - * \internal - * \brief Append a string representation of an XML element to a buffer - * - * \param[in] data XML whose representation to append - * \param[in] options Group of \p pcmk__xml_fmt_options flags - * \param[in,out] buffer Where to append the content (must not be \p NULL) - * \param[in] depth Current indentation level - */ -static void -dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer, - int depth) -{ - bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); - bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered); - int spaces = pretty? (2 * depth) : 0; - - for (int lpc = 0; lpc < spaces; lpc++) { - g_string_append_c(buffer, ' '); - } - - pcmk__g_strcat(buffer, "<", data->name, NULL); - - for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL; - attr = attr->next) { - - if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) { - pcmk__dump_xml_attr(attr, buffer); + g_string_append_len(copy, text, bytes); + text += bytes; + continue; } - } - - if (data->children == NULL) { - g_string_append(buffer, "/>"); - - } else { - g_string_append_c(buffer, '>'); - } - if (pretty) { - g_string_append_c(buffer, '\n'); - } - - if (data->children) { - for (const xmlNode *child = data->children; child != NULL; - child = child->next) { - pcmk__xml2text(child, options, buffer, depth + 1); - } + switch (type) { + case pcmk__xml_escape_text: + switch (*text) { + case '<': + g_string_append(copy, PCMK__XML_ENTITY_LT); + break; + case '>': + g_string_append(copy, PCMK__XML_ENTITY_GT); + break; + case '&': + g_string_append(copy, PCMK__XML_ENTITY_AMP); + break; + case '\n': + case '\t': + g_string_append_c(copy, *text); + break; + default: + if (g_ascii_iscntrl(*text)) { + g_string_append_printf(copy, "&#x%.2X;", *text); + } else { + g_string_append_c(copy, *text); + } + break; + } + break; - for (int lpc = 0; lpc < spaces; lpc++) { - g_string_append_c(buffer, ' '); - } + case pcmk__xml_escape_attr: + switch (*text) { + case '<': + g_string_append(copy, PCMK__XML_ENTITY_LT); + break; + case '>': + g_string_append(copy, PCMK__XML_ENTITY_GT); + break; + case '&': + g_string_append(copy, PCMK__XML_ENTITY_AMP); + break; + case '"': + g_string_append(copy, PCMK__XML_ENTITY_QUOT); + break; + default: + if (g_ascii_iscntrl(*text)) { + g_string_append_printf(copy, "&#x%.2X;", *text); + } else { + g_string_append_c(copy, *text); + } + break; + } + break; - pcmk__g_strcat(buffer, "</", data->name, ">", NULL); + case pcmk__xml_escape_attr_pretty: + switch (*text) { + case '"': + g_string_append(copy, "\\\""); + break; + case '\n': + g_string_append(copy, "\\n"); + break; + case '\r': + g_string_append(copy, "\\r"); + break; + case '\t': + g_string_append(copy, "\\t"); + break; + default: + g_string_append_c(copy, *text); + break; + } + break; - if (pretty) { - g_string_append_c(buffer, '\n'); + default: // Invalid enum value + CRM_ASSERT(false); + break; } - } -} - -/*! - * \internal - * \brief Append XML text content to a buffer - * - * \param[in] data XML whose content to append - * \param[in] options Group of \p xml_log_options flags - * \param[in,out] buffer Where to append the content (must not be \p NULL) - * \param[in] depth Current indentation level - */ -static void -dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer, - int depth) -{ - /* @COMPAT: Remove when log_data_element() is removed. There are no internal - * code paths to this, except through the deprecated log_data_element(). - */ - bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); - int spaces = pretty? (2 * depth) : 0; - - for (int lpc = 0; lpc < spaces; lpc++) { - g_string_append_c(buffer, ' '); - } - - g_string_append(buffer, (const gchar *) data->content); - - if (pretty) { - g_string_append_c(buffer, '\n'); - } -} - -/*! - * \internal - * \brief Append XML CDATA content to a buffer - * - * \param[in] data XML whose content to append - * \param[in] options Group of \p pcmk__xml_fmt_options flags - * \param[in,out] buffer Where to append the content (must not be \p NULL) - * \param[in] depth Current indentation level - */ -static void -dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer, - int depth) -{ - bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); - int spaces = pretty? (2 * depth) : 0; - - for (int lpc = 0; lpc < spaces; lpc++) { - g_string_append_c(buffer, ' '); - } - - pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>", - NULL); - if (pretty) { - g_string_append_c(buffer, '\n'); + text = g_utf8_next_char(text); } -} - -/*! - * \internal - * \brief Append an XML comment to a buffer - * - * \param[in] data XML whose content to append - * \param[in] options Group of \p pcmk__xml_fmt_options flags - * \param[in,out] buffer Where to append the content (must not be \p NULL) - * \param[in] depth Current indentation level - */ -static void -dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer, - int depth) -{ - bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); - int spaces = pretty? (2 * depth) : 0; - - for (int lpc = 0; lpc < spaces; lpc++) { - g_string_append_c(buffer, ' '); - } - - pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL); - - if (pretty) { - g_string_append_c(buffer, '\n'); - } -} - -/*! - * \internal - * \brief Get a string representation of an XML element type - * - * \param[in] type XML element type - * - * \return String representation of \p type - */ -static const char * -xml_element_type2str(xmlElementType type) -{ - static const char *const element_type_names[] = { - [XML_ELEMENT_NODE] = "element", - [XML_ATTRIBUTE_NODE] = "attribute", - [XML_TEXT_NODE] = "text", - [XML_CDATA_SECTION_NODE] = "CDATA section", - [XML_ENTITY_REF_NODE] = "entity reference", - [XML_ENTITY_NODE] = "entity", - [XML_PI_NODE] = "PI", - [XML_COMMENT_NODE] = "comment", - [XML_DOCUMENT_NODE] = "document", - [XML_DOCUMENT_TYPE_NODE] = "document type", - [XML_DOCUMENT_FRAG_NODE] = "document fragment", - [XML_NOTATION_NODE] = "notation", - [XML_HTML_DOCUMENT_NODE] = "HTML document", - [XML_DTD_NODE] = "DTD", - [XML_ELEMENT_DECL] = "element declaration", - [XML_ATTRIBUTE_DECL] = "attribute declaration", - [XML_ENTITY_DECL] = "entity declaration", - [XML_NAMESPACE_DECL] = "namespace declaration", - [XML_XINCLUDE_START] = "XInclude start", - [XML_XINCLUDE_END] = "XInclude end", - }; - - if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) { - return "unrecognized type"; - } - return element_type_names[type]; -} - -/*! - * \internal - * \brief Create a text representation of an XML object - * - * \param[in] data XML to convert - * \param[in] options Group of \p pcmk__xml_fmt_options flags - * \param[in,out] buffer Where to store the text (must not be \p NULL) - * \param[in] depth Current indentation level - */ -void -pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer, - int depth) -{ - if (data == NULL) { - crm_trace("Nothing to dump"); - return; - } - - CRM_ASSERT(buffer != NULL); - CRM_CHECK(depth >= 0, depth = 0); - - switch(data->type) { - case XML_ELEMENT_NODE: - /* Handle below */ - dump_xml_element(data, options, buffer, depth); - break; - case XML_TEXT_NODE: - if (pcmk_is_set(options, pcmk__xml_fmt_text)) { - dump_xml_text(data, options, buffer, depth); - } - break; - case XML_COMMENT_NODE: - dump_xml_comment(data, options, buffer, depth); - break; - case XML_CDATA_SECTION_NODE: - dump_xml_cdata(data, options, buffer, depth); - break; - default: - crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d", - xml_element_type2str(data->type), data->type); - break; - } -} - -char * -dump_xml_formatted_with_text(const xmlNode *xml) -{ - /* libxml's xmlNodeDumpOutput() would work here since we're not specifically - * filtering out any nodes. However, use pcmk__xml2text() for consistency, - * to escape attribute values, and to allow a const argument. - */ - char *buffer = NULL; - GString *g_buffer = g_string_sized_new(1024); - - pcmk__xml2text(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, g_buffer, 0); - - pcmk__str_update(&buffer, g_buffer->str); - g_string_free(g_buffer, TRUE); - return buffer; -} - -char * -dump_xml_formatted(const xmlNode *xml) -{ - char *buffer = NULL; - GString *g_buffer = g_string_sized_new(1024); - - pcmk__xml2text(xml, pcmk__xml_fmt_pretty, g_buffer, 0); - - pcmk__str_update(&buffer, g_buffer->str); - g_string_free(g_buffer, TRUE); - return buffer; -} - -char * -dump_xml_unformatted(const xmlNode *xml) -{ - char *buffer = NULL; - GString *g_buffer = g_string_sized_new(1024); - - pcmk__xml2text(xml, 0, g_buffer, 0); - - pcmk__str_update(&buffer, g_buffer->str); - g_string_free(g_buffer, TRUE); - return buffer; -} - -int -pcmk__xml2fd(int fd, xmlNode *cur) -{ - bool success; - - xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL); - CRM_ASSERT(fd_out != NULL); - xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL); - - success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1; - - success = xmlOutputBufferClose(fd_out) != -1 && success; - - if (!success) { - return EIO; - } - - fsync(fd); - return pcmk_rc_ok; -} - -void -xml_remove_prop(xmlNode * obj, const char *name) -{ - if (crm_element_value(obj, name) == NULL) { - return; - } - - if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) { - crm_trace("Cannot remove %s from %s", name, obj->name); - - } else if (pcmk__tracking_xml_changes(obj, FALSE)) { - /* Leave in place (marked for removal) until after the diff is calculated */ - xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name); - xml_node_private_t *nodepriv = attr->_private; - - set_parent_flag(obj, pcmk__xf_dirty); - pcmk__set_xml_flags(nodepriv, pcmk__xf_deleted); - } else { - xmlUnsetProp(obj, (pcmkXmlStr) name); - } -} - -void -save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename) -{ - char *f = NULL; - - if (filename == NULL) { - char *uuid = crm_generate_uuid(); - - f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid); - filename = f; - free(uuid); - } - - crm_info("Saving %s to %s", desc, filename); - write_xml_file(xml, filename, FALSE); - free(f); + return g_string_free(copy, FALSE); } /*! @@ -1781,7 +1255,7 @@ mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name, nodepriv->flags = 0; // Check ACLs and mark restored value for later removal - xml_remove_prop(new_xml, attr_name); + remove_xe_attr(new_xml, attr); crm_trace("XML attribute %s=%s was removed from %s", attr_name, old_value, element); @@ -1955,10 +1429,10 @@ static void mark_child_deleted(xmlNode *old_child, xmlNode *new_parent) { // Re-create the child element so we can check ACLs - xmlNode *candidate = add_node_copy(new_parent, old_child); + xmlNode *candidate = pcmk__xml_copy(new_parent, old_child); // Clear flags on new child and its children - reset_xml_node_flags(candidate); + pcmk__xml_tree_foreach(candidate, reset_xml_node_flags, NULL); // Check whether ACLs allow the deletion pcmk__apply_acl(xmlDocGetRootElement(candidate->doc)); @@ -1979,8 +1453,9 @@ mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child, { xml_node_private_t *nodepriv = new_child->_private; - crm_trace("Child element %s with id='%s' moved from position %d to %d under %s", - new_child->name, (ID(new_child)? ID(new_child) : "<no id>"), + crm_trace("Child element %s with " + PCMK_XA_ID "='%s' moved from position %d to %d under %s", + new_child->name, pcmk__s(pcmk__xe_id(new_child), "<no id>"), p_old, p_new, new_parent->name); pcmk__mark_xml_node_dirty(new_parent); pcmk__set_xml_flags(nodepriv, pcmk__xf_moved); @@ -1997,12 +1472,13 @@ mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child, static void mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top) { - xmlNode *cIter = NULL; + xmlNode *old_child = NULL; + xmlNode *new_child = NULL; xml_node_private_t *nodepriv = NULL; CRM_CHECK(new_xml != NULL, return); if (old_xml == NULL) { - pcmk__mark_xml_created(new_xml); + pcmk__xml_mark_created(new_xml); pcmk__apply_creation_acl(new_xml, check_top); return; } @@ -2019,13 +1495,13 @@ mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top) xml_diff_attrs(old_xml, new_xml); // Check for differences in the original children - for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) { - xmlNode *old_child = cIter; - xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true); + for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL; + old_child = pcmk__xml_next(old_child)) { + + new_child = pcmk__xml_match(new_xml, old_child, true); - cIter = pcmk__xml_next(cIter); - if(new_child) { - mark_xml_changes(old_child, new_child, TRUE); + if (new_child != NULL) { + mark_xml_changes(old_child, new_child, true); } else { mark_child_deleted(old_child, new_xml); @@ -2033,16 +1509,19 @@ mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top) } // Check for moved or created children - for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) { - xmlNode *new_child = cIter; - xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true); + new_child = pcmk__xml_first_child(new_xml); + while (new_child != NULL) { + xmlNode *next = pcmk__xml_next(new_child); + + old_child = pcmk__xml_match(old_xml, new_child, true); - cIter = pcmk__xml_next(cIter); - if(old_child == NULL) { + if (old_child == NULL) { // This is a newly created child nodepriv = new_child->_private; pcmk__set_xml_flags(nodepriv, pcmk__xf_skip); - mark_xml_changes(old_child, new_child, TRUE); + + // May free new_child + mark_xml_changes(old_child, new_child, true); } else { /* Check for movement, we already checked for differences */ @@ -2053,6 +1532,8 @@ mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top) mark_child_moved(old_child, new_xml, new_child, p_old, p_new); } } + + new_child = next; } } @@ -2069,7 +1550,8 @@ xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml) { CRM_CHECK((old_xml != NULL) && (new_xml != NULL) && pcmk__xe_is(old_xml, (const char *) new_xml->name) - && pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_none), + && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml), + pcmk__str_none), return); if(xml_tracking_changes(new_xml) == FALSE) { @@ -2079,44 +1561,6 @@ xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml) mark_xml_changes(old_xml, new_xml, FALSE); } -gboolean -can_prune_leaf(xmlNode * xml_node) -{ - xmlNode *cIter = NULL; - gboolean can_prune = TRUE; - - CRM_CHECK(xml_node != NULL, return FALSE); - - if (pcmk__strcase_any_of((const char *) xml_node->name, - XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF, - XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, - NULL)) { - return FALSE; - } - - for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) { - const char *p_name = (const char *) a->name; - - if (strcmp(p_name, XML_ATTR_ID) == 0) { - continue; - } - can_prune = FALSE; - } - - cIter = pcmk__xml_first_child(xml_node); - while (cIter) { - xmlNode *child = cIter; - - cIter = pcmk__xml_next(cIter); - if (can_prune_leaf(child)) { - free_xml(child); - } else { - can_prune = FALSE; - } - } - return can_prune; -} - /*! * \internal * \brief Find a comment with matching content in specified XML @@ -2185,7 +1629,7 @@ pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update) } if (target == NULL) { - add_node_copy(parent, update); + pcmk__xml_copy(parent, update); } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) { xmlFree(target->content); @@ -2195,82 +1639,113 @@ pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update) /*! * \internal - * \brief Make one XML tree match another (in children and attributes) + * \brief Merge one XML tree into another + * + * Here, "merge" means: + * 1. Copy attribute values from \p update to the target, overwriting in case of + * conflict. + * 2. Descend through \p update and the target in parallel. At each level, for + * each child of \p update, look for a matching child of the target. + * a. For each child, if a match is found, go to step 1, recursively merging + * the child of \p update into the child of the target. + * b. Otherwise, copy the child of \p update as a child of the target. + * + * A match is defined as the first child of the same type within the target, + * with: + * * the \c PCMK_XA_ID attribute matching, if set in \p update; otherwise, + * * the \c PCMK_XA_ID_REF attribute matching, if set in \p update + * + * This function does not delete any elements or attributes from the target. It + * may add elements or overwrite attributes, as described above. * * \param[in,out] parent If \p target is NULL and this is not, add or update * child of this XML node that matches \p update * \param[in,out] target If not NULL, update this XML - * \param[in] update Make the desired XML match this (must not be NULL) - * \param[in] as_diff If false, expand "++" when making attributes match + * \param[in] update Make the desired XML match this (must not be \c NULL) + * \param[in] flags Group of <tt>enum pcmk__xa_flags</tt> + * \param[in] as_diff If \c true, preserve order of attributes (deprecated + * since 2.0.5) * - * \note At least one of \p parent and \p target must be non-NULL + * \note At least one of \p parent and \p target must be non-<tt>NULL</tt>. + * \note This function is recursive. For the top-level call, \p parent is + * \c NULL and \p target is not \c NULL. For recursive calls, \p target is + * \c NULL and \p parent is not \c NULL. */ void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, - bool as_diff) + uint32_t flags, bool as_diff) { - xmlNode *a_child = NULL; - const char *object_name = NULL, - *object_href = NULL, - *object_href_val = NULL; + /* @COMPAT Refactor further and staticize after v1 patchset deprecation. + * + * @COMPAT Drop as_diff argument when apply_xml_diff() is dropped. + */ + const char *update_name = NULL; + const char *update_id_attr = NULL; + const char *update_id_val = NULL; + char *trace_s = NULL; -#if XML_PARSER_DEBUG - crm_log_xml_trace(update, "update:"); - crm_log_xml_trace(target, "target:"); -#endif + crm_log_xml_trace(update, "update"); + crm_log_xml_trace(target, "target"); - CRM_CHECK(update != NULL, return); + CRM_CHECK(update != NULL, goto done); if (update->type == XML_COMMENT_NODE) { pcmk__xc_update(parent, target, update); - return; + goto done; } - object_name = (const char *) update->name; - object_href_val = ID(update); - if (object_href_val != NULL) { - object_href = XML_ATTR_ID; + update_name = (const char *) update->name; + + CRM_CHECK(update_name != NULL, goto done); + CRM_CHECK((target != NULL) || (parent != NULL), goto done); + + update_id_val = pcmk__xe_id(update); + if (update_id_val != NULL) { + update_id_attr = PCMK_XA_ID; + } else { - object_href_val = crm_element_value(update, XML_ATTR_IDREF); - object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF; + update_id_val = crm_element_value(update, PCMK_XA_ID_REF); + if (update_id_val != NULL) { + update_id_attr = PCMK_XA_ID_REF; + } } - CRM_CHECK(object_name != NULL, return); - CRM_CHECK(target != NULL || parent != NULL, return); + pcmk__if_tracing( + { + if (update_id_attr != NULL) { + trace_s = crm_strdup_printf("<%s %s=%s/>", + update_name, update_id_attr, + update_id_val); + } else { + trace_s = crm_strdup_printf("<%s/>", update_name); + } + }, + {} + ); if (target == NULL) { - target = pcmk__xe_match(parent, object_name, - object_href, object_href_val); + // Recursive call + target = pcmk__xe_first_child(parent, update_name, update_id_attr, + update_id_val); } if (target == NULL) { - target = create_xml_node(parent, object_name); - CRM_CHECK(target != NULL, return); -#if XML_PARSER_DEBUG - crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"), - object_href ? " " : "", - object_href ? object_href : "", - object_href ? "=" : "", - object_href ? object_href_val : ""); + // Recursive call with no existing matching child + target = pcmk__xe_create(parent, update_name); + crm_trace("Added %s", pcmk__s(trace_s, update_name)); } else { - crm_trace("Found node <%s%s%s%s%s/> to update", - pcmk__s(object_name, "<null>"), - object_href ? " " : "", - object_href ? object_href : "", - object_href ? "=" : "", - object_href ? object_href_val : ""); -#endif + // Either recursive call with match, or top-level call + crm_trace("Found node %s to update", pcmk__s(trace_s, update_name)); } CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return); - if (as_diff == FALSE) { - /* So that expand_plus_plus() gets called */ - copy_in_properties(target, update); + if (!as_diff) { + pcmk__xe_copy_attrs(target, update, flags); } else { - /* No need for expand_plus_plus(), just raw speed */ + // Preserve order of attributes. Don't use pcmk__xe_copy_attrs(). for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL; a = a->next) { const char *p_value = pcmk__xml_attr_value(a); @@ -2281,175 +1756,316 @@ pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, } } - for (a_child = pcmk__xml_first_child(update); a_child != NULL; - a_child = pcmk__xml_next(a_child)) { -#if XML_PARSER_DEBUG - crm_trace("Updating child <%s%s%s%s%s/>", - pcmk__s(object_name, "<null>"), - object_href ? " " : "", - object_href ? object_href : "", - object_href ? "=" : "", - object_href ? object_href_val : ""); -#endif - pcmk__xml_update(target, NULL, a_child, as_diff); - } - -#if XML_PARSER_DEBUG - crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"), - object_href ? " " : "", - object_href ? object_href : "", - object_href ? "=" : "", - object_href ? object_href_val : ""); -#endif -} + for (xmlNode *child = pcmk__xml_first_child(update); child != NULL; + child = pcmk__xml_next(child)) { -gboolean -update_xml_child(xmlNode * child, xmlNode * to_update) -{ - gboolean can_update = TRUE; - xmlNode *child_of_child = NULL; + crm_trace("Updating child of %s", pcmk__s(trace_s, update_name)); + pcmk__xml_update(target, NULL, child, flags, as_diff); + } - CRM_CHECK(child != NULL, return FALSE); - CRM_CHECK(to_update != NULL, return FALSE); + crm_trace("Finished with %s", pcmk__s(trace_s, update_name)); - if (!pcmk__xe_is(to_update, (const char *) child->name)) { - can_update = FALSE; +done: + free(trace_s); +} - } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) { - can_update = FALSE; +/*! + * \internal + * \brief Delete an XML subtree if it matches a search element + * + * A match is defined as follows: + * * \p xml and \p user_data are both element nodes of the same type. + * * If \p user_data has attributes set, \p xml has those attributes set to the + * same values. (\p xml may have additional attributes set to arbitrary + * values.) + * + * \param[in,out] xml XML subtree to delete upon match + * \param[in] user_data Search element + * + * \return \c true to continue traversing the tree, or \c false to stop (because + * \p xml was deleted) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +delete_xe_if_matching(xmlNode *xml, void *user_data) +{ + xmlNode *search = user_data; - } else if (can_update) { -#if XML_PARSER_DEBUG - crm_log_xml_trace(child, "Update match found..."); -#endif - pcmk__xml_update(NULL, child, to_update, false); + if (!pcmk__xe_is(search, (const char *) xml->name)) { + // No match: either not both elements, or different element types + return true; } - for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL; - child_of_child = pcmk__xml_next(child_of_child)) { - /* only update the first one */ - if (can_update) { - break; + for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL; + attr = attr->next) { + + const char *search_val = pcmk__xml_attr_value(attr); + const char *xml_val = crm_element_value(xml, (const char *) attr->name); + + if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) { + // No match: an attr in xml doesn't match the attr in search + return true; } - can_update = update_xml_child(child_of_child, to_update); } - return can_update; + crm_log_xml_trace(xml, "delete-match"); + crm_log_xml_trace(search, "delete-search"); + free_xml(xml); + + // Found a match and deleted it; stop traversing tree + return false; } +/*! + * \internal + * \brief Search an XML tree depth-first and delete the first matching element + * + * This function does not attempt to match the tree root (\p xml). + * + * A match with a node \c node is defined as follows: + * * \c node and \p search are both element nodes of the same type. + * * If \p search has attributes set, \c node has those attributes set to the + * same values. (\c node may have additional attributes set to arbitrary + * values.) + * + * \param[in,out] xml XML subtree to search + * \param[in] search Element to match against + * + * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on + * successful deletion and an error code otherwise) + */ int -find_xml_children(xmlNode ** children, xmlNode * root, - const char *tag, const char *field, const char *value, gboolean search_matches) +pcmk__xe_delete_match(xmlNode *xml, xmlNode *search) { - int match_found = 0; + // See @COMPAT comment in pcmk__xe_replace_match() + CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL); - CRM_CHECK(root != NULL, return FALSE); - CRM_CHECK(children != NULL, return FALSE); - - if ((tag != NULL) && !pcmk__xe_is(root, tag)) { + for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL; + xml = pcmk__xe_next(xml)) { - } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) { - - } else { - if (*children == NULL) { - *children = create_xml_node(NULL, __func__); + if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) { + // Found and deleted an element + return pcmk_rc_ok; } - add_node_copy(*children, root); - match_found = 1; } - if (search_matches || match_found == 0) { - xmlNode *child = NULL; + // No match found in this subtree + return ENXIO; +} - for (child = pcmk__xml_first_child(root); child != NULL; - child = pcmk__xml_next(child)) { - match_found += find_xml_children(children, child, tag, field, value, search_matches); - } - } +/*! + * \internal + * \brief Replace one XML node with a copy of another XML node + * + * This function handles change tracking and applies ACLs. + * + * \param[in,out] old XML node to replace + * \param[in] new XML node to copy as replacement for \p old + * + * \note This frees \p old. + */ +static void +replace_node(xmlNode *old, xmlNode *new) +{ + new = xmlCopyNode(new, 1); + pcmk__mem_assert(new); - return match_found; + // May be unnecessary but avoids slight changes to some test outputs + pcmk__xml_tree_foreach(new, reset_xml_node_flags, NULL); + + old = xmlReplaceNode(old, new); + + if (xml_tracking_changes(new)) { + // Replaced sections may have included relevant ACLs + pcmk__apply_acl(new); + } + xml_calculate_changes(old, new); + xmlFreeNode(old); } -gboolean -replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only) +/*! + * \internal + * \brief Replace one XML subtree with a copy of another if the two match + * + * A match is defined as follows: + * * \p xml and \p user_data are both element nodes of the same type. + * * If \p user_data has the \c PCMK_XA_ID attribute set, then \p xml has + * \c PCMK_XA_ID set to the same value. + * + * \param[in,out] xml XML subtree to replace with \p user_data upon match + * \param[in] user_data XML to replace \p xml with a copy of upon match + * + * \return \c true to continue traversing the tree, or \c false to stop (because + * \p xml was replaced by \p user_data) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +replace_xe_if_matching(xmlNode *xml, void *user_data) { - gboolean can_delete = FALSE; - xmlNode *child_of_child = NULL; + xmlNode *replace = user_data; + const char *xml_id = NULL; + const char *replace_id = NULL; - const char *up_id = NULL; - const char *child_id = NULL; - const char *right_val = NULL; + xml_id = pcmk__xe_id(xml); + replace_id = pcmk__xe_id(replace); - CRM_CHECK(child != NULL, return FALSE); - CRM_CHECK(update != NULL, return FALSE); + if (!pcmk__xe_is(replace, (const char *) xml->name)) { + // No match: either not both elements, or different element types + return true; + } - up_id = ID(update); - child_id = ID(child); + if ((replace_id != NULL) + && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) { - if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) { - can_delete = TRUE; + // No match: ID was provided in replace and doesn't match xml's ID + return true; } - if (!pcmk__xe_is(update, (const char *) child->name)) { - can_delete = FALSE; - } - if (can_delete && delete_only) { - for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL; - a = a->next) { - const char *p_name = (const char *) a->name; - const char *p_value = pcmk__xml_attr_value(a); - right_val = crm_element_value(child, p_name); - if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) { - can_delete = FALSE; - } + crm_log_xml_trace(xml, "replace-match"); + crm_log_xml_trace(replace, "replace-with"); + replace_node(xml, replace); + + // Found a match and replaced it; stop traversing tree + return false; +} + +/*! + * \internal + * \brief Search an XML tree depth-first and replace the first matching element + * + * This function does not attempt to match the tree root (\p xml). + * + * A match with a node \c node is defined as follows: + * * \c node and \p replace are both element nodes of the same type. + * * If \p replace has the \c PCMK_XA_ID attribute set, then \c node has + * \c PCMK_XA_ID set to the same value. + * + * \param[in,out] xml XML tree to search + * \param[in] replace XML to replace a matching element with a copy of + * + * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on + * successful replacement and an error code otherwise) + */ +int +pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace) +{ + /* @COMPAT Some of this behavior (like not matching the tree root, which is + * allowed by pcmk__xe_update_match()) is questionable for general use but + * required for backward compatibility by cib_process_replace() and + * cib_process_delete(). Behavior can change at a major version release if + * desired. + */ + CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL); + + for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL; + xml = pcmk__xe_next(xml)) { + + if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) { + // Found and replaced an element + return pcmk_rc_ok; } } - if (can_delete && parent != NULL) { - crm_log_xml_trace(child, "Delete match found..."); - if (delete_only || update == NULL) { - free_xml(child); + // No match found in this subtree + return ENXIO; +} - } else { - xmlNode *old = child; - xmlNode *new = xmlCopyNode(update, 1); +//! User data for \c update_xe_if_matching() +struct update_data { + xmlNode *update; //!< Update source + uint32_t flags; //!< Group of <tt>enum pcmk__xa_flags</tt> +}; - CRM_ASSERT(new != NULL); +/*! + * \internal + * \brief Update one XML subtree with another if the two match + * + * "Update" means to merge a source subtree into a target subtree (see + * \c pcmk__xml_update()). + * + * A match is defined as follows: + * * \p xml and \p user_data->update are both element nodes of the same type. + * * \p xml and \p user_data->update have the same \c PCMK_XA_ID attribute + * value, or \c PCMK_XA_ID is unset in both + * + * \param[in,out] xml XML subtree to update with \p user_data->update + * upon match + * \param[in] user_data <tt>struct update_data</tt> object + * + * \return \c true to continue traversing the tree, or \c false to stop (because + * \p xml was updated by \p user_data->update) + * + * \note This is compatible with \c pcmk__xml_tree_foreach(). + */ +static bool +update_xe_if_matching(xmlNode *xml, void *user_data) +{ + struct update_data *data = user_data; + xmlNode *update = data->update; - // May be unnecessary but avoids slight changes to some test outputs - reset_xml_node_flags(new); + if (!pcmk__xe_is(update, (const char *) xml->name)) { + // No match: either not both elements, or different element types + return true; + } - old = xmlReplaceNode(old, new); + if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) { + // No match: ID mismatch + return true; + } - if (xml_tracking_changes(new)) { - // Replaced sections may have included relevant ACLs - pcmk__apply_acl(new); - } - xml_calculate_changes(old, new); - xmlFreeNode(old); - } - return TRUE; + crm_log_xml_trace(xml, "update-match"); + crm_log_xml_trace(update, "update-with"); + pcmk__xml_update(NULL, xml, update, data->flags, false); - } else if (can_delete) { - crm_log_xml_debug(child, "Cannot delete the search root"); - can_delete = FALSE; - } + // Found a match and replaced it; stop traversing tree + return false; +} - child_of_child = pcmk__xml_first_child(child); - while (child_of_child) { - xmlNode *next = pcmk__xml_next(child_of_child); +/*! + * \internal + * \brief Search an XML tree depth-first and update the first matching element + * + * "Update" means to merge a source subtree into a target subtree (see + * \c pcmk__xml_update()). + * + * A match with a node \c node is defined as follows: + * * \c node and \p update are both element nodes of the same type. + * * \c node and \p update have the same \c PCMK_XA_ID attribute value, or + * \c PCMK_XA_ID is unset in both + * + * \param[in,out] xml XML tree to search + * \param[in] update XML to update a matching element with + * \param[in] flags Group of <tt>enum pcmk__xa_flags</tt> + * + * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on + * successful update and an error code otherwise) + */ +int +pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags) +{ + /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we + * compare IDs only if the equivalent of the update argument has an ID. + * Here, we're stricter: we consider it a mismatch if only one element has + * an ID attribute, or if both elements have IDs but they don't match. + * + * Perhaps we should align the behavior at a major version release. + */ + struct update_data data = { + .update = update, + .flags = flags, + }; - can_delete = replace_xml_child(child, child_of_child, update, delete_only); + CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL); - /* only delete the first one */ - if (can_delete) { - child_of_child = NULL; - } else { - child_of_child = next; - } + if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) { + // Found and updated an element + return pcmk_rc_ok; } - return can_delete; + // No match found in this subtree + return ENXIO; } xmlNode * @@ -2461,61 +2077,42 @@ sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive) CRM_CHECK(input != NULL, return NULL); - result = create_xml_node(parent, (const char *) input->name); + result = pcmk__xe_create(parent, (const char *) input->name); nvpairs = pcmk_xml_attrs2nvpairs(input); nvpairs = pcmk_sort_nvpairs(nvpairs); pcmk_nvpairs2xml_attrs(nvpairs, result); pcmk_free_nvpairs(nvpairs); - for (child = pcmk__xml_first_child(input); child != NULL; - child = pcmk__xml_next(child)) { + for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL; + child = pcmk__xe_next(child)) { if (recursive) { sorted_xml(child, result, recursive); } else { - add_node_copy(result, child); + pcmk__xml_copy(result, child); } } return result; } -xmlNode * -first_named_child(const xmlNode *parent, const char *name) -{ - xmlNode *match = NULL; - - for (match = pcmk__xe_first_child(parent); match != NULL; - match = pcmk__xe_next(match)) { - /* - * name == NULL gives first child regardless of name; this is - * semantically incorrect in this function, but may be necessary - * due to prior use of xml_child_iter_filter - */ - if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) { - return match; - } - } - return NULL; -} - /*! - * \brief Get next instance of same XML tag + * \internal + * \brief Get next sibling XML element with the same name as a given element * - * \param[in] sibling XML tag to start from + * \param[in] node XML element to start from * - * \return Next sibling XML tag with same name + * \return Next sibling XML element with same name */ xmlNode * -crm_next_same_xml(const xmlNode *sibling) +pcmk__xe_next_same(const xmlNode *node) { - xmlNode *match = pcmk__xe_next(sibling); + for (xmlNode *match = pcmk__xe_next(node); match != NULL; + match = pcmk__xe_next(match)) { - while (match != NULL) { - if (pcmk__xe_is(match, (const char *) sibling->name)) { + if (pcmk__xe_is(match, (const char *) node->name)) { return match; } - match = pcmk__xe_next(match); } return NULL; } @@ -2554,31 +2151,32 @@ crm_xml_cleanup(void) xmlNode * expand_idref(xmlNode * input, xmlNode * top) { + char *xpath = NULL; const char *ref = NULL; - xmlNode *result = input; + xmlNode *result = NULL; - if (result == NULL) { + if (input == NULL) { return NULL; - - } else if (top == NULL) { - top = input; } - ref = crm_element_value(result, XML_ATTR_IDREF); - if (ref != NULL) { - char *xpath_string = crm_strdup_printf("//%s[@" XML_ATTR_ID "='%s']", - result->name, ref); + ref = crm_element_value(input, PCMK_XA_ID_REF); + if (ref == NULL) { + return input; + } - result = get_xpath_object(xpath_string, top, LOG_ERR); - if (result == NULL) { - char *nodePath = (char *)xmlGetNodePath(top); + if (top == NULL) { + top = input; + } - crm_err("No match for %s found in %s: Invalid configuration", - xpath_string, pcmk__s(nodePath, "unrecognizable path")); - free(nodePath); - } - free(xpath_string); + xpath = crm_strdup_printf("//%s[@" PCMK_XA_ID "='%s']", input->name, ref); + result = get_xpath_object(xpath, top, LOG_DEBUG); + if (result == NULL) { // Not possible with schema validation enabled + pcmk__config_err("Ignoring invalid %s configuration: " + PCMK_XA_ID_REF " '%s' does not reference " + "a valid object " CRM_XS " xpath=%s", + input->name, ref, xpath); } + free(xpath); return result; } @@ -2610,25 +2208,52 @@ pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns) return ret; } -char * -pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec) +static char * +find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec) { - char *base = pcmk__xml_artefact_root(ns), *ret = NULL; + char *ret = NULL; switch (ns) { case pcmk__xml_artefact_ns_legacy_rng: case pcmk__xml_artefact_ns_base_rng: - ret = crm_strdup_printf("%s/%s.rng", base, filespec); + if (pcmk__ends_with(filespec, ".rng")) { + ret = crm_strdup_printf("%s/%s", path, filespec); + } else { + ret = crm_strdup_printf("%s/%s.rng", path, filespec); + } break; case pcmk__xml_artefact_ns_legacy_xslt: case pcmk__xml_artefact_ns_base_xslt: - ret = crm_strdup_printf("%s/%s.xsl", base, filespec); + if (pcmk__ends_with(filespec, ".xsl")) { + ret = crm_strdup_printf("%s/%s", path, filespec); + } else { + ret = crm_strdup_printf("%s/%s.xsl", path, filespec); + } break; default: crm_err("XML artefact family specified as %u not recognized", ns); } + + return ret; +} + +char * +pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec) +{ + struct stat sb; + char *base = pcmk__xml_artefact_root(ns); + char *ret = NULL; + + ret = find_artefact(ns, base, filespec); free(base); + if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) { + const char *remote_schema_dir = pcmk__remote_schema_dir(); + + free(ret); + ret = find_artefact(ns, remote_schema_dir, filespec); + } + return ret; } @@ -2669,8 +2294,9 @@ pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, CRM_ASSERT(handler != NULL); for (xmlNode *node = children; node != NULL; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - pcmk__str_eq(child_element_name, (const char *) node->name, pcmk__str_null_matches)) { + if ((node->type == XML_ELEMENT_NODE) + && ((child_element_name == NULL) + || pcmk__xe_is(node, child_element_name))) { int rc = handler(node, userdata); if (rc != pcmk_rc_ok) { @@ -2690,8 +2316,8 @@ pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id) { - return pcmk__xe_match(parent, node_name, - ((id == NULL)? id : XML_ATTR_ID), id); + return pcmk__xe_first_child(parent, node_name, + ((id == NULL)? id : PCMK_XA_ID), id); } void @@ -2709,12 +2335,28 @@ getDocPtr(xmlNode *node) doc = node->doc; if (doc == NULL) { - doc = xmlNewDoc((pcmkXmlStr) "1.0"); + doc = xmlNewDoc(PCMK__XML_VERSION); xmlDocSetRootElement(doc, node); } return doc; } +xmlNode * +add_node_copy(xmlNode *parent, xmlNode *src_node) +{ + xmlNode *child = NULL; + + CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL); + + child = xmlDocCopyNode(src_node, parent->doc, 1); + if (child == NULL) { + return NULL; + } + xmlAddChild(parent, child); + pcmk__xml_mark_created(child); + return child; +} + int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) { @@ -2732,5 +2374,352 @@ xml_has_children(const xmlNode * xml_root) return FALSE; } +static char * +replace_text(char *text, size_t *index, size_t *length, const char *replace) +{ + // We have space for 1 char already + size_t offset = strlen(replace) - 1; + + if (offset > 0) { + *length += offset; + text = pcmk__realloc(text, *length + 1); + + // Shift characters to the right to make room for the replacement string + for (size_t i = *length; i > (*index + offset); i--) { + text[i] = text[i - offset]; + } + } + + // Replace the character at index by the replacement string + memcpy(text + *index, replace, offset + 1); + + // Reset index to the end of replacement string + *index += offset; + return text; +} + +char * +crm_xml_escape(const char *text) +{ + size_t length = 0; + char *copy = NULL; + + if (text == NULL) { + return NULL; + } + + length = strlen(text); + copy = pcmk__str_copy(text); + for (size_t index = 0; index <= length; index++) { + if(copy[index] & 0x80 && copy[index+1] & 0x80){ + index++; + continue; + } + switch (copy[index]) { + case 0: + // Sanity only; loop should stop at the last non-null byte + break; + case '<': + copy = replace_text(copy, &index, &length, "<"); + break; + case '>': + copy = replace_text(copy, &index, &length, ">"); + break; + case '"': + copy = replace_text(copy, &index, &length, """); + break; + case '\'': + copy = replace_text(copy, &index, &length, "'"); + break; + case '&': + copy = replace_text(copy, &index, &length, "&"); + break; + case '\t': + /* Might as well just expand to a few spaces... */ + copy = replace_text(copy, &index, &length, " "); + break; + case '\n': + copy = replace_text(copy, &index, &length, "\\n"); + break; + case '\r': + copy = replace_text(copy, &index, &length, "\\r"); + break; + default: + /* Check for and replace non-printing characters with their octal equivalent */ + if(copy[index] < ' ' || copy[index] > '~') { + char *replace = crm_strdup_printf("\\%.3o", copy[index]); + + copy = replace_text(copy, &index, &length, replace); + free(replace); + } + } + } + return copy; +} + +xmlNode * +copy_xml(xmlNode *src) +{ + xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNode *copy = NULL; + + pcmk__mem_assert(doc); + + copy = xmlDocCopyNode(src, doc, 1); + pcmk__mem_assert(copy); + + xmlDocSetRootElement(doc, copy); + return copy; +} + +xmlNode * +create_xml_node(xmlNode *parent, const char *name) +{ + // Like pcmk__xe_create(), but returns NULL on failure + xmlNode *node = NULL; + + CRM_CHECK(!pcmk__str_empty(name), return NULL); + + if (parent == NULL) { + xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION); + + if (doc == NULL) { + return NULL; + } + + node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL); + if (node == NULL) { + xmlFreeDoc(doc); + return NULL; + } + xmlDocSetRootElement(doc, node); + + } else { + node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL); + if (node == NULL) { + return NULL; + } + } + pcmk__xml_mark_created(node); + return node; +} + +xmlNode * +pcmk_create_xml_text_node(xmlNode *parent, const char *name, + const char *content) +{ + xmlNode *node = pcmk__xe_create(parent, name); + + pcmk__xe_set_content(node, "%s", content); + return node; +} + +xmlNode * +pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, + const char *class_name, const char *text) +{ + xmlNode *node = pcmk__html_create(parent, element_name, id, class_name); + + pcmk__xe_set_content(node, "%s", text); + return node; +} + +xmlNode * +first_named_child(const xmlNode *parent, const char *name) +{ + return pcmk__xe_first_child(parent, name, NULL, NULL); +} + +xmlNode * +find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find) +{ + xmlNode *result = NULL; + + if (search_path == NULL) { + crm_warn("Will never find <NULL>"); + return NULL; + } + + result = pcmk__xe_first_child(root, search_path, NULL, NULL); + + if (must_find && (result == NULL)) { + crm_warn("Could not find %s in %s", + search_path, + ((root != NULL)? (const char *) root->name : "<NULL>")); + } + + return result; +} + +xmlNode * +crm_next_same_xml(const xmlNode *sibling) +{ + return pcmk__xe_next_same(sibling); +} + +void +xml_remove_prop(xmlNode * obj, const char *name) +{ + pcmk__xe_remove_attr(obj, name); +} + +gboolean +replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only) +{ + bool is_match = false; + const char *child_id = NULL; + const char *update_id = NULL; + + CRM_CHECK(child != NULL, return FALSE); + CRM_CHECK(update != NULL, return FALSE); + + child_id = pcmk__xe_id(child); + update_id = pcmk__xe_id(update); + + /* Match element name and (if provided in update XML) element ID. Don't + * match search root (child is search root if parent == NULL). + */ + is_match = (parent != NULL) + && pcmk__xe_is(update, (const char *) child->name) + && ((update_id == NULL) + || pcmk__str_eq(update_id, child_id, pcmk__str_none)); + + /* For deletion, match all attributes provided in update. A matching node + * can have additional attributes, but values must match for provided ones. + */ + if (is_match && delete_only) { + for (xmlAttr *attr = pcmk__xe_first_attr(update); attr != NULL; + attr = attr->next) { + const char *name = (const char *) attr->name; + const char *update_val = pcmk__xml_attr_value(attr); + const char *child_val = crm_element_value(child, name); + + if (!pcmk__str_eq(update_val, child_val, pcmk__str_casei)) { + is_match = false; + break; + } + } + } + + if (is_match) { + if (delete_only) { + crm_log_xml_trace(child, "delete-match"); + crm_log_xml_trace(update, "delete-search"); + free_xml(child); + + } else { + crm_log_xml_trace(child, "replace-match"); + crm_log_xml_trace(update, "replace-with"); + replace_node(child, update); + } + return TRUE; + } + + // Current node not a match; search the rest of the subtree depth-first + parent = child; + for (child = pcmk__xml_first_child(parent); child != NULL; + child = pcmk__xml_next(child)) { + + // Only delete/replace the first match + if (replace_xml_child(parent, child, update, delete_only)) { + return TRUE; + } + } + + // No match found in this subtree + return FALSE; +} + +gboolean +update_xml_child(xmlNode *child, xmlNode *to_update) +{ + return pcmk__xe_update_match(child, to_update, + pcmk__xaf_score_update) == pcmk_rc_ok; +} + +int +find_xml_children(xmlNode **children, xmlNode *root, const char *tag, + const char *field, const char *value, gboolean search_matches) +{ + int match_found = 0; + + CRM_CHECK(root != NULL, return FALSE); + CRM_CHECK(children != NULL, return FALSE); + + if ((tag != NULL) && !pcmk__xe_is(root, tag)) { + + } else if ((value != NULL) + && !pcmk__str_eq(value, crm_element_value(root, field), + pcmk__str_casei)) { + + } else { + if (*children == NULL) { + *children = pcmk__xe_create(NULL, __func__); + } + pcmk__xml_copy(*children, root); + match_found = 1; + } + + if (search_matches || match_found == 0) { + xmlNode *child = NULL; + + for (child = pcmk__xml_first_child(root); child != NULL; + child = pcmk__xml_next(child)) { + match_found += find_xml_children(children, child, tag, field, value, + search_matches); + } + } + + return match_found; +} + +void +fix_plus_plus_recursive(xmlNode *target) +{ + /* TODO: Remove recursion and use xpath searches for value++ */ + xmlNode *child = NULL; + + for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) { + const char *p_name = (const char *) a->name; + const char *p_value = pcmk__xml_attr_value(a); + + expand_plus_plus(target, p_name, p_value); + } + for (child = pcmk__xe_first_child(target, NULL, NULL, NULL); child != NULL; + child = pcmk__xe_next(child)) { + + fix_plus_plus_recursive(child); + } +} + +void +copy_in_properties(xmlNode *target, const xmlNode *src) +{ + if (src == NULL) { + crm_warn("No node to copy properties from"); + + } else if (target == NULL) { + crm_err("No node to copy properties into"); + + } else { + for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) { + const char *p_name = (const char *) a->name; + const char *p_value = pcmk__xml_attr_value(a); + + expand_plus_plus(target, p_name, p_value); + if (xml_acl_denied(target)) { + crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name); + return; + } + } + } +} + +void +expand_plus_plus(xmlNode * target, const char *name, const char *value) +{ + pcmk__xe_set_score(target, name, value); +} + // LCOV_EXCL_STOP // End deprecated API diff --git a/lib/common/xml_attr.c b/lib/common/xml_attr.c index 427d267..faed37f 100644 --- a/lib/common/xml_attr.c +++ b/lib/common/xml_attr.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,7 +23,6 @@ #include <libxml/xmlIO.h> /* xmlAllocOutputBuffer */ #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc. #include "crmcommon_private.h" @@ -62,8 +61,9 @@ pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data) void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer) { - char *p_value = NULL; - const char *p_name = NULL; + const char *name = NULL; + const char *value = NULL; + gchar *value_esc = NULL; xml_node_private_t *nodepriv = NULL; if (attr == NULL || attr->children == NULL) { @@ -75,10 +75,21 @@ pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer) return; } - p_name = (const char *) attr->name; - p_value = crm_xml_escape((const char *)attr->children->content); - pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"", - NULL); + name = (const char *) attr->name; + value = (const char *) attr->children->content; + if (value == NULL) { + /* Don't print anything for unset attribute. Any null-indicator value, + * including the empty string, could also be a real value that needs to + * be treated differently from "unset". + */ + return; + } - free(p_value); -}
\ No newline at end of file + if (pcmk__xml_needs_escape(value, pcmk__xml_escape_attr)) { + value_esc = pcmk__xml_escape(value, pcmk__xml_escape_attr); + value = value_esc; + } + + pcmk__g_strcat(buffer, " ", name, "=\"", value, "\"", NULL); + g_free(value_esc); +} diff --git a/lib/common/xml_display.c b/lib/common/xml_display.c index 18cd3b9..b563d3a 100644 --- a/lib/common/xml_display.c +++ b/lib/common/xml_display.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. * @@ -12,7 +12,6 @@ #include <libxml/tree.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc. #include "crmcommon_private.h" @@ -96,7 +95,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix, int rc = pcmk_rc_no_output; if (pcmk_is_set(options, pcmk__xml_fmt_open)) { - const char *hidden = crm_element_value(data, "hidden"); + const char *hidden = crm_element_value(data, PCMK__XA_HIDDEN); g_string_truncate(buffer, 0); @@ -110,7 +109,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix, xml_node_private_t *nodepriv = attr->_private; const char *p_name = (const char *) attr->name; const char *p_value = pcmk__xml_attr_value(attr); - char *p_copy = NULL; + gchar *p_copy = NULL; if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) { continue; @@ -120,21 +119,23 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix, if (pcmk_any_flags_set(options, pcmk__xml_fmt_diff_plus |pcmk__xml_fmt_diff_minus) - && (strcmp(XML_DIFF_MARKER, p_name) == 0)) { + && (strcmp(PCMK__XA_CRM_DIFF_MARKER, p_name) == 0)) { continue; } if ((hidden != NULL) && (p_name[0] != '\0') && (strstr(hidden, p_name) != NULL)) { - pcmk__str_update(&p_copy, "*****"); + + p_value = "*****"; } else { - p_copy = crm_xml_escape(p_value); + p_copy = pcmk__xml_escape(p_value, true); + p_value = p_copy; } pcmk__g_strcat(buffer, " ", p_name, "=\"", - pcmk__s(p_copy, "<null>"), "\"", NULL); - free(p_copy); + pcmk__s(p_value, "<null>"), "\"", NULL); + g_free(p_copy); } if ((data->children != NULL) @@ -477,7 +478,7 @@ log_data_element(int log_level, const char *file, const char *function, if (pcmk_is_set(options, pcmk__xml_fmt_pretty) && ((data->children == NULL) - || (crm_element_value(data, XML_DIFF_MARKER) != NULL))) { + || (crm_element_value(data, PCMK__XA_CRM_DIFF_MARKER) != NULL))) { if (pcmk_is_set(options, pcmk__xml_fmt_diff_plus)) { legacy_options |= xml_log_option_diff_all; diff --git a/lib/common/xml_io.c b/lib/common/xml_io.c new file mode 100644 index 0000000..f88e0b5 --- /dev/null +++ b/lib/common/xml_io.c @@ -0,0 +1,840 @@ +/* + * Copyright 2004-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <bzlib.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xmlIO.h> // xmlOutputBuffer* + +#include <crm/crm.h> +#include <crm/common/xml.h> +#include <crm/common/xml_io.h> +#include "crmcommon_private.h" + +/* @COMPAT XML_PARSE_RECOVER allows some XML errors to be silently worked around + * by libxml2, which is potentially ambiguous and dangerous. We should drop it + * when we can break backward compatibility with configurations that might be + * relying on it (i.e. pacemaker 3.0.0). + */ +#define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER (XML_PARSE_NOBLANKS) +#define PCMK__XML_PARSE_OPTS_WITH_RECOVER (XML_PARSE_NOBLANKS \ + |XML_PARSE_RECOVER) + +/*! + * \internal + * \brief Read from \c stdin until EOF or error + * + * \return Newly allocated string containing the bytes read from \c stdin, or + * \c NULL on error + * + * \note The caller is responsible for freeing the return value using \c free(). + */ +static char * +read_stdin(void) +{ + char *buf = NULL; + size_t length = 0; + + do { + buf = pcmk__realloc(buf, length + PCMK__BUFFER_SIZE + 1); + length += fread(buf + length, 1, PCMK__BUFFER_SIZE, stdin); + } while ((feof(stdin) == 0) && (ferror(stdin) == 0)); + + if (ferror(stdin) != 0) { + crm_err("Error reading input from stdin"); + free(buf); + buf = NULL; + } else { + buf[length] = '\0'; + } + clearerr(stdin); + return buf; +} + +/*! + * \internal + * \brief Decompress a <tt>bzip2</tt>-compressed file into a string buffer + * + * \param[in] filename Name of file to decompress + * + * \return Newly allocated string with the decompressed contents of \p filename, + * or \c NULL on error. + * + * \note The caller is responsible for freeing the return value using \c free(). + */ +static char * +decompress_file(const char *filename) +{ + char *buffer = NULL; + int rc = pcmk_rc_ok; + size_t length = 0; + BZFILE *bz_file = NULL; + FILE *input = fopen(filename, "r"); + + if (input == NULL) { + crm_perror(LOG_ERR, "Could not open %s for reading", filename); + return NULL; + } + + bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); + rc = pcmk__bzlib2rc(rc); + if (rc != pcmk_rc_ok) { + crm_err("Could not prepare to read compressed %s: %s " + CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc); + goto done; + } + + // cppcheck seems not to understand the abort-logic in pcmk__realloc + // cppcheck-suppress memleak + do { + int read_len = 0; + + buffer = pcmk__realloc(buffer, length + PCMK__BUFFER_SIZE + 1); + read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE); + + if ((rc == BZ_OK) || (rc == BZ_STREAM_END)) { + crm_trace("Read %ld bytes from file: %d", (long) read_len, rc); + length += read_len; + } + } while (rc == BZ_OK); + + rc = pcmk__bzlib2rc(rc); + if (rc != pcmk_rc_ok) { + rc = pcmk__bzlib2rc(rc); + crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d", + filename, pcmk_rc_str(rc), rc); + free(buffer); + buffer = NULL; + } else { + buffer[length] = '\0'; + } + +done: + BZ2_bzReadClose(&rc, bz_file); + fclose(input); + return buffer; +} + +// @COMPAT Remove macro at 3.0.0 when we drop XML_PARSE_RECOVER +/*! + * \internal + * \brief Try to parse XML first without and then with recovery enabled + * + * \param[out] result Where to store the resulting XML doc (<tt>xmlDoc **</tt>) + * \param[in] fn XML parser function + * \param[in] ... All arguments for \p fn except the final one (an + * \c xmlParserOption group) + */ +#define parse_xml_recover(result, fn, ...) do { \ + *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER); \ + if (*result == NULL) { \ + *result = fn(__VA_ARGS__, PCMK__XML_PARSE_OPTS_WITH_RECOVER); \ + \ + if (*result != NULL) { \ + crm_warn("Successfully recovered from XML errors " \ + "(note: a future release will treat this as a " \ + "fatal failure)"); \ + } \ + } \ + } while (0); + +/*! + * \internal + * \brief Parse XML from a file + * + * \param[in] filename Name of file containing XML (\c NULL or \c "-" for + * \c stdin); if \p filename ends in \c ".bz2", the file + * will be decompressed using \c bzip2 + * + * \return XML tree parsed from the given file; may be \c NULL or only partial + * on error + */ +xmlNode * +pcmk__xml_read(const char *filename) +{ + bool use_stdin = pcmk__str_eq(filename, "-", pcmk__str_null_matches); + xmlNode *xml = NULL; + xmlDoc *output = NULL; + xmlParserCtxt *ctxt = NULL; + const xmlError *last_error = NULL; + + // Create a parser context + ctxt = xmlNewParserCtxt(); + CRM_CHECK(ctxt != NULL, return NULL); + + xmlCtxtResetLastError(ctxt); + xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err); + + if (use_stdin) { + /* @COMPAT After dropping XML_PARSE_RECOVER, we can avoid capturing + * stdin into a buffer and instead call + * xmlCtxtReadFd(ctxt, STDIN_FILENO, NULL, NULL, XML_PARSE_NOBLANKS); + * + * For now we have to save the input so that we can use it twice. + */ + char *input = read_stdin(); + + if (input != NULL) { + parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, + NULL, NULL); + free(input); + } + + } else if (pcmk__ends_with_ext(filename, ".bz2")) { + char *input = decompress_file(filename); + + if (input != NULL) { + parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, + NULL, NULL); + free(input); + } + + } else { + parse_xml_recover(&output, xmlCtxtReadFile, ctxt, filename, NULL); + } + + if (output != NULL) { + xml = xmlDocGetRootElement(output); + if (xml != NULL) { + /* @TODO Should we really be stripping out text? This seems like an + * overly broad way to get rid of whitespace, if that's the goal. + * Text nodes may be invalid in most or all Pacemaker inputs, but + * stripping them in a generic "parse XML from file" function may + * not be the best way to ignore them. + */ + pcmk__strip_xml_text(xml); + } + } + + // @COMPAT At 3.0.0, free xml and return NULL if xml != NULL on error + last_error = xmlCtxtGetLastError(ctxt); + if (last_error != NULL) { + if (xml != NULL) { + crm_log_xml_info(xml, "Partial"); + } + } + + xmlFreeParserCtxt(ctxt); + return xml; +} + +/*! + * \internal + * \brief Parse XML from a string + * + * \param[in] input String to parse + * + * \return XML tree parsed from the given string; may be \c NULL or only partial + * on error + */ +xmlNode * +pcmk__xml_parse(const char *input) +{ + xmlNode *xml = NULL; + xmlDoc *output = NULL; + xmlParserCtxt *ctxt = NULL; + const xmlError *last_error = NULL; + + if (input == NULL) { + return NULL; + } + + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) { + return NULL; + } + + xmlCtxtResetLastError(ctxt); + xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err); + + parse_xml_recover(&output, xmlCtxtReadDoc, ctxt, (pcmkXmlStr) input, NULL, + NULL); + + if (output != NULL) { + xml = xmlDocGetRootElement(output); + } + + // @COMPAT At 3.0.0, free xml and return NULL if xml != NULL; update doxygen + last_error = xmlCtxtGetLastError(ctxt); + if (last_error != NULL) { + if (xml != NULL) { + crm_log_xml_info(xml, "Partial"); + } + } + + xmlFreeParserCtxt(ctxt); + return xml; +} + +/*! + * \internal + * \brief Append a string representation of an XML element to a buffer + * + * \param[in] data XML whose representation to append + * \param[in] options Group of \p pcmk__xml_fmt_options flags + * \param[in,out] buffer Where to append the content (must not be \p NULL) + * \param[in] depth Current indentation level + */ +static void +dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer, + int depth) +{ + bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); + bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered); + int spaces = pretty? (2 * depth) : 0; + + for (int lpc = 0; lpc < spaces; lpc++) { + g_string_append_c(buffer, ' '); + } + + pcmk__g_strcat(buffer, "<", data->name, NULL); + + for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL; + attr = attr->next) { + + if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) { + pcmk__dump_xml_attr(attr, buffer); + } + } + + if (data->children == NULL) { + g_string_append(buffer, "/>"); + + } else { + g_string_append_c(buffer, '>'); + } + + if (pretty) { + g_string_append_c(buffer, '\n'); + } + + if (data->children) { + for (const xmlNode *child = data->children; child != NULL; + child = child->next) { + pcmk__xml_string(child, options, buffer, depth + 1); + } + + for (int lpc = 0; lpc < spaces; lpc++) { + g_string_append_c(buffer, ' '); + } + + pcmk__g_strcat(buffer, "</", data->name, ">", NULL); + + if (pretty) { + g_string_append_c(buffer, '\n'); + } + } +} + +/*! + * \internal + * \brief Append XML text content to a buffer + * + * \param[in] data XML whose content to append + * \param[in] options Group of \p xml_log_options flags + * \param[in,out] buffer Where to append the content (must not be \p NULL) + * \param[in] depth Current indentation level + */ +static void +dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer, + int depth) +{ + bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); + int spaces = pretty? (2 * depth) : 0; + const char *content = (const char *) data->content; + gchar *content_esc = NULL; + + if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) { + content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text); + content = content_esc; + } + + for (int lpc = 0; lpc < spaces; lpc++) { + g_string_append_c(buffer, ' '); + } + + g_string_append(buffer, content); + + if (pretty) { + g_string_append_c(buffer, '\n'); + } + g_free(content_esc); +} + +/*! + * \internal + * \brief Append XML CDATA content to a buffer + * + * \param[in] data XML whose content to append + * \param[in] options Group of \p pcmk__xml_fmt_options flags + * \param[in,out] buffer Where to append the content (must not be \p NULL) + * \param[in] depth Current indentation level + */ +static void +dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer, + int depth) +{ + bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); + int spaces = pretty? (2 * depth) : 0; + + for (int lpc = 0; lpc < spaces; lpc++) { + g_string_append_c(buffer, ' '); + } + + pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>", + NULL); + + if (pretty) { + g_string_append_c(buffer, '\n'); + } +} + +/*! + * \internal + * \brief Append an XML comment to a buffer + * + * \param[in] data XML whose content to append + * \param[in] options Group of \p pcmk__xml_fmt_options flags + * \param[in,out] buffer Where to append the content (must not be \p NULL) + * \param[in] depth Current indentation level + */ +static void +dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer, + int depth) +{ + bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty); + int spaces = pretty? (2 * depth) : 0; + + for (int lpc = 0; lpc < spaces; lpc++) { + g_string_append_c(buffer, ' '); + } + + pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL); + + if (pretty) { + g_string_append_c(buffer, '\n'); + } +} + +/*! + * \internal + * \brief Get a string representation of an XML element type + * + * \param[in] type XML element type + * + * \return String representation of \p type + */ +static const char * +xml_element_type_text(xmlElementType type) +{ + static const char *const element_type_names[] = { + [XML_ELEMENT_NODE] = "element", + [XML_ATTRIBUTE_NODE] = "attribute", + [XML_TEXT_NODE] = "text", + [XML_CDATA_SECTION_NODE] = "CDATA section", + [XML_ENTITY_REF_NODE] = "entity reference", + [XML_ENTITY_NODE] = "entity", + [XML_PI_NODE] = "PI", + [XML_COMMENT_NODE] = "comment", + [XML_DOCUMENT_NODE] = "document", + [XML_DOCUMENT_TYPE_NODE] = "document type", + [XML_DOCUMENT_FRAG_NODE] = "document fragment", + [XML_NOTATION_NODE] = "notation", + [XML_HTML_DOCUMENT_NODE] = "HTML document", + [XML_DTD_NODE] = "DTD", + [XML_ELEMENT_DECL] = "element declaration", + [XML_ATTRIBUTE_DECL] = "attribute declaration", + [XML_ENTITY_DECL] = "entity declaration", + [XML_NAMESPACE_DECL] = "namespace declaration", + [XML_XINCLUDE_START] = "XInclude start", + [XML_XINCLUDE_END] = "XInclude end", + }; + + if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) { + return "unrecognized type"; + } + return element_type_names[type]; +} + +/*! + * \internal + * \brief Create a string representation of an XML object + * + * libxml2's \c xmlNodeDumpOutput() doesn't allow filtering, doesn't escape + * special characters thoroughly, and doesn't allow a const argument. + * + * \param[in] data XML to convert + * \param[in] options Group of \p pcmk__xml_fmt_options flags + * \param[in,out] buffer Where to store the text (must not be \p NULL) + * \param[in] depth Current indentation level + * + * \todo Create a wrapper that doesn't require \p depth. Only used with + * recursive calls currently. + */ +void +pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, + int depth) +{ + if (data == NULL) { + crm_trace("Nothing to dump"); + return; + } + + CRM_ASSERT(buffer != NULL); + CRM_CHECK(depth >= 0, depth = 0); + + switch(data->type) { + case XML_ELEMENT_NODE: + /* Handle below */ + dump_xml_element(data, options, buffer, depth); + break; + case XML_TEXT_NODE: + if (pcmk_is_set(options, pcmk__xml_fmt_text)) { + dump_xml_text(data, options, buffer, depth); + } + break; + case XML_COMMENT_NODE: + dump_xml_comment(data, options, buffer, depth); + break; + case XML_CDATA_SECTION_NODE: + dump_xml_cdata(data, options, buffer, depth); + break; + default: + crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d", + xml_element_type_text(data->type), data->type); + break; + } +} + +/*! + * \internal + * \brief Write a string to a file stream, compressed using \c bzip2 + * + * \param[in] text String to write + * \param[in] filename Name of file being written (for logging only) + * \param[in,out] stream Open file stream to write to + * \param[out] bytes_out Number of bytes written (valid only on success) + * + * \return Standard Pacemaker return code + */ +static int +write_compressed_stream(char *text, const char *filename, FILE *stream, + unsigned int *bytes_out) +{ + unsigned int bytes_in = 0; + int rc = pcmk_rc_ok; + + // (5, 0, 0): (intermediate block size, silent, default workFactor) + BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0); + + rc = pcmk__bzlib2rc(rc); + if (rc != pcmk_rc_ok) { + crm_warn("Not compressing %s: could not prepare file stream: %s " + CRM_XS " rc=%d", + filename, pcmk_rc_str(rc), rc); + goto done; + } + + BZ2_bzWrite(&rc, bz_file, text, strlen(text)); + rc = pcmk__bzlib2rc(rc); + if (rc != pcmk_rc_ok) { + crm_warn("Not compressing %s: could not compress data: %s " + CRM_XS " rc=%d errno=%d", + filename, pcmk_rc_str(rc), rc, errno); + goto done; + } + + BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out); + bz_file = NULL; + rc = pcmk__bzlib2rc(rc); + if (rc != pcmk_rc_ok) { + crm_warn("Not compressing %s: could not write compressed data: %s " + CRM_XS " rc=%d errno=%d", + filename, pcmk_rc_str(rc), rc, errno); + goto done; + } + + crm_trace("Compressed XML for %s from %u bytes to %u", + filename, bytes_in, *bytes_out); + +done: + if (bz_file != NULL) { + BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL); + } + return rc; +} + +/*! + * \internal + * \brief Write XML to a file stream + * + * \param[in] xml XML to write + * \param[in] filename Name of file being written (for logging only) + * \param[in,out] stream Open file stream corresponding to filename (closed + * when this function returns) + * \param[in] compress Whether to compress XML before writing + * \param[out] nbytes Number of bytes written + * + * \return Standard Pacemaker return code + */ +static int +write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream, + bool compress, unsigned int *nbytes) +{ + // @COMPAT Drop nbytes as arg when we drop write_xml_fd()/write_xml_file() + GString *buffer = g_string_sized_new(1024); + unsigned int bytes_out = 0; + int rc = pcmk_rc_ok; + + pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0); + CRM_CHECK(!pcmk__str_empty(buffer->str), + crm_log_xml_info(xml, "dump-failed"); + rc = pcmk_rc_error; + goto done); + + crm_log_xml_trace(xml, "writing"); + + if (compress + && (write_compressed_stream(buffer->str, filename, stream, + &bytes_out) == pcmk_rc_ok)) { + goto done; + } + + rc = fprintf(stream, "%s", buffer->str); + if (rc < 0) { + rc = EIO; + crm_perror(LOG_ERR, "writing %s", filename); + goto done; + } + bytes_out = (unsigned int) rc; + rc = pcmk_rc_ok; + +done: + if (fflush(stream) != 0) { + rc = errno; + crm_perror(LOG_ERR, "flushing %s", filename); + } + + // Don't report error if the file does not support synchronization + if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) { + rc = errno; + crm_perror(LOG_ERR, "synchronizing %s", filename); + } + + fclose(stream); + crm_trace("Saved %u bytes to %s as XML", bytes_out, filename); + + if (nbytes != NULL) { + *nbytes = bytes_out; + } + g_string_free(buffer, TRUE); + return rc; +} + +/*! + * \internal + * \brief Write XML to a file descriptor + * + * \param[in] xml XML to write + * \param[in] filename Name of file being written (for logging only) + * \param[in] fd Open file descriptor corresponding to \p filename + * \param[in] compress If \c true, compress XML before writing + * \param[out] nbytes Number of bytes written (can be \c NULL) + * + * \return Standard Pacemaker return code + */ +int +pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, + bool compress, unsigned int *nbytes) +{ + // @COMPAT Drop compress and nbytes arguments when we drop write_xml_fd() + FILE *stream = NULL; + + CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL); + stream = fdopen(fd, "w"); + if (stream == NULL) { + return errno; + } + + return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream, + compress, nbytes); +} + +/*! + * \internal + * \brief Write XML to a file + * + * \param[in] xml XML to write + * \param[in] filename Name of file to write + * \param[in] compress If \c true, compress XML before writing + * \param[out] nbytes Number of bytes written (can be \c NULL) + * + * \return Standard Pacemaker return code + */ +int +pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress, + unsigned int *nbytes) +{ + // @COMPAT Drop nbytes argument when we drop write_xml_fd() + FILE *stream = NULL; + + CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL); + stream = fopen(filename, "w"); + if (stream == NULL) { + return errno; + } + + return write_xml_stream(xml, filename, stream, compress, nbytes); +} + +/*! + * \internal + * \brief Serialize XML (using libxml) into provided descriptor + * + * \param[in] fd File descriptor to (piece-wise) write to + * \param[in] cur XML subtree to proceed + * + * \return a standard Pacemaker return code + */ +int +pcmk__xml2fd(int fd, xmlNode *cur) +{ + bool success; + + xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL); + pcmk__mem_assert(fd_out); + xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL); + + success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1; + + success = xmlOutputBufferClose(fd_out) != -1 && success; + + if (!success) { + return EIO; + } + + fsync(fd); + return pcmk_rc_ok; +} + +void +save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename) +{ + char *f = NULL; + + if (filename == NULL) { + char *uuid = crm_generate_uuid(); + + f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid); + filename = f; + free(uuid); + } + + crm_info("Saving %s to %s", desc, filename); + pcmk__xml_write_file(xml, filename, false, NULL); + free(f); +} + + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/common/xml_io_compat.h> + +xmlNode * +filename2xml(const char *filename) +{ + return pcmk__xml_read(filename); +} + +xmlNode * +stdin2xml(void) +{ + return pcmk__xml_read(NULL); +} + +xmlNode * +string2xml(const char *input) +{ + return pcmk__xml_parse(input); +} + +char * +dump_xml_formatted(const xmlNode *xml) +{ + char *str = NULL; + GString *buffer = g_string_sized_new(1024); + + pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0); + + str = pcmk__str_copy(buffer->str); + g_string_free(buffer, TRUE); + return str; +} + +char * +dump_xml_formatted_with_text(const xmlNode *xml) +{ + char *str = NULL; + GString *buffer = g_string_sized_new(1024); + + pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0); + + str = pcmk__str_copy(buffer->str); + g_string_free(buffer, TRUE); + return str; +} + +char * +dump_xml_unformatted(const xmlNode *xml) +{ + char *str = NULL; + GString *buffer = g_string_sized_new(1024); + + pcmk__xml_string(xml, 0, buffer, 0); + + str = pcmk__str_copy(buffer->str); + g_string_free(buffer, TRUE); + return str; +} + +int +write_xml_fd(const xmlNode *xml, const char *filename, int fd, + gboolean compress) +{ + unsigned int nbytes = 0; + int rc = pcmk__xml_write_fd(xml, filename, fd, compress, &nbytes); + + if (rc != pcmk_rc_ok) { + return pcmk_rc2legacy(rc); + } + return (int) nbytes; +} + +int +write_xml_file(const xmlNode *xml, const char *filename, gboolean compress) +{ + unsigned int nbytes = 0; + int rc = pcmk__xml_write_file(xml, filename, compress, &nbytes); + + if (rc != pcmk_rc_ok) { + return pcmk_rc2legacy(rc); + } + return (int) nbytes; +} + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/common/xpath.c b/lib/common/xpath.c index d90f1c5..9fc95c5 100644 --- a/lib/common/xpath.c +++ b/lib/common/xpath.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,7 @@ #include <crm_internal.h> #include <stdio.h> #include <string.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include "crmcommon_private.h" @@ -147,7 +147,7 @@ xpath_search(const xmlNode *xml_top, const char *path) CRM_CHECK(strlen(path) > 0, return NULL); xpathCtx = xmlXPathNewContext(xml_top->doc); - CRM_ASSERT(xpathCtx != NULL); + pcmk__mem_assert(xpathCtx); xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); xmlXPathFreeContext(xpathCtx); @@ -186,28 +186,6 @@ crm_foreach_xpath_result(xmlNode *xml, const char *xpath, } xmlNode * -get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level) -{ - xmlNode *result = NULL; - char *xpath_full = NULL; - char *xpath_prefix = NULL; - - if (xml_obj == NULL || xpath == NULL) { - return NULL; - } - - xpath_prefix = (char *)xmlGetNodePath(xml_obj); - - xpath_full = crm_strdup_printf("%s%s", xpath_prefix, xpath); - - result = get_xpath_object(xpath_full, xml_obj, error_level); - - free(xpath_prefix); - free(xpath_full); - return result; -} - -xmlNode * get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level) { int max; @@ -300,9 +278,9 @@ pcmk__element_xpath(const xmlNode *xml) pcmk__g_strcat(xpath, "/", (const char *) xml->name, NULL); } - id = ID(xml); + id = pcmk__xe_id(xml); if (id != NULL) { - pcmk__g_strcat(xpath, "[@" XML_ATTR_ID "='", id, "']", NULL); + pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", id, "']", NULL); } return xpath; @@ -320,7 +298,7 @@ pcmk__xpath_node_id(const char *xpath, const char *node) return retval; } - patt = crm_strdup_printf("/%s[@" XML_ATTR_ID "=", node); + patt = crm_strdup_printf("/%s[@" PCMK_XA_ID "=", node); start = strstr(xpath, patt); if (!start) { @@ -339,6 +317,30 @@ pcmk__xpath_node_id(const char *xpath, const char *node) return retval; } +static int +output_attr_child(xmlNode *child, void *userdata) +{ + pcmk__output_t *out = userdata; + + out->info(out, " Value: %s \t(id=%s)", + crm_element_value(child, PCMK_XA_VALUE), + pcmk__s(pcmk__xe_id(child), "<none>")); + return pcmk_rc_ok; +} + +void +pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search, + const char *name) +{ + if (out == NULL || name == NULL || search == NULL || + search->children == NULL) { + return; + } + + out->info(out, "Multiple attributes match " PCMK_XA_NAME "=%s", name); + pcmk__xe_foreach_child(search, NULL, output_attr_child, out); +} + // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START @@ -363,13 +365,32 @@ xml_get_path(const xmlNode *xml) if (g_path == NULL) { return NULL; } - - path = strdup((const char *) g_path->str); - CRM_ASSERT(path != NULL); - + path = pcmk__str_copy(g_path->str); g_string_free(g_path, TRUE); return path; } +xmlNode * +get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level) +{ + xmlNode *result = NULL; + char *xpath_full = NULL; + char *xpath_prefix = NULL; + + if (xml_obj == NULL || xpath == NULL) { + return NULL; + } + + xpath_prefix = (char *)xmlGetNodePath(xml_obj); + + xpath_full = crm_strdup_printf("%s%s", xpath_prefix, xpath); + + result = get_xpath_object(xpath_full, xml_obj, error_level); + + free(xpath_prefix); + free(xpath_full); + return result; +} + // LCOV_EXCL_STOP // End deprecated API diff --git a/lib/fencing/Makefile.am b/lib/fencing/Makefile.am index 5302035..116b2f4 100644 --- a/lib/fencing/Makefile.am +++ b/lib/fencing/Makefile.am @@ -14,7 +14,7 @@ noinst_HEADERS = fencing_private.h lib_LTLIBRARIES = libstonithd.la -libstonithd_la_LDFLAGS = -version-info 34:4:8 +libstonithd_la_LDFLAGS = -version-info 34:5:8 libstonithd_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libstonithd_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c index b81015e..6b4d1e7 100644 --- a/lib/fencing/st_actions.c +++ b/lib/fencing/st_actions.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -21,7 +21,7 @@ #include <crm/crm.h> #include <crm/stonith-ng.h> #include <crm/fencing/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/services_internal.h> #include "fencing_private.h" @@ -96,12 +96,11 @@ append_config_arg(gpointer key, gpointer value, gpointer user_data) if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei) && !pcmk_stonith_param(key) && (strstr(key, CRM_META) == NULL) - && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) { + && !pcmk__str_eq(key, PCMK_XA_CRM_FEATURE_SET, pcmk__str_none)) { crm_trace("Passing %s=%s with fence action", (const char *) key, (const char *) (value? value : "")); - g_hash_table_insert((GHashTable *) user_data, - strdup(key), strdup(value? value : "")); + pcmk__insert_dup((GHashTable *) user_data, key, pcmk__s(value, "")); } } @@ -143,8 +142,7 @@ make_args(const char *agent, const char *action, const char *target, action = value; } } - g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP), - strdup(action)); + pcmk__insert_dup(arg_list, STONITH_ATTR_ACTION_OP, action); /* If this is a fencing operation against another node, add more standard * arguments. @@ -155,7 +153,7 @@ make_args(const char *agent, const char *action, const char *target, /* Always pass the target's name, per * https://github.com/ClusterLabs/fence-agents/blob/main/doc/FenceAgentAPI.md */ - g_hash_table_insert(arg_list, strdup("nodename"), strdup(target)); + pcmk__insert_dup(arg_list, "nodename", target); // If the target's node ID was specified, pass it, too if (target_nodeid != 0) { @@ -170,7 +168,7 @@ make_args(const char *agent, const char *action, const char *target, // Check whether target must be specified in some other way param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT); if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none) - && !pcmk__str_eq(param, PCMK__VALUE_NONE, pcmk__str_casei)) { + && !pcmk__str_eq(param, PCMK_VALUE_NONE, pcmk__str_casei)) { if (param == NULL) { /* Use the caller's default for pcmk_host_argument, or "port" if @@ -195,7 +193,7 @@ make_args(const char *agent, const char *action, const char *target, } crm_debug("Passing %s='%s' with fence action %s targeting %s", param, alias, action, pcmk__s(target, "no node")); - g_hash_table_insert(arg_list, strdup(param), strdup(alias)); + pcmk__insert_dup(arg_list, param, alias); } } } @@ -267,9 +265,7 @@ stonith__action_create(const char *agent, const char *action_name, int timeout_sec, GHashTable *device_args, GHashTable *port_map, const char *host_arg) { - stonith_action_t *action = calloc(1, sizeof(stonith_action_t)); - - CRM_ASSERT(action != NULL); + stonith_action_t *action = pcmk__assert_alloc(1, sizeof(stonith_action_t)); action->args = make_args(agent, action_name, target, target_nodeid, device_args, port_map, host_arg); @@ -449,16 +445,16 @@ stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result) rc = pcmk_rc2legacy(stonith__result2rc(result)); } - crm_xml_add_int(xml, XML_LRM_ATTR_OPSTATUS, (int) execution_status); - crm_xml_add_int(xml, XML_LRM_ATTR_RC, exit_status); - crm_xml_add(xml, XML_LRM_ATTR_EXIT_REASON, exit_reason); - crm_xml_add(xml, F_STONITH_OUTPUT, action_stdout); + crm_xml_add_int(xml, PCMK__XA_OP_STATUS, (int) execution_status); + crm_xml_add_int(xml, PCMK__XA_RC_CODE, exit_status); + crm_xml_add(xml, PCMK_XA_EXIT_REASON, exit_reason); + crm_xml_add(xml, PCMK__XA_ST_OUTPUT, action_stdout); /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external * code that use libstonithd <=2.1.2 don't check for the full result, and * need a legacy return code instead. */ - crm_xml_add_int(xml, F_STONITH_RC, rc); + crm_xml_add_int(xml, PCMK__XA_ST_RC, rc); } /*! @@ -472,13 +468,13 @@ stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result) xmlNode * stonith__find_xe_with_result(xmlNode *xml) { - xmlNode *match = get_xpath_object("//@" XML_LRM_ATTR_RC, xml, LOG_NEVER); + xmlNode *match = get_xpath_object("//@" PCMK__XA_RC_CODE, xml, LOG_NEVER); if (match == NULL) { /* @COMPAT Peers <=2.1.2 in a rolling upgrade provide only a legacy * return code, not a full result, so check for that. */ - match = get_xpath_object("//@" F_STONITH_RC, xml, LOG_ERR); + match = get_xpath_object("//@" PCMK__XA_ST_RC, xml, LOG_ERR); } return match; } @@ -500,12 +496,12 @@ stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result) CRM_CHECK((xml != NULL) && (result != NULL), return); - exit_reason = crm_element_value(xml, XML_LRM_ATTR_EXIT_REASON); - action_stdout = crm_element_value_copy(xml, F_STONITH_OUTPUT); + exit_reason = crm_element_value(xml, PCMK_XA_EXIT_REASON); + action_stdout = crm_element_value_copy(xml, PCMK__XA_ST_OUTPUT); // A result must include an exit status and execution status - if ((crm_element_value_int(xml, XML_LRM_ATTR_RC, &exit_status) < 0) - || (crm_element_value_int(xml, XML_LRM_ATTR_OPSTATUS, + if ((crm_element_value_int(xml, PCMK__XA_RC_CODE, &exit_status) < 0) + || (crm_element_value_int(xml, PCMK__XA_OP_STATUS, &execution_status) < 0)) { int rc = pcmk_ok; exit_status = CRM_EX_ERROR; @@ -513,7 +509,7 @@ stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result) /* @COMPAT Peers <=2.1.2 in rolling upgrades provide only a legacy * return code, not a full result, so check for that. */ - if (crm_element_value_int(xml, F_STONITH_RC, &rc) == 0) { + if (crm_element_value_int(xml, PCMK__XA_ST_RC, &rc) == 0) { if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) { exit_status = CRM_EX_OK; } diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index 1d32cc1..27ea86b 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.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. * @@ -21,7 +21,7 @@ #include <crm/crm.h> #include <crm/stonith-ng.h> #include <crm/fencing/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/mainloop.h> @@ -274,7 +274,7 @@ stonith_connection_destroy(gpointer user_data) crm_trace("Sending destroyed notification"); blob.stonith = stonith; - blob.xml = create_xml_node(NULL, "notify"); + blob.xml = pcmk__xe_create(NULL, PCMK__XE_NOTIFY); native = stonith->st_private; native->ipc = NULL; @@ -282,8 +282,8 @@ stonith_connection_destroy(gpointer user_data) free(native->token); native->token = NULL; stonith->state = stonith_disconnected; - crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY); - crm_xml_add(blob.xml, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT); + crm_xml_add(blob.xml, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY); + crm_xml_add(blob.xml, PCMK__XA_SUBT, PCMK__VALUE_ST_NOTIFY_DISCONNECT); foreach_notify_entry(native, stonith_send_notification, &blob); free_xml(blob.xml); @@ -295,8 +295,8 @@ create_device_registration_xml(const char *id, enum stonith_namespace namespace, const stonith_key_value_t *params, const char *rsc_provides) { - xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE); - xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID); + xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES); #if HAVE_STONITH_STONITH_H if (namespace == st_namespace_any) { @@ -308,14 +308,15 @@ create_device_registration_xml(const char *id, enum stonith_namespace namespace, } #endif - crm_xml_add(data, XML_ATTR_ID, id); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); - crm_xml_add(data, "agent", agent); + crm_xml_add(data, PCMK_XA_ID, id); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); + crm_xml_add(data, PCMK_XA_AGENT, agent); if ((namespace != st_namespace_any) && (namespace != st_namespace_invalid)) { - crm_xml_add(data, "namespace", stonith_namespace2text(namespace)); + crm_xml_add(data, PCMK__XA_NAMESPACE, + stonith_namespace2text(namespace)); } if (rsc_provides) { - crm_xml_add(data, "rsc_provides", rsc_provides); + crm_xml_add(data, PCMK__XA_RSC_PROVIDES, rsc_provides); } for (; params; params = params->next) { @@ -327,14 +328,15 @@ create_device_registration_xml(const char *id, enum stonith_namespace namespace, static int stonith_api_register_device(stonith_t *st, int call_options, - const char *id, const char *namespace, + const char *id, const char *namespace_s, const char *agent, const stonith_key_value_t *params) { int rc = 0; xmlNode *data = NULL; - data = create_device_registration_xml(id, stonith_text2namespace(namespace), + data = create_device_registration_xml(id, + stonith_text2namespace(namespace_s), agent, params, NULL); rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0); @@ -349,9 +351,9 @@ stonith_api_remove_device(stonith_t * st, int call_options, const char *name) int rc = 0; xmlNode *data = NULL; - data = create_xml_node(NULL, F_STONITH_DEVICE); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); - crm_xml_add(data, XML_ATTR_ID, name); + data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); + crm_xml_add(data, PCMK_XA_ID, name); rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0); free_xml(data); @@ -368,21 +370,21 @@ stonith_api_remove_level_full(stonith_t *st, int options, CRM_CHECK(node || pattern || (attr && value), return -EINVAL); - data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); + data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); if (node) { - crm_xml_add(data, XML_ATTR_STONITH_TARGET, node); + crm_xml_add(data, PCMK_XA_TARGET, node); } else if (pattern) { - crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern); + crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern); } else { - crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr); - crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value); + crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr); + crm_xml_add(data, PCMK_XA_TARGET_VALUE, value); } - crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level); + crm_xml_add_int(data, PCMK_XA_INDEX, level); rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0); free_xml(data); @@ -421,22 +423,21 @@ create_level_registration_xml(const char *node, const char *pattern, CRM_CHECK(node || pattern || (attr && value), return NULL); - data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL); - CRM_CHECK(data, return NULL); + data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); - crm_xml_add_int(data, XML_ATTR_ID, level); - crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); + crm_xml_add_int(data, PCMK_XA_ID, level); + crm_xml_add_int(data, PCMK_XA_INDEX, level); if (node) { - crm_xml_add(data, XML_ATTR_STONITH_TARGET, node); + crm_xml_add(data, PCMK_XA_TARGET, node); } else if (pattern) { - crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern); + crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern); } else { - crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr); - crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value); + crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr); + crm_xml_add(data, PCMK_XA_TARGET_VALUE, value); } for (; device_list; device_list = device_list->next) { @@ -444,7 +445,7 @@ create_level_registration_xml(const char *node, const char *pattern, } if (list != NULL) { - crm_xml_add(data, XML_ATTR_STONITH_DEVICES, (const char *) list->str); + crm_xml_add(data, PCMK_XA_DEVICES, (const char *) list->str); g_string_free(list, TRUE); } return data; @@ -476,11 +477,12 @@ stonith_api_register_level(stonith_t * st, int options, const char *node, int le } static int -stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace, - stonith_key_value_t ** devices, int timeout) +stonith_api_device_list(stonith_t *stonith, int call_options, + const char *namespace_s, stonith_key_value_t **devices, + int timeout) { int count = 0; - enum stonith_namespace ns = stonith_text2namespace(namespace); + enum stonith_namespace ns = stonith_text2namespace(namespace_s); if (devices == NULL) { crm_err("Parameter error: stonith_api_device_list"); @@ -505,14 +507,14 @@ stonith_api_device_list(stonith_t * stonith, int call_options, const char *names // See stonith_api_operations_t:metadata() documentation static int stonith_api_device_metadata(stonith_t *stonith, int call_options, - const char *agent, const char *namespace, + const char *agent, const char *namespace_s, char **output, int timeout_sec) { /* By executing meta-data directly, we can get it from stonith_admin when * the cluster is not running, which is important for higher-level tools. */ - enum stonith_namespace ns = stonith_get_namespace(agent, namespace); + enum stonith_namespace ns = stonith_get_namespace(agent, namespace_s); if (timeout_sec <= 0) { timeout_sec = PCMK_DEFAULT_METADATA_TIMEOUT_MS; @@ -550,10 +552,10 @@ stonith_api_query(stonith_t * stonith, int call_options, const char *target, CRM_CHECK(devices != NULL, return -EINVAL); - data = create_xml_node(NULL, F_STONITH_DEVICE); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); - crm_xml_add(data, F_STONITH_TARGET, target); - crm_xml_add(data, F_STONITH_ACTION, PCMK_ACTION_OFF); + data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_ST_TARGET, target); + crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, PCMK_ACTION_OFF); rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout); if (rc < 0) { @@ -573,7 +575,9 @@ stonith_api_query(stonith_t * stonith, int call_options, const char *target, crm_info("%s[%d] = %s", "//@agent", lpc, match_path); free(match_path); - *devices = stonith_key_value_add(*devices, NULL, crm_element_value(match, XML_ATTR_ID)); + *devices = stonith_key_value_add(*devices, NULL, + crm_element_value(match, + PCMK_XA_ID)); } } @@ -605,11 +609,11 @@ stonith_api_call(stonith_t *stonith, int call_options, const char *id, int rc = 0; xmlNode *data = NULL; - data = create_xml_node(NULL, F_STONITH_DEVICE); - crm_xml_add(data, F_STONITH_ORIGIN, __func__); - crm_xml_add(data, F_STONITH_DEVICE, id); - crm_xml_add(data, F_STONITH_ACTION, action); - crm_xml_add(data, F_STONITH_TARGET, target); + data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID); + crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_ST_DEVICE_ID, id); + crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action); + crm_xml_add(data, PCMK__XA_ST_TARGET, target); rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output, call_options, timeout_sec); @@ -631,7 +635,7 @@ stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **l if (output && list_info) { const char *list_str; - list_str = crm_element_value(output, F_STONITH_OUTPUT); + list_str = crm_element_value(output, PCMK__XA_ST_OUTPUT); if (list_str) { *list_info = strdup(list_str); @@ -667,12 +671,12 @@ stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char * int rc = 0; xmlNode *data = NULL; - data = create_xml_node(NULL, __func__); - crm_xml_add(data, F_STONITH_TARGET, node); - crm_xml_add(data, F_STONITH_ACTION, action); - crm_xml_add_int(data, F_STONITH_TIMEOUT, timeout); - crm_xml_add_int(data, F_STONITH_TOLERANCE, tolerance); - crm_xml_add_int(data, F_STONITH_DELAY, delay); + data = pcmk__xe_create(NULL, __func__); + crm_xml_add(data, PCMK__XA_ST_TARGET, node); + crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action); + crm_xml_add_int(data, PCMK__XA_ST_TIMEOUT, timeout); + crm_xml_add_int(data, PCMK__XA_ST_TOLERANCE, tolerance); + crm_xml_add_int(data, PCMK__XA_ST_DELAY, delay); rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout); free_xml(data); @@ -708,8 +712,8 @@ stonith_api_history(stonith_t * stonith, int call_options, const char *node, *history = NULL; if (node) { - data = create_xml_node(NULL, __func__); - crm_xml_add(data, F_STONITH_TARGET, node); + data = pcmk__xe_create(NULL, __func__); + crm_xml_add(data, PCMK__XA_ST_TARGET, node); } stonith__set_call_options(call_options, node, st_opt_sync_call); @@ -719,28 +723,27 @@ stonith_api_history(stonith_t * stonith, int call_options, const char *node, if (rc == 0) { xmlNode *op = NULL; - xmlNode *reply = get_xpath_object("//" F_STONITH_HISTORY_LIST, output, + xmlNode *reply = get_xpath_object("//" PCMK__XE_ST_HISTORY, output, LOG_NEVER); - for (op = pcmk__xml_first_child(reply); op != NULL; - op = pcmk__xml_next(op)) { + for (op = pcmk__xe_first_child(reply, NULL, NULL, NULL); op != NULL; + op = pcmk__xe_next(op)) { stonith_history_t *kvp; long long completed; long long completed_nsec = 0L; - kvp = calloc(1, sizeof(stonith_history_t)); - kvp->target = crm_element_value_copy(op, F_STONITH_TARGET); - kvp->action = crm_element_value_copy(op, F_STONITH_ACTION); - kvp->origin = crm_element_value_copy(op, F_STONITH_ORIGIN); - kvp->delegate = crm_element_value_copy(op, F_STONITH_DELEGATE); - kvp->client = crm_element_value_copy(op, F_STONITH_CLIENTNAME); - crm_element_value_ll(op, F_STONITH_DATE, &completed); + kvp = pcmk__assert_alloc(1, sizeof(stonith_history_t)); + kvp->target = crm_element_value_copy(op, PCMK__XA_ST_TARGET); + kvp->action = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ACTION); + kvp->origin = crm_element_value_copy(op, PCMK__XA_ST_ORIGIN); + kvp->delegate = crm_element_value_copy(op, PCMK__XA_ST_DELEGATE); + kvp->client = crm_element_value_copy(op, PCMK__XA_ST_CLIENTNAME); + crm_element_value_ll(op, PCMK__XA_ST_DATE, &completed); kvp->completed = (time_t) completed; - crm_element_value_ll(op, F_STONITH_DATE_NSEC, &completed_nsec); + crm_element_value_ll(op, PCMK__XA_ST_DATE_NSEC, &completed_nsec); kvp->completed_nsec = completed_nsec; - crm_element_value_int(op, F_STONITH_STATE, &kvp->state); - kvp->exit_reason = crm_element_value_copy(op, - XML_LRM_ATTR_EXIT_REASON); + crm_element_value_int(op, PCMK__XA_ST_STATE, &kvp->state); + kvp->exit_reason = crm_element_value_copy(op, PCMK_XA_EXIT_REASON); if (last) { last->next = kvp; @@ -805,22 +808,21 @@ stonithlib_GCompareFunc(gconstpointer a, gconstpointer b) xmlNode * stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options) { - xmlNode *op_msg = create_xml_node(NULL, "stonith_command"); + xmlNode *op_msg = NULL; - CRM_CHECK(op_msg != NULL, return NULL); CRM_CHECK(token != NULL, return NULL); - crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command"); - - crm_xml_add(op_msg, F_TYPE, T_STONITH_NG); - crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token); - crm_xml_add(op_msg, F_STONITH_OPERATION, op); - crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id); + op_msg = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND); + crm_xml_add(op_msg, PCMK__XA_T, PCMK__VALUE_STONITH_NG); + crm_xml_add(op_msg, PCMK__XA_ST_OP, op); + crm_xml_add_int(op_msg, PCMK__XA_ST_CALLID, call_id); crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options); - crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options); + crm_xml_add_int(op_msg, PCMK__XA_ST_CALLOPT, call_options); if (data != NULL) { - add_message_xml(op_msg, F_STONITH_CALLDATA, data); + xmlNode *wrapper = pcmk__xe_create(op_msg, PCMK__XE_ST_CALLDATA); + + pcmk__xml_copy(wrapper, data); } return op_msg; @@ -943,7 +945,7 @@ invoke_registered_callbacks(stonith_t *stonith, const xmlNode *msg, int call_id) } else { // We have the fencer reply - if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0) + if ((crm_element_value_int(msg, PCMK__XA_ST_CALLID, &call_id) != 0) || (call_id <= 0)) { crm_log_xml_warn(msg, "Bad fencer reply"); } @@ -1008,7 +1010,7 @@ set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, } if (!async_timer) { - async_timer = calloc(1, sizeof(struct timer_rec_s)); + async_timer = pcmk__assert_alloc(1, sizeof(struct timer_rec_s)); callback->timer = async_timer; } @@ -1053,27 +1055,29 @@ stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata) private = st->st_private; blob.stonith = st; - blob.xml = string2xml(buffer); + blob.xml = pcmk__xml_parse(buffer); if (blob.xml == NULL) { crm_warn("Received malformed message from fencer: %s", buffer); return 0; } /* do callbacks */ - type = crm_element_value(blob.xml, F_TYPE); + type = crm_element_value(blob.xml, PCMK__XA_T); crm_trace("Activating %s callbacks...", type); - if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_none)) { + if (pcmk__str_eq(type, PCMK__VALUE_STONITH_NG, pcmk__str_none)) { invoke_registered_callbacks(st, blob.xml, 0); - } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_none)) { + } else if (pcmk__str_eq(type, PCMK__VALUE_ST_NOTIFY, pcmk__str_none)) { foreach_notify_entry(private, stonith_send_notification, &blob); - } else if (pcmk__str_eq(type, T_STONITH_TIMEOUT_VALUE, pcmk__str_none)) { + + } else if (pcmk__str_eq(type, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE, + pcmk__str_none)) { int call_id = 0; int timeout = 0; - crm_element_value_int(blob.xml, F_STONITH_TIMEOUT, &timeout); - crm_element_value_int(blob.xml, F_STONITH_CALLID, &call_id); + crm_element_value_int(blob.xml, PCMK__XA_ST_TIMEOUT, &timeout); + crm_element_value_int(blob.xml, PCMK__XA_ST_CALLID, &call_id); update_callback_timeout(call_id, timeout, st); } else { @@ -1136,11 +1140,11 @@ stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd) rc = -ENOTCONN; } else { xmlNode *reply = NULL; - xmlNode *hello = create_xml_node(NULL, "stonith_command"); + xmlNode *hello = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND); - crm_xml_add(hello, F_TYPE, T_STONITH_NG); - crm_xml_add(hello, F_STONITH_OPERATION, CRM_OP_REGISTER); - crm_xml_add(hello, F_STONITH_CLIENTNAME, name); + crm_xml_add(hello, PCMK__XA_T, PCMK__VALUE_STONITH_NG); + crm_xml_add(hello, PCMK__XA_ST_OP, CRM_OP_REGISTER); + crm_xml_add(hello, PCMK__XA_ST_CLIENTNAME, name); rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply); if (rc < 0) { @@ -1153,9 +1157,9 @@ stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd) rc = -EPROTO; } else { - const char *msg_type = crm_element_value(reply, F_STONITH_OPERATION); + const char *msg_type = crm_element_value(reply, PCMK__XA_ST_OP); - native->token = crm_element_value_copy(reply, F_STONITH_CLIENTID); + native->token = crm_element_value_copy(reply, PCMK__XA_ST_CLIENTID); if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) { crm_debug("Couldn't register with the fencer: invalid reply type '%s'", (msg_type? msg_type : "(missing)")); @@ -1190,16 +1194,16 @@ static int stonith_set_notification(stonith_t * stonith, const char *callback, int enabled) { int rc = pcmk_ok; - xmlNode *notify_msg = create_xml_node(NULL, __func__); + xmlNode *notify_msg = pcmk__xe_create(NULL, __func__); stonith_private_t *native = stonith->st_private; if (stonith->state != stonith_disconnected) { - crm_xml_add(notify_msg, F_STONITH_OPERATION, T_STONITH_NOTIFY); + crm_xml_add(notify_msg, PCMK__XA_ST_OP, STONITH_OP_NOTIFY); if (enabled) { - crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback); + crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_ACTIVATE, callback); } else { - crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback); + crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_DEACTIVATE, callback); } rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL); @@ -1226,7 +1230,7 @@ stonith_api_add_notification(stonith_t * stonith, const char *event, private = stonith->st_private; crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list)); - new_client = calloc(1, sizeof(stonith_notify_client_t)); + new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t)); new_client->event = event; new_client->notify = callback; @@ -1275,7 +1279,7 @@ stonith_api_del_notification(stonith_t * stonith, const char *event) crm_debug("Removing callback for %s events", event); - new_client = calloc(1, sizeof(stonith_notify_client_t)); + new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t)); new_client->event = event; new_client->notify = NULL; @@ -1333,7 +1337,7 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti return FALSE; } - blob = calloc(1, sizeof(stonith_callback_client_t)); + blob = pcmk__assert_alloc(1, sizeof(stonith_callback_client_t)); blob->id = callback_name; blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE; blob->user_data = user_data; @@ -1410,27 +1414,24 @@ get_event_data_xml(xmlNode *msg, const char *ntype) static stonith_event_t * xml_to_event(xmlNode *msg) { - stonith_event_t *event = calloc(1, sizeof(stonith_event_t)); + stonith_event_t *event = pcmk__assert_alloc(1, sizeof(stonith_event_t)); struct event_private *event_private = NULL; - CRM_ASSERT(event != NULL); - - event->opaque = calloc(1, sizeof(struct event_private)); - CRM_ASSERT(event->opaque != NULL); + event->opaque = pcmk__assert_alloc(1, sizeof(struct event_private)); event_private = (struct event_private *) event->opaque; crm_log_xml_trace(msg, "stonith_notify"); // All notification types have the operation result and notification subtype stonith__xe_get_result(msg, &event_private->result); - event->operation = crm_element_value_copy(msg, F_STONITH_OPERATION); + event->operation = crm_element_value_copy(msg, PCMK__XA_ST_OP); // @COMPAT The API originally provided the result as a legacy return code event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result)); // Some notification subtypes have additional information - if (pcmk__str_eq(event->operation, T_STONITH_NOTIFY_FENCE, + if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_FENCE, pcmk__str_none)) { xmlNode *data = get_event_data_xml(msg, event->operation); @@ -1438,13 +1439,16 @@ xml_to_event(xmlNode *msg) crm_err("No data for %s event", event->operation); crm_log_xml_notice(msg, "BadEvent"); } else { - event->origin = crm_element_value_copy(data, F_STONITH_ORIGIN); - event->action = crm_element_value_copy(data, F_STONITH_ACTION); - event->target = crm_element_value_copy(data, F_STONITH_TARGET); - event->executioner = crm_element_value_copy(data, F_STONITH_DELEGATE); - event->id = crm_element_value_copy(data, F_STONITH_REMOTE_OP_ID); - event->client_origin = crm_element_value_copy(data, F_STONITH_CLIENTNAME); - event->device = crm_element_value_copy(data, F_STONITH_DEVICE); + event->origin = crm_element_value_copy(data, PCMK__XA_ST_ORIGIN); + event->action = crm_element_value_copy(data, + PCMK__XA_ST_DEVICE_ACTION); + event->target = crm_element_value_copy(data, PCMK__XA_ST_TARGET); + event->executioner = crm_element_value_copy(data, + PCMK__XA_ST_DELEGATE); + event->id = crm_element_value_copy(data, PCMK__XA_ST_REMOTE_OP); + event->client_origin = + crm_element_value_copy(data, PCMK__XA_ST_CLIENTNAME); + event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID); } } else if (pcmk__str_any_of(event->operation, @@ -1457,7 +1461,7 @@ xml_to_event(xmlNode *msg) crm_err("No data for %s event", event->operation); crm_log_xml_notice(msg, "BadEvent"); } else { - event->device = crm_element_value_copy(data, F_STONITH_DEVICE); + event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID); } } @@ -1497,7 +1501,7 @@ stonith_send_notification(gpointer data, gpointer user_data) return; } - event = crm_element_value(blob->xml, F_SUBTYPE); + event = crm_element_value(blob->xml, PCMK__XA_SUBT); if (entry == NULL) { crm_warn("Skipping callback - NULL callback client"); @@ -1575,14 +1579,14 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod return -EINVAL; } - crm_xml_add_int(op_msg, F_STONITH_TIMEOUT, timeout); + crm_xml_add_int(op_msg, PCMK__XA_ST_TIMEOUT, timeout); crm_trace("Sending %s message to fencer with timeout %ds", op, timeout); if (data) { - const char *delay_s = crm_element_value(data, F_STONITH_DELAY); + const char *delay_s = crm_element_value(data, PCMK__XA_ST_DELAY); if (delay_s) { - crm_xml_add(op_msg, F_STONITH_DELAY, delay_s); + crm_xml_add(op_msg, PCMK__XA_ST_DELAY, delay_s); } } @@ -1612,7 +1616,7 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod return stonith->call_id; } - crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id); + crm_element_value_int(op_reply, PCMK__XA_ST_CALLID, &reply_id); if (reply_id == stonith->call_id) { pcmk__action_result_t result = PCMK__UNKNOWN_RESULT; @@ -1754,8 +1758,7 @@ stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id, host_arg = params->value; } if (!pcmk_stonith_param(params->key)) { - g_hash_table_insert(params_table, strdup(params->key), - strdup(params->value)); + pcmk__insert_dup(params_table, params->key, params->value); } } @@ -1928,9 +1931,9 @@ stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *v { stonith_key_value_t *p, *end; - p = calloc(1, sizeof(stonith_key_value_t)); - pcmk__str_update(&p->key, key); - pcmk__str_update(&p->value, value); + p = pcmk__assert_alloc(1, sizeof(stonith_key_value_t)); + p->key = pcmk__str_copy(key); + p->value = pcmk__str_copy(value); end = head; while (end && end->next) { @@ -2157,8 +2160,7 @@ parse_list_line(const char *line, int len, GList **output) continue; } - entry = calloc(i - entry_start + 1, sizeof(char)); - CRM_ASSERT(entry != NULL); + entry = pcmk__assert_alloc(i - entry_start + 1, sizeof(char)); /* Read entry, stopping at first separator * @@ -2406,7 +2408,7 @@ stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name, CRM_CHECK((device_flags != NULL) && (metadata != NULL), return); - xpath = xpath_search(metadata, "//parameter"); + xpath = xpath_search(metadata, "//" PCMK_XE_PARAMETER); max = numXpathResults(xpath); if (max <= 0) { @@ -2423,7 +2425,7 @@ stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name, continue; } - parameter = crm_element_value(match, "name"); + parameter = crm_element_value(match, PCMK_XA_NAME); if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) { stonith__set_device_flags(*device_flags, device_name, @@ -2470,8 +2472,9 @@ stonith__metadata_async(const char *agent, int timeout_sec, stonith_action_t *action = NULL; int rc = pcmk_ok; - action = stonith__action_create(agent, "metadata", NULL, 0, - timeout_sec, NULL, NULL, NULL); + action = stonith__action_create(agent, PCMK_ACTION_METADATA, + NULL, 0, timeout_sec, NULL, + NULL, NULL); rc = stonith__execute_async(action, user_data, callback, NULL); if (rc != pcmk_ok) { @@ -2665,7 +2668,7 @@ stonith__event_description(const stonith_event_t *event) status = crm_exit_str(CRM_EX_OK); } - if (pcmk__str_eq(event->operation, T_STONITH_NOTIFY_HISTORY, + if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_HISTORY, pcmk__str_none)) { return crm_strdup_printf("Fencing history may have changed"); @@ -2688,7 +2691,7 @@ stonith__event_description(const stonith_event_t *event) device); } - // event->operation should be T_STONITH_NOTIFY_FENCE at this point + // event->operation should be PCMK__VALUE_ST_NOTIFY_FENCE at this point return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)", action, target, executioner, origin, origin_node, diff --git a/lib/fencing/st_lha.c b/lib/fencing/st_lha.c index fd26217..a379c10 100644 --- a/lib/fencing/st_lha.c +++ b/lib/fencing/st_lha.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,7 +19,6 @@ #include <crm/crm.h> #include <crm/stonith-ng.h> #include <crm/fencing/internal.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <stonith/stonith.h> @@ -30,25 +29,35 @@ static void *lha_agents_lib = NULL; +// @TODO Use XML string constants and maybe a real XML object static const char META_TEMPLATE[] = - "<?xml version=\"1.0\"?>\n" - "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" - "<resource-agent name=\"%s\">\n" - " <version>1.0</version>\n" - " <longdesc lang=\"en\">\n" + "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" + "<" PCMK_XE_RESOURCE_AGENT " " PCMK_XA_NAME "=\"%s\">\n" + " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" + " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" + "%s\n" + " </" PCMK_XE_LONGDESC ">\n" + " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" + "%s" + "</" PCMK_XE_SHORTDESC ">\n" "%s\n" - " </longdesc>\n" - " <shortdesc lang=\"en\">%s</shortdesc>\n" - "%s\n" - " <actions>\n" - " <action name=\"start\" timeout=\"%s\" />\n" - " <action name=\"stop\" timeout=\"15\" />\n" - " <action name=\"status\" timeout=\"%s\" />\n" - " <action name=\"monitor\" timeout=\"%s\" interval=\"3600\"/>\n" - " <action name=\"meta-data\" timeout=\"15\" />\n" - " </actions>\n" - " <special tag=\"heartbeat\">\n" - " <version>2.0</version>\n" " </special>\n" "</resource-agent>\n"; + " <" PCMK_XE_ACTIONS ">\n" + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" + " " PCMK_META_TIMEOUT "=\"%s\" />\n" + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" + " " PCMK_META_TIMEOUT "=\"15s\" />\n" + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" + " " PCMK_META_TIMEOUT "=\"%s\" />\n" + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" + " " PCMK_META_TIMEOUT "=\"%s\"" + " " PCMK_META_INTERVAL "=\"3600s\" />\n" + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" + " " PCMK_META_TIMEOUT "=\"15s\" />\n" + " </" PCMK_XE_ACTIONS ">\n" + " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"heartbeat\">\n" + " <" PCMK_XE_VERSION ">2.0</" PCMK_XE_VERSION ">\n" + " </" PCMK_XE_SPECIAL ">\n" + "</" PCMK_XE_RESOURCE_AGENT ">\n"; static void * find_library_function(void **handle, const char *lib, const char *fn) @@ -194,60 +203,66 @@ stonith__lha_metadata(const char *agent, int timeout, char **output) } if (lha_agents_lib && st_new_fn && st_del_fn && st_info_fn && st_log_fn) { - char *xml_meta_longdesc = NULL; - char *xml_meta_shortdesc = NULL; - - char *meta_param = NULL; - char *meta_longdesc = NULL; - char *meta_shortdesc = NULL; + const char *meta_longdesc = NULL; + const char *meta_shortdesc = NULL; + const char *meta_param = NULL; const char *timeout_str = NULL; - stonith_obj = (*st_new_fn) (agent); - if (stonith_obj) { - (*st_log_fn) (stonith_obj, (PILLogFun) & stonith_plugin); - pcmk__str_update(&meta_longdesc, - (*st_info_fn) (stonith_obj, ST_DEVICEDESCR)); + gchar *meta_longdesc_esc = NULL; + gchar *meta_shortdesc_esc = NULL; + + stonith_obj = st_new_fn(agent); + if (stonith_obj != NULL) { + st_log_fn(stonith_obj, (PILLogFun) &stonith_plugin); + + meta_longdesc = st_info_fn(stonith_obj, ST_DEVICEDESCR); if (meta_longdesc == NULL) { crm_warn("no long description in %s's metadata.", agent); - meta_longdesc = strdup(no_parameter_info); + meta_longdesc = no_parameter_info; } - pcmk__str_update(&meta_shortdesc, - (*st_info_fn) (stonith_obj, ST_DEVICEID)); + meta_shortdesc = st_info_fn(stonith_obj, ST_DEVICEID); if (meta_shortdesc == NULL) { crm_warn("no short description in %s's metadata.", agent); - meta_shortdesc = strdup(no_parameter_info); + meta_shortdesc = no_parameter_info; } - pcmk__str_update(&meta_param, - (*st_info_fn) (stonith_obj, ST_CONF_XML)); + meta_param = st_info_fn(stonith_obj, ST_CONF_XML); if (meta_param == NULL) { crm_warn("no list of parameters in %s's metadata.", agent); - meta_param = strdup(no_parameter_info); + meta_param = no_parameter_info; } - (*st_del_fn) (stonith_obj); + + st_del_fn(stonith_obj); + } else { errno = EINVAL; crm_perror(LOG_ERR, "Agent %s not found", agent); return -EINVAL; } - xml_meta_longdesc = - (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); - xml_meta_shortdesc = - (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); + if (pcmk__xml_needs_escape(meta_longdesc, pcmk__xml_escape_text)) { + meta_longdesc_esc = pcmk__xml_escape(meta_longdesc, + pcmk__xml_escape_text); + meta_longdesc = meta_longdesc_esc; + } + if (pcmk__xml_needs_escape(meta_shortdesc, pcmk__xml_escape_text)) { + meta_shortdesc_esc = pcmk__xml_escape(meta_shortdesc, + pcmk__xml_escape_text); + meta_shortdesc = meta_shortdesc_esc; + } + /* @TODO This needs a string that's parsable by crm_get_msec(). In + * general, pcmk__readable_interval() doesn't provide that. It works + * here because PCMK_DEFAULT_ACTION_TIMEOUT_MS is 20000 -> "20s". + */ timeout_str = pcmk__readable_interval(PCMK_DEFAULT_ACTION_TIMEOUT_MS); - buffer = crm_strdup_printf(META_TEMPLATE, agent, xml_meta_longdesc, - xml_meta_shortdesc, meta_param, + buffer = crm_strdup_printf(META_TEMPLATE, agent, meta_longdesc, + meta_shortdesc, meta_param, timeout_str, timeout_str, timeout_str); - xmlFree(xml_meta_longdesc); - xmlFree(xml_meta_shortdesc); - - free(meta_shortdesc); - free(meta_longdesc); - free(meta_param); + g_free(meta_longdesc_esc); + g_free(meta_shortdesc_esc); } if (output) { *output = buffer; diff --git a/lib/fencing/st_output.c b/lib/fencing/st_output.c index a0ce8e7..786f9d5 100644 --- a/lib/fencing/st_output.c +++ b/lib/fencing/st_output.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 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,6 @@ #include <stdint.h> #include <crm/stonith-ng.h> -#include <crm/msg_xml.h> #include <crm/common/iso8601.h> #include <crm/common/util.h> #include <crm/common/xml.h> @@ -132,13 +131,14 @@ stonith__history_description(const stonith_history_t *history, if (((history->state == st_failed) || (history->state == st_done)) && (history->delegate != NULL)) { - pcmk__g_strcat(str, "delegate=", history->delegate, ", ", NULL); + pcmk__g_strcat(str, PCMK_XA_DELEGATE "=", history->delegate, ", ", + NULL); } // Add information about originator pcmk__g_strcat(str, - "client=", history->client, ", origin=", history->origin, - NULL); + PCMK_XA_CLIENT "=", history->client, ", " + PCMK_XA_ORIGIN "=", history->origin, NULL); // For completed actions, add completion time if (completed_time_s != NULL) { @@ -293,8 +293,8 @@ full_history_xml(pcmk__output_t *out, va_list args) } else { char *rc_s = pcmk__itoa(history_rc); - pcmk__output_create_xml_node(out, "fence_history", - "status", rc_s, + pcmk__output_create_xml_node(out, PCMK_XE_FENCE_HISTORY, + PCMK_XA_STATUS, rc_s, NULL); free(rc_s); @@ -312,7 +312,7 @@ last_fenced_html(pcmk__output_t *out, va_list args) { if (when) { char *buf = crm_strdup_printf("Node %s last fenced at: %s", target, ctime(&when)); - pcmk__output_create_html_node(out, "div", NULL, NULL, buf); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf); free(buf); return pcmk_rc_ok; } else { @@ -344,9 +344,9 @@ last_fenced_xml(pcmk__output_t *out, va_list args) { if (when) { char *buf = timespec_string(when, 0, false); - pcmk__output_create_xml_node(out, "last-fenced", - "target", target, - "when", buf, + pcmk__output_create_xml_node(out, PCMK_XE_LAST_FENCED, + PCMK_XA_TARGET, target, + PCMK_XA_WHEN, buf, NULL); free(buf); @@ -456,28 +456,32 @@ stonith_event_xml(pcmk__output_t *out, va_list args) const char *succeeded G_GNUC_UNUSED = va_arg(args, const char *); uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t); - xmlNodePtr node = pcmk__output_create_xml_node(out, "fence_event", - "action", event->action, - "target", event->target, - "client", event->client, - "origin", event->origin, - NULL); + xmlNodePtr node = NULL; + + node = pcmk__output_create_xml_node(out, PCMK_XE_FENCE_EVENT, + PCMK_XA_ACTION, event->action, + PCMK_XA_TARGET, event->target, + PCMK_XA_CLIENT, event->client, + PCMK_XA_ORIGIN, event->origin, + NULL); switch (event->state) { case st_failed: - pcmk__xe_set_props(node, "status", "failed", - XML_LRM_ATTR_EXIT_REASON, event->exit_reason, + pcmk__xe_set_props(node, + PCMK_XA_STATUS, PCMK_VALUE_FAILED, + PCMK_XA_EXIT_REASON, event->exit_reason, NULL); break; case st_done: - crm_xml_add(node, "status", "success"); + crm_xml_add(node, PCMK_XA_STATUS, PCMK_VALUE_SUCCESS); break; default: { char *state = pcmk__itoa(event->state); - pcmk__xe_set_props(node, "status", "pending", - "extended-status", state, + pcmk__xe_set_props(node, + PCMK_XA_STATUS, PCMK_VALUE_PENDING, + PCMK_XA_EXTENDED_STATUS, state, NULL); free(state); break; @@ -485,14 +489,14 @@ stonith_event_xml(pcmk__output_t *out, va_list args) } if (event->delegate != NULL) { - crm_xml_add(node, "delegate", event->delegate); + crm_xml_add(node, PCMK_XA_DELEGATE, event->delegate); } if ((event->state == st_failed) || (event->state == st_done)) { char *time_s = timespec_string(event->completed, event->completed_nsec, true); - crm_xml_add(node, "completed", time_s); + crm_xml_add(node, PCMK_XA_COMPLETED, time_s); free(time_s); } @@ -512,12 +516,12 @@ validate_agent_html(pcmk__output_t *out, va_list args) { if (device) { char *buf = crm_strdup_printf("Validation of %s on %s %s", agent, device, rc ? "failed" : "succeeded"); - pcmk__output_create_html_node(out, "div", NULL, NULL, buf); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf); free(buf); } else { char *buf = crm_strdup_printf("Validation of %s %s", agent, rc ? "failed" : "succeeded"); - pcmk__output_create_html_node(out, "div", NULL, NULL, buf); + pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf); free(buf); } @@ -557,12 +561,14 @@ validate_agent_xml(pcmk__output_t *out, va_list args) { const char *error_output = va_arg(args, const char *); int rc = va_arg(args, int); - xmlNodePtr node = pcmk__output_create_xml_node( - out, "validate", "agent", agent, "valid", pcmk__btoa(rc == pcmk_ok), - NULL); + const char *valid = pcmk__btoa(rc == pcmk_ok); + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_VALIDATE, + PCMK_XA_AGENT, agent, + PCMK_XA_VALID, valid, + NULL); if (device != NULL) { - crm_xml_add(node, "device", device); + crm_xml_add(node, PCMK_XA_DEVICE, device); } pcmk__output_xml_push_parent(out, node); diff --git a/lib/fencing/st_rhcs.c b/lib/fencing/st_rhcs.c index 854d333..d104fe8 100644 --- a/lib/fencing/st_rhcs.c +++ b/lib/fencing/st_rhcs.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. * @@ -16,6 +16,7 @@ #include <dirent.h> #include <crm/crm.h> +#include <crm/common/xml.h> #include <crm/stonith-ng.h> #include <crm/fencing/internal.h> @@ -98,7 +99,8 @@ stonith_rhcs_parameter_not_required(xmlNode *metadata, const char *parameter) CRM_CHECK(metadata != NULL, return); CRM_CHECK(parameter != NULL, return); - xpath = crm_strdup_printf("//parameter[@name='%s']", parameter); + xpath = crm_strdup_printf("//" PCMK_XE_PARAMETER "[@" PCMK_XA_NAME "='%s']", + parameter); /* Fudge metadata so that the parameter isn't required in config * Pacemaker handles and adds it */ xpathObj = xpath_search(metadata, xpath); @@ -125,9 +127,10 @@ stonith__rhcs_get_metadata(const char *agent, int timeout_sec, xmlNode *xml = NULL; xmlNode *actions = NULL; xmlXPathObject *xpathObj = NULL; - stonith_action_t *action = stonith__action_create(agent, "metadata", NULL, - 0, timeout_sec, NULL, - NULL, NULL); + stonith_action_t *action = stonith__action_create(agent, + PCMK_ACTION_METADATA, + NULL, 0, timeout_sec, + NULL, NULL, NULL); int rc = stonith__execute(action); pcmk__action_result_t *result = stonith__action_result(action); @@ -162,7 +165,7 @@ stonith__rhcs_get_metadata(const char *agent, int timeout_sec, return -ENODATA; } - xml = string2xml(result->action_stdout); + xml = pcmk__xml_parse(result->action_stdout); stonith__destroy_action(action); if (xml == NULL) { @@ -170,27 +173,29 @@ stonith__rhcs_get_metadata(const char *agent, int timeout_sec, return -pcmk_err_schema_validation; } - xpathObj = xpath_search(xml, "//actions"); + xpathObj = xpath_search(xml, "//" PCMK_XE_ACTIONS); if (numXpathResults(xpathObj) > 0) { actions = getXpathResult(xpathObj, 0); } freeXpathObject(xpathObj); // Add start and stop (implemented by pacemaker, not agent) to meta-data - xpathObj = xpath_search(xml, "//action[@name='stop']"); + xpathObj = xpath_search(xml, + "//" PCMK_XE_ACTION + "[@" PCMK_XA_NAME "='" PCMK_ACTION_STOP "']"); if (numXpathResults(xpathObj) <= 0) { xmlNode *tmp = NULL; const char *timeout_str = NULL; timeout_str = pcmk__readable_interval(PCMK_DEFAULT_ACTION_TIMEOUT_MS); - tmp = create_xml_node(actions, "action"); - crm_xml_add(tmp, "name", PCMK_ACTION_STOP); - crm_xml_add(tmp, "timeout", timeout_str); + tmp = pcmk__xe_create(actions, PCMK_XE_ACTION); + crm_xml_add(tmp, PCMK_XA_NAME, PCMK_ACTION_STOP); + crm_xml_add(tmp, PCMK_META_TIMEOUT, timeout_str); - tmp = create_xml_node(actions, "action"); - crm_xml_add(tmp, "name", PCMK_ACTION_START); - crm_xml_add(tmp, "timeout", timeout_str); + tmp = pcmk__xe_create(actions, PCMK_XE_ACTION); + crm_xml_add(tmp, PCMK_XA_NAME, PCMK_ACTION_START); + crm_xml_add(tmp, PCMK_META_TIMEOUT, timeout_str); } freeXpathObject(xpathObj); @@ -219,27 +224,33 @@ stonith__rhcs_get_metadata(const char *agent, int timeout_sec, int stonith__rhcs_metadata(const char *agent, int timeout_sec, char **output) { - char *buffer = NULL; + GString *buffer = NULL; xmlNode *xml = NULL; int rc = stonith__rhcs_get_metadata(agent, timeout_sec, &xml); if (rc != pcmk_ok) { - free_xml(xml); - return rc; + goto done; } - buffer = dump_xml_formatted_with_text(xml); - free_xml(xml); - if (buffer == NULL) { - return -pcmk_err_schema_validation; + buffer = g_string_sized_new(1024); + pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0); + + if (pcmk__str_empty(buffer->str)) { + rc = -pcmk_err_schema_validation; + goto done; } - if (output) { - *output = buffer; - } else { - free(buffer); + + if (output != NULL) { + pcmk__str_update(output, buffer->str); } - return pcmk_ok; + +done: + if (buffer != NULL) { + g_string_free(buffer, TRUE); + } + free_xml(xml); + return rc; } bool @@ -291,7 +302,7 @@ stonith__rhcs_validate(stonith_t *st, int call_options, const char *target, return -ETIME; } - } else if (pcmk__str_eq(host_arg, PCMK__VALUE_NONE, pcmk__str_casei)) { + } else if (pcmk__str_eq(host_arg, PCMK_VALUE_NONE, pcmk__str_casei)) { host_arg = NULL; } diff --git a/lib/lrmd/Makefile.am b/lib/lrmd/Makefile.am index a9b9c67..f0bc784 100644 --- a/lib/lrmd/Makefile.am +++ b/lib/lrmd/Makefile.am @@ -10,7 +10,7 @@ include $(top_srcdir)/mk/common.mk lib_LTLIBRARIES = liblrmd.la -liblrmd_la_LDFLAGS = -version-info 30:0:2 +liblrmd_la_LDFLAGS = -version-info 31:0:3 liblrmd_la_CFLAGS = $(CFLAGS_HARDENED_LIB) liblrmd_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) diff --git a/lib/lrmd/lrmd_alerts.c b/lib/lrmd/lrmd_alerts.c index 2a8c988..9e01bbe 100644 --- a/lib/lrmd/lrmd_alerts.c +++ b/lib/lrmd/lrmd_alerts.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,7 @@ #include <unistd.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/services.h> #include <crm/common/mainloop.h> #include <crm/common/alerts_internal.h> diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c index 400d3b0..ebd6f0f 100644 --- a/lib/lrmd/lrmd_client.c +++ b/lib/lrmd/lrmd_client.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. * @@ -32,10 +32,10 @@ #include <crm/common/mainloop.h> #include <crm/common/ipc_internal.h> #include <crm/common/remote_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__* #ifdef HAVE_GNUTLS_GNUTLS_H # include <gnutls/gnutls.h> @@ -110,7 +110,7 @@ lrmd_list_add(lrmd_list_t * head, const char *value) { lrmd_list_t *p, *end; - p = calloc(1, sizeof(lrmd_list_t)); + p = pcmk__assert_alloc(1, sizeof(lrmd_list_t)); p->val = strdup(value); end = head; @@ -147,7 +147,7 @@ lrmd_key_value_add(lrmd_key_value_t * head, const char *key, const char *value) { lrmd_key_value_t *p, *end; - p = calloc(1, sizeof(lrmd_key_value_t)); + p = pcmk__assert_alloc(1, sizeof(lrmd_key_value_t)); p->key = strdup(key); p->value = strdup(value); @@ -194,11 +194,11 @@ lrmd_key_value_freeall(lrmd_key_value_t * head) lrmd_event_data_t * lrmd_new_event(const char *rsc_id, const char *task, guint interval_ms) { - lrmd_event_data_t *event = calloc(1, sizeof(lrmd_event_data_t)); + lrmd_event_data_t *event = pcmk__assert_alloc(1, sizeof(lrmd_event_data_t)); - CRM_ASSERT(event != NULL); - pcmk__str_update((char **) &event->rsc_id, rsc_id); - pcmk__str_update((char **) &event->op_type, task); + // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees + event->rsc_id = pcmk__str_copy(rsc_id); + event->op_type = pcmk__str_copy(task); event->interval_ms = interval_ms; return event; } @@ -208,12 +208,18 @@ lrmd_copy_event(lrmd_event_data_t * event) { lrmd_event_data_t *copy = NULL; - copy = calloc(1, sizeof(lrmd_event_data_t)); + copy = pcmk__assert_alloc(1, sizeof(lrmd_event_data_t)); copy->type = event->type; - pcmk__str_update((char **) ©->rsc_id, event->rsc_id); - pcmk__str_update((char **) ©->op_type, event->op_type); - pcmk__str_update((char **) ©->user_data, event->user_data); + + // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees + copy->rsc_id = pcmk__str_copy(event->rsc_id); + copy->op_type = pcmk__str_copy(event->op_type); + copy->user_data = pcmk__str_copy(event->user_data); + copy->output = pcmk__str_copy(event->output); + copy->remote_nodename = pcmk__str_copy(event->remote_nodename); + copy->exit_reason = pcmk__str_copy(event->exit_reason); + copy->call_id = event->call_id; copy->timeout = event->timeout; copy->interval_ms = event->interval_ms; @@ -221,15 +227,12 @@ lrmd_copy_event(lrmd_event_data_t * event) copy->rsc_deleted = event->rsc_deleted; copy->rc = event->rc; copy->op_status = event->op_status; - pcmk__str_update((char **) ©->output, event->output); copy->t_run = event->t_run; copy->t_rcchange = event->t_rcchange; copy->exec_time = event->exec_time; copy->queue_time = event->queue_time; copy->connection_rc = event->connection_rc; copy->params = pcmk__str_table_dup(event->params); - pcmk__str_update((char **) ©->remote_nodename, event->remote_nodename); - pcmk__str_update((char **) ©->exit_reason, event->exit_reason); return copy; } @@ -261,7 +264,8 @@ static void lrmd_dispatch_internal(lrmd_t * lrmd, xmlNode * msg) { const char *type; - const char *proxy_session = crm_element_value(msg, F_LRMD_IPC_SESSION); + const char *proxy_session = crm_element_value(msg, + PCMK__XA_LRMD_IPC_SESSION); lrmd_private_t *native = lrmd->lrmd_private; lrmd_event_data_t event = { 0, }; @@ -276,41 +280,57 @@ lrmd_dispatch_internal(lrmd_t * lrmd, xmlNode * msg) } event.remote_nodename = native->remote_nodename; - type = crm_element_value(msg, F_LRMD_OPERATION); - crm_element_value_int(msg, F_LRMD_CALLID, &event.call_id); - event.rsc_id = crm_element_value(msg, F_LRMD_RSC_ID); + type = crm_element_value(msg, PCMK__XA_LRMD_OP); + crm_element_value_int(msg, PCMK__XA_LRMD_CALLID, &event.call_id); + event.rsc_id = crm_element_value(msg, PCMK__XA_LRMD_RSC_ID); if (pcmk__str_eq(type, LRMD_OP_RSC_REG, pcmk__str_none)) { event.type = lrmd_event_register; } else if (pcmk__str_eq(type, LRMD_OP_RSC_UNREG, pcmk__str_none)) { event.type = lrmd_event_unregister; } else if (pcmk__str_eq(type, LRMD_OP_RSC_EXEC, pcmk__str_none)) { + int rc = 0; + int exec_time = 0; + int queue_time = 0; time_t epoch = 0; - crm_element_value_int(msg, F_LRMD_TIMEOUT, &event.timeout); - crm_element_value_ms(msg, F_LRMD_RSC_INTERVAL, &event.interval_ms); - crm_element_value_int(msg, F_LRMD_RSC_START_DELAY, &event.start_delay); - crm_element_value_int(msg, F_LRMD_EXEC_RC, (int *)&event.rc); - crm_element_value_int(msg, F_LRMD_OP_STATUS, &event.op_status); - crm_element_value_int(msg, F_LRMD_RSC_DELETED, &event.rsc_deleted); + crm_element_value_int(msg, PCMK__XA_LRMD_TIMEOUT, &event.timeout); + crm_element_value_ms(msg, PCMK__XA_LRMD_RSC_INTERVAL, + &event.interval_ms); + crm_element_value_int(msg, PCMK__XA_LRMD_RSC_START_DELAY, + &event.start_delay); + + crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_RC, &rc); + event.rc = (enum ocf_exitcode) rc; + + crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_OP_STATUS, + &event.op_status); + crm_element_value_int(msg, PCMK__XA_LRMD_RSC_DELETED, + &event.rsc_deleted); - crm_element_value_epoch(msg, F_LRMD_RSC_RUN_TIME, &epoch); + crm_element_value_epoch(msg, PCMK__XA_LRMD_RUN_TIME, &epoch); event.t_run = (unsigned int) epoch; - crm_element_value_epoch(msg, F_LRMD_RSC_RCCHANGE_TIME, &epoch); + crm_element_value_epoch(msg, PCMK__XA_LRMD_RCCHANGE_TIME, &epoch); event.t_rcchange = (unsigned int) epoch; - crm_element_value_int(msg, F_LRMD_RSC_EXEC_TIME, (int *)&event.exec_time); - crm_element_value_int(msg, F_LRMD_RSC_QUEUE_TIME, (int *)&event.queue_time); + crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_TIME, &exec_time); + CRM_LOG_ASSERT(exec_time >= 0); + event.exec_time = QB_MAX(0, exec_time); - event.op_type = crm_element_value(msg, F_LRMD_RSC_ACTION); - event.user_data = crm_element_value(msg, F_LRMD_RSC_USERDATA_STR); + crm_element_value_int(msg, PCMK__XA_LRMD_QUEUE_TIME, &queue_time); + CRM_LOG_ASSERT(queue_time >= 0); + event.queue_time = QB_MAX(0, queue_time); + + event.op_type = crm_element_value(msg, PCMK__XA_LRMD_RSC_ACTION); + event.user_data = crm_element_value(msg, + PCMK__XA_LRMD_RSC_USERDATA_STR); event.type = lrmd_event_exec_complete; /* output and exit_reason may be freed by a callback */ - event.output = crm_element_value_copy(msg, F_LRMD_RSC_OUTPUT); + event.output = crm_element_value_copy(msg, PCMK__XA_LRMD_RSC_OUTPUT); lrmd__set_result(&event, event.rc, event.op_status, - crm_element_value(msg, F_LRMD_RSC_EXIT_REASON)); + crm_element_value(msg, PCMK__XA_LRMD_RSC_EXIT_REASON)); event.params = xml2list(msg); } else if (pcmk__str_eq(type, LRMD_OP_NEW_CLIENT, pcmk__str_none)) { @@ -338,7 +358,7 @@ lrmd_ipc_dispatch(const char *buffer, ssize_t length, gpointer userdata) lrmd_private_t *native = lrmd->lrmd_private; if (native->callback != NULL) { - xmlNode *msg = string2xml(buffer); + xmlNode *msg = pcmk__xml_parse(buffer); lrmd_dispatch_internal(lrmd, msg); free_xml(msg); @@ -415,7 +435,8 @@ lrmd_tls_dispatch(gpointer userdata) break; } while (xml) { - const char *msg_type = crm_element_value(xml, F_LRMD_REMOTE_MSG_TYPE); + const char *msg_type = crm_element_value(xml, + PCMK__XA_LRMD_REMOTE_MSG_TYPE); if (pcmk__str_eq(msg_type, "notify", pcmk__str_casei)) { lrmd_dispatch_internal(lrmd, xml); } else if (pcmk__str_eq(msg_type, "reply", pcmk__str_casei)) { @@ -423,7 +444,7 @@ lrmd_tls_dispatch(gpointer userdata) native->expected_late_replies--; } else { int reply_id = 0; - crm_element_value_int(xml, F_LRMD_CALLID, &reply_id); + crm_element_value_int(xml, PCMK__XA_LRMD_CALLID, &reply_id); /* if this happens, we want to know about it */ crm_err("Got outdated Pacemaker Remote reply %d", reply_id); } @@ -517,20 +538,20 @@ static xmlNode * lrmd_create_op(const char *token, const char *op, xmlNode *data, int timeout, enum lrmd_call_options options) { - xmlNode *op_msg = create_xml_node(NULL, "lrmd_command"); + xmlNode *op_msg = NULL; - CRM_CHECK(op_msg != NULL, return NULL); CRM_CHECK(token != NULL, return NULL); - crm_xml_add(op_msg, F_XML_TAGNAME, "lrmd_command"); - crm_xml_add(op_msg, F_TYPE, T_LRMD); - crm_xml_add(op_msg, F_LRMD_CALLBACK_TOKEN, token); - crm_xml_add(op_msg, F_LRMD_OPERATION, op); - crm_xml_add_int(op_msg, F_LRMD_TIMEOUT, timeout); - crm_xml_add_int(op_msg, F_LRMD_CALLOPTS, options); + op_msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_COMMAND); + crm_xml_add(op_msg, PCMK__XA_T, PCMK__VALUE_LRMD); + crm_xml_add(op_msg, PCMK__XA_LRMD_OP, op); + crm_xml_add_int(op_msg, PCMK__XA_LRMD_TIMEOUT, timeout); + crm_xml_add_int(op_msg, PCMK__XA_LRMD_CALLOPT, options); if (data != NULL) { - add_message_xml(op_msg, F_LRMD_CALLDATA, data); + xmlNode *wrapper = pcmk__xe_create(op_msg, PCMK__XE_LRMD_CALLDATA); + + pcmk__xml_copy(wrapper, data); } crm_trace("Created executor %s command with call options %.8lx (%d)", @@ -584,6 +605,7 @@ lrmd_tls_connection_destroy(gpointer userdata) gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR); gnutls_deinit(*native->remote->tls_session); gnutls_free(native->remote->tls_session); + native->remote->tls_session = NULL; } if (native->psk_cred_c) { gnutls_psk_free_client_credentials(native->psk_cred_c); @@ -607,7 +629,6 @@ lrmd_tls_connection_destroy(gpointer userdata) native->source = 0; native->sock = 0; native->psk_cred_c = NULL; - native->remote->tls_session = NULL; native->sock = 0; if (native->callback) { @@ -624,8 +645,8 @@ int lrmd__remote_send_xml(pcmk__remote_t *session, xmlNode *msg, uint32_t id, const char *msg_type) { - crm_xml_add_int(msg, F_LRMD_REMOTE_MSG_ID, id); - crm_xml_add(msg, F_LRMD_REMOTE_MSG_TYPE, msg_type); + crm_xml_add_int(msg, PCMK__XA_LRMD_REMOTE_MSG_ID, id); + crm_xml_add(msg, PCMK__XA_LRMD_REMOTE_MSG_TYPE, msg_type); return pcmk__remote_send_xml(session, msg); } @@ -672,8 +693,8 @@ read_remote_reply(lrmd_t *lrmd, int total_timeout, int expected_reply_id, } } - crm_element_value_int(*reply, F_LRMD_REMOTE_MSG_ID, &reply_id); - msg_type = crm_element_value(*reply, F_LRMD_REMOTE_MSG_TYPE); + crm_element_value_int(*reply, PCMK__XA_LRMD_REMOTE_MSG_ID, &reply_id); + msg_type = crm_element_value(*reply, PCMK__XA_LRMD_REMOTE_MSG_TYPE); if (!msg_type) { crm_err("Empty msg type received while waiting for reply"); @@ -905,7 +926,7 @@ lrmd_send_command(lrmd_t *lrmd, const char *op, xmlNode *data, rc = pcmk_ok; crm_trace("%s op reply received", op); - if (crm_element_value_int(op_reply, F_LRMD_RC, &rc) != 0) { + if (crm_element_value_int(op_reply, PCMK__XA_LRMD_RC, &rc) != 0) { rc = -ENOMSG; goto done; } @@ -932,9 +953,9 @@ lrmd_api_poke_connection(lrmd_t * lrmd) { int rc; lrmd_private_t *native = lrmd->lrmd_private; - xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); rc = lrmd_send_command(lrmd, LRMD_OP_POKE, data, NULL, 0, 0, (native->type == pcmk__client_ipc)); free_xml(data); @@ -949,14 +970,14 @@ lrmd__validate_remote_settings(lrmd_t *lrmd, GHashTable *hash) int rc = pcmk_rc_ok; const char *value; lrmd_private_t *native = lrmd->lrmd_private; - xmlNode *data = create_xml_node(NULL, F_LRMD_OPERATION); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XA_LRMD_OP); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); - value = g_hash_table_lookup(hash, "stonith-watchdog-timeout"); + value = g_hash_table_lookup(hash, PCMK_OPT_STONITH_WATCHDOG_TIMEOUT); if ((value) && (stonith__watchdog_fencing_enabled_for_node(native->remote_nodename))) { - crm_xml_add(data, F_LRMD_WATCHDOG, value); + crm_xml_add(data, PCMK__XA_LRMD_WATCHDOG, value); } rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0, @@ -971,16 +992,16 @@ lrmd_handshake(lrmd_t * lrmd, const char *name) int rc = pcmk_ok; lrmd_private_t *native = lrmd->lrmd_private; xmlNode *reply = NULL; - xmlNode *hello = create_xml_node(NULL, "lrmd_command"); + xmlNode *hello = pcmk__xe_create(NULL, PCMK__XE_LRMD_COMMAND); - crm_xml_add(hello, F_TYPE, T_LRMD); - crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_REGISTER); - crm_xml_add(hello, F_LRMD_CLIENTNAME, name); - crm_xml_add(hello, F_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION); + crm_xml_add(hello, PCMK__XA_T, PCMK__VALUE_LRMD); + crm_xml_add(hello, PCMK__XA_LRMD_OP, CRM_OP_REGISTER); + crm_xml_add(hello, PCMK__XA_LRMD_CLIENTNAME, name); + crm_xml_add(hello, PCMK__XA_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION); /* advertise that we are a proxy provider */ if (native->proxy_callback) { - pcmk__xe_set_bool_attr(hello, F_LRMD_IS_IPC_PROVIDER, true); + pcmk__xe_set_bool_attr(hello, PCMK__XA_LRMD_IS_IPC_PROVIDER, true); } rc = lrmd_send_xml(lrmd, hello, -1, &reply); @@ -992,13 +1013,15 @@ lrmd_handshake(lrmd_t * lrmd, const char *name) crm_err("Did not receive registration reply"); rc = -EPROTO; } else { - const char *version = crm_element_value(reply, F_LRMD_PROTOCOL_VERSION); - const char *msg_type = crm_element_value(reply, F_LRMD_OPERATION); - const char *tmp_ticket = crm_element_value(reply, F_LRMD_CLIENTID); + const char *version = crm_element_value(reply, + PCMK__XA_LRMD_PROTOCOL_VERSION); + const char *msg_type = crm_element_value(reply, PCMK__XA_LRMD_OP); + const char *tmp_ticket = crm_element_value(reply, + PCMK__XA_LRMD_CLIENTID); const char *start_state = crm_element_value(reply, PCMK__XA_NODE_START_STATE); long long uptime = -1; - crm_element_value_int(reply, F_LRMD_RC, &rc); + crm_element_value_int(reply, PCMK__XA_LRMD_RC, &rc); /* The remote executor may add its uptime to the XML reply, which is * useful in handling transient attributes when the connection to the @@ -1088,7 +1111,7 @@ copy_gnutls_datum(gnutls_datum_t *dest, gnutls_datum_t *source) CRM_ASSERT((dest != NULL) && (source != NULL) && (source->data != NULL)); dest->data = gnutls_malloc(source->size); - CRM_ASSERT(dest->data); + pcmk__mem_assert(dest->data); memcpy(dest->data, source->data, source->size); dest->size = source->size; @@ -1281,6 +1304,8 @@ lrmd__init_remote_key(gnutls_datum_t *key) * would be better to never use the Corosync location as a fallback. * However, that would break any deployments currently working with the * fallbacks. + * + * @COMPAT Change at 3.0.0 */ } @@ -1292,6 +1317,7 @@ lrmd__init_remote_key(gnutls_datum_t *key) } // Try fallback location, if environment wasn't set to it and default failed + // @COMPAT Drop at 3.0.0 if (env_is_fallback) { alt_rc = env_rc; } else if (default_rc != pcmk_rc_ok) { @@ -1321,16 +1347,28 @@ lrmd__init_remote_key(gnutls_datum_t *key) (default_rc == pcmk_rc_ok)? "default" : "fallback", (default_rc == pcmk_rc_ok)? DEFAULT_REMOTE_KEY_LOCATION : ALT_REMOTE_KEY_LOCATION, pcmk_rc_str(env_rc)); + crm_warn("This undocumented behavior is deprecated and unsafe and will " + "be removed in a future release"); return pcmk_rc_ok; } - if ((default_rc != pcmk_rc_ok) && (alt_rc != pcmk_rc_ok)) { - // Environment unset, defaults failed - crm_warn("Could not read Pacemaker Remote key from default location %s" - " (or fallback location %s): %s", - DEFAULT_REMOTE_KEY_LOCATION, ALT_REMOTE_KEY_LOCATION, - pcmk_rc_str(default_rc)); - return ENOKEY; + if (default_rc != pcmk_rc_ok) { + if (alt_rc == pcmk_rc_ok) { + // Environment variable unset, used alternate location + // This gets caught by the default return below, but we additionally + // warn on this behavior here. + crm_warn("Read Pacemaker Remote key from alternate location %s", + ALT_REMOTE_KEY_LOCATION); + crm_warn("This undocumented behavior is deprecated and unsafe and will " + "be removed in a future release"); + } else { + // Environment unset, defaults failed + crm_warn("Could not read Pacemaker Remote key from default location %s" + " (or fallback location %s): %s", + DEFAULT_REMOTE_KEY_LOCATION, ALT_REMOTE_KEY_LOCATION, + pcmk_rc_str(default_rc)); + return ENOKEY; + } } return pcmk_rc_ok; // Environment variable unset, a default worked @@ -1650,7 +1688,7 @@ lrmd_tls_disconnect(lrmd_t * lrmd) gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR); gnutls_deinit(*native->remote->tls_session); gnutls_free(native->remote->tls_session); - native->remote->tls_session = 0; + native->remote->tls_session = NULL; } if (native->async_timer) { @@ -1724,13 +1762,13 @@ lrmd_api_register_rsc(lrmd_t * lrmd, return -EINVAL; } - data = create_xml_node(NULL, F_LRMD_RSC); + data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); - crm_xml_add(data, F_LRMD_CLASS, class); - crm_xml_add(data, F_LRMD_PROVIDER, provider); - crm_xml_add(data, F_LRMD_TYPE, type); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, PCMK__XA_LRMD_CLASS, class); + crm_xml_add(data, PCMK__XA_LRMD_PROVIDER, provider); + crm_xml_add(data, PCMK__XA_LRMD_TYPE, type); rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options, TRUE); free_xml(data); @@ -1741,10 +1779,10 @@ static int lrmd_api_unregister_rsc(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options) { int rc = pcmk_ok; - xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options, TRUE); free_xml(data); @@ -1755,13 +1793,12 @@ lrmd_rsc_info_t * lrmd_new_rsc_info(const char *rsc_id, const char *standard, const char *provider, const char *type) { - lrmd_rsc_info_t *rsc_info = calloc(1, sizeof(lrmd_rsc_info_t)); + lrmd_rsc_info_t *rsc_info = pcmk__assert_alloc(1, sizeof(lrmd_rsc_info_t)); - CRM_ASSERT(rsc_info); - pcmk__str_update(&rsc_info->id, rsc_id); - pcmk__str_update(&rsc_info->standard, standard); - pcmk__str_update(&rsc_info->provider, provider); - pcmk__str_update(&rsc_info->type, type); + rsc_info->id = pcmk__str_copy(rsc_id); + rsc_info->standard = pcmk__str_copy(standard); + rsc_info->provider = pcmk__str_copy(provider); + rsc_info->type = pcmk__str_copy(type); return rsc_info; } @@ -1789,14 +1826,14 @@ static lrmd_rsc_info_t * lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options) { lrmd_rsc_info_t *rsc_info = NULL; - xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); xmlNode *output = NULL; const char *class = NULL; const char *provider = NULL; const char *type = NULL; - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options, TRUE); free_xml(data); @@ -1804,9 +1841,9 @@ lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options return NULL; } - class = crm_element_value(output, F_LRMD_CLASS); - provider = crm_element_value(output, F_LRMD_PROVIDER); - type = crm_element_value(output, F_LRMD_TYPE); + class = crm_element_value(output, PCMK__XA_LRMD_CLASS); + provider = crm_element_value(output, PCMK__XA_LRMD_PROVIDER); + type = crm_element_value(output, PCMK__XA_LRMD_TYPE); if (!class || !type) { free_xml(output); @@ -1849,9 +1886,9 @@ lrmd_api_get_recurring_ops(lrmd_t *lrmd, const char *rsc_id, int timeout_ms, // Send request if (rsc_id) { - data = create_xml_node(NULL, F_LRMD_RSC); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); } rc = lrmd_send_command(lrmd, LRMD_OP_GET_RECURRING, data, &output_xml, timeout_ms, options, TRUE); @@ -1863,17 +1900,21 @@ lrmd_api_get_recurring_ops(lrmd_t *lrmd, const char *rsc_id, int timeout_ms, if ((rc != pcmk_ok) || (output_xml == NULL)) { return rc; } - for (xmlNode *rsc_xml = first_named_child(output_xml, F_LRMD_RSC); + for (const xmlNode *rsc_xml = pcmk__xe_first_child(output_xml, + PCMK__XE_LRMD_RSC, NULL, + NULL); (rsc_xml != NULL) && (rc == pcmk_ok); - rsc_xml = crm_next_same_xml(rsc_xml)) { + rsc_xml = pcmk__xe_next_same(rsc_xml)) { - rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID); if (rsc_id == NULL) { crm_err("Could not parse recurring operation information from executor"); continue; } - for (xmlNode *op_xml = first_named_child(rsc_xml, T_LRMD_RSC_OP); - op_xml != NULL; op_xml = crm_next_same_xml(op_xml)) { + for (const xmlNode *op_xml = pcmk__xe_first_child(rsc_xml, + PCMK__XE_LRMD_RSC_OP, + NULL, NULL); + op_xml != NULL; op_xml = pcmk__xe_next_same(op_xml)) { lrmd_op_info_t *op_info = calloc(1, sizeof(lrmd_op_info_t)); @@ -1882,11 +1923,12 @@ lrmd_api_get_recurring_ops(lrmd_t *lrmd, const char *rsc_id, int timeout_ms, break; } op_info->rsc_id = strdup(rsc_id); - op_info->action = crm_element_value_copy(op_xml, F_LRMD_RSC_ACTION); - op_info->interval_ms_s = crm_element_value_copy(op_xml, - F_LRMD_RSC_INTERVAL); - op_info->timeout_ms_s = crm_element_value_copy(op_xml, - F_LRMD_TIMEOUT); + op_info->action = crm_element_value_copy(op_xml, + PCMK__XA_LRMD_RSC_ACTION); + op_info->interval_ms_s = + crm_element_value_copy(op_xml, PCMK__XA_LRMD_RSC_INTERVAL); + op_info->timeout_ms_s = + crm_element_value_copy(op_xml, PCMK__XA_LRMD_TIMEOUT); *output = g_list_prepend(*output, op_info); } } @@ -1929,7 +1971,7 @@ lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg) if (lrmd == NULL) { return -ENOTCONN; } - crm_xml_add(msg, F_LRMD_OPERATION, CRM_OP_IPC_FWD); + crm_xml_add(msg, PCMK__XA_LRMD_OP, CRM_OP_IPC_FWD); crm_log_xml_trace(msg, "PROXY_OUTBOUND"); return lrmd_send_xml_no_reply(lrmd, msg); @@ -1985,7 +2027,7 @@ lrmd_api_get_metadata_params(lrmd_t *lrmd, const char *standard, params_table = pcmk__strkey_table(free, free); for (const lrmd_key_value_t *param = params; param; param = param->next) { - g_hash_table_insert(params_table, strdup(param->key), strdup(param->value)); + pcmk__insert_dup(params_table, param->key, param->value); } action = services__create_resource_action(type, standard, provider, type, PCMK_ACTION_META_DATA, 0, @@ -2029,17 +2071,17 @@ lrmd_api_exec(lrmd_t *lrmd, const char *rsc_id, const char *action, enum lrmd_call_options options, lrmd_key_value_t * params) { int rc = pcmk_ok; - xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); - xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); + xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES); lrmd_key_value_t *tmp = NULL; - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); - crm_xml_add(data, F_LRMD_RSC_ACTION, action); - crm_xml_add(data, F_LRMD_RSC_USERDATA_STR, userdata); - crm_xml_add_ms(data, F_LRMD_RSC_INTERVAL, interval_ms); - crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout); - crm_xml_add_int(data, F_LRMD_RSC_START_DELAY, start_delay); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ACTION, action); + crm_xml_add(data, PCMK__XA_LRMD_RSC_USERDATA_STR, userdata); + crm_xml_add_ms(data, PCMK__XA_LRMD_RSC_INTERVAL, interval_ms); + crm_xml_add_int(data, PCMK__XA_LRMD_TIMEOUT, timeout); + crm_xml_add_int(data, PCMK__XA_LRMD_RSC_START_DELAY, start_delay); for (tmp = params; tmp; tmp = tmp->next) { hash2smartfield((gpointer) tmp->key, (gpointer) tmp->value, args); @@ -2058,14 +2100,14 @@ lrmd_api_exec_alert(lrmd_t *lrmd, const char *alert_id, const char *alert_path, int timeout, lrmd_key_value_t *params) { int rc = pcmk_ok; - xmlNode *data = create_xml_node(NULL, F_LRMD_ALERT); - xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_ALERT); + xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES); lrmd_key_value_t *tmp = NULL; - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_ALERT_ID, alert_id); - crm_xml_add(data, F_LRMD_ALERT_PATH, alert_path); - crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_ALERT_ID, alert_id); + crm_xml_add(data, PCMK__XA_LRMD_ALERT_PATH, alert_path); + crm_xml_add_int(data, PCMK__XA_LRMD_TIMEOUT, timeout); for (tmp = params; tmp; tmp = tmp->next) { hash2smartfield((gpointer) tmp->key, (gpointer) tmp->value, args); @@ -2084,12 +2126,12 @@ lrmd_api_cancel(lrmd_t *lrmd, const char *rsc_id, const char *action, guint interval_ms) { int rc = pcmk_ok; - xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC); - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - crm_xml_add(data, F_LRMD_RSC_ACTION, action); - crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); - crm_xml_add_ms(data, F_LRMD_RSC_INTERVAL, interval_ms); + crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ACTION, action); + crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id); + crm_xml_add_ms(data, PCMK__XA_LRMD_RSC_INTERVAL, interval_ms); rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0, TRUE); free_xml(data); return rc; @@ -2512,6 +2554,8 @@ lrmd__set_result(lrmd_event_data_t *event, enum ocf_exitcode rc, int op_status, event->rc = rc; event->op_status = op_status; + + // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees pcmk__str_update((char **) &event->exit_reason, exit_reason); } diff --git a/lib/lrmd/lrmd_output.c b/lib/lrmd/lrmd_output.c index b0524c6..49900e5 100644 --- a/lib/lrmd/lrmd_output.c +++ b/lib/lrmd/lrmd_output.c @@ -1,5 +1,5 @@ /* - * Copyright 2020 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -11,6 +11,7 @@ #include <stdarg.h> #include <crm/lrmd_internal.h> +#include <crm/common/xml.h> #include <crm/common/output_internal.h> static int @@ -46,10 +47,14 @@ lrmd__alternatives_list_xml(pcmk__output_t *out, va_list args) { lrmd_list_t *list = va_arg(args, lrmd_list_t *); const char *agent_spec = va_arg(args, const char *); - pcmk__output_xml_create_parent(out, "providers", - "for", agent_spec, + int rc = pcmk_rc_ok; + + pcmk__output_xml_create_parent(out, PCMK_XE_PROVIDERS, + PCMK_XA_FOR, agent_spec, NULL); - return xml_list(out, list, "provider"); + rc = xml_list(out, list, PCMK_XE_PROVIDER); + pcmk__output_xml_pop_parent(out); + return rc; } PCMK__OUTPUT_ARGS("alternatives-list", "lrmd_list_t *", "const char *") @@ -68,15 +73,20 @@ lrmd__agents_list_xml(pcmk__output_t *out, va_list args) { const char *agent_spec = va_arg(args, const char *); const char *provider = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_xml_create_parent(out, "agents", - "standard", agent_spec, - NULL); + int rc = pcmk_rc_ok; + xmlNodePtr node = NULL; + + node = pcmk__output_xml_create_parent(out, PCMK_XE_AGENTS, + PCMK_XA_STANDARD, agent_spec, + NULL); if (!pcmk__str_empty(provider)) { - crm_xml_add(node, "provider", provider); + crm_xml_add(node, PCMK_XA_PROVIDER, provider); } - return xml_list(out, list, "agent"); + rc = xml_list(out, list, PCMK_XE_AGENT); + pcmk__output_xml_pop_parent(out); + return rc; } PCMK__OUTPUT_ARGS("agents-list", "lrmd_list_t *", "const char *", "const char *") @@ -100,15 +110,18 @@ lrmd__providers_list_xml(pcmk__output_t *out, va_list args) { lrmd_list_t *list = va_arg(args, lrmd_list_t *); const char *agent_spec = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_xml_create_parent(out, "providers", - "standard", "ocf", + int rc = pcmk_rc_ok; + xmlNodePtr node = pcmk__output_xml_create_parent(out, PCMK_XE_PROVIDERS, + PCMK_XA_STANDARD, "ocf", NULL); if (agent_spec != NULL) { - crm_xml_add(node, "agent", agent_spec); + crm_xml_add(node, PCMK_XA_AGENT, agent_spec); } - return xml_list(out, list, "provider"); + rc = xml_list(out, list, PCMK_XE_PROVIDER); + pcmk__output_xml_pop_parent(out); + return rc; } PCMK__OUTPUT_ARGS("providers-list", "lrmd_list_t *", "const char *") diff --git a/lib/lrmd/proxy_common.c b/lib/lrmd/proxy_common.c index 24776c2..8602fe7 100644 --- a/lib/lrmd/proxy_common.c +++ b/lib/lrmd/proxy_common.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,7 +13,7 @@ #include <unistd.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/services.h> #include <crm/common/mainloop.h> @@ -29,9 +29,9 @@ static void remote_proxy_notify_destroy(lrmd_t *lrmd, const char *session_id) { /* sending to the remote node that an ipc connection has been destroyed */ - xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY); - crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY); - crm_xml_add(msg, F_LRMD_IPC_SESSION, session_id); + xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY); + crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY); + crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, session_id); lrmd_internal_proxy_send(lrmd, msg); free_xml(msg); } @@ -45,8 +45,8 @@ remote_proxy_notify_destroy(lrmd_t *lrmd, const char *session_id) void remote_proxy_ack_shutdown(lrmd_t *lrmd) { - xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY); - crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_ACK); + xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY); + crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_ACK); lrmd_internal_proxy_send(lrmd, msg); free_xml(msg); } @@ -60,8 +60,8 @@ remote_proxy_ack_shutdown(lrmd_t *lrmd) void remote_proxy_nack_shutdown(lrmd_t *lrmd) { - xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY); - crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_NACK); + xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY); + crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_NACK); lrmd_internal_proxy_send(lrmd, msg); free_xml(msg); } @@ -70,10 +70,15 @@ void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg) { /* sending to the remote node an event msg. */ - xmlNode *event = create_xml_node(NULL, T_LRMD_IPC_PROXY); - crm_xml_add(event, F_LRMD_IPC_OP, LRMD_IPC_OP_EVENT); - crm_xml_add(event, F_LRMD_IPC_SESSION, proxy->session_id); - add_message_xml(event, F_LRMD_IPC_MSG, msg); + xmlNode *event = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY); + xmlNode *wrapper = NULL; + + crm_xml_add(event, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_EVENT); + crm_xml_add(event, PCMK__XA_LRMD_IPC_SESSION, proxy->session_id); + + wrapper = pcmk__xe_create(event, PCMK__XE_LRMD_IPC_MSG); + pcmk__xml_copy(wrapper, msg); + crm_log_xml_explicit(event, "EventForProxy"); lrmd_internal_proxy_send(proxy->lrm, event); free_xml(event); @@ -83,11 +88,16 @@ void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id) { /* sending to the remote node a response msg. */ - xmlNode *response = create_xml_node(NULL, T_LRMD_IPC_PROXY); - crm_xml_add(response, F_LRMD_IPC_OP, LRMD_IPC_OP_RESPONSE); - crm_xml_add(response, F_LRMD_IPC_SESSION, proxy->session_id); - crm_xml_add_int(response, F_LRMD_IPC_MSG_ID, msg_id); - add_message_xml(response, F_LRMD_IPC_MSG, msg); + xmlNode *response = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY); + xmlNode *wrapper = NULL; + + crm_xml_add(response, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_RESPONSE); + crm_xml_add(response, PCMK__XA_LRMD_IPC_SESSION, proxy->session_id); + crm_xml_add_int(response, PCMK__XA_LRMD_IPC_MSG_ID, msg_id); + + wrapper = pcmk__xe_create(response, PCMK__XE_LRMD_IPC_MSG); + pcmk__xml_copy(wrapper, msg); + lrmd_internal_proxy_send(proxy->lrm, response); free_xml(response); } @@ -124,7 +134,7 @@ remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata) uint32_t flags = 0; remote_proxy_t *proxy = userdata; - xml = string2xml(buffer); + xml = pcmk__xml_parse(buffer); if (xml == NULL) { crm_warn("Received a NULL msg from IPC service."); return 1; @@ -175,7 +185,7 @@ remote_proxy_new(lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks, return NULL; } - proxy = calloc(1, sizeof(remote_proxy_t)); + proxy = pcmk__assert_alloc(1, sizeof(remote_proxy_t)); proxy->node_name = strdup(node_name); proxy->session_id = strdup(session_id); @@ -206,8 +216,8 @@ remote_proxy_new(lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks, void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) { - const char *op = crm_element_value(msg, F_LRMD_IPC_OP); - const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION); + const char *op = crm_element_value(msg, PCMK__XA_LRMD_IPC_OP); + const char *session = crm_element_value(msg, PCMK__XA_LRMD_IPC_SESSION); remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); int msg_id = 0; @@ -218,7 +228,7 @@ remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) CRM_CHECK(op != NULL, return); CRM_CHECK(session != NULL, return); - crm_element_value_int(msg, F_LRMD_IPC_MSG_ID, &msg_id); + crm_element_value_int(msg, PCMK__XA_LRMD_IPC_MSG_ID, &msg_id); /* This is msg from remote ipc client going to real ipc server */ if (pcmk__str_eq(op, LRMD_IPC_OP_DESTROY, pcmk__str_casei)) { @@ -226,8 +236,11 @@ remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) } else if (pcmk__str_eq(op, LRMD_IPC_OP_REQUEST, pcmk__str_casei)) { int flags = 0; - xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG); - const char *name = crm_element_value(msg, F_LRMD_IPC_CLIENT); + const char *name = crm_element_value(msg, PCMK__XA_LRMD_IPC_CLIENT); + + xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_LRMD_IPC_MSG, + NULL, NULL); + xmlNode *request = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); CRM_CHECK(request != NULL, return); @@ -246,20 +259,19 @@ remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) return; } proxy->last_request_id = 0; - crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags); - crm_xml_add(request, XML_ACL_TAG_ROLE, "pacemaker-remote"); + crm_element_value_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, &flags); + crm_xml_add(request, PCMK_XE_ACL_ROLE, "pacemaker-remote"); CRM_ASSERT(node_name); - pcmk__update_acl_user(request, F_LRMD_IPC_USER, node_name); + pcmk__update_acl_user(request, PCMK__XA_LRMD_IPC_USER, node_name); if (pcmk_is_set(flags, crm_ipc_proxied)) { - const char *type = crm_element_value(request, F_TYPE); + const char *type = crm_element_value(request, PCMK__XA_T); int rc = 0; - if (pcmk__str_eq(type, T_ATTRD, pcmk__str_casei) - && crm_element_value(request, - PCMK__XA_ATTR_NODE_NAME) == NULL - && pcmk__str_any_of(crm_element_value(request, PCMK__XA_TASK), + if (pcmk__str_eq(type, PCMK__VALUE_ATTRD, pcmk__str_none) + && (crm_element_value(request, PCMK__XA_ATTR_HOST) == NULL) + && pcmk__str_any_of(crm_element_value(request, PCMK_XA_TASK), PCMK__ATTRD_CMD_UPDATE, PCMK__ATTRD_CMD_UPDATE_BOTH, PCMK__ATTRD_CMD_UPDATE_DELAY, NULL)) { @@ -269,15 +281,15 @@ remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) rc = crm_ipc_send(proxy->ipc, request, flags, 5000, NULL); if(rc < 0) { - xmlNode *op_reply = create_xml_node(NULL, "nack"); + xmlNode *op_reply = pcmk__xe_create(NULL, PCMK__XE_NACK); crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); /* Send a n'ack so the caller doesn't block */ - crm_xml_add(op_reply, "function", __func__); - crm_xml_add_int(op_reply, "line", __LINE__); - crm_xml_add_int(op_reply, "rc", rc); + crm_xml_add(op_reply, PCMK_XA_FUNCTION, __func__); + crm_xml_add_int(op_reply, PCMK__XA_LINE, __LINE__); + crm_xml_add_int(op_reply, PCMK_XA_RC, rc); remote_proxy_relay_response(proxy, op_reply, msg_id); free_xml(op_reply); diff --git a/lib/pacemaker/Makefile.am b/lib/pacemaker/Makefile.am index 06f8dfb..8c1671d 100644 --- a/lib/pacemaker/Makefile.am +++ b/lib/pacemaker/Makefile.am @@ -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. # @@ -11,12 +11,14 @@ include $(top_srcdir)/mk/common.mk AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +SUBDIRS = tests + noinst_HEADERS = libpacemaker_private.h ## libraries lib_LTLIBRARIES = libpacemaker.la -libpacemaker_la_LDFLAGS = -version-info 8:0:7 +libpacemaker_la_LDFLAGS = -version-info 9:0:8 libpacemaker_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libpacemaker_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) @@ -40,6 +42,7 @@ libpacemaker_la_SOURCES += pcmk_graph_consumer.c libpacemaker_la_SOURCES += pcmk_graph_logging.c libpacemaker_la_SOURCES += pcmk_graph_producer.c libpacemaker_la_SOURCES += pcmk_injections.c +libpacemaker_la_SOURCES += pcmk_options.c libpacemaker_la_SOURCES += pcmk_output.c libpacemaker_la_SOURCES += pcmk_resource.c libpacemaker_la_SOURCES += pcmk_result_code.c @@ -65,5 +68,8 @@ libpacemaker_la_SOURCES += pcmk_sched_resource.c libpacemaker_la_SOURCES += pcmk_sched_tickets.c libpacemaker_la_SOURCES += pcmk_sched_utilization.c libpacemaker_la_SOURCES += pcmk_scheduler.c +libpacemaker_la_SOURCES += pcmk_setup.c libpacemaker_la_SOURCES += pcmk_simulate.c libpacemaker_la_SOURCES += pcmk_status.c +libpacemaker_la_SOURCES += pcmk_ticket.c +libpacemaker_la_SOURCES += pcmk_verify.c diff --git a/lib/pacemaker/libpacemaker_private.h b/lib/pacemaker/libpacemaker_private.h index c4a0c90..4738e47 100644 --- a/lib/pacemaker/libpacemaker_private.h +++ b/lib/pacemaker/libpacemaker_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,7 +16,7 @@ #include <crm/lrmd_events.h> // lrmd_event_data_t #include <crm/common/scheduler.h> // pcmk_action_t, pcmk_node_t, etc. -#include <crm/pengine/internal.h> // pe__location_t +#include <crm/pengine/internal.h> // pcmk__location_t // Colocation flags enum pcmk__coloc_flags { @@ -241,7 +241,7 @@ struct resource_alloc_functions_s { * \param[in,out] rsc Resource to apply constraint to * \param[in,out] location Location constraint to apply */ - void (*apply_location)(pcmk_resource_t *rsc, pe__location_t *location); + void (*apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location); /*! * \internal @@ -471,15 +471,15 @@ G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL -pe__location_t *pcmk__new_location(const char *id, pcmk_resource_t *rsc, - int node_score, const char *discover_mode, - pcmk_node_t *foo_node); +pcmk__location_t *pcmk__new_location(const char *id, pcmk_resource_t *rsc, + int node_score, const char *discover_mode, + pcmk_node_t *foo_node); G_GNUC_INTERNAL void pcmk__apply_locations(pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL -void pcmk__apply_location(pcmk_resource_t *rsc, pe__location_t *constraint); +void pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *constraint); // Colocation constraints (pcmk_sched_colocation.c) @@ -494,12 +494,6 @@ enum pcmk__coloc_affects { * \internal * \brief Get the value of a colocation's node attribute * - * When looking up a colocation node attribute on a bundle node for a bundle - * primitive, we should always look on the bundle node's assigned host, - * regardless of the value of XML_RSC_ATTR_TARGET. At most one resource (the - * bundle primitive, if any) can run on a bundle node, so any colocation must - * necessarily be evaluated with respect to the bundle node (the container). - * * \param[in] node Node on which to look up the attribute * \param[in] attr Name of attribute to look up * \param[in] rsc Resource on whose behalf to look up the attribute @@ -510,13 +504,23 @@ static inline const char * pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr, const pcmk_resource_t *rsc) { - const pcmk_resource_t *top = pe__const_top_resource(rsc, false); - const bool force_host = pe__is_bundle_node(node) - && pe_rsc_is_bundled(rsc) - && (top == pe__bundled_resource(rsc)); + const char *target = NULL; + + /* A resource colocated with a bundle or its primitive can't run on the + * bundle node itself (where only the primitive, if any, can run). Instead, + * we treat it as a colocation with the bundle's containers, so always look + * up colocation node attributes on the container host. + */ + if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc) + && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) { + target = PCMK_VALUE_HOST; - return pe__node_attribute_calculated(node, attr, rsc, - pcmk__rsc_node_assigned, force_host); + } else if (rsc != NULL) { + target = g_hash_table_lookup(rsc->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); + } + + return pcmk__node_attr(node, attr, target, pcmk__rsc_node_assigned); } G_GNUC_INTERNAL @@ -629,7 +633,7 @@ pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, } /* The dependent in a colocation influences the primary's location - * if the influence option is true or the primary is not yet active. + * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active. */ return pcmk_is_set(colocation->flags, pcmk__coloc_influence) || (rsc->running_on == NULL); @@ -747,8 +751,8 @@ G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params); G_GNUC_INTERNAL -void pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, - const pcmk_action_t *action); +void pcmk__add_guest_meta_to_xml(xmlNode *args_xml, + const pcmk_action_t *action); // Primitives (pcmk_sched_primitive.c) @@ -837,7 +841,8 @@ void pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, float factor, uint32_t flags); G_GNUC_INTERNAL -void pcmk__group_apply_location(pcmk_resource_t *rsc, pe__location_t *location); +void pcmk__group_apply_location(pcmk_resource_t *rsc, + pcmk__location_t *location); G_GNUC_INTERNAL uint32_t pcmk__group_action_flags(pcmk_action_t *action, @@ -898,7 +903,7 @@ void pcmk__clone_with_colocations(const pcmk_resource_t *rsc, G_GNUC_INTERNAL void pcmk__clone_apply_location(pcmk_resource_t *rsc, - pe__location_t *constraint); + pcmk__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__clone_action_flags(pcmk_action_t *action, @@ -951,7 +956,7 @@ void pcmk__bundle_with_colocations(const pcmk_resource_t *rsc, G_GNUC_INTERNAL void pcmk__bundle_apply_location(pcmk_resource_t *rsc, - pe__location_t *constraint); + pcmk__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__bundle_action_flags(pcmk_action_t *action, @@ -1024,9 +1029,9 @@ xmlNode *pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, const char *rprovider); G_GNUC_INTERNAL -void pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node, - const char *resource, const char *task, - guint interval_ms, int rc); +void pcmk__inject_failcount(pcmk__output_t *out, cib_t *cib_conn, + xmlNode *cib_node, const char *resource, + const char *task, guint interval_ms, int rc); G_GNUC_INTERNAL xmlNode *pcmk__inject_action_result(xmlNode *cib_resource, @@ -1132,7 +1137,7 @@ void pcmk__abort_dangling_migration(void *data, void *user_data); bool pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current); -void pcmk__order_migration_equivalents(pe__ordering_t *order); +void pcmk__order_migration_equivalents(pcmk__action_relation_t *order); // Functions related to node utilization (pcmk_sched_utilization.c) @@ -1159,4 +1164,18 @@ void pcmk__create_utilization_constraints(pcmk_resource_t *rsc, G_GNUC_INTERNAL void pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler); + +// Functions related to the scheduler (pcmk_scheduler.c) + +G_GNUC_INTERNAL +int pcmk__init_scheduler(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, + pcmk_scheduler_t **scheduler); + + +// General setup functions (pcmk_setup.c) + +G_GNUC_INTERNAL +int pcmk__setup_output_cib_sched(pcmk__output_t **out, cib_t **cib, + pcmk_scheduler_t **scheduler, xmlNode **xml); + #endif // PCMK__LIBPACEMAKER_PRIVATE__H diff --git a/lib/pacemaker/pcmk_acl.c b/lib/pacemaker/pcmk_acl.c index 85c461e..206c71e 100644 --- a/lib/pacemaker/pcmk_acl.c +++ b/lib/pacemaker/pcmk_acl.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 <libxslt/xsltutils.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/internal.h> @@ -204,7 +203,7 @@ int pcmk__acl_annotate_permissions(const char *cred, const xmlDoc *cib_doc, xmlDoc **acl_evaled_doc) { - int ret, version; + int ret; xmlNode *target, *comment; const char *validation; @@ -223,15 +222,15 @@ pcmk__acl_annotate_permissions(const char *cred, const xmlDoc *cib_doc, } // @COMPAT xmlDocGetRootElement() requires non-const in libxml2 < 2.9.2 - validation = crm_element_value(xmlDocGetRootElement((xmlDoc *) cib_doc), - XML_ATTR_VALIDATION); - version = get_schema_version(validation); - if (get_schema_version(PCMK__COMPAT_ACL_2_MIN_INCL) > version) { + PCMK_XA_VALIDATE_WITH); + + if (pcmk__cmp_schemas_by_name(PCMK__COMPAT_ACL_2_MIN_INCL, + validation) > 0) { return pcmk_rc_schema_validation; } - target = copy_xml(xmlDocGetRootElement((xmlDoc *) cib_doc)); + target = pcmk__xml_copy(NULL, xmlDocGetRootElement((xmlDoc *) cib_doc)); if (target == NULL) { return EINVAL; } @@ -295,7 +294,7 @@ pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how how, NULL }; const char **params; - int ret; + int rc = pcmk_rc_ok; xmlParserCtxtPtr parser_ctxt; /* unfortunately, the input (coming from CIB originally) was parsed with @@ -330,21 +329,20 @@ pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how how, parser_ctxt = xmlNewParserCtxt(); CRM_ASSERT(sfile != NULL); - CRM_ASSERT(parser_ctxt != NULL); + pcmk__mem_assert(parser_ctxt); xslt_doc = xmlCtxtReadFile(parser_ctxt, sfile, NULL, XML_PARSE_NONET); xslt = xsltParseStylesheetDoc(xslt_doc); /* acquires xslt_doc! */ if (xslt == NULL) { crm_crit("Problem in parsing %s", sfile); - return EINVAL; + rc = EINVAL; + goto done; } - free(sfile); - sfile = NULL; xmlFreeParserCtxt(parser_ctxt); xslt_ctxt = xsltNewTransformContext(xslt, annotated_doc); - CRM_ASSERT(xslt_ctxt != NULL); + pcmk__mem_assert(xslt_ctxt); switch (how) { case pcmk__acl_render_namespace: @@ -381,17 +379,20 @@ pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how how, } if (res == NULL) { - ret = EINVAL; + rc = EINVAL; } else { int doc_txt_len; int temp = xsltSaveResultToString(doc_txt_ptr, &doc_txt_len, res, xslt); xmlFreeDoc(res); - if (temp == 0) { - ret = pcmk_rc_ok; - } else { - ret = EINVAL; + if (temp != 0) { + rc = EINVAL; } } - xsltFreeStylesheet(xslt); - return ret; + +done: + if (xslt != NULL) { + xsltFreeStylesheet(xslt); + } + free(sfile); + return rc; } diff --git a/lib/pacemaker/pcmk_agents.c b/lib/pacemaker/pcmk_agents.c index 6fec140..4dacf40 100644 --- a/lib/pacemaker/pcmk_agents.c +++ b/lib/pacemaker/pcmk_agents.c @@ -60,7 +60,7 @@ pcmk_list_alternatives(xmlNodePtr *xml, const char *agent_spec) lrmd__register_messages(out); rc = pcmk__list_alternatives(out, agent_spec); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -131,7 +131,7 @@ pcmk_list_agents(xmlNodePtr *xml, char *agent_spec) lrmd__register_messages(out); rc = pcmk__list_agents(out, agent_spec); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -187,7 +187,7 @@ pcmk_list_providers(xmlNodePtr *xml, const char *agent_spec) lrmd__register_messages(out); rc = pcmk__list_providers(out, agent_spec); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -238,6 +238,6 @@ pcmk_list_standards(xmlNodePtr *xml) lrmd__register_messages(out); rc = pcmk__list_standards(out); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c index 6a12c45..3229fae 100644 --- a/lib/pacemaker/pcmk_cluster_queries.c +++ b/lib/pacemaker/pcmk_cluster_queries.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,6 @@ #include <crm/crm.h> #include <crm/cib.h> #include <crm/cib/internal.h> -#include <crm/msg_xml.h> #include <crm/common/output_internal.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> @@ -284,7 +283,7 @@ node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, if (data->show_output) { out->message(out, "node-info", - reply->data.node_info.id, reply->data.node_info.uname, + (uint32_t) reply->data.node_info.id, reply->data.node_info.uname, reply->data.node_info.uuid, reply->data.node_info.state, reply->data.node_info.have_quorum, reply->data.node_info.is_remote); @@ -509,7 +508,7 @@ pcmk_controller_status(xmlNodePtr *xml, const char *node_name, pcmk__register_lib_messages(out); rc = pcmk__controller_status(out, node_name, message_timeout_ms); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -577,7 +576,7 @@ pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms) pcmk__register_lib_messages(out); rc = pcmk__designated_controller(out, message_timeout_ms); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -707,7 +706,7 @@ pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name, rc = pcmk__query_node_info(out, node_id, node_name, uuid, state, have_quorum, is_remote, show_output, message_timeout_ms); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -797,7 +796,7 @@ pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name, pcmk__register_lib_messages(out); rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -815,7 +814,7 @@ remote_node_print_helper(xmlNode *result, void *user_data) { struct node_data *data = user_data; pcmk__output_t *out = data->out; - const char *name = crm_element_value(result, XML_ATTR_UNAME); + const char *name = crm_element_value(result, PCMK_XA_UNAME); const char *id = crm_element_value(result, data->field); // node name and node id are the same for remote/guest nodes @@ -840,21 +839,25 @@ pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export) .bash_export = bash_export }; - out->begin_list(out, NULL, NULL, "nodes"); + /* PCMK_XE_NODES acts as the list's element name for CLI tools that + * use pcmk__output_enable_list_element. Otherwise PCMK_XE_NODES is + * the value of the list's PCMK_XA_NAME attribute. + */ + out->begin_list(out, NULL, NULL, PCMK_XE_NODES); if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) { node_types = NULL; } if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) { - data.field = "id"; + data.field = PCMK_XA_ID; data.type = "cluster"; crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG, remote_node_print_helper, &data); } if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) { - data.field = "value"; + data.field = PCMK_XA_VALUE; data.type = "guest"; crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG, remote_node_print_helper, &data); @@ -862,7 +865,7 @@ pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export) if (pcmk__str_empty(node_types) || pcmk__str_eq(node_types, ",|^remote", pcmk__str_regex)) { - data.field = "id"; + data.field = PCMK_XA_ID; data.type = "remote"; crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG, remote_node_print_helper, &data); @@ -894,6 +897,6 @@ pcmk_list_nodes(xmlNodePtr *xml, const char *node_types) pcmk__register_lib_messages(out); rc = pcmk__list_nodes(out, node_types, FALSE); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c index 9f86e46..eea7816 100644 --- a/lib/pacemaker/pcmk_fence.c +++ b/lib/pacemaker/pcmk_fence.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. * @@ -13,7 +13,7 @@ #include <crm/common/output.h> #include <crm/common/output_internal.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__* #include <glib.h> #include <libxml/tree.h> @@ -156,7 +156,7 @@ async_fence_helper(gpointer user_data) return TRUE; } - st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, + st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE, notify_callback); call_id = st->cmds->fence_with_delay(st, @@ -320,7 +320,7 @@ pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, const char *target, rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast, cleanup); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -364,7 +364,7 @@ pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout) stonith__register_messages(out); rc = pcmk__fence_installed(out, st, timeout); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -402,7 +402,7 @@ pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid) stonith__register_messages(out); rc = pcmk__fence_last(out, target, as_nodeid); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -449,7 +449,7 @@ pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st, const char *device_id, stonith__register_messages(out); rc = pcmk__fence_list_targets(out, st, device_id, timeout); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -466,7 +466,7 @@ pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, const char *agent, return pcmk_legacy2rc(rc); } - out->output_xml(out, "metadata", buffer); + out->output_xml(out, PCMK_XE_METADATA, buffer); free(buffer); return rc; } @@ -487,7 +487,7 @@ pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, const char *agent, stonith__register_messages(out); rc = pcmk__fence_metadata(out, st, agent, timeout); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -536,7 +536,7 @@ pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, const char *target, stonith__register_messages(out); rc = pcmk__fence_registered(out, st, target, timeout); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif @@ -603,7 +603,7 @@ pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent, stonith__register_messages(out); rc = pcmk__fence_validate(out, st, agent, id, params, timeout); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } #endif diff --git a/lib/pacemaker/pcmk_graph_consumer.c b/lib/pacemaker/pcmk_graph_consumer.c index 0daa00d..6f1943a 100644 --- a/lib/pacemaker/pcmk_graph_consumer.c +++ b/lib/pacemaker/pcmk_graph_consumer.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. * @@ -13,7 +13,6 @@ #include <sys/stat.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/lrmd_internal.h> @@ -21,6 +20,67 @@ /* + * Functions for freeing transition graph objects + */ + +/*! + * \internal + * \brief Free a transition graph action object + * + * \param[in,out] user_data Action to free + */ +static void +free_graph_action(gpointer user_data) +{ + pcmk__graph_action_t *action = user_data; + + if (action->timer != 0) { + crm_warn("Cancelling timer for graph action %d", action->id); + g_source_remove(action->timer); + } + if (action->params != NULL) { + g_hash_table_destroy(action->params); + } + free_xml(action->xml); + free(action); +} + +/*! + * \internal + * \brief Free a transition graph synapse object + * + * \param[in,out] user_data Synapse to free + */ +static void +free_graph_synapse(gpointer user_data) +{ + pcmk__graph_synapse_t *synapse = user_data; + + g_list_free_full(synapse->actions, free_graph_action); + g_list_free_full(synapse->inputs, free_graph_action); + free(synapse); +} + +/*! + * \internal + * \brief Free a transition graph object + * + * \param[in,out] graph Transition graph to free + */ +void +pcmk__free_graph(pcmk__graph_t *graph) +{ + if (graph != NULL) { + g_list_free_full(graph->synapses, free_graph_synapse); + free(graph->source); + free(graph->failed_stop_offset); + free(graph->failed_start_offset); + free(graph); + } +} + + +/* * Functions for updating graph */ @@ -126,7 +186,7 @@ pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action) update_synapse_confirmed(synapse, action->id); } else if (!pcmk_is_set(action->flags, pcmk__graph_action_failed) - || (synapse->priority == INFINITY)) { + || (synapse->priority == PCMK_SCORE_INFINITY)) { update_synapse_ready(synapse, action->id); } } @@ -235,7 +295,7 @@ should_fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse) static int initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) { - const char *id = ID(action->xml); + const char *id = pcmk__xe_id(action->xml); CRM_CHECK(id != NULL, return EINVAL); CRM_CHECK(!pcmk_is_set(action->flags, pcmk__graph_action_executed), @@ -252,7 +312,7 @@ initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) return graph_fns->rsc(graph, action); case pcmk__cluster_graph_action: - if (pcmk__str_eq(crm_element_value(action->xml, XML_LRM_ATTR_TASK), + if (pcmk__str_eq(crm_element_value(action->xml, PCMK_XA_OPERATION), PCMK_ACTION_STONITH, pcmk__str_none)) { crm_trace("Executing fencing action %d (%s)", action->id, id); @@ -262,7 +322,7 @@ initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) return graph_fns->cluster(graph, action); default: - crm_err("Unsupported graph action type <%s " XML_ATTR_ID "='%s'> " + crm_err("Unsupported graph action type <%s " PCMK_XA_ID "='%s'> " "(bug?)", action->xml->name, id); return EINVAL; @@ -287,7 +347,7 @@ fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse) int rc = initiate_action(graph, action); if (rc != pcmk_rc_ok) { - crm_err("Failed initiating <%s " XML_ATTR_ID "=%d> in synapse %d: " + crm_err("Failed initiating <%s " PCMK_XA_ID "=%d> in synapse %d: " "%s", action->xml->name, action->id, synapse->id, pcmk_rc_str(rc)); @@ -331,7 +391,7 @@ pseudo_action_dummy(pcmk__graph_t *graph, pcmk__graph_action_t *action) if (action->id == fail) { crm_err("Dummy event handler: pretending action %d failed", action->id); pcmk__set_graph_action_flags(action, pcmk__graph_action_failed); - graph->abort_priority = INFINITY; + graph->abort_priority = PCMK_SCORE_INFINITY; } else { crm_trace("Dummy event handler: action %d initiated", action->id); } @@ -416,7 +476,7 @@ pcmk__execute_graph(pcmk__graph_t *graph) if (fire_synapse(graph, synapse) != pcmk_rc_ok) { crm_err("Synapse %d failed to fire", synapse->id); log_level = LOG_ERR; - graph->abort_priority = INFINITY; + graph->abort_priority = PCMK_SCORE_INFINITY; graph->incomplete++; graph->fired--; } @@ -482,21 +542,22 @@ unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action) { enum pcmk__graph_action_type action_type; pcmk__graph_action_t *action = NULL; - const char *value = ID(xml_action); + const char *value = pcmk__xe_id(xml_action); if (value == NULL) { - crm_err("Ignoring transition graph action without id (bug?)"); + crm_err("Ignoring transition graph action without " PCMK_XA_ID + " (bug?)"); crm_log_xml_trace(xml_action, "invalid"); return NULL; } - if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_RSC_OP)) { + if (pcmk__xe_is(xml_action, PCMK__XE_RSC_OP)) { action_type = pcmk__rsc_graph_action; - } else if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_PSEUDO_EVENT)) { + } else if (pcmk__xe_is(xml_action, PCMK__XE_PSEUDO_EVENT)) { action_type = pcmk__pseudo_graph_action; - } else if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_CRM_EVENT)) { + } else if (pcmk__xe_is(xml_action, PCMK__XE_CRM_EVENT)) { action_type = pcmk__cluster_graph_action; } else { @@ -515,16 +576,18 @@ unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action) pcmk__scan_min_int(value, &(action->id), -1); action->type = pcmk__rsc_graph_action; - action->xml = copy_xml(xml_action); + action->xml = pcmk__xml_copy(NULL, xml_action); action->synapse = parent; action->type = action_type; action->params = xml2list(action->xml); - value = g_hash_table_lookup(action->params, "CRM_meta_timeout"); + value = crm_meta_value(action->params, PCMK_META_TIMEOUT); pcmk__scan_min_int(value, &(action->timeout), 0); - /* Take start-delay into account for the timeout of the action timer */ - value = g_hash_table_lookup(action->params, "CRM_meta_start_delay"); + /* Take PCMK_META_START_DELAY into account for the timeout of the action + * timer + */ + value = crm_meta_value(action->params, PCMK_META_START_DELAY); { int start_delay; @@ -532,13 +595,12 @@ unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action) action->timeout += start_delay; } - if (pcmk__guint_from_hash(action->params, - CRM_META "_" XML_LRM_ATTR_INTERVAL, 0, - &(action->interval_ms)) != pcmk_rc_ok) { + if (pcmk__guint_from_hash(action->params, CRM_META "_" PCMK_META_INTERVAL, + 0, &(action->interval_ms)) != pcmk_rc_ok) { action->interval_ms = 0; } - value = g_hash_table_lookup(action->params, "CRM_meta_can_fail"); + value = crm_meta_value(action->params, PCMK__META_CAN_FAIL); if (value != NULL) { int can_fail = 0; @@ -548,12 +610,10 @@ unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action) pcmk__clear_graph_action_flags(action, pcmk__graph_action_can_fail); } -#ifndef PCMK__COMPAT_2_0 if (pcmk_is_set(action->flags, pcmk__graph_action_can_fail)) { - crm_warn("Support for the can_fail meta-attribute is deprecated" - " and will be removed in a future release"); + crm_warn("Support for the " PCMK__META_CAN_FAIL " meta-attribute " + "is deprecated and will be removed in a future release"); } -#endif } crm_trace("Action %d has timer set to %dms", action->id, action->timeout); @@ -577,31 +637,33 @@ unpack_synapse(pcmk__graph_t *new_graph, const xmlNode *xml_synapse) xmlNode *action_set = NULL; pcmk__graph_synapse_t *new_synapse = NULL; - crm_trace("Unpacking synapse %s", ID(xml_synapse)); + crm_trace("Unpacking synapse %s", pcmk__xe_id(xml_synapse)); new_synapse = calloc(1, sizeof(pcmk__graph_synapse_t)); if (new_synapse == NULL) { return NULL; } - pcmk__scan_min_int(ID(xml_synapse), &(new_synapse->id), 0); + pcmk__scan_min_int(pcmk__xe_id(xml_synapse), &(new_synapse->id), 0); - value = crm_element_value(xml_synapse, XML_CIB_ATTR_PRIORITY); + value = crm_element_value(xml_synapse, PCMK__XA_PRIORITY); pcmk__scan_min_int(value, &(new_synapse->priority), 0); - CRM_CHECK(new_synapse->id >= 0, free(new_synapse); - return NULL); + CRM_CHECK(new_synapse->id >= 0, + free_graph_synapse((gpointer) new_synapse); return NULL); new_graph->num_synapses++; crm_trace("Unpacking synapse %s action sets", - crm_element_value(xml_synapse, XML_ATTR_ID)); + crm_element_value(xml_synapse, PCMK_XA_ID)); - for (action_set = first_named_child(xml_synapse, "action_set"); - action_set != NULL; action_set = crm_next_same_xml(action_set)) { + for (action_set = pcmk__xe_first_child(xml_synapse, "action_set", NULL, + NULL); + action_set != NULL; action_set = pcmk__xe_next_same(action_set)) { - for (xmlNode *action = pcmk__xml_first_child(action_set); - action != NULL; action = pcmk__xml_next(action)) { + for (xmlNode *action = pcmk__xe_first_child(action_set, NULL, NULL, + NULL); + action != NULL; action = pcmk__xe_next(action)) { pcmk__graph_action_t *new_action = unpack_action(new_synapse, action); @@ -618,16 +680,19 @@ unpack_synapse(pcmk__graph_t *new_graph, const xmlNode *xml_synapse) } } - crm_trace("Unpacking synapse %s inputs", ID(xml_synapse)); + crm_trace("Unpacking synapse %s inputs", pcmk__xe_id(xml_synapse)); - for (xmlNode *inputs = first_named_child(xml_synapse, "inputs"); - inputs != NULL; inputs = crm_next_same_xml(inputs)) { + for (xmlNode *inputs = pcmk__xe_first_child(xml_synapse, "inputs", NULL, + NULL); + inputs != NULL; inputs = pcmk__xe_next_same(inputs)) { - for (xmlNode *trigger = first_named_child(inputs, "trigger"); - trigger != NULL; trigger = crm_next_same_xml(trigger)) { + for (xmlNode *trigger = pcmk__xe_first_child(inputs, "trigger", NULL, + NULL); + trigger != NULL; trigger = pcmk__xe_next_same(trigger)) { - for (xmlNode *input = pcmk__xml_first_child(trigger); - input != NULL; input = pcmk__xml_next(input)) { + for (xmlNode *input = pcmk__xe_first_child(trigger, NULL, NULL, + NULL); + input != NULL; input = pcmk__xe_next(input)) { pcmk__graph_action_t *new_input = unpack_action(new_synapse, input); @@ -683,9 +748,9 @@ pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference) return NULL; } - new_graph->source = strdup((reference == NULL)? "unknown" : reference); + new_graph->source = strdup(pcmk__s(reference, "unknown")); if (new_graph->source == NULL) { - free(new_graph); + pcmk__free_graph(new_graph); return NULL; } @@ -695,41 +760,41 @@ pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference) new_graph->stonith_timeout = 0; new_graph->completion_action = pcmk__graph_done; - // Parse top-level attributes from <transition_graph> + // Parse top-level attributes from PCMK__XE_TRANSITION_GRAPH if (xml_graph != NULL) { const char *buf = crm_element_value(xml_graph, "transition_id"); - CRM_CHECK(buf != NULL, free(new_graph); - return NULL); + CRM_CHECK(buf != NULL, + pcmk__free_graph(new_graph); return NULL); pcmk__scan_min_int(buf, &(new_graph->id), -1); - buf = crm_element_value(xml_graph, "cluster-delay"); - CRM_CHECK(buf != NULL, free(new_graph); - return NULL); - new_graph->network_delay = crm_parse_interval_spec(buf); + buf = crm_element_value(xml_graph, PCMK_OPT_CLUSTER_DELAY); + CRM_CHECK(buf != NULL, + pcmk__free_graph(new_graph); return NULL); + pcmk_parse_interval_spec(buf, &(new_graph->network_delay)); - buf = crm_element_value(xml_graph, "stonith-timeout"); + buf = crm_element_value(xml_graph, PCMK_OPT_STONITH_TIMEOUT); if (buf == NULL) { new_graph->stonith_timeout = new_graph->network_delay; } else { - new_graph->stonith_timeout = crm_parse_interval_spec(buf); + pcmk_parse_interval_spec(buf, &(new_graph->stonith_timeout)); } // Use 0 (dynamic limit) as default/invalid, -1 (no limit) as minimum - buf = crm_element_value(xml_graph, "batch-limit"); + buf = crm_element_value(xml_graph, PCMK_OPT_BATCH_LIMIT); if ((buf == NULL) || (pcmk__scan_min_int(buf, &(new_graph->batch_limit), -1) != pcmk_rc_ok)) { new_graph->batch_limit = 0; } - buf = crm_element_value(xml_graph, "migration-limit"); + buf = crm_element_value(xml_graph, PCMK_OPT_MIGRATION_LIMIT); pcmk__scan_min_int(buf, &(new_graph->migration_limit), -1); - pcmk__str_update(&(new_graph->failed_stop_offset), - crm_element_value(xml_graph, "failed-stop-offset")); - pcmk__str_update(&(new_graph->failed_start_offset), - crm_element_value(xml_graph, "failed-start-offset")); + new_graph->failed_stop_offset = + crm_element_value_copy(xml_graph, "failed-stop-offset"); + new_graph->failed_start_offset = + crm_element_value_copy(xml_graph, "failed-start-offset"); if (crm_element_value_epoch(xml_graph, "recheck-by", &(new_graph->recheck_by)) != pcmk_ok) { @@ -738,8 +803,10 @@ pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference) } // Unpack each child <synapse> element - for (const xmlNode *synapse_xml = first_named_child(xml_graph, "synapse"); - synapse_xml != NULL; synapse_xml = crm_next_same_xml(synapse_xml)) { + for (const xmlNode *synapse_xml = pcmk__xe_first_child(xml_graph, + "synapse", NULL, + NULL); + synapse_xml != NULL; synapse_xml = pcmk__xe_next_same(synapse_xml)) { pcmk__graph_synapse_t *new_synapse = unpack_synapse(new_graph, synapse_xml); @@ -759,67 +826,6 @@ pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference) /* - * Functions for freeing transition graph objects - */ - -/*! - * \internal - * \brief Free a transition graph action object - * - * \param[in,out] user_data Action to free - */ -static void -free_graph_action(gpointer user_data) -{ - pcmk__graph_action_t *action = user_data; - - if (action->timer != 0) { - crm_warn("Cancelling timer for graph action %d", action->id); - g_source_remove(action->timer); - } - if (action->params != NULL) { - g_hash_table_destroy(action->params); - } - free_xml(action->xml); - free(action); -} - -/*! - * \internal - * \brief Free a transition graph synapse object - * - * \param[in,out] user_data Synapse to free - */ -static void -free_graph_synapse(gpointer user_data) -{ - pcmk__graph_synapse_t *synapse = user_data; - - g_list_free_full(synapse->actions, free_graph_action); - g_list_free_full(synapse->inputs, free_graph_action); - free(synapse); -} - -/*! - * \internal - * \brief Free a transition graph object - * - * \param[in,out] graph Transition graph to free - */ -void -pcmk__free_graph(pcmk__graph_t *graph) -{ - if (graph != NULL) { - g_list_free_full(graph->synapses, free_graph_synapse); - free(graph->source); - free(graph->failed_stop_offset); - free(graph->failed_start_offset); - free(graph); - } -} - - -/* * Other transition graph utilities */ @@ -849,12 +855,13 @@ pcmk__event_from_graph_action(const xmlNode *resource, CRM_CHECK(action != NULL, return NULL); CRM_CHECK(action->type == pcmk__rsc_graph_action, return NULL); - action_resource = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); + action_resource = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL, + NULL); CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "invalid"); return NULL); - op = lrmd_new_event(ID(action_resource), - crm_element_value(action->xml, XML_LRM_ATTR_TASK), + op = lrmd_new_event(pcmk__xe_id(action_resource), + crm_element_value(action->xml, PCMK_XA_OPERATION), action->interval_ms); lrmd__set_result(op, rc, status, exit_reason); op->t_run = time(NULL); @@ -863,15 +870,16 @@ pcmk__event_from_graph_action(const xmlNode *resource, g_hash_table_iter_init(&iter, action->params); while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) { - g_hash_table_insert(op->params, strdup(name), strdup(value)); + pcmk__insert_dup(op->params, name, value); } - for (xmlNode *xop = pcmk__xml_first_child(resource); xop != NULL; - xop = pcmk__xml_next(xop)) { + for (xmlNode *xop = pcmk__xe_first_child(resource, NULL, NULL, NULL); + xop != NULL; xop = pcmk__xe_next(xop)) { + int tmp = 0; - crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); - crm_debug("Got call_id=%d for %s", tmp, ID(resource)); + crm_element_value_int(xop, PCMK__XA_CALL_ID, &tmp); + crm_debug("Got call_id=%d for %s", tmp, pcmk__xe_id(resource)); if (tmp > op->call_id) { op->call_id = tmp; } diff --git a/lib/pacemaker/pcmk_graph_logging.c b/lib/pacemaker/pcmk_graph_logging.c index f6fc179..a7d27d8 100644 --- a/lib/pacemaker/pcmk_graph_logging.c +++ b/lib/pacemaker/pcmk_graph_logging.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 <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <pacemaker-internal.h> @@ -126,14 +125,14 @@ synapse_pending_inputs(const pcmk__graph_t *graph, const pcmk__graph_action_t *input = (pcmk__graph_action_t *) lpc->data; if (pcmk_is_set(input->flags, pcmk__graph_action_failed)) { - pcmk__add_word(&pending, 1024, ID(input->xml)); + pcmk__add_word(&pending, 1024, pcmk__xe_id(input->xml)); } else if (pcmk_is_set(input->flags, pcmk__graph_action_confirmed)) { // Confirmed successful inputs are not pending } else if (find_graph_action_by_id(graph, input->id) != NULL) { // In-flight or pending - pcmk__add_word(&pending, 1024, ID(input->xml)); + pcmk__add_word(&pending, 1024, pcmk__xe_id(input->xml)); } } return pending; @@ -146,8 +145,8 @@ log_unresolved_inputs(unsigned int log_level, pcmk__graph_t *graph, { for (GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) { pcmk__graph_action_t *input = (pcmk__graph_action_t *) lpc->data; - const char *key = crm_element_value(input->xml, XML_LRM_ATTR_TASK_KEY); - const char *host = crm_element_value(input->xml, XML_LRM_ATTR_TARGET); + const char *key = crm_element_value(input->xml, PCMK__XA_OPERATION_KEY); + const char *host = crm_element_value(input->xml, PCMK__META_ON_NODE); if (find_graph_action_by_id(graph, input->id) == NULL) { do_crm_log(log_level, @@ -162,8 +161,8 @@ static void log_synapse_action(unsigned int log_level, pcmk__graph_synapse_t *synapse, pcmk__graph_action_t *action, const char *pending_inputs) { - const char *key = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); - const char *host = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); + const char *key = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY); + const char *host = crm_element_value(action->xml, PCMK__META_ON_NODE); char *desc = crm_strdup_printf("%s %s op %s", synapse_state_str(synapse), actiontype2text(action->type), key); @@ -220,8 +219,9 @@ pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph) return; } - do_crm_log(log_level, "Graph %d with %d actions:" - " batch-limit=%d jobs, network-delay=%ums", + do_crm_log(log_level, + "Graph %d with %d actions: " PCMK_OPT_BATCH_LIMIT "=%d jobs, " + "network-delay=%ums", graph->id, graph->num_actions, graph->batch_limit, graph->network_delay); diff --git a/lib/pacemaker/pcmk_graph_producer.c b/lib/pacemaker/pcmk_graph_producer.c index 59b6176..286a518 100644 --- a/lib/pacemaker/pcmk_graph_producer.c +++ b/lib/pacemaker/pcmk_graph_producer.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. * @@ -12,7 +12,6 @@ #include <sys/param.h> #include <crm/crm.h> #include <crm/cib.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <glib.h> @@ -47,8 +46,8 @@ add_node_to_xml_by_id(const char *id, xmlNode *xml) { xmlNode *node_xml; - node_xml = create_xml_node(xml, XML_CIB_TAG_NODE); - crm_xml_add(node_xml, XML_ATTR_ID, id); + node_xml = pcmk__xe_create(xml, PCMK_XE_NODE); + crm_xml_add(node_xml, PCMK_XA_ID, id); return node_xml; } @@ -83,19 +82,19 @@ add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler) int count = 0; if (xml != NULL) { - maintenance = create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE); + maintenance = pcmk__xe_create(xml, PCMK__XE_MAINTENANCE); } for (const GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) { const pcmk_node_t *node = iter->data; - if (pe__is_guest_or_remote_node(node) && + if (pcmk__is_pacemaker_remote_node(node) && (node->details->maintenance != node->details->remote_maintenance)) { if (maintenance != NULL) { crm_xml_add(add_node_to_xml_by_id(node->details->id, maintenance), - XML_NODE_IS_MAINTENANCE, + PCMK__XA_NODE_IN_MAINTENANCE, (node->details->maintenance? "1" : "0")); } count++; @@ -119,7 +118,7 @@ add_maintenance_update(pcmk_scheduler_t *scheduler) if (add_maintenance_nodes(NULL, scheduler) != 0) { action = get_pseudo_op(PCMK_ACTION_MAINTENANCE_NODES, scheduler); - pe__set_action_flags(action, pcmk_action_always_in_graph); + pcmk__set_action_flags(action, pcmk_action_always_in_graph); } } @@ -143,17 +142,18 @@ add_downed_nodes(xmlNode *xml, const pcmk_action_t *action) if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) { /* Shutdown makes the action's node down */ - xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); + xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED); add_node_to_xml_by_id(action->node->details->id, downed); } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) { /* Fencing makes the action's node and any hosted guest nodes down */ - const char *fence = g_hash_table_lookup(action->meta, "stonith_action"); + const char *fence = g_hash_table_lookup(action->meta, + PCMK__META_STONITH_ACTION); if (pcmk__is_fencing_action(fence)) { - xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); + xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED); add_node_to_xml_by_id(action->node->details->id, downed); pe_foreach_guest_node(action->node->details->data_set, action->node, add_node_to_xml, downed); @@ -181,7 +181,7 @@ add_downed_nodes(xmlNode *xml, const pcmk_action_t *action) } } if (!migrating) { - xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); + xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED); add_node_to_xml_by_id(action->rsc->id, downed); } } @@ -227,10 +227,10 @@ add_node_details(const pcmk_action_t *action, xmlNode *xml) { pcmk_node_t *router_node = pcmk__connection_host_for_action(action); - crm_xml_add(xml, XML_LRM_ATTR_TARGET, action->node->details->uname); - crm_xml_add(xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id); + crm_xml_add(xml, PCMK__META_ON_NODE, action->node->details->uname); + crm_xml_add(xml, PCMK__META_ON_NODE_UUID, action->node->details->id); if (router_node != NULL) { - crm_xml_add(xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname); + crm_xml_add(xml, PCMK__XA_ROUTER_NODE, router_node->details->uname); } } @@ -246,22 +246,23 @@ add_resource_details(const pcmk_action_t *action, xmlNode *action_xml) { xmlNode *rsc_xml = NULL; const char *attr_list[] = { - XML_AGENT_ATTR_CLASS, - XML_AGENT_ATTR_PROVIDER, - XML_ATTR_TYPE + PCMK_XA_CLASS, + PCMK_XA_PROVIDER, + PCMK_XA_TYPE, }; - /* If a resource is locked to a node via shutdown-lock, mark its actions - * so the controller can preserve the lock when the action completes. + /* If a resource is locked to a node via PCMK_OPT_SHUTDOWN_LOCK, mark its + * actions so the controller can preserve the lock when the action + * completes. */ if (pcmk__action_locks_rsc_to_node(action)) { - crm_xml_add_ll(action_xml, XML_CONFIG_ATTR_SHUTDOWN_LOCK, + crm_xml_add_ll(action_xml, PCMK_OPT_SHUTDOWN_LOCK, (long long) action->rsc->lock_time); } // List affected resource - rsc_xml = create_xml_node(action_xml, + rsc_xml = pcmk__xe_create(action_xml, (const char *) action->rsc->xml->name); if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed) && (action->rsc->clone_name != NULL)) { @@ -275,19 +276,19 @@ add_resource_details(const pcmk_action_t *action, xmlNode *action_xml) */ crm_debug("Using orphan clone name %s instead of %s", action->rsc->id, action->rsc->clone_name); - crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name); - crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); + crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->clone_name); + crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id); } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) { - const char *xml_id = ID(action->rsc->xml); + const char *xml_id = pcmk__xe_id(action->rsc->xml); crm_debug("Using anonymous clone name %s for %s (aka %s)", xml_id, action->rsc->id, action->rsc->clone_name); /* ID is what we'd like client to use - * ID_LONG is what they might know it as instead + * LONG_ID is what they might know it as instead * - * ID_LONG is only strictly needed /here/ during the + * LONG_ID is only strictly needed /here/ during the * transition period until all nodes in the cluster * are running the new software /and/ have rebooted * once (meaning that they've only ever spoken to a DC @@ -297,18 +298,18 @@ add_resource_details(const pcmk_action_t *action, xmlNode *action_xml) * 'instance free' name will correspond to an orphan * and fall into the clause above instead */ - crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id); + crm_xml_add(rsc_xml, PCMK_XA_ID, xml_id); if ((action->rsc->clone_name != NULL) && !pcmk__str_eq(xml_id, action->rsc->clone_name, pcmk__str_none)) { - crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name); + crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->clone_name); } else { - crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); + crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id); } } else { CRM_ASSERT(action->rsc->clone_name == NULL); - crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id); + crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->id); } for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) { @@ -333,9 +334,9 @@ add_action_attributes(pcmk_action_t *action, xmlNode *action_xml) * before adding it to action_xml, which keeps the scheduler regression * test graphs comparable. */ - args_xml = create_xml_node(NULL, XML_TAG_ATTRS); + args_xml = pcmk__xe_create(NULL, PCMK__XE_ATTRIBUTES); - crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); + crm_xml_add(args_xml, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); g_hash_table_foreach(action->extra, hash2field, args_xml); if ((action->rsc != NULL) && (action->node != NULL)) { @@ -364,7 +365,7 @@ add_action_attributes(pcmk_action_t *action, xmlNode *action_xml) parent = parent->parent; } - pcmk__add_bundle_meta_to_xml(args_xml, action); + pcmk__add_guest_meta_to_xml(args_xml, action); } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none) && (action->node != NULL)) { @@ -408,60 +409,60 @@ create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details, if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) { /* All fences need node info; guest node fences are pseudo-events */ if (pcmk_is_set(action->flags, pcmk_action_pseudo)) { - action_xml = create_xml_node(parent, XML_GRAPH_TAG_PSEUDO_EVENT); + action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT); } else { - action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT); + action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT); } } else if (pcmk__str_any_of(action->task, PCMK_ACTION_DO_SHUTDOWN, PCMK_ACTION_CLEAR_FAILCOUNT, NULL)) { - action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT); + action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT); } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE, pcmk__str_none)) { // CIB-only clean-up for shutdown locks - action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT); - crm_xml_add(action_xml, PCMK__XA_MODE, XML_TAG_CIB); + action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT); + crm_xml_add(action_xml, PCMK__XA_MODE, PCMK__VALUE_CIB); } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) { if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES, pcmk__str_none)) { needs_maintenance_info = true; } - action_xml = create_xml_node(parent, XML_GRAPH_TAG_PSEUDO_EVENT); + action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT); needs_node_info = false; } else { - action_xml = create_xml_node(parent, XML_GRAPH_TAG_RSC_OP); + action_xml = pcmk__xe_create(parent, PCMK__XE_RSC_OP); } - crm_xml_add_int(action_xml, XML_ATTR_ID, action->id); - crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task); + crm_xml_add_int(action_xml, PCMK_XA_ID, action->id); + crm_xml_add(action_xml, PCMK_XA_OPERATION, action->task); if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) { char *clone_key = NULL; guint interval_ms; - if (pcmk__guint_from_hash(action->meta, XML_LRM_ATTR_INTERVAL_MS, 0, + if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0, &interval_ms) != pcmk_rc_ok) { interval_ms = 0; } clone_key = clone_op_key(action, interval_ms); - crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key); - crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, + crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, clone_key); + crm_xml_add(action_xml, "internal_" PCMK__XA_OPERATION_KEY, action->uuid); free(clone_key); } else { - crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid); + crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, action->uuid); } if (needs_node_info && (action->node != NULL)) { add_node_details(action, action_xml); - g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET), - strdup(action->node->details->uname)); - g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID), - strdup(action->node->details->id)); + pcmk__insert_dup(action->meta, PCMK__META_ON_NODE, + action->node->details->uname); + pcmk__insert_dup(action->meta, PCMK__META_ON_NODE_UUID, + action->node->details->id); } if (skip_details) { @@ -526,8 +527,7 @@ should_add_action_to_graph(const pcmk_action_t *action) * recognize it. The interval has been normalized to milliseconds by * this point, so a string comparison is sufficient. */ - interval_ms_s = g_hash_table_lookup(action->meta, - XML_LRM_ATTR_INTERVAL_MS); + interval_ms_s = g_hash_table_lookup(action->meta, PCMK_META_INTERVAL); if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) { crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)", action->uuid, action->id, action->rsc->id); @@ -545,9 +545,9 @@ should_add_action_to_graph(const pcmk_action_t *action) } if (action->node == NULL) { - pe_err("Skipping action %s (%d) " - "because it was not assigned to a node (bug?)", - action->uuid, action->id); + pcmk__sched_err("Skipping action %s (%d) " + "because it was not assigned to a node (bug?)", + action->uuid, action->id); pcmk__log_action("Unassigned", action, false); return false; } @@ -555,25 +555,25 @@ should_add_action_to_graph(const pcmk_action_t *action) if (pcmk_is_set(action->flags, pcmk_action_on_dc)) { crm_trace("Action %s (%d) should be dumped: " "can run on DC instead of %s", - action->uuid, action->id, pe__node_name(action->node)); + action->uuid, action->id, pcmk__node_name(action->node)); - } else if (pe__is_guest_node(action->node) + } else if (pcmk__is_guest_or_bundle_node(action->node) && !action->node->details->remote_requires_reset) { crm_trace("Action %s (%d) should be dumped: " "assuming will be runnable on guest %s", - action->uuid, action->id, pe__node_name(action->node)); + action->uuid, action->id, pcmk__node_name(action->node)); } else if (!action->node->details->online) { - pe_err("Skipping action %s (%d) " - "because it was scheduled for offline node (bug?)", - action->uuid, action->id); + pcmk__sched_err("Skipping action %s (%d) " + "because it was scheduled for offline node (bug?)", + action->uuid, action->id); pcmk__log_action("Offline node", action, false); return false; } else if (action->node->details->unclean) { - pe_err("Skipping action %s (%d) " - "because it was scheduled for unclean node (bug?)", - action->uuid, action->id); + pcmk__sched_err("Skipping action %s (%d) " + "because it was scheduled for unclean node (bug?)", + action->uuid, action->id); pcmk__log_action("Unclean node", action, false); return false; } @@ -677,7 +677,7 @@ should_add_input_to_graph(const pcmk_action_t *action, * the resource has been assigned, not where migrate_to will be * executed. */ - if (!pe__same_node(input_node, assigned)) { + if (!pcmk__same_node(input_node, assigned)) { crm_trace("Ignoring %s (%d) input %s (%d): " "migration target %s is not same as input node %s", action->uuid, action->id, @@ -688,7 +688,7 @@ should_add_input_to_graph(const pcmk_action_t *action, return false; } - } else if (!pe__same_node(input_node, action->node)) { + } else if (!pcmk__same_node(input_node, action->node)) { crm_trace("Ignoring %s (%d) input %s (%d): " "not on same node (%s vs %s)", action->uuid, action->id, @@ -709,13 +709,13 @@ should_add_input_to_graph(const pcmk_action_t *action, } else if ((uint32_t) input->type == pcmk__ar_if_required_on_same_node) { if (input->action->node && action->node - && !pe__same_node(input->action->node, action->node)) { + && !pcmk__same_node(input->action->node, action->node)) { crm_trace("Ignoring %s (%d) input %s (%d): " "not on same node (%s vs %s)", action->uuid, action->id, input->action->uuid, input->action->id, - pe__node_name(action->node), - pe__node_name(input->action->node)); + pcmk__node_name(action->node), + pcmk__node_name(input->action->node)); input->type = (enum pe_ordering) pcmk__ar_none; return false; @@ -732,7 +732,7 @@ should_add_input_to_graph(const pcmk_action_t *action, && pcmk_is_set(input->action->rsc->flags, pcmk_rsc_failed) && !pcmk_is_set(input->action->rsc->flags, pcmk_rsc_managed) && pcmk__ends_with(input->action->uuid, "_stop_0") - && action->rsc && pe_rsc_is_clone(action->rsc)) { + && pcmk__is_clone(action->rsc)) { crm_warn("Ignoring requirement that %s complete before %s:" " unmanaged failed resources cannot prevent clone shutdown", input->action->uuid, action->uuid); @@ -801,7 +801,7 @@ pcmk__graph_has_loop(const pcmk_action_t *init_action, return true; } - pe__set_action_flags(input->action, pcmk_action_detect_loop); + pcmk__set_action_flags(input->action, pcmk_action_detect_loop); crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)" "for graph loop with %s@%s ", @@ -825,7 +825,7 @@ pcmk__graph_has_loop(const pcmk_action_t *init_action, } } - pe__clear_action_flags(input->action, pcmk_action_detect_loop); + pcmk__clear_action_flags(input->action, pcmk_action_detect_loop); if (!has_loop) { crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)", @@ -851,9 +851,9 @@ static xmlNode * create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler) { int synapse_priority = 0; - xmlNode *syn = create_xml_node(scheduler->graph, "synapse"); + xmlNode *syn = pcmk__xe_create(scheduler->graph, "synapse"); - crm_xml_add_int(syn, XML_ATTR_ID, scheduler->num_synapse); + crm_xml_add_int(syn, PCMK_XA_ID, scheduler->num_synapse); scheduler->num_synapse++; if (action->rsc != NULL) { @@ -863,7 +863,7 @@ create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler) synapse_priority = action->priority; } if (synapse_priority > 0) { - crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority); + crm_xml_add_int(syn, PCMK__XA_PRIORITY, synapse_priority); } return syn; } @@ -900,14 +900,14 @@ add_action_to_graph(gpointer data, gpointer user_data) */ if (!pcmk_is_set(action->flags, pcmk_action_inputs_deduplicated)) { pcmk__deduplicate_action_inputs(action); - pe__set_action_flags(action, pcmk_action_inputs_deduplicated); + pcmk__set_action_flags(action, pcmk_action_inputs_deduplicated); } if (pcmk_is_set(action->flags, pcmk_action_added_to_graph) || !should_add_action_to_graph(action)) { return; // Already added, or shouldn't be } - pe__set_action_flags(action, pcmk_action_added_to_graph); + pcmk__set_action_flags(action, pcmk_action_added_to_graph); crm_trace("Adding action %d (%s%s%s) to graph", action->id, action->uuid, @@ -915,8 +915,8 @@ add_action_to_graph(gpointer data, gpointer user_data) ((action->node == NULL)? "" : action->node->details->uname)); syn = create_graph_synapse(action, scheduler); - set = create_xml_node(syn, "action_set"); - in = create_xml_node(syn, "inputs"); + set = pcmk__xe_create(syn, "action_set"); + in = pcmk__xe_create(syn, "inputs"); create_graph_action(set, action, false, scheduler); @@ -924,7 +924,7 @@ add_action_to_graph(gpointer data, gpointer user_data) pcmk__related_action_t *input = lpc->data; if (should_add_input_to_graph(action, input)) { - xmlNode *input_xml = create_xml_node(in, "trigger"); + xmlNode *input_xml = pcmk__xe_create(in, "trigger"); input->state = pe_link_dumped; create_graph_action(input_xml, input->action, true, scheduler); @@ -943,13 +943,13 @@ static int transition_id = -1; void pcmk__log_transition_summary(const char *filename) { - if (was_processing_error) { + if (was_processing_error || crm_config_error) { crm_err("Calculated transition %d (with errors)%s%s", transition_id, (filename == NULL)? "" : ", saving inputs in ", (filename == NULL)? "" : filename); - } else if (was_processing_warning) { + } else if (was_processing_warning || crm_config_warning) { crm_warn("Calculated transition %d (with warnings)%s%s", transition_id, (filename == NULL)? "" : ", saving inputs in ", @@ -979,7 +979,7 @@ pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc) GList *iter = NULL; CRM_ASSERT(rsc != NULL); - pe_rsc_trace(rsc, "Adding actions for %s to graph", rsc->id); + pcmk__rsc_trace(rsc, "Adding actions for %s to graph", rsc->id); // First add the resource's own actions g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster); @@ -1004,17 +1004,18 @@ pcmk__create_graph(pcmk_scheduler_t *scheduler) GList *iter = NULL; const char *value = NULL; long long limit = 0LL; + GHashTable *config_hash = scheduler->config_hash; transition_id++; crm_trace("Creating transition graph %d", transition_id); - scheduler->graph = create_xml_node(NULL, XML_TAG_GRAPH); + scheduler->graph = pcmk__xe_create(NULL, PCMK__XE_TRANSITION_GRAPH); - value = pe_pref(scheduler->config_hash, "cluster-delay"); - crm_xml_add(scheduler->graph, "cluster-delay", value); + value = pcmk__cluster_option(config_hash, PCMK_OPT_CLUSTER_DELAY); + crm_xml_add(scheduler->graph, PCMK_OPT_CLUSTER_DELAY, value); - value = pe_pref(scheduler->config_hash, "stonith-timeout"); - crm_xml_add(scheduler->graph, "stonith-timeout", value); + value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT); + crm_xml_add(scheduler->graph, PCMK_OPT_STONITH_TIMEOUT, value); crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY"); @@ -1024,14 +1025,14 @@ pcmk__create_graph(pcmk_scheduler_t *scheduler) crm_xml_add(scheduler->graph, "failed-start-offset", "1"); } - value = pe_pref(scheduler->config_hash, "batch-limit"); - crm_xml_add(scheduler->graph, "batch-limit", value); + value = pcmk__cluster_option(config_hash, PCMK_OPT_BATCH_LIMIT); + crm_xml_add(scheduler->graph, PCMK_OPT_BATCH_LIMIT, value); crm_xml_add_int(scheduler->graph, "transition_id", transition_id); - value = pe_pref(scheduler->config_hash, "migration-limit"); + value = pcmk__cluster_option(config_hash, PCMK_OPT_MIGRATION_LIMIT); if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) { - crm_xml_add(scheduler->graph, "migration-limit", value); + crm_xml_add(scheduler->graph, PCMK_OPT_MIGRATION_LIMIT, value); } if (scheduler->recheck_by > 0) { @@ -1052,7 +1053,7 @@ pcmk__create_graph(pcmk_scheduler_t *scheduler) for (iter = scheduler->resources; iter != NULL; iter = iter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; - pe_rsc_trace(rsc, "Processing actions for %s", rsc->id); + pcmk__rsc_trace(rsc, "Processing actions for %s", rsc->id); rsc->cmds->add_actions_to_graph(rsc); } @@ -1083,7 +1084,7 @@ pcmk__create_graph(pcmk_scheduler_t *scheduler) crm_crit("Cannot %s %s because of %s:%s%s (%s)", action->node->details->unclean? "fence" : "shut down", - pe__node_name(action->node), action->rsc->id, + pcmk__node_name(action->node), action->rsc->id, (managed? " blocked" : " unmanaged"), (failed? " failed" : ""), action->uuid); } diff --git a/lib/pacemaker/pcmk_injections.c b/lib/pacemaker/pcmk_injections.c index f6b36e8..60ddef9 100644 --- a/lib/pacemaker/pcmk_injections.c +++ b/lib/pacemaker/pcmk_injections.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. * @@ -33,11 +33,11 @@ bool pcmk__simulate_node_config = false; -#define XPATH_NODE_CONFIG "//" XML_CIB_TAG_NODE "[@" XML_ATTR_UNAME "='%s']" -#define XPATH_NODE_STATE "//" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" -#define XPATH_NODE_STATE_BY_ID "//" XML_CIB_TAG_STATE "[@" XML_ATTR_ID "='%s']" +#define XPATH_NODE_CONFIG "//" PCMK_XE_NODE "[@" PCMK_XA_UNAME "='%s']" +#define XPATH_NODE_STATE "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']" +#define XPATH_NODE_STATE_BY_ID "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_ID "='%s']" #define XPATH_RSC_HISTORY XPATH_NODE_STATE \ - "//" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']" + "//" PCMK__XE_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']" /*! @@ -45,7 +45,7 @@ bool pcmk__simulate_node_config = false; * \brief Inject a fictitious transient node attribute into scheduler input * * \param[in,out] out Output object for displaying error messages - * \param[in,out] cib_node node_state XML to inject attribute into + * \param[in,out] cib_node \c PCMK__XE_NODE_STATE XML to inject attribute into * \param[in] name Transient node attribute name to inject * \param[in] value Transient node attribute value to inject */ @@ -55,20 +55,22 @@ inject_transient_attr(pcmk__output_t *out, xmlNode *cib_node, { xmlNode *attrs = NULL; xmlNode *instance_attrs = NULL; - const char *node_uuid = ID(cib_node); + const char *node_uuid = pcmk__xe_id(cib_node); out->message(out, "inject-attr", name, value, cib_node); - attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS); + attrs = pcmk__xe_first_child(cib_node, PCMK__XE_TRANSIENT_ATTRIBUTES, NULL, + NULL); if (attrs == NULL) { - attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS); - crm_xml_add(attrs, XML_ATTR_ID, node_uuid); + attrs = pcmk__xe_create(cib_node, PCMK__XE_TRANSIENT_ATTRIBUTES); + crm_xml_add(attrs, PCMK_XA_ID, node_uuid); } - instance_attrs = first_named_child(attrs, XML_TAG_ATTR_SETS); + instance_attrs = pcmk__xe_first_child(attrs, PCMK_XE_INSTANCE_ATTRIBUTES, + NULL, NULL); if (instance_attrs == NULL) { - instance_attrs = create_xml_node(attrs, XML_TAG_ATTR_SETS); - crm_xml_add(instance_attrs, XML_ATTR_ID, node_uuid); + instance_attrs = pcmk__xe_create(attrs, PCMK_XE_INSTANCE_ATTRIBUTES); + crm_xml_add(instance_attrs, PCMK_XA_ID, node_uuid); } crm_create_nvpair_xml(instance_attrs, NULL, name, value); @@ -79,38 +81,58 @@ inject_transient_attr(pcmk__output_t *out, xmlNode *cib_node, * \brief Inject a fictitious fail count into a scheduler input * * \param[in,out] out Output object for displaying error messages + * \param[in,out] cib_conn CIB connection * \param[in,out] cib_node Node state XML to inject into * \param[in] resource ID of resource for fail count to inject * \param[in] task Action name for fail count to inject * \param[in] interval_ms Action interval (in milliseconds) for fail count - * \param[in] rc Action result for fail count to inject (if 0, or - * 7 when interval_ms is 0, inject nothing) + * \param[in] exit_status Action result for fail count to inject (if + * \c PCMK_OCF_OK, or \c PCMK_OCF_NOT_RUNNING when + * \p interval_ms is 0, inject nothing) */ void -pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node, +pcmk__inject_failcount(pcmk__output_t *out, cib_t *cib_conn, xmlNode *cib_node, const char *resource, const char *task, - guint interval_ms, int rc) + guint interval_ms, int exit_status) { - if (rc == 0) { - return; + char *name = NULL; + char *value = NULL; - } else if ((rc == 7) && (interval_ms == 0)) { - return; + int failcount = 0; + xmlNode *output = NULL; - } else { - char *name = NULL; - char *now = pcmk__ttoa(time(NULL)); + CRM_CHECK((out != NULL) && (cib_conn != NULL) && (cib_node != NULL) + && (resource != NULL) && (task != NULL), return); + + if ((exit_status == PCMK_OCF_OK) + || ((exit_status == PCMK_OCF_NOT_RUNNING) && (interval_ms == 0))) { + return; + } - name = pcmk__failcount_name(resource, task, interval_ms); - inject_transient_attr(out, cib_node, name, "value++"); - free(name); + // Get current failcount and increment it + name = pcmk__failcount_name(resource, task, interval_ms); - name = pcmk__lastfailure_name(resource, task, interval_ms); - inject_transient_attr(out, cib_node, name, now); - free(name); + if (cib__get_node_attrs(out, cib_conn, PCMK_XE_STATUS, + pcmk__xe_id(cib_node), NULL, NULL, NULL, name, + NULL, &output) == pcmk_rc_ok) { - free(now); + if (crm_element_value_int(output, name, &failcount) != 0) { + failcount = 0; + } } + value = pcmk__itoa(failcount + 1); + inject_transient_attr(out, cib_node, name, value); + + free(name); + free(value); + free_xml(output); + + name = pcmk__lastfailure_name(resource, task, interval_ms); + value = pcmk__ttoa(time(NULL)); + inject_transient_attr(out, cib_node, name, value); + + free(name); + free(value); } /*! @@ -130,11 +152,11 @@ create_node_entry(cib_t *cib_conn, const char *node) cib_xpath|cib_sync_call|cib_scope_local); if (rc == -ENXIO) { // Only add if not already existing - xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE); + xmlNode *cib_object = pcmk__xe_create(NULL, PCMK_XE_NODE); - crm_xml_add(cib_object, XML_ATTR_ID, node); // Use node name as ID - crm_xml_add(cib_object, XML_ATTR_UNAME, node); - cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object, + crm_xml_add(cib_object, PCMK_XA_ID, node); // Use node name as ID + crm_xml_add(cib_object, PCMK_XA_UNAME, node); + cib_conn->cmds->create(cib_conn, PCMK_XE_NODES, cib_object, cib_sync_call|cib_scope_local); /* Not bothering with subsequent query to see if it exists, we'll bomb out later in the call to query_node_uuid()... */ @@ -165,7 +187,7 @@ create_op(const xmlNode *cib_resource, const char *task, guint interval_ms, lrmd_event_data_t *op = NULL; xmlNode *xop = NULL; - op = lrmd_new_event(ID(cib_resource), task, interval_ms); + op = lrmd_new_event(pcmk__xe_id(cib_resource), task, interval_ms); lrmd__set_result(op, outcome, PCMK_EXEC_DONE, "Simulated action result"); op->params = NULL; // Not needed for simulation purposes op->t_run = (unsigned int) time(NULL); @@ -173,12 +195,12 @@ create_op(const xmlNode *cib_resource, const char *task, guint interval_ms, // Use a call ID higher than any existing history entries op->call_id = 0; - for (xop = pcmk__xe_first_child(cib_resource); xop != NULL; - xop = pcmk__xe_next(xop)) { + for (xop = pcmk__xe_first_child(cib_resource, NULL, NULL, NULL); + xop != NULL; xop = pcmk__xe_next(xop)) { int tmp = 0; - crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); + crm_element_value_int(xop, PCMK__XA_CALL_ID, &tmp); if (tmp > op->call_id) { op->call_id = tmp; } @@ -214,7 +236,7 @@ pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, * \param[in] node Name of node to inject * \param[in] uuid UUID of node to inject * - * \return XML of node_state entry for new node + * \return XML of \c PCMK__XE_NODE_STATE entry for new node * \note If the global pcmk__simulate_node_config has been set to true, a * node entry in the configuration section will be added, as well as a * node state entry in the status section. @@ -235,8 +257,9 @@ pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid) rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); - if ((cib_object != NULL) && (ID(cib_object) == NULL)) { - crm_err("Detected multiple node_state entries for xpath=%s, bailing", + if ((cib_object != NULL) && (pcmk__xe_id(cib_object) == NULL)) { + crm_err("Detected multiple " PCMK__XE_NODE_STATE " entries for " + "xpath=%s, bailing", xpath); duplicate = true; goto done; @@ -253,11 +276,13 @@ pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid) char *xpath_by_uuid = crm_strdup_printf(XPATH_NODE_STATE_BY_ID, found_uuid); - // It's possible that a node_state entry doesn't have an uname yet. + /* It's possible that a PCMK__XE_NODE_STATE entry doesn't have a + * PCMK_XA_UNAME yet + */ rc = cib_conn->cmds->query(cib_conn, xpath_by_uuid, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); - if ((cib_object != NULL) && (ID(cib_object) == NULL)) { + if ((cib_object != NULL) && (pcmk__xe_id(cib_object) == NULL)) { crm_err("Can't inject node state for %s because multiple " "state entries found for ID %s", node, found_uuid); duplicate = true; @@ -265,9 +290,9 @@ pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid) goto done; } else if (cib_object != NULL) { - crm_xml_add(cib_object, XML_ATTR_UNAME, node); + crm_xml_add(cib_object, PCMK_XA_UNAME, node); - rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_STATUS, + rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_STATUS, cib_object, cib_sync_call|cib_scope_local); } @@ -277,10 +302,10 @@ pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid) } if (rc == -ENXIO) { - cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); - crm_xml_add(cib_object, XML_ATTR_ID, found_uuid); - crm_xml_add(cib_object, XML_ATTR_UNAME, node); - cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, + cib_object = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE); + crm_xml_add(cib_object, PCMK_XA_ID, found_uuid); + crm_xml_add(cib_object, PCMK_XA_UNAME, node); + cib_conn->cmds->create(cib_conn, PCMK_XE_STATUS, cib_object, cib_sync_call|cib_scope_local); free_xml(cib_object); @@ -320,20 +345,20 @@ pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up) if (up) { pcmk__xe_set_props(cib_node, - PCMK__XA_IN_CCM, XML_BOOLEAN_YES, - PCMK__XA_CRMD, ONLINESTATUS, + PCMK__XA_IN_CCM, PCMK_VALUE_TRUE, + PCMK_XA_CRMD, PCMK_VALUE_ONLINE, PCMK__XA_JOIN, CRMD_JOINSTATE_MEMBER, - PCMK__XA_EXPECTED, CRMD_JOINSTATE_MEMBER, + PCMK_XA_EXPECTED, CRMD_JOINSTATE_MEMBER, NULL); } else { pcmk__xe_set_props(cib_node, - PCMK__XA_IN_CCM, XML_BOOLEAN_NO, - PCMK__XA_CRMD, OFFLINESTATUS, + PCMK__XA_IN_CCM, PCMK_VALUE_FALSE, + PCMK_XA_CRMD, PCMK_VALUE_OFFLINE, PCMK__XA_JOIN, CRMD_JOINSTATE_DOWN, - PCMK__XA_EXPECTED, CRMD_JOINSTATE_DOWN, + PCMK_XA_EXPECTED, CRMD_JOINSTATE_DOWN, NULL); } - crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); + crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, crm_system_name); return cib_node; } @@ -344,13 +369,13 @@ pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up) * \param[in,out] cib_node Node state XML to check * \param[in] resource Resource name to check for * - * \return Resource's lrm_resource XML entry beneath \p cib_node if found, - * otherwise NULL + * \return Resource's \c PCMK__XE_LRM_RESOURCE XML entry beneath \p cib_node if + * found, otherwise \c NULL */ static xmlNode * find_resource_xml(xmlNode *cib_node, const char *resource) { - const char *node = crm_element_value(cib_node, XML_ATTR_UNAME); + const char *node = crm_element_value(cib_node, PCMK_XA_UNAME); char *xpath = crm_strdup_printf(XPATH_RSC_HISTORY, node, resource); xmlNode *match = get_xpath_object(xpath, cib_node, LOG_TRACE); @@ -405,7 +430,7 @@ pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, out->err(out, "Resource %s not found in the status section of %s " "(supply class and type to continue)", - resource, ID(cib_node)); + resource, pcmk__xe_id(cib_node)); return NULL; } else if (!pcmk__strcase_any_of(rclass, @@ -426,71 +451,33 @@ pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, } crm_info("Injecting new resource %s into node state '%s'", - lrm_name, ID(cib_node)); + lrm_name, pcmk__xe_id(cib_node)); - lrm = first_named_child(cib_node, XML_CIB_TAG_LRM); + lrm = pcmk__xe_first_child(cib_node, PCMK__XE_LRM, NULL, NULL); if (lrm == NULL) { - const char *node_uuid = ID(cib_node); + const char *node_uuid = pcmk__xe_id(cib_node); - lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM); - crm_xml_add(lrm, XML_ATTR_ID, node_uuid); + lrm = pcmk__xe_create(cib_node, PCMK__XE_LRM); + crm_xml_add(lrm, PCMK_XA_ID, node_uuid); } - container = first_named_child(lrm, XML_LRM_TAG_RESOURCES); + container = pcmk__xe_first_child(lrm, PCMK__XE_LRM_RESOURCES, NULL, NULL); if (container == NULL) { - container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES); + container = pcmk__xe_create(lrm, PCMK__XE_LRM_RESOURCES); } - cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE); + cib_resource = pcmk__xe_create(container, PCMK__XE_LRM_RESOURCE); // If we're creating a new entry, use the preferred name - crm_xml_add(cib_resource, XML_ATTR_ID, lrm_name); + crm_xml_add(cib_resource, PCMK_XA_ID, lrm_name); - crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass); - crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider); - crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype); + crm_xml_add(cib_resource, PCMK_XA_CLASS, rclass); + crm_xml_add(cib_resource, PCMK_XA_PROVIDER, rprovider); + crm_xml_add(cib_resource, PCMK_XA_TYPE, rtype); return cib_resource; } -static int -find_ticket_state(pcmk__output_t *out, cib_t *the_cib, const char *ticket_id, - xmlNode **ticket_state_xml) -{ - int rc = pcmk_ok; - xmlNode *xml_search = NULL; - - GString *xpath = g_string_sized_new(256); - - CRM_ASSERT(ticket_state_xml != NULL); - *ticket_state_xml = NULL; - - g_string_append(xpath, - "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS - "/" XML_CIB_TAG_TICKETS); - - if (ticket_id) { - 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); - g_string_free(xpath, TRUE); - - if (rc != pcmk_ok) { - return rc; - } - - crm_log_xml_debug(xml_search, "Match"); - if ((xml_search->children != NULL) && (ticket_id != NULL)) { - out->err(out, "Multiple ticket_states match ticket_id=%s", ticket_id); - } - *ticket_state_xml = xml_search; - - return rc; -} - /*! * \internal * \brief Inject a ticket attribute into ticket state @@ -512,8 +499,13 @@ set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id, xmlNode *ticket_state_xml = NULL; // Check for an existing ticket state entry - rc = find_ticket_state(out, cib, ticket_id, &ticket_state_xml); - rc = pcmk_legacy2rc(rc); + rc = pcmk__get_ticket_state(cib, ticket_id, &ticket_state_xml); + + if (rc == pcmk_rc_duplicate_id) { + out->err(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket_id=%s", + ticket_id); + rc = pcmk_rc_ok; + } if (rc == pcmk_rc_ok) { // Ticket state found, use it crm_debug("Injecting attribute into existing ticket state %s", @@ -523,10 +515,10 @@ set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id, } else if (rc == ENXIO) { // No ticket state, create it 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); + xml_top = pcmk__xe_create(NULL, PCMK_XE_STATUS); + xml_obj = pcmk__xe_create(xml_top, PCMK_XE_TICKETS); + ticket_state_xml = pcmk__xe_create(xml_obj, PCMK__XE_TICKET_STATE); + crm_xml_add(ticket_state_xml, PCMK_XA_ID, ticket_id); } else { // Error return rc; @@ -537,7 +529,7 @@ set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id, crm_log_xml_debug(xml_top, "Update"); // Commit the change to the CIB - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call|cib_scope_local); rc = pcmk_legacy2rc(rc); @@ -579,8 +571,8 @@ inject_action(pcmk__output_t *out, const char *spec, cib_t *cib, out->message(out, "inject-spec", spec); - key = calloc(1, strlen(spec) + 1); - node = calloc(1, strlen(spec) + 1); + key = pcmk__assert_alloc(1, strlen(spec) + 1); + node = pcmk__assert_alloc(1, strlen(spec) + 1); rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome); if (rc != 3) { out->err(out, "Invalid operation spec: %s. Only found %d fields", @@ -596,14 +588,15 @@ inject_action(pcmk__output_t *out, const char *spec, cib_t *cib, goto done; } - rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); - rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS); + rtype = crm_element_value(rsc->xml, PCMK_XA_TYPE); + rprovider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); cib_node = pcmk__inject_node(cib, node, NULL); CRM_ASSERT(cib_node != NULL); - pcmk__inject_failcount(out, cib_node, resource, task, interval_ms, outcome); + pcmk__inject_failcount(out, cib, cib_node, resource, task, interval_ms, + outcome); cib_resource = pcmk__inject_resource_history(out, cib_node, resource, resource, @@ -617,7 +610,7 @@ inject_action(pcmk__output_t *out, const char *spec, cib_t *cib, CRM_ASSERT(cib_op != NULL); lrmd_free_event(op); - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); @@ -647,10 +640,10 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-config", injections->quorum, injections->watchdog); if (injections->quorum != NULL) { - xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); + xmlNode *top = pcmk__xe_create(NULL, PCMK_XE_CIB); - /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid); */ - crm_xml_add(top, XML_ATTR_HAVE_QUORUM, injections->quorum); + /* crm_xml_add(top, PCMK_XA_DC_UUID, dc_uuid); */ + crm_xml_add(top, PCMK_XA_HAVE_QUORUM, injections->quorum); rc = cib->cmds->modify(cib, NULL, top, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); @@ -658,8 +651,8 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, if (injections->watchdog != NULL) { rc = cib__update_node_attr(out, cib, cib_sync_call|cib_scope_local, - XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, - NULL, XML_ATTR_HAVE_WATCHDOG, + PCMK_XE_CRM_CONFIG, NULL, NULL, NULL, + NULL, PCMK_OPT_HAVE_WATCHDOG, injections->watchdog, NULL, NULL); CRM_ASSERT(rc == pcmk_rc_ok); } @@ -672,7 +665,7 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, cib_node = pcmk__inject_node_state_change(cib, node, true); CRM_ASSERT(cib_node != NULL); - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); @@ -687,19 +680,23 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, cib_node = pcmk__inject_node_state_change(cib, node, false); CRM_ASSERT(cib_node != NULL); - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); - xpath = crm_strdup_printf("//node_state[@uname='%s']/%s", - node, XML_CIB_TAG_LRM); + xpath = crm_strdup_printf("//" PCMK__XE_NODE_STATE + "[@" PCMK_XA_UNAME "='%s']" + "/" PCMK__XE_LRM, + node); cib->cmds->remove(cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); free(xpath); - xpath = crm_strdup_printf("//node_state[@uname='%s']/%s", - node, XML_TAG_TRANSIENT_NODEATTRS); + xpath = crm_strdup_printf("//" PCMK__XE_NODE_STATE + "[@" PCMK_XA_UNAME "='%s']" + "/" PCMK__XE_TRANSIENT_ATTRIBUTES, + node); cib->cmds->remove(cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); free(xpath); @@ -711,10 +708,10 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-node", "Failing", node); cib_node = pcmk__inject_node_state_change(cib, node, true); - crm_xml_add(cib_node, PCMK__XA_IN_CCM, XML_BOOLEAN_NO); + crm_xml_add(cib_node, PCMK__XA_IN_CCM, PCMK_VALUE_FALSE); CRM_ASSERT(cib_node != NULL); - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); @@ -725,7 +722,7 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-ticket", "Granting", ticket_id); - rc = set_ticket_state_attr(out, ticket_id, "granted", true, cib); + rc = set_ticket_state_attr(out, ticket_id, PCMK__XA_GRANTED, true, cib); CRM_ASSERT(rc == pcmk_rc_ok); } @@ -734,7 +731,8 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-ticket", "Revoking", ticket_id); - rc = set_ticket_state_attr(out, ticket_id, "granted", false, cib); + rc = set_ticket_state_attr(out, ticket_id, PCMK__XA_GRANTED, false, + cib); CRM_ASSERT(rc == pcmk_rc_ok); } @@ -743,7 +741,7 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-ticket", "Standby", ticket_id); - rc = set_ticket_state_attr(out, ticket_id, "standby", true, cib); + rc = set_ticket_state_attr(out, ticket_id, PCMK_XA_STANDBY, true, cib); CRM_ASSERT(rc == pcmk_rc_ok); } @@ -752,7 +750,7 @@ pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, out->message(out, "inject-modify-ticket", "Activating", ticket_id); - rc = set_ticket_state_attr(out, ticket_id, "standby", false, cib); + rc = set_ticket_state_attr(out, ticket_id, PCMK_XA_STANDBY, false, cib); CRM_ASSERT(rc == pcmk_rc_ok); } diff --git a/lib/pacemaker/pcmk_options.c b/lib/pacemaker/pcmk_options.c new file mode 100644 index 0000000..37956a4 --- /dev/null +++ b/lib/pacemaker/pcmk_options.c @@ -0,0 +1,153 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <libxml/tree.h> // xmlNode + +#include <pacemaker.h> +#include <pacemaker-internal.h> + +/*! + * \internal + * \brief List all available cluster options + * + * These are options that affect the entire cluster. + * + * \param[in,out] out Output object + * \param[in] all If \c true, include advanced and deprecated options (this + * is always treated as true for XML output objects) + * + * \return Standard Pacemaker return code + */ +int +pcmk__list_cluster_options(pcmk__output_t *out, bool all) +{ + const char *name = "cluster-options"; + const char *desc_short = "Pacemaker cluster options"; + const char *desc_long = NULL; + + // Can't use string constants because desc_long may be translated by gettext + desc_long = "Also known as properties, these are options that affect " + "behavior across the entire cluster. They are configured " + "within cluster_property_set elements inside the crm_config " + "subsection of the CIB configuration section."; + + return pcmk__output_cluster_options(out, name, desc_short, desc_long, + pcmk__opt_none, all); +} + +// Documented in header +int +pcmk_list_cluster_options(xmlNode **xml, bool all) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__xml_output_new(&out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + pcmk__register_lib_messages(out); + + rc = pcmk__list_cluster_options(out, all); + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} + +/*! + * \internal + * \brief List common fencing resource parameters + * + * These are parameters that are available for all fencing resources, regardless + * of type. They are processed by Pacemaker, rather than by the fence agent or + * the fencing library. + * + * \param[in,out] out Output object + * \param[in] all If \c true, include advanced and deprecated options (this + * is always treated as true for XML output objects) + * + * \return Standard Pacemaker return code + */ +int +pcmk__list_fencing_params(pcmk__output_t *out, bool all) +{ + const char *name = "fence-attributes"; + const char *desc_short = "Fencing resource common parameters"; + const char *desc_long = NULL; + + desc_long = "Special parameters that are available for all fencing " + "resources, regardless of type. They are processed by " + "Pacemaker, rather than by the fence agent or the fencing " + "library."; + + return pcmk__output_fencing_params(out, name, desc_short, desc_long, all); +} + +// Documented in header +int +pcmk_list_fencing_params(xmlNode **xml, bool all) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__xml_output_new(&out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + pcmk__register_lib_messages(out); + + rc = pcmk__list_fencing_params(out, all); + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} + +/*! + * \internal + * \brief List meta-attributes applicable to primitive resources as OCF-like XML + * + * \param[in,out] out Output object + * \param[in] all If \c true, include advanced and deprecated options (this + * is always treated as true for XML output objects) + * + * \return Standard Pacemaker return code + */ +int +pcmk__list_primitive_meta(pcmk__output_t *out, bool all) +{ + const char *name = "primitive-meta"; + const char *desc_short = "Primitive meta-attributes"; + const char *desc_long = "Meta-attributes applicable to primitive resources"; + + return pcmk__output_primitive_meta(out, name, desc_short, desc_long, all); +} + +// Documented in header +int +pcmk_list_primitive_meta(xmlNode **xml, bool all) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__xml_output_new(&out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + pcmk__register_lib_messages(out); + + rc = pcmk__list_primitive_meta(out, all); + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c index 85001da..12a6710 100644 --- a/lib/pacemaker/pcmk_output.c +++ b/lib/pacemaker/pcmk_output.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. * @@ -10,9 +10,9 @@ #include <crm_internal.h> #include <crm/common/output.h> #include <crm/common/results.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/stonith-ng.h> -#include <crm/fencing/internal.h> +#include <crm/fencing/internal.h> // stonith__* #include <crm/pengine/internal.h> #include <libxml/tree.h> #include <pacemaker-internal.h> @@ -29,7 +29,8 @@ colocations_header(pcmk_resource_t *rsc, pcmk__colocation_t *cons, retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)", rsc->id, pcmk_readable_score(cons->score), (dependents? "needs" : "with"), - role2text(cons->primary_role), cons->id); + pcmk_role_text(cons->primary_role), + cons->id); } else { retval = crm_strdup_printf("%s (score=%s, id=%s)", rsc->id, pcmk_readable_score(cons->score), @@ -43,27 +44,27 @@ colocations_xml_node(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk__colocation_t *cons) { xmlNodePtr node = NULL; - node = pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_DEPEND, - "id", cons->id, - "rsc", cons->dependent->id, - "with-rsc", cons->primary->id, - "score", + node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_COLOCATION, + PCMK_XA_ID, cons->id, + PCMK_XA_RSC, cons->dependent->id, + PCMK_XA_WITH_RSC, cons->primary->id, + PCMK_XA_SCORE, pcmk_readable_score(cons->score), NULL); if (cons->node_attribute) { - xmlSetProp(node, (pcmkXmlStr) "node-attribute", + xmlSetProp(node, (pcmkXmlStr) PCMK_XA_NODE_ATTRIBUTE, (pcmkXmlStr) cons->node_attribute); } if (cons->dependent_role != pcmk_role_unknown) { - xmlSetProp(node, (pcmkXmlStr) "rsc-role", - (pcmkXmlStr) role2text(cons->dependent_role)); + xmlSetProp(node, (pcmkXmlStr) PCMK_XA_RSC_ROLE, + (pcmkXmlStr) pcmk_role_text(cons->dependent_role)); } if (cons->primary_role != pcmk_role_unknown) { - xmlSetProp(node, (pcmkXmlStr) "with-rsc-role", - (pcmkXmlStr) role2text(cons->primary_role)); + xmlSetProp(node, (pcmkXmlStr) PCMK_XA_WITH_RSC_ROLE, + (pcmkXmlStr) pcmk_role_text(cons->primary_role)); } } @@ -76,22 +77,22 @@ do_locations_list_xml(pcmk__output_t *out, pcmk_resource_t *rsc, int rc = pcmk_rc_no_output; for (lpc = list; lpc != NULL; lpc = lpc->next) { - pe__location_t *cons = lpc->data; + pcmk__location_t *cons = lpc->data; GList *lpc2 = NULL; - for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) { + for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) { pcmk_node_t *node = (pcmk_node_t *) lpc2->data; if (add_header) { PCMK__OUTPUT_LIST_HEADER(out, false, rc, "locations"); } - pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_LOCATION, - "node", node->details->uname, - "rsc", rsc->id, - "id", cons->id, - "score", + pcmk__output_create_xml_node(out, PCMK_XE_RSC_LOCATION, + PCMK_XA_NODE, node->details->uname, + PCMK_XA_RSC, rsc->id, + PCMK_XA_ID, cons->id, + PCMK_XA_SCORE, pcmk_readable_score(node->weight), NULL); } @@ -144,7 +145,7 @@ rsc_action_item(pcmk__output_t *out, va_list args) need_role = true; } - if (pe__same_node(origin, destination)) { + if (pcmk__same_node(origin, destination)) { same_host = true; } @@ -154,55 +155,56 @@ rsc_action_item(pcmk__output_t *out, va_list args) if (need_role && (origin == NULL)) { /* Starting and promoting a promotable clone instance */ - details = crm_strdup_printf("%s -> %s %s", role2text(rsc->role), - role2text(rsc->next_role), - pe__node_name(destination)); + details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role), + pcmk_role_text(rsc->next_role), + pcmk__node_name(destination)); } else if (origin == NULL) { /* Starting a resource */ - details = crm_strdup_printf("%s", pe__node_name(destination)); + details = crm_strdup_printf("%s", pcmk__node_name(destination)); } else if (need_role && (destination == NULL)) { /* Stopping a promotable clone instance */ - details = crm_strdup_printf("%s %s", role2text(rsc->role), - pe__node_name(origin)); + details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role), + pcmk__node_name(origin)); } else if (destination == NULL) { /* Stopping a resource */ - details = crm_strdup_printf("%s", pe__node_name(origin)); + details = crm_strdup_printf("%s", pcmk__node_name(origin)); } else if (need_role && same_role && same_host) { /* Recovering, restarting or re-promoting a promotable clone instance */ - details = crm_strdup_printf("%s %s", role2text(rsc->role), - pe__node_name(origin)); + details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role), + pcmk__node_name(origin)); } else if (same_role && same_host) { /* Recovering or Restarting a normal resource */ - details = crm_strdup_printf("%s", pe__node_name(origin)); + details = crm_strdup_printf("%s", pcmk__node_name(origin)); } else if (need_role && same_role) { /* Moving a promotable clone instance */ - details = crm_strdup_printf("%s -> %s %s", pe__node_name(origin), - pe__node_name(destination), - role2text(rsc->role)); + details = crm_strdup_printf("%s -> %s %s", pcmk__node_name(origin), + pcmk__node_name(destination), + pcmk_role_text(rsc->role)); } else if (same_role) { /* Moving a normal resource */ - details = crm_strdup_printf("%s -> %s", pe__node_name(origin), - pe__node_name(destination)); + details = crm_strdup_printf("%s -> %s", pcmk__node_name(origin), + pcmk__node_name(destination)); } else if (same_host) { /* Promoting or demoting a promotable clone instance */ - details = crm_strdup_printf("%s -> %s %s", role2text(rsc->role), - role2text(rsc->next_role), - pe__node_name(origin)); + details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role), + pcmk_role_text(rsc->next_role), + pcmk__node_name(origin)); } else { /* Moving and promoting/demoting */ - details = crm_strdup_printf("%s %s -> %s %s", role2text(rsc->role), - pe__node_name(origin), - role2text(rsc->next_role), - pe__node_name(destination)); + details = crm_strdup_printf("%s %s -> %s %s", + pcmk_role_text(rsc->role), + pcmk__node_name(origin), + pcmk_role_text(rsc->next_role), + pcmk__node_name(destination)); } len = strlen(details); @@ -263,7 +265,7 @@ rsc_action_item_xml(pcmk__output_t *out, va_list args) need_role = true; } - if (pe__same_node(origin, destination)) { + if (pcmk__same_node(origin, destination)) { same_host = true; } @@ -272,91 +274,91 @@ rsc_action_item_xml(pcmk__output_t *out, va_list args) } change_str = g_ascii_strdown(change, -1); - xml = pcmk__output_create_xml_node(out, "rsc_action", - "action", change_str, - "resource", rsc->id, + xml = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION, + PCMK_XA_ACTION, change_str, + PCMK_XA_RESOURCE, rsc->id, NULL); g_free(change_str); if (need_role && (origin == NULL)) { /* Starting and promoting a promotable clone instance */ pcmk__xe_set_props(xml, - "role", role2text(rsc->role), - "next-role", role2text(rsc->next_role), - "dest", destination->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), + PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role), + PCMK_XA_DEST, destination->details->uname, NULL); } else if (origin == NULL) { /* Starting a resource */ - crm_xml_add(xml, "node", destination->details->uname); + crm_xml_add(xml, PCMK_XA_NODE, destination->details->uname); } else if (need_role && (destination == NULL)) { /* Stopping a promotable clone instance */ pcmk__xe_set_props(xml, - "role", role2text(rsc->role), - "node", origin->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), + PCMK_XA_NODE, origin->details->uname, NULL); } else if (destination == NULL) { /* Stopping a resource */ - crm_xml_add(xml, "node", origin->details->uname); + crm_xml_add(xml, PCMK_XA_NODE, origin->details->uname); } else if (need_role && same_role && same_host) { /* Recovering, restarting or re-promoting a promotable clone instance */ pcmk__xe_set_props(xml, - "role", role2text(rsc->role), - "source", origin->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), + PCMK_XA_SOURCE, origin->details->uname, NULL); } else if (same_role && same_host) { /* Recovering or Restarting a normal resource */ - crm_xml_add(xml, "source", origin->details->uname); + crm_xml_add(xml, PCMK_XA_SOURCE, origin->details->uname); } else if (need_role && same_role) { /* Moving a promotable clone instance */ pcmk__xe_set_props(xml, - "source", origin->details->uname, - "dest", destination->details->uname, - "role", role2text(rsc->role), + PCMK_XA_SOURCE, origin->details->uname, + PCMK_XA_DEST, destination->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), NULL); } else if (same_role) { /* Moving a normal resource */ pcmk__xe_set_props(xml, - "source", origin->details->uname, - "dest", destination->details->uname, + PCMK_XA_SOURCE, origin->details->uname, + PCMK_XA_DEST, destination->details->uname, NULL); } else if (same_host) { /* Promoting or demoting a promotable clone instance */ pcmk__xe_set_props(xml, - "role", role2text(rsc->role), - "next-role", role2text(rsc->next_role), - "source", origin->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), + PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role), + PCMK_XA_SOURCE, origin->details->uname, NULL); } else { /* Moving and promoting/demoting */ pcmk__xe_set_props(xml, - "role", role2text(rsc->role), - "source", origin->details->uname, - "next-role", role2text(rsc->next_role), - "dest", destination->details->uname, + PCMK_XA_ROLE, pcmk_role_text(rsc->role), + PCMK_XA_SOURCE, origin->details->uname, + PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role), + PCMK_XA_DEST, destination->details->uname, NULL); } if ((source->reason != NULL) && !pcmk_is_set(action->flags, pcmk_action_runnable)) { pcmk__xe_set_props(xml, - "reason", source->reason, - "blocked", "true", + PCMK_XA_REASON, source->reason, + PCMK_XA_BLOCKED, PCMK_VALUE_TRUE, NULL); } else if (source->reason != NULL) { - crm_xml_add(xml, "reason", source->reason); + crm_xml_add(xml, PCMK_XA_REASON, source->reason); } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) { - pcmk__xe_set_bool_attr(xml, "blocked", true); + pcmk__xe_set_bool_attr(xml, PCMK_XA_BLOCKED, true); } @@ -378,7 +380,7 @@ rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) { /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons * directly rather than rsc->cmds->this_with_colocations(). */ - pe__set_resource_flags(rsc, pcmk_rsc_detect_loop); + pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop); for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) { pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data; char *hdr = NULL; @@ -427,7 +429,7 @@ rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) { /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons * directly rather than rsc->cmds->this_with_colocations(). */ - pe__set_resource_flags(rsc, pcmk_rsc_detect_loop); + pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop); for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) { pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data; @@ -464,7 +466,7 @@ rscs_colocated_with_list(pcmk__output_t *out, va_list args) { * rsc->rsc_cons_lhs directly rather than * rsc->cmds->with_this_colocations(). */ - pe__set_resource_flags(rsc, pcmk_rsc_detect_loop); + 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; char *hdr = NULL; @@ -514,7 +516,7 @@ rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) { * rsc->rsc_cons_lhs directly rather than * rsc->cmds->with_this_colocations(). */ - pe__set_resource_flags(rsc, pcmk_rsc_detect_loop); + 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; @@ -545,16 +547,16 @@ locations_list(pcmk__output_t *out, va_list args) { int rc = pcmk_rc_no_output; for (lpc = list; lpc != NULL; lpc = lpc->next) { - pe__location_t *cons = lpc->data; + pcmk__location_t *cons = lpc->data; GList *lpc2 = NULL; - for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) { + for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) { pcmk_node_t *node = (pcmk_node_t *) lpc2->data; PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Locations"); out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)", - pe__node_name(node), + pcmk__node_name(node), pcmk_readable_score(node->weight), cons->id, rsc->id); } @@ -613,7 +615,7 @@ locations_and_colocations_xml(pcmk__output_t *out, va_list args) rsc = uber_parent(rsc); } - pcmk__output_xml_create_parent(out, "constraints", NULL); + pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL); do_locations_list_xml(out, rsc, false); pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop); @@ -675,9 +677,9 @@ health_xml(pcmk__output_t *out, va_list args) const char *result = va_arg(args, const char *); pcmk__output_create_xml_node(out, pcmk__s(sys_from, ""), - "node_name", pcmk__s(host_from, ""), - "state", pcmk__s(fsa_state, ""), - "result", pcmk__s(result, ""), + PCMK_XA_NODE_NAME, pcmk__s(host_from, ""), + PCMK_XA_STATE, pcmk__s(fsa_state, ""), + PCMK_XA_RESULT, pcmk__s(result, ""), NULL); return pcmk_rc_ok; } @@ -820,10 +822,10 @@ pacemakerd_health_xml(pcmk__output_t *out, va_list args) |crm_time_log_with_timezone); } - pcmk__output_create_xml_node(out, "pacemakerd", - "sys_from", sys_from, - "state", state_s, - "last_updated", last_updated_s, + pcmk__output_create_xml_node(out, PCMK_XE_PACEMAKERD, + PCMK_XA_SYS_FROM, sys_from, + PCMK_XA_STATE, state_s, + PCMK_XA_LAST_UPDATED, last_updated_s, NULL); free(last_updated_s); return pcmk_rc_ok; @@ -851,9 +853,9 @@ profile_xml(pcmk__output_t *out, va_list args) { char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC); - pcmk__output_create_xml_node(out, "timing", - "file", xml_file, - "duration", duration, + pcmk__output_create_xml_node(out, PCMK_XE_TIMING, + PCMK_XA_FILE, xml_file, + PCMK_XA_DURATION, duration, NULL); free(duration); @@ -894,8 +896,8 @@ dc_xml(pcmk__output_t *out, va_list args) { const char *dc = va_arg(args, const char *); - pcmk__output_create_xml_node(out, "dc", - "node_name", pcmk__s(dc, ""), + pcmk__output_create_xml_node(out, PCMK_XE_DC, + PCMK_XA_NODE_NAME, pcmk__s(dc, ""), NULL); return pcmk_rc_ok; } @@ -947,16 +949,16 @@ crmadmin_node_xml(pcmk__output_t *out, va_list args) const char *id = va_arg(args, const char *); bool bash_export G_GNUC_UNUSED = va_arg(args, int); - pcmk__output_create_xml_node(out, "node", - "type", type ? type : "cluster", - "name", pcmk__s(name, ""), - "id", pcmk__s(id, ""), + pcmk__output_create_xml_node(out, PCMK_XE_NODE, + PCMK_XA_TYPE, pcmk__s(type, "cluster"), + PCMK_XA_NAME, pcmk__s(name, ""), + PCMK_XA_ID, pcmk__s(id, ""), NULL); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *", - "const char *", "guint", "const op_digest_cache_t *") + "const char *", "guint", "const pcmk__op_digest_t *") static int digests_text(pcmk__output_t *out, va_list args) { @@ -964,7 +966,7 @@ digests_text(pcmk__output_t *out, va_list args) const pcmk_node_t *node = va_arg(args, const pcmk_node_t *); const char *task = va_arg(args, const char *); guint interval_ms = va_arg(args, guint); - const op_digest_cache_t *digests = va_arg(args, const op_digest_cache_t *); + const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *); char *action_desc = NULL; const char *rsc_desc = "unknown resource"; @@ -1015,18 +1017,16 @@ add_digest_xml(xmlNode *parent, const char *type, const char *digest, xmlNode *digest_source) { if (digest != NULL) { - xmlNodePtr digest_xml = create_xml_node(parent, "digest"); + xmlNodePtr digest_xml = pcmk__xe_create(parent, PCMK_XE_DIGEST); - crm_xml_add(digest_xml, "type", ((type == NULL)? "unspecified" : type)); - crm_xml_add(digest_xml, "hash", digest); - if (digest_source != NULL) { - add_node_copy(digest_xml, digest_source); - } + crm_xml_add(digest_xml, PCMK_XA_TYPE, pcmk__s(type, "unspecified")); + crm_xml_add(digest_xml, PCMK_XA_HASH, digest); + pcmk__xml_copy(digest_xml, digest_source); } } PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *", - "const char *", "guint", "const op_digest_cache_t *") + "const char *", "guint", "const pcmk__op_digest_t *") static int digests_xml(pcmk__output_t *out, va_list args) { @@ -1034,17 +1034,17 @@ digests_xml(pcmk__output_t *out, va_list args) const pcmk_node_t *node = va_arg(args, const pcmk_node_t *); const char *task = va_arg(args, const char *); guint interval_ms = va_arg(args, guint); - const op_digest_cache_t *digests = va_arg(args, const op_digest_cache_t *); + const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *); char *interval_s = crm_strdup_printf("%ums", interval_ms); xmlNode *xml = NULL; - xml = pcmk__output_create_xml_node(out, "digests", - "resource", pcmk__s(rsc->id, ""), - "node", + xml = pcmk__output_create_xml_node(out, PCMK_XE_DIGESTS, + PCMK_XA_RESOURCE, pcmk__s(rsc->id, ""), + PCMK_XA_NODE, pcmk__s(node->details->uname, ""), - "task", pcmk__s(task, ""), - "interval", interval_s, + PCMK_XA_TASK, pcmk__s(task, ""), + PCMK_XA_INTERVAL, interval_s, NULL); free(interval_s); if (digests != NULL) { @@ -1097,14 +1097,14 @@ rsc_action_default(pcmk__output_t *out, va_list args) || (current == NULL && next == NULL)) { const bool managed = pcmk_is_set(rsc->flags, pcmk_rsc_managed); - pe_rsc_info(rsc, "Leave %s\t(%s%s)", - rsc->id, role2text(rsc->role), - (managed? "" : " unmanaged")); + pcmk__rsc_info(rsc, "Leave %s\t(%s%s)", + rsc->id, pcmk_role_text(rsc->role), + (managed? "" : " unmanaged")); return rc; } moving = (current != NULL) && (next != NULL) - && !pe__same_node(current, next); + && !pcmk__same_node(current, next); possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_START, false); @@ -1125,9 +1125,9 @@ rsc_action_default(pcmk__output_t *out, va_list args) stop = possible_matches->data; g_list_free(possible_matches); } else if (pcmk_is_set(rsc->flags, pcmk_rsc_stop_unexpected)) { - /* The resource is multiply active with multiple-active set to - * stop_unexpected, and not stopping on its current node, but it should - * be stopping elsewhere. + /* The resource is multiply active with PCMK_META_MULTIPLE_ACTIVE set to + * PCMK_VALUE_STOP_UNEXPECTED, and not stopping on its current node, but + * it should be stopping elsewhere. */ possible_matches = pe__resource_actions(rsc, NULL, PCMK_ACTION_STOP, false); @@ -1180,8 +1180,9 @@ rsc_action_default(pcmk__output_t *out, va_list args) rc = out->message(out, "rsc-action-item", "Re-promote", rsc, current, next, promote, demote); } else { - pe_rsc_info(rsc, "Leave %s\t(%s %s)", rsc->id, - role2text(rsc->role), pe__node_name(next)); + pcmk__rsc_info(rsc, "Leave %s\t(%s %s)", rsc->id, + pcmk_role_text(rsc->role), + pcmk__node_name(next)); } } else if (!pcmk_is_set(start->flags, pcmk_action_runnable)) { @@ -1327,10 +1328,10 @@ node_action_xml(pcmk__output_t *out, va_list args) if (task == NULL) { return pcmk_rc_no_output; } else if (reason) { - pcmk__output_create_xml_node(out, "node_action", - "task", task, - "node", node_name, - "reason", reason, + pcmk__output_create_xml_node(out, PCMK_XE_NODE_ACTION, + PCMK_XA_TASK, task, + PCMK_XA_NODE, node_name, + PCMK_XA_REASON, reason, NULL); } else { crm_notice(" * %s %s", task, node_name); @@ -1373,20 +1374,20 @@ node_info_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, - XML_ATTR_ID, uuid, - PCMK__XA_CRMD, state, - XML_ATTR_HAVE_QUORUM, pcmk__btoa(have_quorum), - XML_NODE_IS_REMOTE, pcmk__btoa(is_remote), + pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO, + PCMK_XA_NODEID, id_s, + PCMK_XA_UNAME, node_name, + PCMK_XA_ID, uuid, + PCMK_XA_CRMD, state, + PCMK_XA_HAVE_QUORUM, pcmk__btoa(have_quorum), + PCMK_XA_REMOTE_NODE, pcmk__btoa(is_remote), NULL); free(id_s); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *", - "xmlNodePtr") + "xmlNode *") static int inject_cluster_action(pcmk__output_t *out, va_list args) { @@ -1400,7 +1401,7 @@ inject_cluster_action(pcmk__output_t *out, va_list args) if (rsc != NULL) { out->list_item(out, NULL, "Cluster action: %s for %s on %s", - task, ID(rsc), node); + task, pcmk__xe_id(rsc), node); } else { out->list_item(out, NULL, "Cluster action: %s on %s", task, node); } @@ -1409,7 +1410,7 @@ inject_cluster_action(pcmk__output_t *out, va_list args) } PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *", - "xmlNodePtr") + "xmlNode *") static int inject_cluster_action_xml(pcmk__output_t *out, va_list args) { @@ -1423,13 +1424,13 @@ inject_cluster_action_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - xml_node = pcmk__output_create_xml_node(out, "cluster_action", - "task", task, - "node", node, + xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_ACTION, + PCMK_XA_TASK, task, + PCMK_XA_NODE, node, NULL); if (rsc) { - crm_xml_add(xml_node, "id", ID(rsc)); + crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(rsc)); } return pcmk_rc_ok; @@ -1461,14 +1462,14 @@ inject_fencing_action_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - pcmk__output_create_xml_node(out, "fencing_action", - "target", target, - "op", op, + pcmk__output_create_xml_node(out, PCMK_XE_FENCING_ACTION, + PCMK_XA_TARGET, target, + PCMK_XA_OP, op, NULL); return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNodePtr") +PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *") static int inject_attr(pcmk__output_t *out, va_list args) { @@ -1485,13 +1486,13 @@ inject_attr(pcmk__output_t *out, va_list args) node_path = xmlGetNodePath(cib_node); out->list_item(out, NULL, "Injecting attribute %s=%s into %s '%s'", - name, value, node_path, ID(cib_node)); + name, value, node_path, pcmk__xe_id(cib_node)); free(node_path); return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNodePtr") +PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *") static int inject_attr_xml(pcmk__output_t *out, va_list args) { @@ -1507,11 +1508,11 @@ inject_attr_xml(pcmk__output_t *out, va_list args) node_path = xmlGetNodePath(cib_node); - pcmk__output_create_xml_node(out, "inject_attr", - "name", name, - "value", value, - "node_path", node_path, - "cib_node", ID(cib_node), + pcmk__output_create_xml_node(out, PCMK_XE_INJECT_ATTR, + PCMK_XA_NAME, name, + PCMK_XA_VALUE, value, + PCMK_XA_NODE_PATH, node_path, + PCMK_XA_CIB_NODE, pcmk__xe_id(cib_node), NULL); free(node_path); return pcmk_rc_ok; @@ -1541,8 +1542,8 @@ inject_spec_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - pcmk__output_create_xml_node(out, "inject_spec", - "spec", spec, + pcmk__output_create_xml_node(out, PCMK_XE_INJECT_SPEC, + PCMK_XA_SPEC, spec, NULL); return pcmk_rc_ok; } @@ -1584,16 +1585,17 @@ inject_modify_config_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - node = pcmk__output_xml_create_parent(out, "modifications", NULL); + node = pcmk__output_xml_create_parent(out, PCMK_XE_MODIFICATIONS, NULL); if (quorum) { - crm_xml_add(node, "quorum", quorum); + crm_xml_add(node, PCMK_XA_QUORUM, quorum); } if (watchdog) { - crm_xml_add(node, "watchdog", watchdog); + crm_xml_add(node, PCMK_XA_WATCHDOG, watchdog); } + pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } @@ -1633,9 +1635,9 @@ inject_modify_node_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - pcmk__output_create_xml_node(out, "modify_node", - "action", action, - "node", node, + pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_NODE, + PCMK_XA_ACTION, action, + PCMK_XA_NODE, node, NULL); return pcmk_rc_ok; } @@ -1671,9 +1673,9 @@ inject_modify_ticket_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - pcmk__output_create_xml_node(out, "modify_ticket", - "action", action, - "ticket", ticket, + pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_TICKET, + PCMK_XA_ACTION, action, + PCMK_XA_TICKET, ticket, NULL); return pcmk_rc_ok; } @@ -1707,11 +1709,11 @@ inject_pseudo_action_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - xml_node = pcmk__output_create_xml_node(out, "pseudo_action", - "task", task, + xml_node = pcmk__output_create_xml_node(out, PCMK_XE_PSEUDO_ACTION, + PCMK_XA_TASK, task, NULL); if (node) { - crm_xml_add(xml_node, "node", node); + crm_xml_add(xml_node, PCMK_XA_NODE, node); } return pcmk_rc_ok; @@ -1758,16 +1760,16 @@ inject_rsc_action_xml(pcmk__output_t *out, va_list args) return pcmk_rc_no_output; } - xml_node = pcmk__output_create_xml_node(out, "rsc_action", - "resource", rsc, - "op", operation, - "node", node, + xml_node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION, + PCMK_XA_RESOURCE, rsc, + PCMK_XA_OP, operation, + PCMK_XA_NODE, node, NULL); if (interval_ms) { char *interval_s = pcmk__itoa(interval_ms); - crm_xml_add(xml_node, "interval", interval_s); + crm_xml_add(xml_node, PCMK_XA_INTERVAL, interval_s); free(interval_s); } @@ -1868,8 +1870,8 @@ pcmk__cluster_status_text(pcmk__output_t *out, va_list args) /* Print tickets if requested */ if (pcmk_is_set(section_opts, pcmk_section_tickets)) { - CHECK_RC(rc, out->message(out, "ticket-list", scheduler, - (rc == pcmk_rc_ok))); + CHECK_RC(rc, out->message(out, "ticket-list", scheduler->tickets, + (rc == pcmk_rc_ok), false, false)); } /* Print negative location constraints if requested */ @@ -1987,7 +1989,7 @@ cluster_status_xml(pcmk__output_t *out, va_list args) /* Print tickets if requested */ if (pcmk_is_set(section_opts, pcmk_section_tickets)) { - out->message(out, "ticket-list", scheduler, false); + out->message(out, "ticket-list", scheduler->tickets, false, false, false); } /* Print negative location constraints if requested */ @@ -2114,7 +2116,7 @@ cluster_status_html(pcmk__output_t *out, va_list args) /* Print tickets if requested */ if (pcmk_is_set(section_opts, pcmk_section_tickets)) { - out->message(out, "ticket-list", scheduler, false); + out->message(out, "ticket-list", scheduler->tickets, false, false, false); } /* Print negative location constraints if requested */ @@ -2126,8 +2128,16 @@ cluster_status_html(pcmk__output_t *out, va_list args) return pcmk_rc_ok; } +#define KV_PAIR(k, v) do { \ + if (legacy) { \ + pcmk__g_strcat(s, k "=", pcmk__s(v, ""), " ", NULL); \ + } else { \ + pcmk__g_strcat(s, k "=\"", pcmk__s(v, ""), "\"", NULL); \ + } \ +} while (0) + PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *", - "const char *", "const char *") + "const char *", "const char *", "bool", "bool") static int attribute_default(pcmk__output_t *out, va_list args) { @@ -2136,33 +2146,65 @@ attribute_default(pcmk__output_t *out, va_list args) const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); const char *host = va_arg(args, const char *); + bool quiet = va_arg(args, int); + bool legacy = va_arg(args, int); + + gchar *value_esc = NULL; + GString *s = NULL; + + if (quiet) { + if (value != NULL) { + /* Quiet needs to be turned off for ->info() to do anything */ + bool was_quiet = out->is_quiet(out); + + if (was_quiet) { + out->quiet = false; + } + + out->info(out, "%s", value); + + out->quiet = was_quiet; + } + + return pcmk_rc_ok; + } - GString *s = g_string_sized_new(50); + s = g_string_sized_new(50); + + if (pcmk__xml_needs_escape(value, pcmk__xml_escape_attr_pretty)) { + value_esc = pcmk__xml_escape(value, pcmk__xml_escape_attr_pretty); + value = value_esc; + } if (!pcmk__str_empty(scope)) { - pcmk__g_strcat(s, "scope=\"", scope, "\" ", NULL); + KV_PAIR(PCMK_XA_SCOPE, scope); } if (!pcmk__str_empty(instance)) { - pcmk__g_strcat(s, "id=\"", instance, "\" ", NULL); + KV_PAIR(PCMK_XA_ID, instance); } - pcmk__g_strcat(s, "name=\"", pcmk__s(name, ""), "\" ", NULL); + KV_PAIR(PCMK_XA_NAME, name); if (!pcmk__str_empty(host)) { - pcmk__g_strcat(s, "host=\"", host, "\" ", NULL); + KV_PAIR(PCMK_XA_HOST, host); } - pcmk__g_strcat(s, "value=\"", pcmk__s(value, ""), "\"", NULL); + if (legacy) { + pcmk__g_strcat(s, PCMK_XA_VALUE "=", pcmk__s(value, "(null)"), NULL); + } else { + pcmk__g_strcat(s, PCMK_XA_VALUE "=\"", pcmk__s(value, ""), "\"", NULL); + } out->info(out, "%s", s->str); - g_string_free(s, TRUE); + g_free(value_esc); + g_string_free(s, TRUE); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *", - "const char *", "const char *") + "const char *", "const char *", "bool", "bool") static int attribute_xml(pcmk__output_t *out, va_list args) { @@ -2171,24 +2213,26 @@ attribute_xml(pcmk__output_t *out, va_list args) const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); const char *host = va_arg(args, const char *); + bool quiet G_GNUC_UNUSED = va_arg(args, int); + bool legacy G_GNUC_UNUSED = va_arg(args, int); xmlNodePtr node = NULL; - node = pcmk__output_create_xml_node(out, "attribute", - "name", name, - "value", value ? value : "", + node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE, + PCMK_XA_NAME, name, + PCMK_XA_VALUE, pcmk__s(value, ""), NULL); if (!pcmk__str_empty(scope)) { - crm_xml_add(node, "scope", scope); + crm_xml_add(node, PCMK_XA_SCOPE, scope); } if (!pcmk__str_empty(instance)) { - crm_xml_add(node, "id", instance); + crm_xml_add(node, PCMK_XA_ID, instance); } if (!pcmk__str_empty(host)) { - crm_xml_add(node, "host", host); + crm_xml_add(node, PCMK_XA_HOST, host); } return pcmk_rc_ok; @@ -2232,9 +2276,9 @@ rule_check_xml(pcmk__output_t *out, va_list args) char *rc_str = pcmk__itoa(pcmk_rc2exitc(result)); - pcmk__output_create_xml_node(out, "rule-check", - "rule-id", rule_id, - "rc", rc_str, + pcmk__output_create_xml_node(out, PCMK_XE_RULE_CHECK, + PCMK_XA_RULE_ID, rule_id, + PCMK_XA_RC, rc_str, NULL); free(rc_str); @@ -2325,15 +2369,246 @@ result_code_xml(pcmk__output_t *out, va_list args) char *code_str = pcmk__itoa(code); - pcmk__output_create_xml_node(out, "result-code", - "code", code_str, - XML_ATTR_NAME, name, - XML_ATTR_DESC, desc, + pcmk__output_create_xml_node(out, PCMK_XE_RESULT_CODE, + PCMK_XA_CODE, code_str, + PCMK_XA_NAME, name, + PCMK_XA_DESCRIPTION, desc, NULL); free(code_str); return pcmk_rc_ok; } +PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *") +static int +ticket_attribute_default(pcmk__output_t *out, va_list args) +{ + const char *ticket_id G_GNUC_UNUSED = va_arg(args, const char *); + const char *name G_GNUC_UNUSED = va_arg(args, const char *); + const char *value = va_arg(args, const char *); + + out->info(out, "%s", value); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *") +static int +ticket_attribute_xml(pcmk__output_t *out, va_list args) +{ + const char *ticket_id = va_arg(args, const char *); + const char *name = va_arg(args, const char *); + const char *value = va_arg(args, const char *); + + /* Create: + * <tickets> + * <ticket id=""> + * <attribute name="" value="" /> + * </ticket> + * </tickets> + */ + pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL); + pcmk__output_xml_create_parent(out, PCMK_XE_TICKET, + PCMK_XA_ID, ticket_id, NULL); + pcmk__output_create_xml_node(out, PCMK_XA_ATTRIBUTE, + PCMK_XA_NAME, name, + PCMK_XA_VALUE, value, + NULL); + pcmk__output_xml_pop_parent(out); + pcmk__output_xml_pop_parent(out); + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *") +static int +ticket_constraints_default(pcmk__output_t *out, va_list args) +{ + xmlNode *constraint_xml = va_arg(args, xmlNode *); + + /* constraint_xml can take two forms: + * + * <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... /> + * + * for when there's only one ticket in the CIB, or when the user asked + * for a specific ticket (crm_ticket -c -t for instance) + * + * <xpath-query> + * <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... /> + * <rsc_ticket id="rsc1-req-ticketB" rsc="rsc2" ticket="ticketB" ... /> + * </xpath-query> + * + * for when there's multiple tickets in the and the user did not ask for + * a specific one. + * + * In both cases, we simply output a <rsc_ticket> element for each ticket + * in the results. + */ + out->info(out, "Constraints XML:\n"); + + if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) { + xmlNode *child = pcmk__xe_first_child(constraint_xml, NULL, NULL, NULL); + + do { + GString *buf = g_string_sized_new(1024); + + pcmk__xml_string(child, pcmk__xml_fmt_pretty, buf, 0); + out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str); + g_string_free(buf, TRUE); + + child = pcmk__xe_next(child); + } while (child != NULL); + } else { + GString *buf = g_string_sized_new(1024); + + pcmk__xml_string(constraint_xml, pcmk__xml_fmt_pretty, buf, 0); + out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str); + g_string_free(buf, TRUE); + } + + return pcmk_rc_ok; +} + +static int +add_ticket_element_with_constraints(xmlNode *node, void *userdata) +{ + pcmk__output_t *out = (pcmk__output_t *) userdata; + const char *ticket_id = crm_element_value(node, PCMK_XA_TICKET); + + pcmk__output_xml_create_parent(out, PCMK_XE_TICKET, + PCMK_XA_ID, ticket_id, NULL); + pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL); + pcmk__output_xml_add_node_copy(out, node); + + /* Pop two parents so now we are back under the <tickets> element */ + pcmk__output_xml_pop_parent(out); + pcmk__output_xml_pop_parent(out); + + return pcmk_rc_ok; +} + +static int +add_resource_element(xmlNode *node, void *userdata) +{ + pcmk__output_t *out = (pcmk__output_t *) userdata; + const char *rsc = crm_element_value(node, PCMK_XA_RSC); + + pcmk__output_create_xml_node(out, PCMK_XE_RESOURCE, + PCMK_XA_ID, rsc, NULL); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *") +static int +ticket_constraints_xml(pcmk__output_t *out, va_list args) +{ + xmlNode *constraint_xml = va_arg(args, xmlNode *); + + /* Create: + * <tickets> + * <ticket id=""> + * <constraints> + * <rsc_ticket /> + * </constraints> + * </ticket> + * ... + * </tickets> + */ + pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL); + + if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) { + /* Iterate through the list of children once to create all the + * ticket/constraint elements. + */ + pcmk__xe_foreach_child(constraint_xml, NULL, add_ticket_element_with_constraints, out); + + /* Put us back at the same level as where <tickets> was created. */ + pcmk__output_xml_pop_parent(out); + + /* Constraints can reference a resource ID that is defined in the XML + * schema as an IDREF. This requires some other element to be present + * with an id= attribute that matches. + * + * Iterate through the list of children a second time to create the + * following: + * + * <resources> + * <resource id="" /> + * ... + * </resources> + */ + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); + pcmk__xe_foreach_child(constraint_xml, NULL, add_resource_element, out); + pcmk__output_xml_pop_parent(out); + + } else { + /* Creating the output for a single constraint is much easier. All the + * comments in the above block apply here. + */ + add_ticket_element_with_constraints(constraint_xml, out); + pcmk__output_xml_pop_parent(out); + + pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); + add_resource_element(constraint_xml, out); + pcmk__output_xml_pop_parent(out); + } + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *") +static int +ticket_state_default(pcmk__output_t *out, va_list args) +{ + xmlNode *state_xml = va_arg(args, xmlNode *); + + GString *buf = g_string_sized_new(1024); + + out->info(out, "State XML:\n"); + pcmk__xml_string(state_xml, pcmk__xml_fmt_pretty, buf, 0); + out->output_xml(out, PCMK__XE_TICKET_STATE, buf->str); + + g_string_free(buf, TRUE); + return pcmk_rc_ok; +} + +static int +add_ticket_element(xmlNode *node, void *userdata) +{ + pcmk__output_t *out = (pcmk__output_t *) userdata; + xmlNode *ticket_node = NULL; + + ticket_node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET, NULL); + pcmk__xe_copy_attrs(ticket_node, node, pcmk__xaf_none); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *") +static int +ticket_state_xml(pcmk__output_t *out, va_list args) +{ + xmlNode *state_xml = va_arg(args, xmlNode *); + + /* Create: + * <tickets> + * <ticket /> + * ... + * </tickets> + */ + pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL); + + if (state_xml->children != NULL) { + /* Iterate through the list of children once to create all the + * ticket elements. + */ + pcmk__xe_foreach_child(state_xml, PCMK__XE_TICKET_STATE, add_ticket_element, out); + + } else { + add_ticket_element(state_xml, out); + } + + pcmk__output_xml_pop_parent(out); + return pcmk_rc_ok; +} + static pcmk__message_entry_t fmt_functions[] = { { "attribute", "default", attribute_default }, { "attribute", "xml", attribute_xml }, @@ -2369,6 +2644,8 @@ static pcmk__message_entry_t fmt_functions[] = { { "inject-rsc-action", "xml", inject_rsc_action_xml }, { "inject-spec", "default", inject_spec }, { "inject-spec", "xml", inject_spec_xml }, + { "locations-and-colocations", "default", locations_and_colocations }, + { "locations-and-colocations", "xml", locations_and_colocations_xml }, { "locations-list", "default", locations_list }, { "locations-list", "xml", locations_list_xml }, { "node-action", "default", node_action }, @@ -2381,7 +2658,7 @@ static pcmk__message_entry_t fmt_functions[] = { { "pacemakerd-health", "xml", pacemakerd_health_xml }, { "profile", "default", profile_default, }, { "profile", "xml", profile_xml }, - { "result-code", "none", result_code_none }, + { "result-code", PCMK_VALUE_NONE, result_code_none }, { "result-code", "text", result_code_text }, { "result-code", "xml", result_code_xml }, { "rsc-action", "default", rsc_action_default }, @@ -2393,8 +2670,12 @@ static pcmk__message_entry_t fmt_functions[] = { { "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml }, { "rule-check", "default", rule_check_default }, { "rule-check", "xml", rule_check_xml }, - { "locations-and-colocations", "default", locations_and_colocations }, - { "locations-and-colocations", "xml", locations_and_colocations_xml }, + { "ticket-attribute", "default", ticket_attribute_default }, + { "ticket-attribute", "xml", ticket_attribute_xml }, + { "ticket-constraints", "default", ticket_constraints_default }, + { "ticket-constraints", "xml", ticket_constraints_xml }, + { "ticket-state", "default", ticket_state_default }, + { "ticket-state", "xml", ticket_state_xml }, { NULL, NULL, NULL } }; diff --git a/lib/pacemaker/pcmk_resource.c b/lib/pacemaker/pcmk_resource.c index 7a17838..61de2a4 100644 --- a/lib/pacemaker/pcmk_resource.c +++ b/lib/pacemaker/pcmk_resource.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -13,6 +13,7 @@ #include <glib.h> #include <libxml/tree.h> +#include <crm/cib/internal.h> #include <crm/common/mainloop.h> #include <crm/common/results.h> #include <crm/common/output_internal.h> @@ -22,10 +23,11 @@ #include <pacemaker-internal.h> // Search path for resource operation history (takes node name and resource ID) -#define XPATH_OP_HISTORY "//" XML_CIB_TAG_STATUS \ - "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \ - "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES \ - "/" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']" +#define XPATH_OP_HISTORY "//" PCMK_XE_STATUS \ + "/" PCMK__XE_NODE_STATE \ + "[@" PCMK_XA_UNAME "='%s']" \ + "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES \ + "/" PCMK__XE_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']" static xmlNode * best_op(const pcmk_resource_t *rsc, const pcmk_node_t *node) @@ -44,18 +46,21 @@ best_op(const pcmk_resource_t *rsc, const pcmk_node_t *node) free(xpath); // Examine each history entry - for (xmlNode *lrm_rsc_op = first_named_child(history, XML_LRM_TAG_RSC_OP); - lrm_rsc_op != NULL; lrm_rsc_op = crm_next_same_xml(lrm_rsc_op)) { + for (xmlNode *lrm_rsc_op = pcmk__xe_first_child(history, + PCMK__XE_LRM_RSC_OP, NULL, + NULL); + lrm_rsc_op != NULL; lrm_rsc_op = pcmk__xe_next_same(lrm_rsc_op)) { const char *digest = crm_element_value(lrm_rsc_op, - XML_LRM_ATTR_RESTART_DIGEST); + PCMK__XA_OP_RESTART_DIGEST); guint interval_ms = 0; - const char *task = crm_element_value(lrm_rsc_op, XML_LRM_ATTR_TASK); + const char *task = crm_element_value(lrm_rsc_op, PCMK_XA_OPERATION); bool effective_op = false; - bool failure = pcmk__ends_with(ID(lrm_rsc_op), "_last_failure_0"); + bool failure = pcmk__ends_with(pcmk__xe_id(lrm_rsc_op), + "_last_failure_0"); - crm_element_value_ms(lrm_rsc_op, XML_LRM_ATTR_INTERVAL, &interval_ms); + crm_element_value_ms(lrm_rsc_op, PCMK_META_INTERVAL, &interval_ms); effective_op = interval_ms == 0 && pcmk__strcase_any_of(task, PCMK_ACTION_MONITOR, PCMK_ACTION_START, @@ -105,6 +110,79 @@ is_best: /*! * \internal + * \brief Remove a resource + * + * \param[in,out] cib An open connection to the CIB + * \param[in] cib_opts Options to use in the CIB operation call + * \param[in] rsc_id Resource to remove + * \param[in] rsc_type Type of the resource ("primitive", "group", etc.) + * + * \return Standard Pacemaker return code + */ +int +pcmk__resource_delete(cib_t *cib, uint32_t cib_opts, const char *rsc_id, + const char *rsc_type) +{ + int rc = pcmk_rc_ok; + xmlNode *msg_data = NULL; + + if (cib == NULL) { + return ENOTCONN; + } + + if (rsc_id == NULL || rsc_type == NULL) { + return EINVAL; + } + + msg_data = pcmk__xe_create(NULL, rsc_type); + crm_xml_add(msg_data, PCMK_XA_ID, rsc_id); + + rc = cib->cmds->remove(cib, PCMK_XE_RESOURCES, msg_data, cib_opts); + rc = pcmk_legacy2rc(rc); + + free_xml(msg_data); + return rc; +} + +int +pcmk_resource_delete(xmlNodePtr *xml, const char *rsc_id, const char *rsc_type) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + uint32_t cib_opts = cib_sync_call; + cib_t *cib = NULL; + + rc = pcmk__xml_output_new(&out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + cib = cib_new(); + if (cib == NULL) { + rc = pcmk_rc_cib_corrupt; + goto done; + } + + rc = cib->cmds->signon(cib, crm_system_name, cib_command); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__resource_delete(cib, cib_opts, rsc_id, rsc_type); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} + +/*! + * \internal * \brief Calculate and output resource operation digests * * \param[in,out] out Output object @@ -120,14 +198,14 @@ pcmk__resource_digests(pcmk__output_t *out, pcmk_resource_t *rsc, { const char *task = NULL; xmlNode *xml_op = NULL; - op_digest_cache_t *digests = NULL; + pcmk__op_digest_t *digests = NULL; guint interval_ms = 0; int rc = pcmk_rc_ok; if ((out == NULL) || (rsc == NULL) || (node == NULL)) { return EINVAL; } - if (rsc->variant != pcmk_rsc_variant_primitive) { + if (!pcmk__is_primitive(rsc)) { // Only primitives get operation digests return EOPNOTSUPP; } @@ -137,8 +215,8 @@ pcmk__resource_digests(pcmk__output_t *out, pcmk_resource_t *rsc, // Generate an operation key if (xml_op != NULL) { - task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + task = crm_element_value(xml_op, PCMK_XA_OPERATION); + crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms); } if (task == NULL) { // Assume start if no history is available task = PCMK_ACTION_START; @@ -156,8 +234,7 @@ pcmk__resource_digests(pcmk__output_t *out, pcmk_resource_t *rsc, int pcmk_resource_digests(xmlNodePtr *xml, pcmk_resource_t *rsc, - const pcmk_node_t *node, GHashTable *overrides, - pcmk_scheduler_t *scheduler) + const pcmk_node_t *node, GHashTable *overrides) { pcmk__output_t *out = NULL; int rc = pcmk_rc_ok; @@ -168,6 +245,6 @@ pcmk_resource_digests(xmlNodePtr *xml, pcmk_resource_t *rsc, } pcmk__register_lib_messages(out); rc = pcmk__resource_digests(out, rsc, node, overrides); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_result_code.c b/lib/pacemaker/pcmk_result_code.c index 4f50276..b45eee1 100644 --- a/lib/pacemaker/pcmk_result_code.c +++ b/lib/pacemaker/pcmk_result_code.c @@ -73,7 +73,7 @@ pcmk_show_result_code(xmlNodePtr *xml, int code, enum pcmk_result_type type, pcmk__register_lib_messages(out); rc = pcmk__show_result_code(out, code, type, flags); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } @@ -162,6 +162,6 @@ pcmk_list_result_codes(xmlNodePtr *xml, enum pcmk_result_type type, pcmk__register_lib_messages(out); rc = pcmk__list_result_codes(out, type, flags); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_rule.c b/lib/pacemaker/pcmk_rule.c index 99c0b23..cda8774 100644 --- a/lib/pacemaker/pcmk_rule.c +++ b/lib/pacemaker/pcmk_rule.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -12,101 +12,14 @@ #include <crm/cib/internal.h> #include <crm/common/cib.h> #include <crm/common/iso8601.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/internal.h> #include <crm/pengine/rules_internal.h> #include <pacemaker-internal.h> -/*! - * \internal - * \brief Evaluate a date expression for a specific time - * - * \param[in] expr date_expression XML - * \param[in] now Time for which to evaluate expression - * - * \return Standard Pacemaker return code - */ -static int -eval_date_expression(const xmlNode *expr, crm_time_t *now) -{ - pe_rule_eval_data_t rule_data = { - .node_hash = NULL, - .role = pcmk_role_unknown, - .now = now, - .match_data = NULL, - .rsc_data = NULL, - .op_data = NULL - }; - - return pe__eval_date_expr(expr, &rule_data, NULL); -} - -/*! - * \internal - * \brief Initialize scheduler data for checking rules - * - * Make our own copies of the CIB XML and date/time object, if they're not - * \c NULL. This way we don't have to take ownership of the objects passed via - * the API. - * - * \param[in,out] out Output object - * \param[in] input The CIB XML to check (if \c NULL, use current CIB) - * \param[in] date Check whether the rule is in effect at this date - * and time (if \c NULL, use current date and time) - * \param[out] scheduler Where to store initialized scheduler data - * - * \return Standard Pacemaker return code - */ -static int -init_rule_check(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, - pcmk_scheduler_t **scheduler) -{ - // Allows for cleaner syntax than dereferencing the scheduler argument - pcmk_scheduler_t *new_scheduler = NULL; - - new_scheduler = pe_new_working_set(); - if (new_scheduler == NULL) { - return ENOMEM; - } - - pe__set_working_set_flags(new_scheduler, - pcmk_sched_no_counts|pcmk_sched_no_compat); - - // Populate the scheduler data - - // Make our own copy of the given input or fetch the CIB and use that - if (input != NULL) { - new_scheduler->input = copy_xml(input); - if (new_scheduler->input == NULL) { - out->err(out, "Failed to copy input XML"); - pe_free_working_set(new_scheduler); - return ENOMEM; - } - - } else { - int rc = cib__signon_query(out, NULL, &(new_scheduler->input)); - - if (rc != pcmk_rc_ok) { - pe_free_working_set(new_scheduler); - return rc; - } - } - - // Make our own copy of the given crm_time_t object; otherwise - // cluster_status() populates with the current time - if (date != NULL) { - // pcmk_copy_time() guarantees non-NULL - new_scheduler->now = pcmk_copy_time(date); - } - - // Unpack everything - cluster_status(new_scheduler); - *scheduler = new_scheduler; - - return pcmk_rc_ok; -} +#include "libpacemaker_private.h" -#define XPATH_NODE_RULE "//" XML_TAG_RULE "[@" XML_ATTR_ID "='%s']" +#define XPATH_NODE_RULE "//" PCMK_XE_RULE "[@" PCMK_XA_ID "='%s']" /*! * \internal @@ -132,7 +45,7 @@ eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error) /* Rules are under the constraints node in the XML, so first find that. */ cib_constraints = pcmk_find_cib_element(scheduler->input, - XML_CIB_TAG_CONSTRAINTS); + PCMK_XE_CONSTRAINTS); /* Get all rules matching the given ID that are also simple enough for us * to check. For the moment, these rules must only have a single @@ -180,8 +93,10 @@ eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error) } /* Then, check that it's something we actually support. */ - xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression[" - "@" XML_EXPR_ATTR_OPERATION "!='date_spec']", + xpath = crm_strdup_printf(XPATH_NODE_RULE + "//" PCMK_XE_DATE_EXPRESSION + "[@" PCMK_XA_OPERATION + "!='" PCMK_VALUE_DATE_SPEC "']", rule_id); xpath_obj = xpath_search(cib_constraints, xpath); num_results = numXpathResults(xpath_obj); @@ -191,10 +106,15 @@ eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error) if (num_results == 0) { freeXpathObject(xpath_obj); - xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression[" - "@" XML_EXPR_ATTR_OPERATION "='date_spec' " - "and date_spec/@years " - "and not(date_spec/@moon)]", rule_id); + xpath = crm_strdup_printf(XPATH_NODE_RULE + "//" PCMK_XE_DATE_EXPRESSION + "[@" PCMK_XA_OPERATION + "='" PCMK_VALUE_DATE_SPEC "' " + "and " PCMK_XE_DATE_SPEC + "/@" PCMK_XA_YEARS " " + "and not(" PCMK_XE_DATE_SPEC + "/@" PCMK__XA_MOON ")]", + rule_id); xpath_obj = xpath_search(cib_constraints, xpath); num_results = numXpathResults(xpath_obj); @@ -202,8 +122,9 @@ eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error) if (num_results == 0) { freeXpathObject(xpath_obj); - *error = "Rule must either not use date_spec, or use date_spec " - "with years= but not moon="; + *error = "Rule must either not use " PCMK_XE_DATE_SPEC ", or use " + PCMK_XE_DATE_SPEC " with " PCMK_XA_YEARS "= but not " + PCMK__XA_MOON "="; return EOPNOTSUPP; } } @@ -214,13 +135,10 @@ eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error) * checking can't hurt. */ CRM_ASSERT(match != NULL); - CRM_ASSERT(find_expression_type(match) == time_expr); + CRM_ASSERT(pcmk__condition_type(match) == pcmk__condition_datetime); - rc = eval_date_expression(match, scheduler->now); - if (rc == pcmk_rc_undetermined) { - /* pe__eval_date_expr() should return this only if something is - * malformed or missing - */ + rc = pcmk__evaluate_date_expression(match, scheduler->now, NULL); + if (rc == pcmk_rc_undetermined) { // Malformed or missing *error = "Error parsing rule"; } @@ -255,7 +173,7 @@ pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, return pcmk_rc_ok; } - rc = init_rule_check(out, input, date, &scheduler); + rc = pcmk__init_scheduler(out, input, date, &scheduler); if (rc != pcmk_rc_ok) { return rc; } @@ -291,6 +209,6 @@ pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date, pcmk__register_lib_messages(out); rc = pcmk__check_rules(out, input, date, rule_ids); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_sched_actions.c b/lib/pacemaker/pcmk_sched_actions.c index 76b5584..6992045 100644 --- a/lib/pacemaker/pcmk_sched_actions.c +++ b/lib/pacemaker/pcmk_sched_actions.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. * @@ -44,7 +44,7 @@ action_flags_for_ordering(pcmk_action_t *action, const pcmk_node_t *node) * specified. */ flags = action->rsc->cmds->action_flags(action, NULL); - if ((node == NULL) || !pe_rsc_is_clone(action->rsc)) { + if ((node == NULL) || !pcmk__is_clone(action->rsc)) { return flags; } @@ -65,7 +65,8 @@ action_flags_for_ordering(pcmk_action_t *action, const pcmk_node_t *node) * changes. Not very satisfying, but it's logical and appears to work well. */ if (runnable && !pcmk_is_set(flags, pcmk_action_runnable)) { - pe__set_raw_action_flags(flags, action->rsc->id, pcmk_action_runnable); + pcmk__set_raw_action_flags(flags, action->rsc->id, + pcmk_action_runnable); } return flags; } @@ -111,7 +112,7 @@ action_uuid_for_ordering(const char *first_uuid, goto done; } - first_task = text2task(first_task_str); + first_task = pcmk_parse_action(first_task_str); switch (first_task) { case pcmk_action_stop: case pcmk_action_start: @@ -141,25 +142,21 @@ action_uuid_for_ordering(const char *first_uuid, * relative to when notifications have been sent for the remapped task. */ if (pcmk_is_set(first_rsc->flags, pcmk_rsc_notify) - && (pe_rsc_is_clone(first_rsc) || pe_rsc_is_bundled(first_rsc))) { + && (pcmk__is_clone(first_rsc) || pcmk__is_bundled(first_rsc))) { uuid = pcmk__notify_key(rid, "confirmed-post", - task2text(remapped_task)); + pcmk_action_text(remapped_task)); } else { - uuid = pcmk__op_key(rid, task2text(remapped_task), 0); + uuid = pcmk__op_key(rid, pcmk_action_text(remapped_task), 0); } - pe_rsc_trace(first_rsc, - "Remapped action UUID %s to %s for ordering purposes", - first_uuid, uuid); + pcmk__rsc_trace(first_rsc, + "Remapped action UUID %s to %s for ordering purposes", + first_uuid, uuid); } done: - if (uuid == NULL) { - uuid = strdup(first_uuid); - CRM_ASSERT(uuid != NULL); - } free(first_task_str); free(rid); - return uuid; + return (uuid != NULL)? uuid : pcmk__str_copy(first_uuid); } /*! @@ -261,14 +258,15 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, * first->node, at which point this case is handled like a normal * pcmk__ar_first_implies_then. */ - pe__clear_order_flags(order->type, - pcmk__ar_first_implies_same_node_then); - pe__set_order_flags(order->type, pcmk__ar_first_implies_then); + pcmk__clear_relation_flags(order->type, + pcmk__ar_first_implies_same_node_then); + pcmk__set_relation_flags(order->type, pcmk__ar_first_implies_then); node = first->node; - pe_rsc_trace(then->rsc, - "%s then %s: mapped pcmk__ar_first_implies_same_node_then " - "to pcmk__ar_first_implies_then on %s", - first->uuid, then->uuid, pe__node_name(node)); + pcmk__rsc_trace(then->rsc, + "%s then %s: mapped " + "pcmk__ar_first_implies_same_node_then to " + "pcmk__ar_first_implies_then on %s", + first->uuid, then->uuid, pcmk__node_name(node)); } if (pcmk_is_set(order->type, pcmk__ar_first_implies_then)) { @@ -279,13 +277,13 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, scheduler); } else if (!pcmk_is_set(first_flags, pcmk_action_optional) && pcmk_is_set(then->flags, pcmk_action_optional)) { - pe__clear_action_flags(then, pcmk_action_optional); + pcmk__clear_action_flags(then, pcmk_action_optional); pcmk__set_updated_flags(changed, first, pcmk__updated_then); } - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_first_implies_then", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_first_implies_then", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_intermediate_stop) @@ -295,10 +293,10 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, changed |= update(then->rsc, first, then, node, first_flags, restart, pcmk__ar_intermediate_stop, scheduler); - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_intermediate_stop", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_intermediate_stop", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_then_implies_first)) { @@ -308,13 +306,13 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, scheduler); } else if (!pcmk_is_set(first_flags, pcmk_action_optional) && pcmk_is_set(first->flags, pcmk_action_runnable)) { - pe__clear_action_flags(first, pcmk_action_runnable); + pcmk__clear_action_flags(first, pcmk_action_runnable); pcmk__set_updated_flags(changed, first, pcmk__updated_first); } - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_then_implies_first", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_then_implies_first", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_promoted_then_implies_first)) { @@ -324,10 +322,11 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, pcmk_action_optional, pcmk__ar_promoted_then_implies_first, scheduler); } - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_promoted_then_implies_first", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after " + "pcmk__ar_promoted_then_implies_first", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_min_runnable)) { @@ -346,13 +345,13 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, if ((then->runnable_before >= then->required_runnable_before) && !pcmk_is_set(then->flags, pcmk_action_runnable)) { - pe__set_action_flags(then, pcmk_action_runnable); + pcmk__set_action_flags(then, pcmk_action_runnable); pcmk__set_updated_flags(changed, first, pcmk__updated_then); } } - pe_rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_min_runnable", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_min_runnable", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_nested_remote_probe) @@ -361,19 +360,19 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, if (!pcmk_is_set(first_flags, pcmk_action_runnable) && (first->rsc != NULL) && (first->rsc->running_on != NULL)) { - pe_rsc_trace(then->rsc, - "%s then %s: ignoring because first is stopping", - first->uuid, then->uuid); + pcmk__rsc_trace(then->rsc, + "%s then %s: ignoring because first is stopping", + first->uuid, then->uuid); order->type = (enum pe_ordering) pcmk__ar_none; } else { changed |= update(then->rsc, first, then, node, first_flags, pcmk_action_runnable, pcmk__ar_unrunnable_first_blocks, scheduler); } - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_nested_remote_probe", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_nested_remote_probe", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_unrunnable_first_blocks)) { @@ -385,13 +384,13 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, } else if (!pcmk_is_set(first_flags, pcmk_action_runnable) && pcmk_is_set(then->flags, pcmk_action_runnable)) { - pe__clear_action_flags(then, pcmk_action_runnable); + pcmk__clear_action_flags(then, pcmk_action_runnable); pcmk__set_updated_flags(changed, first, pcmk__updated_then); } - pe_rsc_trace(then->rsc, - "%s then %s: %s after pcmk__ar_unrunnable_first_blocks", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_unrunnable_first_blocks", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_unmigratable_then_blocks)) { @@ -400,10 +399,11 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, pcmk_action_optional, pcmk__ar_unmigratable_then_blocks, scheduler); } - pe_rsc_trace(then->rsc, "%s then %s: %s after " - "pcmk__ar_unmigratable_then_blocks", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after " + "pcmk__ar_unmigratable_then_blocks", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_first_else_then)) { @@ -412,9 +412,10 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, pcmk_action_optional, pcmk__ar_first_else_then, scheduler); } - pe_rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_first_else_then", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after pcmk__ar_first_else_then", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_ordered)) { @@ -423,9 +424,9 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, pcmk_action_runnable, pcmk__ar_ordered, scheduler); } - pe_rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_ordered", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_ordered", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(order->type, pcmk__ar_asymmetric)) { @@ -434,27 +435,27 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, pcmk_action_runnable, pcmk__ar_asymmetric, scheduler); } - pe_rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_asymmetric", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_asymmetric", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } if (pcmk_is_set(first->flags, pcmk_action_runnable) && pcmk_is_set(order->type, pcmk__ar_first_implies_then_graphed) && !pcmk_is_set(first_flags, pcmk_action_optional)) { - pe_rsc_trace(then->rsc, "%s will be in graph because %s is required", - then->uuid, first->uuid); - pe__set_action_flags(then, pcmk_action_always_in_graph); + pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required", + then->uuid, first->uuid); + pcmk__set_action_flags(then, pcmk_action_always_in_graph); // Don't bother marking 'then' as changed just for this } if (pcmk_is_set(order->type, pcmk__ar_then_implies_first_graphed) && !pcmk_is_set(then_flags, pcmk_action_optional)) { - pe_rsc_trace(then->rsc, "%s will be in graph because %s is required", - first->uuid, then->uuid); - pe__set_action_flags(first, pcmk_action_always_in_graph); + pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required", + first->uuid, then->uuid); + pcmk__set_action_flags(first, pcmk_action_always_in_graph); // Don't bother marking 'first' as changed just for this } @@ -468,13 +469,14 @@ update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then, && pcmk__str_eq(first->task, PCMK_ACTION_STOP, pcmk__str_none)) { if (pcmk_is_set(then->flags, pcmk_action_runnable)) { - pe__clear_action_flags(then, pcmk_action_runnable); + pcmk__clear_action_flags(then, pcmk_action_runnable); pcmk__set_updated_flags(changed, first, pcmk__updated_then); } - pe_rsc_trace(then->rsc, "%s then %s: %s after checking whether first " - "is blocked, unmanaged, unrunnable stop", - first->uuid, then->uuid, - (changed? "changed" : "unchanged")); + pcmk__rsc_trace(then->rsc, + "%s then %s: %s after checking whether first " + "is blocked, unmanaged, unrunnable stop", + first->uuid, then->uuid, + (changed? "changed" : "unchanged")); } return changed; @@ -509,10 +511,10 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, uint32_t changed = pcmk__updated_none; int last_flags = then->flags; - pe_rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s", - action_type_str(then->flags), then->uuid, - action_optional_str(then->flags), - action_runnable_str(then->flags), action_node_str(then)); + pcmk__rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s", + action_type_str(then->flags), then->uuid, + action_optional_str(then->flags), + action_runnable_str(then->flags), action_node_str(then)); if (pcmk_is_set(then->flags, pcmk_action_min_runnable)) { /* Initialize current known "runnable before" actions. As @@ -524,7 +526,8 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, if (then->required_runnable_before == 0) { /* @COMPAT This ordering constraint uses the deprecated - * "require-all=false" attribute. Treat it like "clone-min=1". + * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE attribute. Treat it like + * PCMK_META_CLONE_MIN=1. */ then->required_runnable_before = 1; } @@ -533,7 +536,7 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, * update_action_for_ordering_flags() (called below) * will reset runnable if appropriate. */ - pe__clear_action_flags(then, pcmk_action_runnable); + pcmk__clear_action_flags(then, pcmk_action_runnable); } for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) { @@ -544,37 +547,36 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, pcmk_node_t *first_node = first->node; if ((first->rsc != NULL) - && (first->rsc->variant == pcmk_rsc_variant_group) + && pcmk__is_group(first->rsc) && pcmk__str_eq(first->task, PCMK_ACTION_START, pcmk__str_none)) { first_node = first->rsc->fns->location(first->rsc, NULL, FALSE); if (first_node != NULL) { - pe_rsc_trace(first->rsc, "Found %s for 'first' %s", - pe__node_name(first_node), first->uuid); + pcmk__rsc_trace(first->rsc, "Found %s for 'first' %s", + pcmk__node_name(first_node), first->uuid); } } - if ((then->rsc != NULL) - && (then->rsc->variant == pcmk_rsc_variant_group) + if (pcmk__is_group(then->rsc) && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)) { then_node = then->rsc->fns->location(then->rsc, NULL, FALSE); if (then_node != NULL) { - pe_rsc_trace(then->rsc, "Found %s for 'then' %s", - pe__node_name(then_node), then->uuid); + pcmk__rsc_trace(then->rsc, "Found %s for 'then' %s", + pcmk__node_name(then_node), then->uuid); } } // Disable constraint if it only applies when on same node, but isn't if (pcmk_is_set(other->type, pcmk__ar_if_on_same_node) && (first_node != NULL) && (then_node != NULL) - && !pe__same_node(first_node, then_node)) { + && !pcmk__same_node(first_node, then_node)) { - pe_rsc_trace(then->rsc, - "Disabled ordering %s on %s then %s on %s: " - "not same node", - other->action->uuid, pe__node_name(first_node), - then->uuid, pe__node_name(then_node)); + pcmk__rsc_trace(then->rsc, + "Disabled ordering %s on %s then %s on %s: " + "not same node", + other->action->uuid, pcmk__node_name(first_node), + then->uuid, pcmk__node_name(then_node)); other->type = (enum pe_ordering) pcmk__ar_none; continue; } @@ -588,9 +590,9 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, /* 'then' is required, so we must abandon 'first' * (e.g. a required stop cancels any agent reload). */ - pe__set_action_flags(other->action, pcmk_action_optional); + pcmk__set_action_flags(other->action, pcmk_action_optional); if (!strcmp(first->task, PCMK_ACTION_RELOAD_AGENT)) { - pe__clear_resource_flags(first->rsc, pcmk_rsc_reload); + pcmk__clear_rsc_flags(first->rsc, pcmk_rsc_reload); } } @@ -599,14 +601,14 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, first = action_for_ordering(first); } if (first != other->action) { - pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s", - then->uuid, first->uuid, other->action->uuid); + pcmk__rsc_trace(then->rsc, "Ordering %s after %s instead of %s", + then->uuid, first->uuid, other->action->uuid); } - pe_rsc_trace(then->rsc, - "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s", - first->uuid, first->flags, then->uuid, then->flags, - other->type, action_node_str(first)); + pcmk__rsc_trace(then->rsc, + "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s", + first->uuid, first->flags, then->uuid, then->flags, + other->type, action_node_str(first)); if (first == other->action) { /* 'first' was not remapped (e.g. from 'start' to 'running'), which @@ -630,10 +632,11 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, * start again to get the new actions_before list */ pcmk__set_updated_flags(changed, then, pcmk__updated_then); - pe_rsc_trace(then->rsc, - "Disabled ordering %s then %s in favor of %s then %s", - other->action->uuid, then->uuid, first->uuid, - then->uuid); + pcmk__rsc_trace(then->rsc, + "Disabled ordering %s then %s in favor of %s " + "then %s", + other->action->uuid, then->uuid, first->uuid, + then->uuid); other->type = (enum pe_ordering) pcmk__ar_none; } @@ -678,8 +681,7 @@ pcmk__update_action_for_orderings(pcmk_action_t *then, static inline bool is_primitive_action(const pcmk_action_t *action) { - return (action != NULL) && (action->rsc != NULL) - && (action->rsc->variant == pcmk_rsc_variant_primitive); + return (action != NULL) && pcmk__is_primitive(action->rsc); } /*! @@ -692,7 +694,7 @@ is_primitive_action(const pcmk_action_t *action) */ #define clear_action_flag_because(action, flag, reason) do { \ if (pcmk_is_set((action)->flags, (flag))) { \ - pe__clear_action_flags(action, flag); \ + pcmk__clear_action_flags(action, flag); \ if ((action)->rsc != (reason)->rsc) { \ char *reason_text = pe__action2reason((reason), (flag)); \ pe_action_set_reason((action), reason_text, false); \ @@ -789,8 +791,8 @@ handle_restart_ordering(pcmk_action_t *first, pcmk_action_t *then, return; } - pe_rsc_trace(first->rsc, "Handling %s -> %s for %s", - first->uuid, then->uuid, reason); + pcmk__rsc_trace(first->rsc, "Handling %s -> %s for %s", + first->uuid, then->uuid, reason); // Make 'first' required if it is runnable if (pcmk_is_set(first->flags, pcmk_action_runnable)) { @@ -901,7 +903,7 @@ pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, && !pcmk_is_set(first->flags, pcmk_action_runnable)) { clear_action_flag_because(then, pcmk_action_migratable, first); - pe__clear_action_flags(then, pcmk_action_pseudo); + pcmk__clear_action_flags(then, pcmk_action_pseudo); } if (pcmk_is_set(type, pcmk__ar_unrunnable_first_blocks) @@ -928,11 +930,11 @@ pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, if (then_flags != then->flags) { pcmk__set_updated_flags(changed, first, pcmk__updated_then); - pe_rsc_trace(then->rsc, - "%s on %s: flags are now %#.6x (was %#.6x) " - "because of 'first' %s (%#.6x)", - then->uuid, pe__node_name(then->node), - then->flags, then_flags, first->uuid, first->flags); + pcmk__rsc_trace(then->rsc, + "%s on %s: flags are now %#.6x (was %#.6x) " + "because of 'first' %s (%#.6x)", + then->uuid, pcmk__node_name(then->node), + then->flags, then_flags, first->uuid, first->flags); if ((then->rsc != NULL) && (then->rsc->parent != NULL)) { // Required to handle "X_stop then X_start" for cloned groups @@ -942,11 +944,11 @@ pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, if (first_flags != first->flags) { pcmk__set_updated_flags(changed, first, pcmk__updated_first); - pe_rsc_trace(first->rsc, - "%s on %s: flags are now %#.6x (was %#.6x) " - "because of 'then' %s (%#.6x)", - first->uuid, pe__node_name(first->node), - first->flags, first_flags, then->uuid, then->flags); + pcmk__rsc_trace(first->rsc, + "%s on %s: flags are now %#.6x (was %#.6x) " + "because of 'then' %s (%#.6x)", + first->uuid, pcmk__node_name(first->node), + first->flags, first_flags, then->uuid, then->flags); } return changed; @@ -979,7 +981,7 @@ pcmk__log_action(const char *pre_text, const pcmk_action_t *action, } } - switch (text2task(action->task)) { + switch (pcmk_parse_action(action->task)) { case pcmk_action_fence: case pcmk_action_shutdown: if (pcmk_is_set(action->flags, pcmk_action_pseudo)) { @@ -1066,7 +1068,7 @@ pcmk__new_shutdown_action(pcmk_node_t *node) node, FALSE, node->details->data_set); pcmk__order_stops_before_shutdown(node, shutdown_op); - add_hash_param(shutdown_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); + pcmk__insert_meta(shutdown_op, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE); return shutdown_op; } @@ -1090,11 +1092,11 @@ add_op_digest_to_xml(const lrmd_event_data_t *op, xmlNode *update) if (op->params == NULL) { return; } - args_xml = create_xml_node(NULL, XML_TAG_PARAMS); + args_xml = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS); g_hash_table_foreach(op->params, hash2field, args_xml); pcmk__filter_op_for_digest(args_xml); digest = calculate_operation_digest(args_xml, NULL); - crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest); + crm_xml_add(update, PCMK__XA_OP_DIGEST, digest); free_xml(args_xml); free(digest); } @@ -1183,7 +1185,9 @@ pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, } else if (did_rsc_op_fail(op, target_rc)) { op_id = pcmk__op_key(op->rsc_id, "last_failure", 0); if (op->interval_ms == 0) { - // Ensure 'last' gets updated, in case record-pending is true + /* Ensure 'last' gets updated, in case PCMK_META_RECORD_PENDING is + * true + */ op_id_additional = pcmk__op_key(op->rsc_id, "last", 0); } exit_reason = op->exit_reason; @@ -1196,9 +1200,10 @@ pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, } again: - xml_op = pcmk__xe_match(parent, XML_LRM_TAG_RSC_OP, XML_ATTR_ID, op_id); + xml_op = pcmk__xe_first_child(parent, PCMK__XE_LRM_RSC_OP, PCMK_XA_ID, + op_id); if (xml_op == NULL) { - xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP); + xml_op = pcmk__xe_create(parent, PCMK__XE_LRM_RSC_OP); } if (op->user_data == NULL) { @@ -1215,20 +1220,20 @@ pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, (const char *) op->user_data); } - crm_xml_add(xml_op, XML_ATTR_ID, op_id); - crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key); - crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task); - crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin); - crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic); - crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, pcmk__s(exit_reason, "")); - crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); // For context during triage + crm_xml_add(xml_op, PCMK_XA_ID, op_id); + crm_xml_add(xml_op, PCMK__XA_OPERATION_KEY, key); + crm_xml_add(xml_op, PCMK_XA_OPERATION, task); + crm_xml_add(xml_op, PCMK_XA_CRM_DEBUG_ORIGIN, origin); + crm_xml_add(xml_op, PCMK_XA_CRM_FEATURE_SET, caller_version); + crm_xml_add(xml_op, PCMK__XA_TRANSITION_KEY, op->user_data); + crm_xml_add(xml_op, PCMK__XA_TRANSITION_MAGIC, magic); + crm_xml_add(xml_op, PCMK_XA_EXIT_REASON, pcmk__s(exit_reason, "")); + crm_xml_add(xml_op, PCMK__META_ON_NODE, node); // For context during triage - crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id); - crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc); - crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status); - crm_xml_add_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, op->interval_ms); + crm_xml_add_int(xml_op, PCMK__XA_CALL_ID, op->call_id); + crm_xml_add_int(xml_op, PCMK__XA_RC_CODE, op->rc); + crm_xml_add_int(xml_op, PCMK__XA_OP_STATUS, op->op_status); + crm_xml_add_ms(xml_op, PCMK_META_INTERVAL, op->interval_ms); if (compare_version("2.1", caller_version) <= 0) { if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) { @@ -1239,28 +1244,28 @@ pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, if ((op->interval_ms != 0) && (op->t_rcchange != 0)) { // Recurring ops may have changed rc after initial run - crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE, + crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE, (long long) op->t_rcchange); } else { - crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE, + crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE, (long long) op->t_run); } - crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time); - crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time); + crm_xml_add_int(xml_op, PCMK_XA_EXEC_TIME, op->exec_time); + crm_xml_add_int(xml_op, PCMK_XA_QUEUE_TIME, op->queue_time); } } if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM, NULL)) { - /* - * Record migrate_source and migrate_target always for migrate ops. + /* Record PCMK__META_MIGRATE_SOURCE and PCMK__META_MIGRATE_TARGET always + * for migrate ops. */ - const char *name = XML_LRM_ATTR_MIGRATE_SOURCE; + const char *name = PCMK__META_MIGRATE_SOURCE; crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); - name = XML_LRM_ATTR_MIGRATE_TARGET; + name = PCMK__META_MIGRATE_TARGET; crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); } @@ -1287,10 +1292,10 @@ pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, * \internal * \brief Check whether an action shutdown-locks a resource to a node * - * If the shutdown-lock cluster property is set, resources will not be recovered - * on a different node if cleanly stopped, and may start only on that same node. - * This function checks whether that applies to a given action, so that the - * transition graph can be marked appropriately. + * If the PCMK_OPT_SHUTDOWN_LOCK cluster property is set, resources will not be + * recovered on a different node if cleanly stopped, and may start only on that + * same node. This function checks whether that applies to a given action, so + * that the transition graph can be marked appropriately. * * \param[in] action Action to check * @@ -1302,7 +1307,7 @@ pcmk__action_locks_rsc_to_node(const pcmk_action_t *action) { // Only resource actions taking place on resource's lock node are locked if ((action == NULL) || (action->rsc == NULL) - || !pe__same_node(action->node, action->rsc->lock_node)) { + || !pcmk__same_node(action->node, action->rsc->lock_node)) { return false; } @@ -1414,7 +1419,7 @@ pcmk__output_actions(pcmk_scheduler_t *scheduler) } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) { const char *op = g_hash_table_lookup(action->meta, - "stonith_action"); + PCMK__META_STONITH_ACTION); task = crm_strdup_printf("Fence (%s)", op); @@ -1422,14 +1427,14 @@ pcmk__output_actions(pcmk_scheduler_t *scheduler) continue; // Don't display other node action types } - if (pe__is_guest_node(action->node)) { + if (pcmk__is_guest_or_bundle_node(action->node)) { const pcmk_resource_t *remote = action->node->details->remote_rsc; node_name = crm_strdup_printf("%s (resource: %s)", - pe__node_name(action->node), + pcmk__node_name(action->node), remote->container->id); } else if (action->node != NULL) { - node_name = crm_strdup_printf("%s", pe__node_name(action->node)); + node_name = crm_strdup_printf("%s", pcmk__node_name(action->node)); } out->message(out, "node-action", task, node_name, action->reason); @@ -1488,7 +1493,7 @@ task_for_digest(const char *task, guint interval_ms) */ static bool only_sanitized_changed(const xmlNode *xml_op, - const op_digest_cache_t *digest_data, + const pcmk__op_digest_t *digest_data, const pcmk_scheduler_t *scheduler) { const char *digest_secure = NULL; @@ -1498,7 +1503,7 @@ only_sanitized_changed(const xmlNode *xml_op, return false; } - digest_secure = crm_element_value(xml_op, XML_LRM_ATTR_SECURE_DIGEST); + digest_secure = crm_element_value(xml_op, PCMK__XA_OP_SECURE_DIGEST); return (digest_data->rc != pcmk__digest_match) && (digest_secure != NULL) && (digest_data->digest_secure_calc != NULL) @@ -1551,11 +1556,11 @@ schedule_reload(gpointer data, gpointer user_data) if ((node == NULL) || !pcmk_is_set(rsc->flags, pcmk_rsc_managed) || pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { - pe_rsc_trace(rsc, "Skip reload of %s:%s%s %s", - rsc->id, - pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " unmanaged", - pcmk_is_set(rsc->flags, pcmk_rsc_failed)? " failed" : "", - (node == NULL)? "inactive" : node->details->uname); + pcmk__rsc_trace(rsc, "Skip reload of %s:%s%s %s", + rsc->id, + pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " unmanaged", + pcmk_is_set(rsc->flags, pcmk_rsc_failed)? " failed" : "", + (node == NULL)? "inactive" : node->details->uname); return; } @@ -1563,15 +1568,16 @@ schedule_reload(gpointer data, gpointer user_data) * force a full restart instead of a reload. */ if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) { - pe_rsc_trace(rsc, "%s: preventing agent reload because start pending", - rsc->id); + pcmk__rsc_trace(rsc, + "%s: preventing agent reload because start pending", + rsc->id); custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE, rsc->cluster); return; } // Schedule the reload - pe__set_resource_flags(rsc, pcmk_rsc_reload); + pcmk__set_rsc_flags(rsc, pcmk_rsc_reload); reload = custom_action(rsc, reload_key(rsc), PCMK_ACTION_RELOAD_AGENT, node, FALSE, rsc->cluster); pe_action_set_reason(reload, "resource definition change", FALSE); @@ -1605,40 +1611,40 @@ pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, { guint interval_ms = 0; const char *task = NULL; - const op_digest_cache_t *digest_data = NULL; + const pcmk__op_digest_t *digest_data = NULL; CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL), return false); - task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + task = crm_element_value(xml_op, PCMK_XA_OPERATION); CRM_CHECK(task != NULL, return false); - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms); // If this is a recurring action, check whether it has been orphaned if (interval_ms > 0) { if (pcmk__find_action_config(rsc, task, interval_ms, false) != NULL) { - pe_rsc_trace(rsc, "%s-interval %s for %s on %s is in configuration", - pcmk__readable_interval(interval_ms), task, rsc->id, - pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s-interval %s for %s on %s is in configuration", + pcmk__readable_interval(interval_ms), task, rsc->id, + pcmk__node_name(node)); } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_cancel_removed_actions)) { pcmk__schedule_cancel(rsc, - crm_element_value(xml_op, - XML_LRM_ATTR_CALLID), + crm_element_value(xml_op, PCMK__XA_CALL_ID), task, interval_ms, node, "orphan"); return true; } else { - pe_rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned", - pcmk__readable_interval(interval_ms), task, rsc->id, - pe__node_name(node)); + pcmk__rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned", + pcmk__readable_interval(interval_ms), task, rsc->id, + pcmk__node_name(node)); return true; } } crm_trace("Checking %s-interval %s for %s on %s for configuration changes", pcmk__readable_interval(interval_ms), task, rsc->id, - pe__node_name(node)); + pcmk__node_name(node)); task = task_for_digest(task, interval_ms); digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster); @@ -1650,8 +1656,8 @@ pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, "Only 'private' parameters to %s-interval %s for %s " "on %s changed: %s", pcmk__readable_interval(interval_ms), task, rsc->id, - pe__node_name(node), - crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + pcmk__node_name(node), + crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC)); } return false; } @@ -1675,7 +1681,7 @@ pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, pcmk__reschedule_recurring(rsc, task, interval_ms, node); } else if (crm_element_value(xml_op, - XML_LRM_ATTR_RESTART_DIGEST) != NULL) { + PCMK__XA_OP_RESTART_DIGEST) != NULL) { // Agent supports reload, so use it trigger_unfencing(rsc, node, "Device parameters changed (reload)", NULL, @@ -1684,9 +1690,10 @@ pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, schedule_reload((gpointer) rsc, (gpointer) node); } else { - pe_rsc_trace(rsc, - "Restarting %s " - "because agent doesn't support reload", rsc->id); + pcmk__rsc_trace(rsc, + "Restarting %s " + "because agent doesn't support reload", + rsc->id); crm_log_xml_debug(digest_data->params_restart, "params:restart"); force_restart(rsc, task, interval_ms, node); @@ -1703,7 +1710,7 @@ pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, * \internal * \brief Create a list of resource's action history entries, sorted by call ID * - * \param[in] rsc_entry Resource's <lrm_rsc_op> status XML + * \param[in] rsc_entry Resource's \c PCMK__XE_LRM_RSC_OP status XML * \param[out] start_index Where to store index of start-like action, if any * \param[out] stop_index Where to store index of stop action, if any */ @@ -1712,8 +1719,10 @@ rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index) { GList *ops = NULL; - for (xmlNode *rsc_op = first_named_child(rsc_entry, XML_LRM_TAG_RSC_OP); - rsc_op != NULL; rsc_op = crm_next_same_xml(rsc_op)) { + for (xmlNode *rsc_op = pcmk__xe_first_child(rsc_entry, PCMK__XE_LRM_RSC_OP, + NULL, NULL); + rsc_op != NULL; rsc_op = pcmk__xe_next_same(rsc_op)) { + ops = g_list_prepend(ops, rsc_op); } ops = g_list_sort(ops, sort_op_by_callid); @@ -1731,7 +1740,7 @@ rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index) * (This also cancels recurring actions for maintenance mode, which is not * entirely related but convenient to do here.) * - * \param[in] rsc_entry Resource's <lrm_rsc_op> status XML + * \param[in] rsc_entry Resource's \c PCMK__XE_LRM_RSC_OP status XML * \param[in,out] rsc Resource whose history is being processed * \param[in,out] node Node whose history is being processed */ @@ -1745,15 +1754,15 @@ process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc, GList *sorted_op_list = NULL; if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) { - if (pe_rsc_is_anon_clone(pe__const_top_resource(rsc, false))) { - pe_rsc_trace(rsc, - "Skipping configuration check " - "for orphaned clone instance %s", - rsc->id); + if (pcmk__is_anonymous_clone(pe__const_top_resource(rsc, false))) { + pcmk__rsc_trace(rsc, + "Skipping configuration check " + "for orphaned clone instance %s", + rsc->id); } else { - pe_rsc_trace(rsc, - "Skipping configuration check and scheduling clean-up " - "for orphaned resource %s", rsc->id); + pcmk__rsc_trace(rsc, + "Skipping configuration check and scheduling " + "clean-up for orphaned resource %s", rsc->id); pcmk__schedule_cleanup(rsc, node, false); } return; @@ -1763,15 +1772,15 @@ process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc, if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) { pcmk__schedule_cleanup(rsc, node, false); } - pe_rsc_trace(rsc, - "Skipping configuration check for %s " - "because no longer active on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "Skipping configuration check for %s " + "because no longer active on %s", + rsc->id, pcmk__node_name(node)); return; } - pe_rsc_trace(rsc, "Checking for configuration changes for %s on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Checking for configuration changes for %s on %s", + rsc->id, pcmk__node_name(node)); if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) { pcmk__schedule_cleanup(rsc, node, false); @@ -1792,16 +1801,15 @@ process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc, continue; } - task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); - crm_element_value_ms(rsc_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + task = crm_element_value(rsc_op, PCMK_XA_OPERATION); + crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms); if ((interval_ms > 0) && (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance) || node->details->maintenance)) { // Maintenance mode cancels recurring operations pcmk__schedule_cancel(rsc, - crm_element_value(rsc_op, - XML_LRM_ATTR_CALLID), + crm_element_value(rsc_op, PCMK__XA_CALL_ID), task, interval_ms, node, "maintenance mode"); } else if ((interval_ms > 0) @@ -1846,24 +1854,25 @@ process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc, * entirely related but convenient to do here.) * * \param[in,out] node Node whose history is being processed - * \param[in] lrm_rscs Node's <lrm_resources> from CIB status XML + * \param[in] lrm_rscs Node's \c PCMK__XE_LRM_RESOURCES from CIB status XML */ static void process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs) { - crm_trace("Processing node history for %s", pe__node_name(node)); - for (const xmlNode *rsc_entry = first_named_child(lrm_rscs, - XML_LRM_TAG_RESOURCE); - rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) { + crm_trace("Processing node history for %s", pcmk__node_name(node)); + for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rscs, + PCMK__XE_LRM_RESOURCE, + NULL, NULL); + rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) { if (rsc_entry->children != NULL) { - GList *result = pcmk__rscs_matching_id(ID(rsc_entry), + GList *result = pcmk__rscs_matching_id(pcmk__xe_id(rsc_entry), node->details->data_set); for (GList *iter = result; iter != NULL; iter = iter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; - if (rsc->variant == pcmk_rsc_variant_primitive) { + if (pcmk__is_primitive(rsc)) { process_rsc_history(rsc_entry, rsc, node); } } @@ -1873,9 +1882,10 @@ process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs) } // XPath to find a node's resource history -#define XPATH_NODE_HISTORY "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS \ - "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \ - "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES +#define XPATH_NODE_HISTORY "/" PCMK_XE_CIB "/" PCMK_XE_STATUS \ + "/" PCMK__XE_NODE_STATE \ + "[@" PCMK_XA_UNAME "='%s']" \ + "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES /*! * \internal diff --git a/lib/pacemaker/pcmk_sched_bundle.c b/lib/pacemaker/pcmk_sched_bundle.c index 1c66314..167f519 100644 --- a/lib/pacemaker/pcmk_sched_bundle.c +++ b/lib/pacemaker/pcmk_sched_bundle.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. * @@ -11,7 +11,7 @@ #include <stdbool.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -31,7 +31,7 @@ struct assign_data { * \return true (to indicate that any further replicas should be processed) */ static bool -assign_replica(pe__bundle_replica_t *replica, void *user_data) +assign_replica(pcmk__bundle_replica_t *replica, void *user_data) { pcmk_node_t *container_host = NULL; @@ -43,25 +43,25 @@ assign_replica(pe__bundle_replica_t *replica, void *user_data) true); if (replica->ip != NULL) { - pe_rsc_trace(bundle, "Assigning bundle %s IP %s", - bundle->id, replica->ip->id); + pcmk__rsc_trace(bundle, "Assigning bundle %s IP %s", + bundle->id, replica->ip->id); replica->ip->cmds->assign(replica->ip, prefer, stop_if_fail); } container_host = replica->container->allocated_to; if (replica->remote != NULL) { - if (pe__is_guest_or_remote_node(container_host)) { + if (pcmk__is_pacemaker_remote_node(container_host)) { /* REMOTE_CONTAINER_HACK: "Nested" connection resources must be on * the same host because Pacemaker Remote only supports a single * active connection. */ pcmk__new_colocation("#replica-remote-with-host-remote", NULL, - INFINITY, replica->remote, + PCMK_SCORE_INFINITY, replica->remote, container_host->details->remote_rsc, NULL, NULL, pcmk__coloc_influence); } - pe_rsc_trace(bundle, "Assigning bundle %s connection %s", - bundle->id, replica->remote->id); + pcmk__rsc_trace(bundle, "Assigning bundle %s connection %s", + bundle->id, replica->remote->id); replica->remote->cmds->assign(replica->remote, prefer, stop_if_fail); } @@ -71,19 +71,19 @@ assign_replica(pe__bundle_replica_t *replica, void *user_data) g_hash_table_iter_init(&iter, replica->child->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { - if (!pe__same_node(node, replica->node)) { - node->weight = -INFINITY; + if (!pcmk__same_node(node, replica->node)) { + node->weight = -PCMK_SCORE_INFINITY; } else if (!pcmk__threshold_reached(replica->child, node, NULL)) { - node->weight = INFINITY; + node->weight = PCMK_SCORE_INFINITY; } } - pe__set_resource_flags(replica->child->parent, pcmk_rsc_assigning); - pe_rsc_trace(bundle, "Assigning bundle %s replica child %s", - bundle->id, replica->child->id); + pcmk__set_rsc_flags(replica->child->parent, pcmk_rsc_assigning); + pcmk__rsc_trace(bundle, "Assigning bundle %s replica child %s", + bundle->id, replica->child->id); replica->child->cmds->assign(replica->child, replica->node, stop_if_fail); - pe__clear_resource_flags(replica->child->parent, pcmk_rsc_assigning); + pcmk__clear_rsc_flags(replica->child->parent, pcmk_rsc_assigning); } return true; } @@ -115,10 +115,10 @@ pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, pcmk_resource_t *bundled_resource = NULL; struct assign_data assign_data = { prefer, stop_if_fail }; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); - pe_rsc_trace(rsc, "Assigning bundle %s", rsc->id); - pe__set_resource_flags(rsc, pcmk_rsc_assigning); + pcmk__rsc_trace(rsc, "Assigning bundle %s", rsc->id); + pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, pcmk_sched_output_scores), @@ -144,13 +144,13 @@ pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, if (pe__node_is_bundle_instance(rsc, node)) { node->weight = 0; } else { - node->weight = -INFINITY; + node->weight = -PCMK_SCORE_INFINITY; } } bundled_resource->cmds->assign(bundled_resource, prefer, stop_if_fail); } - pe__clear_resource_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); return NULL; } @@ -164,7 +164,7 @@ pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, * \return true (to indicate that any further replicas should be processed) */ static bool -create_replica_actions(pe__bundle_replica_t *replica, void *user_data) +create_replica_actions(pcmk__bundle_replica_t *replica, void *user_data) { if (replica->ip != NULL) { replica->ip->cmds->create_actions(replica->ip); @@ -191,7 +191,7 @@ pcmk__bundle_create_actions(pcmk_resource_t *rsc) GList *containers = NULL; pcmk_resource_t *bundled_resource = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, create_replica_actions, NULL); @@ -207,12 +207,12 @@ pcmk__bundle_create_actions(pcmk_resource_t *rsc) pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true); action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED, true, true); - action->priority = INFINITY; + action->priority = PCMK_SCORE_INFINITY; pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true); action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED, true, true); - action->priority = INFINITY; + action->priority = PCMK_SCORE_INFINITY; } } } @@ -227,7 +227,7 @@ pcmk__bundle_create_actions(pcmk_resource_t *rsc) * \return true (to indicate that any further replicas should be processed) */ static bool -replica_internal_constraints(pe__bundle_replica_t *replica, void *user_data) +replica_internal_constraints(pcmk__bundle_replica_t *replica, void *user_data) { pcmk_resource_t *bundle = user_data; @@ -266,8 +266,8 @@ replica_internal_constraints(pe__bundle_replica_t *replica, void *user_data) pcmk__order_stops(replica->container, replica->ip, pcmk__ar_then_implies_first|pcmk__ar_guest_allowed); - pcmk__new_colocation("#ip-with-container", NULL, INFINITY, replica->ip, - replica->container, NULL, NULL, + pcmk__new_colocation("#ip-with-container", NULL, PCMK_SCORE_INFINITY, + replica->ip, replica->container, NULL, NULL, pcmk__coloc_influence); } @@ -298,7 +298,7 @@ pcmk__bundle_internal_constraints(pcmk_resource_t *rsc) { pcmk_resource_t *bundled_resource = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc); @@ -371,7 +371,7 @@ struct match_data { * should be processed), otherwise false */ static bool -match_replica_container(const pe__bundle_replica_t *replica, void *user_data) +match_replica_container(const pcmk__bundle_replica_t *replica, void *user_data) { struct match_data *match_data = user_data; @@ -395,7 +395,7 @@ match_replica_container(const pe__bundle_replica_t *replica, void *user_data) static const pcmk_node_t * get_bundle_node_host(const pcmk_node_t *node) { - if (pe__is_bundle_node(node)) { + if (pcmk__is_bundle_node(node)) { const pcmk_resource_t *container = node->details->remote_rsc->container; return container->fns->location(container, NULL, 0); @@ -466,12 +466,13 @@ struct coloc_data { * \return true (to indicate that any further replicas should be processed) */ static bool -replica_apply_coloc_score(const pe__bundle_replica_t *replica, void *user_data) +replica_apply_coloc_score(const pcmk__bundle_replica_t *replica, + void *user_data) { struct coloc_data *coloc_data = user_data; pcmk_node_t *chosen = NULL; - if (coloc_data->colocation->score < INFINITY) { + if (coloc_data->colocation->score < PCMK_SCORE_INFINITY) { replica->container->cmds->apply_coloc_score(coloc_data->dependent, replica->container, coloc_data->colocation, @@ -491,10 +492,10 @@ replica_apply_coloc_score(const pe__bundle_replica_t *replica, void *user_data) return true; } - pe_rsc_trace(pe__const_top_resource(replica->container, true), - "Allowing mandatory colocation %s using %s @%d", - coloc_data->colocation->id, pe__node_name(chosen), - chosen->weight); + pcmk__rsc_trace(pe__const_top_resource(replica->container, true), + "Allowing mandatory colocation %s using %s @%d", + coloc_data->colocation->id, pcmk__node_name(chosen), + chosen->weight); coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts, chosen); return true; @@ -525,22 +526,19 @@ pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, * Instead, we add its colocation constraints to its containers and bundled * primitive and call the apply_coloc_score() method for them as dependents. */ - CRM_ASSERT((primary != NULL) - && (primary->variant == pcmk_rsc_variant_bundle) - && (dependent != NULL) - && (dependent->variant == pcmk_rsc_variant_primitive) + CRM_ASSERT(pcmk__is_bundle(primary) && pcmk__is_primitive(dependent) && (colocation != NULL) && !for_dependent); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { - pe_rsc_trace(primary, - "Skipping applying colocation %s " - "because %s is still provisional", - colocation->id, primary->id); + pcmk__rsc_trace(primary, + "Skipping applying colocation %s " + "because %s is still provisional", + colocation->id, primary->id); return; } - pe_rsc_trace(primary, "Applying colocation %s (%s with %s at %s)", - colocation->id, dependent->id, primary->id, - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(primary, "Applying colocation %s (%s with %s at %s)", + colocation->id, dependent->id, primary->id, + pcmk_readable_score(colocation->score)); /* If the constraint dependent is a clone or bundle, "dependent" here is one * of its instances. Look for a compatible instance of this bundle. @@ -550,21 +548,22 @@ pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, primary_container = compatible_container(dependent, primary); if (primary_container != NULL) { // Success, we found one - pe_rsc_debug(primary, "Pairing %s with %s", - dependent->id, primary_container->id); + pcmk__rsc_debug(primary, "Pairing %s with %s", + dependent->id, primary_container->id); dependent->cmds->apply_coloc_score(dependent, primary_container, colocation, true); - } else if (colocation->score >= INFINITY) { // Failure, and it's fatal + } else if (colocation->score >= PCMK_SCORE_INFINITY) { + // Failure, and it's fatal crm_notice("%s cannot run because there is no compatible " "instance of %s to colocate with", dependent->id, primary->id); pcmk__assign_resource(dependent, NULL, true, true); } else { // Failure, but we can ignore it - pe_rsc_debug(primary, - "%s cannot be colocated with any instance of %s", - dependent->id, primary->id); + pcmk__rsc_debug(primary, + "%s cannot be colocated with any instance of %s", + dependent->id, primary->id); } return; } @@ -572,7 +571,7 @@ pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent, pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score, &coloc_data); - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { pcmk__colocation_intersect_nodes(dependent, primary, colocation, coloc_data.container_hosts, false); } @@ -586,8 +585,7 @@ pcmk__with_bundle_colocations(const pcmk_resource_t *rsc, { const pcmk_resource_t *bundled_rsc = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle) - && (orig_rsc != NULL) && (list != NULL)); + CRM_ASSERT(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL)); // The bundle itself and its containers always get its colocations if ((orig_rsc == rsc) @@ -631,8 +629,7 @@ pcmk__bundle_with_colocations(const pcmk_resource_t *rsc, { const pcmk_resource_t *bundled_rsc = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle) - && (orig_rsc != NULL) && (list != NULL)); + CRM_ASSERT(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL)); // The bundle itself and its containers always get its colocations if ((orig_rsc == rsc) @@ -685,8 +682,7 @@ pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node) uint32_t flags = 0; pcmk_resource_t *bundled_resource = NULL; - CRM_ASSERT((action != NULL) && (action->rsc != NULL) - && (action->rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT((action != NULL) && pcmk__is_bundle(action->rsc)); bundled_resource = pe__bundled_resource(action->rsc); if (bundled_resource != NULL) { @@ -723,9 +719,9 @@ pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node) * \return true (to indicate that any further replicas should be processed) */ static bool -apply_location_to_replica(pe__bundle_replica_t *replica, void *user_data) +apply_location_to_replica(pcmk__bundle_replica_t *replica, void *user_data) { - pe__location_t *location = user_data; + pcmk__location_t *location = user_data; if (replica->container != NULL) { replica->container->cmds->apply_location(replica->container, location); @@ -744,12 +740,11 @@ apply_location_to_replica(pe__bundle_replica_t *replica, void *user_data) * \param[in,out] location Location constraint to apply */ void -pcmk__bundle_apply_location(pcmk_resource_t *rsc, pe__location_t *location) +pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { pcmk_resource_t *bundled_resource = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle) - && (location != NULL)); + CRM_ASSERT((location != NULL) && pcmk__is_bundle(rsc)); pcmk__apply_location(rsc, location); pe__foreach_bundle_replica(rsc, apply_location_to_replica, location); @@ -764,7 +759,7 @@ pcmk__bundle_apply_location(pcmk_resource_t *rsc, pe__location_t *location) } } -#define XPATH_REMOTE "//nvpair[@name='" XML_RSC_ATTR_REMOTE_RA_ADDR "']" +#define XPATH_REMOTE "//nvpair[@name='" PCMK_REMOTE_RA_ADDR "']" /*! * \internal @@ -776,7 +771,7 @@ pcmk__bundle_apply_location(pcmk_resource_t *rsc, pe__location_t *location) * \return true (to indicate that any further replicas should be processed) */ static bool -add_replica_actions_to_graph(pe__bundle_replica_t *replica, void *user_data) +add_replica_actions_to_graph(pcmk__bundle_replica_t *replica, void *user_data) { if ((replica->remote != NULL) && (replica->container != NULL) && pe__bundle_needs_remote_name(replica->remote)) { @@ -792,9 +787,8 @@ add_replica_actions_to_graph(pe__bundle_replica_t *replica, void *user_data) const char *calculated_addr = NULL; // Replace the value in replica->remote->xml (if appropriate) - calculated_addr = pe__add_bundle_remote_name(replica->remote, - replica->remote->cluster, - nvpair, "value"); + calculated_addr = pe__add_bundle_remote_name(replica->remote, nvpair, + PCMK_XA_VALUE); if (calculated_addr != NULL) { /* Since this is for the bundle as a resource, and not any * particular action, replace the value in the default @@ -805,9 +799,7 @@ add_replica_actions_to_graph(pe__bundle_replica_t *replica, void *user_data) GHashTable *params = pe_rsc_params(replica->remote, NULL, replica->remote->cluster); - g_hash_table_replace(params, - strdup(XML_RSC_ATTR_REMOTE_RA_ADDR), - strdup(calculated_addr)); + pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, calculated_addr); } else { pcmk_resource_t *bundle = user_data; @@ -818,9 +810,9 @@ add_replica_actions_to_graph(pe__bundle_replica_t *replica, void *user_data) * unpacking status, promote, and migrate_from history, but * that's already happened by this point). */ - pe_rsc_info(bundle, - "Unable to determine address for bundle %s " - "remote connection", bundle->id); + pcmk__rsc_info(bundle, + "Unable to determine address for bundle %s " + "remote connection", bundle->id); } } if (replica->ip != NULL) { @@ -846,7 +838,7 @@ pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc) { pcmk_resource_t *bundled_resource = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); bundled_resource = pe__bundled_resource(rsc); if (bundled_resource != NULL) { @@ -871,9 +863,9 @@ struct probe_data { * \return true (to indicate that any further replicas should be processed) */ static bool -order_replica_start_after(pe__bundle_replica_t *replica, void *user_data) +order_replica_start_after(pcmk__bundle_replica_t *replica, void *user_data) { - pe__bundle_replica_t *probed_replica = user_data; + pcmk__bundle_replica_t *probed_replica = user_data; if ((replica == probed_replica) || (replica->container == NULL)) { return true; @@ -899,7 +891,7 @@ order_replica_start_after(pe__bundle_replica_t *replica, void *user_data) * \return true (to indicate that any further replicas should be processed) */ static bool -create_replica_probes(pe__bundle_replica_t *replica, void *user_data) +create_replica_probes(pcmk__bundle_replica_t *replica, void *user_data) { struct probe_data *probe_data = user_data; @@ -908,7 +900,7 @@ create_replica_probes(pe__bundle_replica_t *replica, void *user_data) probe_data->any_created = true; } if ((replica->child != NULL) - && pe__same_node(probe_data->node, replica->node) + && pcmk__same_node(probe_data->node, replica->node) && replica->child->cmds->create_probe(replica->child, probe_data->node)) { probe_data->any_created = true; @@ -951,8 +943,9 @@ create_replica_probes(pe__bundle_replica_t *replica, void *user_data) free(probe_uuid); if (probe != NULL) { probe_data->any_created = true; - pe_rsc_trace(probe_data->bundle, "Ordering %s probe on %s", - replica->remote->id, pe__node_name(probe_data->node)); + pcmk__rsc_trace(probe_data->bundle, "Ordering %s probe on %s", + replica->remote->id, + pcmk__node_name(probe_data->node)); pcmk__new_ordering(replica->container, pcmk__op_key(replica->container->id, PCMK_ACTION_START, 0), @@ -979,7 +972,7 @@ pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) { struct probe_data probe_data = { rsc, node, false }; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data); return probe_data.any_created; } @@ -994,7 +987,7 @@ pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) * \return true (to indicate that any further replicas should be processed) */ static bool -output_replica_actions(pe__bundle_replica_t *replica, void *user_data) +output_replica_actions(pcmk__bundle_replica_t *replica, void *user_data) { if (replica->ip != NULL) { replica->ip->cmds->output_actions(replica->ip); @@ -1020,7 +1013,7 @@ output_replica_actions(pe__bundle_replica_t *replica, void *user_data) void pcmk__output_bundle_actions(pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); pe__foreach_bundle_replica(rsc, output_replica_actions, NULL); } @@ -1032,7 +1025,7 @@ pcmk__bundle_add_utilization(const pcmk_resource_t *rsc, { pcmk_resource_t *container = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; @@ -1053,6 +1046,6 @@ pcmk__bundle_add_utilization(const pcmk_resource_t *rsc, void pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)); + CRM_ASSERT(pcmk__is_bundle(rsc)); // Bundles currently don't support shutdown locks } diff --git a/lib/pacemaker/pcmk_sched_clone.c b/lib/pacemaker/pcmk_sched_clone.c index 7b422d8..dacee7f 100644 --- a/lib/pacemaker/pcmk_sched_clone.c +++ b/lib/pacemaker/pcmk_sched_clone.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,7 +9,7 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -39,7 +39,7 @@ pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, { GList *colocations = NULL; - CRM_ASSERT(pe_rsc_is_clone(rsc)); + CRM_ASSERT(pcmk__is_clone(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return NULL; // Assignment has already been done @@ -47,10 +47,10 @@ pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, // Detect assignment loops if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { - pe_rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); + pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); return NULL; } - pe__set_resource_flags(rsc, pcmk_rsc_assigning); + pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); // If this clone is promotable, consider nodes' promotion scores if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { @@ -62,8 +62,8 @@ pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, for (GList *iter = colocations; iter != NULL; iter = iter->next) { pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data; - pe_rsc_trace(rsc, "%s: Assigning colocation %s primary %s first", - rsc->id, constraint->id, constraint->primary->id); + pcmk__rsc_trace(rsc, "%s: Assigning colocation %s primary %s first", + rsc->id, constraint->id, constraint->primary->id); constraint->primary->cmds->assign(constraint->primary, prefer, stop_if_fail); } @@ -86,8 +86,8 @@ pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, pcmk__set_instance_roles(rsc); } - pe__clear_resource_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning); - pe_rsc_trace(rsc, "Assigned clone %s", rsc->id); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning); + pcmk__rsc_trace(rsc, "Assigned clone %s", rsc->id); return NULL; } @@ -100,9 +100,9 @@ pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, void pcmk__clone_create_actions(pcmk_resource_t *rsc) { - CRM_ASSERT(pe_rsc_is_clone(rsc)); + CRM_ASSERT(pcmk__is_clone(rsc)); - pe_rsc_trace(rsc, "Creating actions for clone %s", rsc->id); + pcmk__rsc_trace(rsc, "Creating actions for clone %s", rsc->id); pcmk__create_instance_actions(rsc, rsc->children); if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { pcmk__create_promotable_actions(rsc); @@ -120,9 +120,9 @@ pcmk__clone_internal_constraints(pcmk_resource_t *rsc) { bool ordered = false; - CRM_ASSERT(pe_rsc_is_clone(rsc)); + CRM_ASSERT(pcmk__is_clone(rsc)); - pe_rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id); + pcmk__rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id); // Restart ordering: Stop -> stopped -> start -> started pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED, @@ -210,7 +210,7 @@ can_interleave(const pcmk__colocation_t *colocation) // Only the dependent needs to be marked for interleaving if (!crm_is_true(g_hash_table_lookup(dependent->meta, - XML_RSC_ATTR_INTERLEAVE))) { + PCMK_META_INTERLEAVE))) { return false; } @@ -255,21 +255,20 @@ pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, */ CRM_ASSERT(!for_dependent); - CRM_ASSERT((colocation != NULL) && pe_rsc_is_clone(primary) - && (dependent != NULL) - && (dependent->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT((colocation != NULL) && pcmk__is_clone(primary) + && pcmk__is_primitive(dependent)); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { - pe_rsc_trace(primary, - "Delaying processing colocation %s " - "because cloned primary %s is still provisional", - colocation->id, primary->id); + pcmk__rsc_trace(primary, + "Delaying processing colocation %s " + "because cloned primary %s is still provisional", + colocation->id, primary->id); return; } - pe_rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)", - colocation->id, dependent->id, primary->id, - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)", + colocation->id, dependent->id, primary->id, + pcmk_readable_score(colocation->score)); // Apply role-specific colocations if (pcmk_is_set(primary->flags, pcmk_rsc_promotable) @@ -298,28 +297,28 @@ pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, pcmk_role_unknown, false); if (primary_instance != NULL) { - pe_rsc_debug(primary, "Interleaving %s with %s", - dependent->id, primary_instance->id); + pcmk__rsc_debug(primary, "Interleaving %s with %s", + dependent->id, primary_instance->id); dependent->cmds->apply_coloc_score(dependent, primary_instance, colocation, true); - } else if (colocation->score >= INFINITY) { + } else if (colocation->score >= PCMK_SCORE_INFINITY) { crm_notice("%s cannot run because it cannot interleave with " "any instance of %s", dependent->id, primary->id); pcmk__assign_resource(dependent, NULL, true, true); } else { - pe_rsc_debug(primary, - "%s will not colocate with %s " - "because no instance can interleave with it", - dependent->id, primary->id); + pcmk__rsc_debug(primary, + "%s will not colocate with %s " + "because no instance can interleave with it", + dependent->id, primary->id); } return; } // Apply mandatory colocations - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { GList *primary_nodes = NULL; // Dependent can run only where primary will have unblocked instances @@ -329,9 +328,9 @@ pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent, if ((chosen != NULL) && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) { - pe_rsc_trace(primary, "Allowing %s: %s %d", - colocation->id, pe__node_name(chosen), - chosen->weight); + pcmk__rsc_trace(primary, "Allowing %s: %s %d", + colocation->id, pcmk__node_name(chosen), + chosen->weight); primary_nodes = g_list_prepend(primary_nodes, chosen); } } @@ -390,7 +389,7 @@ pcmk__clone_with_colocations(const pcmk_resource_t *rsc, uint32_t pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node) { - CRM_ASSERT((action != NULL) && pe_rsc_is_clone(action->rsc)); + CRM_ASSERT((action != NULL) && pcmk__is_clone(action->rsc)); return pcmk__collective_action_flags(action, action->rsc->children, node); } @@ -403,9 +402,9 @@ pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node) * \param[in,out] location Location constraint to apply */ void -pcmk__clone_apply_location(pcmk_resource_t *rsc, pe__location_t *location) +pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { - CRM_CHECK((location != NULL) && pe_rsc_is_clone(rsc), return); + CRM_CHECK((location != NULL) && pcmk__is_clone(rsc), return); pcmk__apply_location(rsc, location); @@ -434,7 +433,7 @@ call_action_flags(gpointer data, gpointer user_data) void pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc) { - CRM_ASSERT(pe_rsc_is_clone(rsc)); + CRM_ASSERT(pcmk__is_clone(rsc)); g_list_foreach(rsc->actions, call_action_flags, rsc); pe__create_clone_notifications(rsc); @@ -481,7 +480,7 @@ rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node) g_hash_table_iter_init(&iter, rsc->known_on); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) { - if (pe__same_node(node, known_node)) { + if (pcmk__same_node(node, known_node)) { return true; } } @@ -532,7 +531,7 @@ probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node) const pcmk_node_t *instance_node = NULL; instance_node = instance->fns->location(instance, NULL, 0); - if (pe__same_node(instance_node, node)) { + if (pcmk__same_node(instance_node, node)) { child = instance; } } @@ -558,11 +557,11 @@ probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node) bool pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) { - CRM_ASSERT((node != NULL) && pe_rsc_is_clone(rsc)); + CRM_ASSERT((node != NULL) && pcmk__is_clone(rsc)); if (rsc->exclusive_discover) { /* The clone is configured to be probed only where a location constraint - * exists with resource-discovery set to exclusive. + * exists with PCMK_XA_RESOURCE_DISCOVERY set to exclusive. * * This check is not strictly necessary here since the instance's * create_probe() method would also check, but doing it here is more @@ -579,10 +578,10 @@ pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node) * allowed_nodes so that notifications contain only nodes that the * clone can possibly run on. */ - pe_rsc_trace(rsc, - "Skipping probe for %s on %s because resource has " - "exclusive discovery but is not allowed on node", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "Skipping probe for %s on %s because resource has " + "exclusive discovery but is not allowed on node", + rsc->id, pcmk__node_name(node)); g_hash_table_remove(rsc->allowed_nodes, node->details->id); return false; } @@ -610,14 +609,14 @@ pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) { char *name = NULL; - CRM_ASSERT(pe_rsc_is_clone(rsc) && (xml != NULL)); + CRM_ASSERT(pcmk__is_clone(rsc) && (xml != NULL)); - name = crm_meta_name(XML_RSC_ATTR_UNIQUE); - crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pcmk_rsc_unique)); + name = crm_meta_name(PCMK_META_GLOBALLY_UNIQUE); + crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); free(name); - name = crm_meta_name(XML_RSC_ATTR_NOTIFY); - crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pcmk_rsc_notify)); + name = crm_meta_name(PCMK_META_NOTIFY); + crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_notify)); free(name); name = crm_meta_name(PCMK_META_CLONE_MAX); @@ -643,11 +642,11 @@ pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) /* @COMPAT Maintain backward compatibility with resource agents that * expect the old names (deprecated since 2.0.0). */ - name = crm_meta_name(PCMK_XA_PROMOTED_MAX_LEGACY); + name = crm_meta_name(PCMK__META_PROMOTED_MAX_LEGACY); crm_xml_add_int(xml, name, promoted_max); free(name); - name = crm_meta_name(PCMK_XA_PROMOTED_NODE_MAX_LEGACY); + name = crm_meta_name(PCMK__META_PROMOTED_NODE_MAX_LEGACY); crm_xml_add_int(xml, name, promoted_node_max); free(name); } @@ -662,7 +661,7 @@ pcmk__clone_add_utilization(const pcmk_resource_t *rsc, bool existing = false; pcmk_resource_t *child = NULL; - CRM_ASSERT(pe_rsc_is_clone(rsc) && (orig_rsc != NULL) + CRM_ASSERT(pcmk__is_clone(rsc) && (orig_rsc != NULL) && (utilization != NULL)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { @@ -704,6 +703,6 @@ pcmk__clone_add_utilization(const pcmk_resource_t *rsc, void pcmk__clone_shutdown_lock(pcmk_resource_t *rsc) { - CRM_ASSERT(pe_rsc_is_clone(rsc)); + CRM_ASSERT(pcmk__is_clone(rsc)); return; // Clones currently don't support shutdown locks } diff --git a/lib/pacemaker/pcmk_sched_colocation.c b/lib/pacemaker/pcmk_sched_colocation.c index 733d70a..5c16cff 100644 --- a/lib/pacemaker/pcmk_sched_colocation.c +++ b/lib/pacemaker/pcmk_sched_colocation.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/util.h" #include "crm/common/xml_internal.h" -#include "crm/msg_xml.h" +#include "crm/common/xml.h" #include "libpacemaker_private.h" // Used to temporarily mark a node as unusable -#define INFINITY_HACK (INFINITY * -100) +#define INFINITY_HACK (PCMK_SCORE_INFINITY * -100) /*! * \internal @@ -92,7 +92,7 @@ cmp_colocation_priority(const pcmk__colocation_t *colocation1, * clones (probably unnecessary, but avoids having to update regression * tests) */ - if (rsc1->variant == pcmk_rsc_variant_clone) { + if (pcmk__is_clone(rsc1)) { if (pcmk_is_set(rsc1->flags, pcmk_rsc_promotable) && !pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) { return -1; @@ -175,12 +175,12 @@ pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, { CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL)); - pe_rsc_trace(rsc, - "Adding colocation %s (%s with %s using %s @%s) to " - "'this with' list for %s", - colocation->id, colocation->dependent->id, - colocation->primary->id, colocation->node_attribute, - pcmk_readable_score(colocation->score), rsc->id); + pcmk__rsc_trace(rsc, + "Adding colocation %s (%s with %s using %s @%s) to " + "'this with' list for %s", + colocation->id, colocation->dependent->id, + colocation->primary->id, colocation->node_attribute, + pcmk_readable_score(colocation->score), rsc->id); *list = g_list_insert_sorted(*list, (gpointer) colocation, cmp_primary_priority); } @@ -235,12 +235,12 @@ pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, { CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL)); - pe_rsc_trace(rsc, - "Adding colocation %s (%s with %s using %s @%s) to " - "'with this' list for %s", - colocation->id, colocation->dependent->id, - colocation->primary->id, colocation->node_attribute, - pcmk_readable_score(colocation->score), rsc->id); + pcmk__rsc_trace(rsc, + "Adding colocation %s (%s with %s using %s @%s) to " + "'with this' list for %s", + colocation->id, colocation->dependent->id, + colocation->primary->id, colocation->node_attribute, + pcmk_readable_score(colocation->score), rsc->id); *list = g_list_insert_sorted(*list, (gpointer) colocation, cmp_dependent_priority); } @@ -361,21 +361,20 @@ pcmk__new_colocation(const char *id, const char *node_attr, int score, } if (score == 0) { - pe_rsc_trace(dependent, - "Ignoring colocation '%s' (%s with %s) because score is 0", - id, dependent->id, primary->id); + pcmk__rsc_trace(dependent, + "Ignoring colocation '%s' (%s with %s) because score is 0", + id, dependent->id, primary->id); return; } - new_con = calloc(1, sizeof(pcmk__colocation_t)); - CRM_ASSERT(new_con != NULL); + new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t)); - if (pcmk__str_eq(dependent_role, PCMK__ROLE_STARTED, + if (pcmk__str_eq(dependent_role, PCMK_ROLE_STARTED, pcmk__str_null_matches|pcmk__str_casei)) { dependent_role = PCMK__ROLE_UNKNOWN; } - if (pcmk__str_eq(primary_role, PCMK__ROLE_STARTED, + if (pcmk__str_eq(primary_role, PCMK_ROLE_STARTED, pcmk__str_null_matches|pcmk__str_casei)) { primary_role = PCMK__ROLE_UNKNOWN; } @@ -384,8 +383,8 @@ pcmk__new_colocation(const char *id, const char *node_attr, int score, new_con->dependent = dependent; new_con->primary = primary; new_con->score = score; - new_con->dependent_role = text2role(dependent_role); - new_con->primary_role = text2role(primary_role); + new_con->dependent_role = pcmk_parse_role(dependent_role); + new_con->primary_role = pcmk_parse_role(primary_role); new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME); new_con->flags = flags; @@ -395,7 +394,7 @@ pcmk__new_colocation(const char *id, const char *node_attr, int score, dependent->cluster->colocation_constraints = g_list_prepend( dependent->cluster->colocation_constraints, new_con); - if (score <= -INFINITY) { + if (score <= -PCMK_SCORE_INFINITY) { anti_colocation_order(dependent, new_con->dependent_role, primary, new_con->primary_role); anti_colocation_order(primary, new_con->primary_role, dependent, @@ -409,11 +408,11 @@ pcmk__new_colocation(const char *id, const char *node_attr, int score, * * \param[in] coloc_id Colocation XML ID (for error logging) * \param[in] rsc Resource involved in constraint (for default) - * \param[in] influence_s String value of influence option + * \param[in] influence_s String value of \c PCMK_XA_INFLUENCE option * - * \return pcmk__coloc_influence if string evaluates true, or string is NULL or - * invalid and resource's critical option evaluates true, otherwise - * pcmk__coloc_none + * \return \c pcmk__coloc_influence if string evaluates true, or string is + * \c NULL or invalid and resource's \c PCMK_META_CRITICAL option + * evaluates true, otherwise \c pcmk__coloc_none */ static uint32_t unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc, @@ -424,7 +423,7 @@ unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc, if (crm_str_to_boolean(influence_s, &influence_i) < 0) { pcmk__config_err("Constraint '%s' has invalid value for " - XML_COLOC_ATTR_INFLUENCE " (using default)", + PCMK_XA_INFLUENCE " (using default)", coloc_id); } else { return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence; @@ -443,14 +442,14 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, xmlNode *xml_rsc = NULL; pcmk_resource_t *other = NULL; pcmk_resource_t *resource = NULL; - const char *set_id = ID(set); - const char *role = crm_element_value(set, "role"); + const char *set_id = pcmk__xe_id(set); + const char *role = crm_element_value(set, PCMK_XA_ROLE); bool with_previous = false; int local_score = score; bool sequential = false; uint32_t flags = pcmk__coloc_none; const char *xml_rsc_id = NULL; - const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE); + const char *score_s = crm_element_value(set, PCMK_XA_SCORE); if (score_s) { local_score = char2score(score_s); @@ -461,29 +460,35 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, return; } - /* @COMPAT The deprecated "ordering" attribute specifies whether resources - * in a positive-score set are colocated with the previous or next resource. + /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether + * resources in a positive-score set are colocated with the previous or next + * resource. */ - if (pcmk__str_eq(crm_element_value(set, "ordering"), "group", + if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING), + PCMK__VALUE_GROUP, pcmk__str_null_matches|pcmk__str_casei)) { with_previous = true; } else { - pe_warn_once(pcmk__wo_set_ordering, - "Support for 'ordering' other than 'group' in " - XML_CONS_TAG_RSC_SET " (such as %s) is deprecated and " - "will be removed in a future release", set_id); + pcmk__warn_once(pcmk__wo_set_ordering, + "Support for '" PCMK__XA_ORDERING "' other than" + " '" PCMK__VALUE_GROUP "' in " PCMK_XE_RESOURCE_SET + " (such as %s) is deprecated and will be removed in a" + " future release", + set_id); } - if ((pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok) + if ((pcmk__xe_get_bool_attr(set, PCMK_XA_SEQUENTIAL, + &sequential) == pcmk_rc_ok) && !sequential) { return; } if (local_score > 0) { - for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); resource = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (resource == NULL) { @@ -496,13 +501,13 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, flags = pcmk__coloc_explicit | unpack_influence(coloc_id, resource, influence_s); if (with_previous) { - pe_rsc_trace(resource, "Colocating %s with %s in set %s", - resource->id, other->id, set_id); + pcmk__rsc_trace(resource, "Colocating %s with %s in set %s", + resource->id, other->id, set_id); pcmk__new_colocation(set_id, NULL, local_score, resource, other, role, role, flags); } else { - pe_rsc_trace(resource, "Colocating %s with %s in set %s", - other->id, resource->id, set_id); + pcmk__rsc_trace(resource, "Colocating %s with %s in set %s", + other->id, resource->id, set_id); pcmk__new_colocation(set_id, NULL, local_score, other, resource, role, role, flags); } @@ -516,12 +521,13 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, * (i.e. that no one in the set can run with anyone else in the set) */ - for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xmlNode *xml_rsc_with = NULL; - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); resource = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (resource == NULL) { @@ -532,11 +538,12 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, } flags = pcmk__coloc_explicit | unpack_influence(coloc_id, resource, influence_s); - for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF); + for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, + NULL, NULL); xml_rsc_with != NULL; - xml_rsc_with = crm_next_same_xml(xml_rsc_with)) { + xml_rsc_with = pcmk__xe_next_same(xml_rsc_with)) { - xml_rsc_id = ID(xml_rsc_with); + xml_rsc_id = pcmk__xe_id(xml_rsc_with); if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) { break; } @@ -558,7 +565,8 @@ unpack_colocation_set(xmlNode *set, int score, const char *coloc_id, * \param[in] set1 Dependent set * \param[in] set2 Primary set * \param[in] score Colocation score - * \param[in] influence_s Value of colocation's "influence" attribute + * \param[in] influence_s Value of colocation's \c PCMK_XA_INFLUENCE + * attribute * \param[in,out] scheduler Scheduler data */ static void @@ -571,8 +579,8 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, pcmk_resource_t *rsc_2 = NULL; const char *xml_rsc_id = NULL; - const char *role_1 = crm_element_value(set1, "role"); - const char *role_2 = crm_element_value(set2, "role"); + const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE); + const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE); int rc = pcmk_rc_ok; bool sequential = false; @@ -580,35 +588,38 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, if (score == 0) { crm_trace("Ignoring colocation '%s' between sets %s and %s " - "because score is 0", id, ID(set1), ID(set2)); + "because score is 0", + id, pcmk__xe_id(set1), pcmk__xe_id(set2)); return; } - rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential); + rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential); if ((rc != pcmk_rc_ok) || sequential) { // Get the first one - xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); + xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL); if (xml_rsc != NULL) { - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s with set %s " "because first resource %s not found", - ID(set1), ID(set2), xml_rsc_id); + pcmk__xe_id(set1), pcmk__xe_id(set2), + xml_rsc_id); return; } } } - rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential); + rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential); if ((rc != pcmk_rc_ok) || sequential) { // Get the last one - for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); } rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); @@ -616,7 +627,7 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s with set %s " "because last resource %s not found", - ID(set1), ID(set2), xml_rsc_id); + pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id); return; } } @@ -628,17 +639,19 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, } else if (rsc_1 != NULL) { // Only set1 is sequential flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); - for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_2 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring set %s colocation with resource %s " "in set %s: No such resource", - ID(set1), xml_rsc_id, ID(set2)); + pcmk__xe_id(set1), xml_rsc_id, + pcmk__xe_id(set2)); continue; } pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, @@ -646,17 +659,19 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, } } else if (rsc_2 != NULL) { // Only set2 is sequential - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource %s " "with set %s: No such resource", - ID(set1), xml_rsc_id, ID(set2)); + pcmk__xe_id(set1), xml_rsc_id, + pcmk__xe_id(set2)); continue; } flags = pcmk__coloc_explicit @@ -666,37 +681,40 @@ colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, } } else { // Neither set is sequential - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { xmlNode *xml_rsc_2 = NULL; - xml_rsc_id = ID(xml_rsc); + xml_rsc_id = pcmk__xe_id(xml_rsc); rsc_1 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_1 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource %s " "with set %s: No such resource", - ID(set1), xml_rsc_id, ID(set2)); + pcmk__xe_id(set1), xml_rsc_id, + pcmk__xe_id(set2)); continue; } flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s); - for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc_2 != NULL; - xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) { + for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) { - xml_rsc_id = ID(xml_rsc_2); + xml_rsc_id = pcmk__xe_id(xml_rsc_2); rsc_2 = pcmk__find_constraint_resource(scheduler->resources, xml_rsc_id); if (rsc_2 == NULL) { // Should be possible only with validation disabled pcmk__config_err("Ignoring colocation of set %s resource " "%s with set %s resource %s: No such " - "resource", ID(set1), ID(xml_rsc), - ID(set2), xml_rsc_id); + "resource", + pcmk__xe_id(set1), pcmk__xe_id(xml_rsc), + pcmk__xe_id(set2), xml_rsc_id); continue; } pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, @@ -713,15 +731,13 @@ unpack_simple_colocation(xmlNode *xml_obj, const char *id, int score_i = 0; uint32_t flags = pcmk__coloc_none; - const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); - const char *dependent_id = crm_element_value(xml_obj, - XML_COLOC_ATTR_SOURCE); - const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET); - const char *dependent_role = crm_element_value(xml_obj, - XML_COLOC_ATTR_SOURCE_ROLE); + const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE); + const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC); + const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC); + const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); const char *primary_role = crm_element_value(xml_obj, - XML_COLOC_ATTR_TARGET_ROLE); - const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR); + PCMK_XA_WITH_RSC_ROLE); + const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE); const char *primary_instance = NULL; const char *dependent_instance = NULL; @@ -733,19 +749,17 @@ unpack_simple_colocation(xmlNode *xml_obj, const char *id, dependent_id); // @COMPAT: Deprecated since 2.1.5 - primary_instance = crm_element_value(xml_obj, - XML_COLOC_ATTR_TARGET_INSTANCE); - dependent_instance = crm_element_value(xml_obj, - XML_COLOC_ATTR_SOURCE_INSTANCE); + primary_instance = crm_element_value(xml_obj, PCMK__XA_WITH_RSC_INSTANCE); + dependent_instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE); if (dependent_instance != NULL) { - pe_warn_once(pcmk__wo_coloc_inst, - "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is " - "deprecated and will be removed in a future release."); + pcmk__warn_once(pcmk__wo_coloc_inst, + "Support for " PCMK__XA_RSC_INSTANCE " is deprecated " + "and will be removed in a future release"); } if (primary_instance != NULL) { - pe_warn_once(pcmk__wo_coloc_inst, - "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is " - "deprecated and will be removed in a future release."); + pcmk__warn_once(pcmk__wo_coloc_inst, + "Support for " PCMK__XA_WITH_RSC_INSTANCE " is " + "deprecated and will be removed in a future release"); } if (dependent == NULL) { @@ -758,13 +772,13 @@ unpack_simple_colocation(xmlNode *xml_obj, const char *id, "does not exist", id, primary_id); return; - } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) { + } else if ((dependent_instance != NULL) && !pcmk__is_clone(dependent)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", id, dependent_id, dependent_instance); return; - } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) { + } else if ((primary_instance != NULL) && !pcmk__is_clone(primary)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", id, primary_id, primary_instance); @@ -791,10 +805,10 @@ unpack_simple_colocation(xmlNode *xml_obj, const char *id, } } - if (pcmk__xe_attr_is_true(xml_obj, XML_CONS_ATTR_SYMMETRICAL)) { - pcmk__config_warn("The colocation constraint '" - XML_CONS_ATTR_SYMMETRICAL - "' attribute has been removed"); + if (pcmk__xe_attr_is_true(xml_obj, PCMK_XA_SYMMETRICAL)) { + pcmk__config_warn("The colocation constraint " + "'" PCMK_XA_SYMMETRICAL "' attribute has been " + "removed"); } if (score) { @@ -831,9 +845,9 @@ unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, CRM_CHECK(xml_obj != NULL, return EINVAL); - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return pcmk_rc_unpack_error; } @@ -841,12 +855,12 @@ unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, // Check whether there are any resource sets with template or tag references *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler); if (*expanded_xml != NULL) { - crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION); return pcmk_rc_ok; } - dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE); - primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET); + dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC); + primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC); if ((dependent_id == NULL) || (primary_id == NULL)) { return pcmk_rc_ok; } @@ -877,14 +891,16 @@ unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, return pcmk_rc_unpack_error; } - dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE); - primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE); + dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); + primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE); - *expanded_xml = copy_xml(xml_obj); + *expanded_xml = pcmk__xml_copy(NULL, xml_obj); - // Convert dependent's template/tag reference into constraint resource_set - if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE, - true, scheduler)) { + /* Convert dependent's template/tag reference into constraint + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true, + scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; @@ -892,16 +908,20 @@ unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (dependent_set != NULL) { if (dependent_role != NULL) { - // Move "rsc-role" into converted resource_set as "role" - crm_xml_add(dependent_set, "role", dependent_role); - xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE); + /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as + * PCMK_XA_ROLE + */ + crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE); } any_sets = true; } - // Convert primary's template/tag reference into constraint resource_set - if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET, - true, scheduler)) { + /* Convert primary's template/tag reference into constraint + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true, + scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; @@ -909,15 +929,17 @@ unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (primary_set != NULL) { if (primary_role != NULL) { - // Move "with-rsc-role" into converted resource_set as "role" - crm_xml_add(primary_set, "role", primary_role); - xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_TARGET_ROLE); + /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as + * PCMK_XA_ROLE + */ + crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_WITH_RSC_ROLE); } any_sets = true; } if (any_sets) { - crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION); } else { free_xml(*expanded_xml); *expanded_xml = NULL; @@ -943,12 +965,12 @@ pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) xmlNode *orig_xml = NULL; xmlNode *expanded_xml = NULL; - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *id = crm_element_value(xml_obj, PCMK_XA_ID); const char *score = NULL; const char *influence_s = NULL; if (pcmk__str_empty(id)) { - pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_DEPEND + pcmk__config_err("Ignoring " PCMK_XE_RSC_COLOCATION " without " CRM_ATTR_ID); return; } @@ -962,14 +984,14 @@ pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) xml_obj = expanded_xml; } - score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + score = crm_element_value(xml_obj, PCMK_XA_SCORE); if (score != NULL) { score_i = char2score(score); } - influence_s = crm_element_value(xml_obj, XML_COLOC_ATTR_INFLUENCE); + influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE); - for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL; - set = crm_next_same_xml(set)) { + for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL); + set != NULL; set = pcmk__xe_next_same(set)) { set = expand_idref(set, scheduler->input); if (set == NULL) { // Configuration error, message already logged @@ -979,8 +1001,8 @@ pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) return; } - if (pcmk__str_empty(ID(set))) { - pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET + if (pcmk__str_empty(pcmk__xe_id(set))) { + pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET " without " CRM_ATTR_ID); continue; } @@ -1023,7 +1045,7 @@ mark_action_blocked(pcmk_resource_t *rsc, const char *task, if (pcmk_is_set(action->flags, pcmk_action_runnable) && pcmk__str_eq(action->task, task, pcmk__str_none)) { - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__clear_action_flags(action, pcmk_action_runnable); pe_action_set_reason(action, reason_text, false); pcmk__block_colocation_dependents(action); pcmk__update_action_for_orderings(action, rsc->cluster); @@ -1099,7 +1121,7 @@ pcmk__block_colocation_dependents(pcmk_action_t *action) for (iter = colocations; iter != NULL; iter = iter->next) { pcmk__colocation_t *colocation = iter->data; - if (colocation->score < INFINITY) { + if (colocation->score < PCMK_SCORE_INFINITY) { continue; // Only mandatory colocations block dependent } @@ -1217,24 +1239,25 @@ pcmk__colocation_affects(const pcmk_resource_t *dependent, crm_trace("Skipping colocation '%s': %s will not run anywhere", colocation->id, dependent->id); - } else if (colocation->score >= INFINITY) { + } else if (colocation->score >= PCMK_SCORE_INFINITY) { // Dependent resource must colocate with primary resource - if (!pe__same_node(primary_node, dependent->allocated_to)) { - crm_err("%s must be colocated with %s but is not (%s vs. %s)", - dependent->id, primary->id, - pe__node_name(dependent->allocated_to), - pe__node_name(primary_node)); + if (!pcmk__same_node(primary_node, dependent->allocated_to)) { + pcmk__sched_err("%s must be colocated with %s but is not " + "(%s vs. %s)", + dependent->id, primary->id, + pcmk__node_name(dependent->allocated_to), + pcmk__node_name(primary_node)); } - } else if (colocation->score <= -CRM_SCORE_INFINITY) { + } else if (colocation->score <= -PCMK_SCORE_INFINITY) { // Dependent resource must anti-colocate with primary resource - if (pe__same_node(dependent->allocated_to, primary_node)) { - crm_err("%s and %s must be anti-colocated but are assigned " - "to the same node (%s)", - dependent->id, primary->id, - pe__node_name(primary_node)); + if (pcmk__same_node(dependent->allocated_to, primary_node)) { + pcmk__sched_err("%s and %s must be anti-colocated but are " + "assigned to the same node (%s)", + dependent->id, primary->id, + pcmk__node_name(primary_node)); } } return pcmk__coloc_affects_nothing; @@ -1246,9 +1269,9 @@ pcmk__colocation_affects(const pcmk_resource_t *dependent, "but %s next role is %s", ((colocation->score < 0)? "anti-" : ""), - colocation->id, role2text(colocation->dependent_role), + colocation->id, pcmk_role_text(colocation->dependent_role), dependent_role_rsc->id, - role2text(dependent_role_rsc->next_role)); + pcmk_role_text(dependent_role_rsc->next_role)); return pcmk__coloc_affects_nothing; } @@ -1257,8 +1280,9 @@ pcmk__colocation_affects(const pcmk_resource_t *dependent, crm_trace("Skipping %scolocation '%s': primary limited to %s role " "but %s next role is %s", ((colocation->score < 0)? "anti-" : ""), - colocation->id, role2text(colocation->primary_role), - primary_role_rsc->id, role2text(primary_role_rsc->next_role)); + colocation->id, pcmk_role_text(colocation->primary_role), + primary_role_rsc->id, + pcmk_role_text(primary_role_rsc->next_role)); return pcmk__coloc_affects_nothing; } @@ -1302,12 +1326,13 @@ pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { if (primary->allocated_to == NULL) { node->weight = pcmk__add_scores(-colocation->score, node->weight); - pe_rsc_trace(dependent, - "Applied %s to %s score on %s (now %s after " - "subtracting %s because primary %s inactive)", - colocation->id, dependent->id, pe__node_name(node), - pcmk_readable_score(node->weight), - pcmk_readable_score(colocation->score), primary->id); + pcmk__rsc_trace(dependent, + "Applied %s to %s score on %s (now %s after " + "subtracting %s because primary %s inactive)", + colocation->id, dependent->id, + pcmk__node_name(node), + pcmk_readable_score(node->weight), + pcmk_readable_score(colocation->score), primary->id); continue; } @@ -1320,35 +1345,37 @@ pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, * The resource will simply be forbidden from running on the node if * the primary isn't active there (via the condition above). */ - if (colocation->score < CRM_SCORE_INFINITY) { + if (colocation->score < PCMK_SCORE_INFINITY) { node->weight = pcmk__add_scores(colocation->score, node->weight); - pe_rsc_trace(dependent, - "Applied %s to %s score on %s (now %s after " - "adding %s)", - colocation->id, dependent->id, pe__node_name(node), - pcmk_readable_score(node->weight), - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(dependent, + "Applied %s to %s score on %s (now %s after " + "adding %s)", + colocation->id, dependent->id, + pcmk__node_name(node), + pcmk_readable_score(node->weight), + pcmk_readable_score(colocation->score)); } continue; } - if (colocation->score >= CRM_SCORE_INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { /* Only mandatory colocations are relevant when the colocation * attribute doesn't match, because an attribute not matching is not * a negative preference -- the colocation is simply relevant only * where it matches. */ - node->weight = -CRM_SCORE_INFINITY; - pe_rsc_trace(dependent, - "Banned %s from %s because colocation %s attribute %s " - "does not match", - dependent->id, pe__node_name(node), colocation->id, - attr); + node->weight = -PCMK_SCORE_INFINITY; + pcmk__rsc_trace(dependent, + "Banned %s from %s because colocation %s attribute %s " + "does not match", + dependent->id, pcmk__node_name(node), + colocation->id, attr); } } - if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY) + if ((colocation->score <= -PCMK_SCORE_INFINITY) + || (colocation->score >= PCMK_SCORE_INFINITY) || pcmk__any_node_available(work)) { g_hash_table_destroy(dependent->allowed_nodes); @@ -1356,9 +1383,9 @@ pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, work = NULL; } else { - pe_rsc_info(dependent, - "%s: Rolling back scores from %s (no available nodes)", - dependent->id, primary->id); + pcmk__rsc_info(dependent, + "%s: Rolling back scores from %s (no available nodes)", + dependent->id, primary->id); } if (work != NULL) { @@ -1404,9 +1431,9 @@ pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, primary_role_rsc = get_resource_for_role(primary); if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) { - if ((colocation->score == INFINITY) + if ((colocation->score == PCMK_SCORE_INFINITY) && (colocation->dependent_role == pcmk_role_promoted)) { - dependent->priority = -INFINITY; + dependent->priority = -PCMK_SCORE_INFINITY; } return; } @@ -1422,12 +1449,12 @@ pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, dependent->priority = pcmk__add_scores(score_multiplier * colocation->score, dependent->priority); - pe_rsc_trace(dependent, - "Applied %s to %s promotion priority (now %s after %s %s)", - colocation->id, dependent->id, - pcmk_readable_score(dependent->priority), - ((score_multiplier == 1)? "adding" : "subtracting"), - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(dependent, + "Applied %s to %s promotion priority (now %s after %s %s)", + colocation->id, dependent->id, + pcmk_readable_score(dependent->priority), + ((score_multiplier == 1)? "adding" : "subtracting"), + pcmk_readable_score(colocation->score)); } /*! @@ -1444,7 +1471,7 @@ best_node_score_matching_attr(const pcmk_resource_t *rsc, const char *attr, { GHashTableIter iter; pcmk_node_t *node = NULL; - int best_score = -INFINITY; + int best_score = -PCMK_SCORE_INFINITY; const char *best_node = NULL; // Find best allowed node with matching attribute @@ -1492,12 +1519,12 @@ allowed_on_one(const pcmk_resource_t *rsc) g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) { if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) { - pe_rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id); + pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id); return false; } } - pe_rsc_trace(rsc, "%s is allowed %s", rsc->id, - ((allowed_nodes == 1)? "on a single node" : "nowhere")); + pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id, + ((allowed_nodes == 1)? "on a single node" : "nowhere")); return (allowed_nodes == 1); } @@ -1580,14 +1607,14 @@ add_node_scores_matching_attr(GHashTable *nodes, || !allowed_on_one(colocation->dependent)) { crm_trace("%s: Filtering %d + %f * %d " "(double negative disallowed)", - pe__node_name(node), node->weight, factor, score); + pcmk__node_name(node), node->weight, factor, score); continue; } } if (node->weight == INFINITY_HACK) { crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)", - pe__node_name(node), node->weight, factor, score); + pcmk__node_name(node), node->weight, factor, score); continue; } @@ -1613,7 +1640,7 @@ add_node_scores_matching_attr(GHashTable *nodes, if (only_positive && (new_score < 0) && (node->weight > 0)) { crm_trace("%s: Filtering %d + %f * %d = %d " "(negative disallowed, marking node unusable)", - pe__node_name(node), node->weight, factor, score, + pcmk__node_name(node), node->weight, factor, score, new_score); node->weight = INFINITY_HACK; continue; @@ -1621,12 +1648,12 @@ add_node_scores_matching_attr(GHashTable *nodes, if (only_positive && (new_score < 0) && (node->weight == 0)) { crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)", - pe__node_name(node), node->weight, factor, score, + pcmk__node_name(node), node->weight, factor, score, new_score); continue; } - crm_trace("%s: %d + %f * %d = %d", pe__node_name(node), + crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node), node->weight, factor, score, new_score); node->weight = new_score; } @@ -1682,11 +1709,11 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, // Avoid infinite recursion if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) { - pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s", - log_id, source_rsc->id); + pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s", + log_id, source_rsc->id); return; } - pe__set_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); if (*nodes == NULL) { work = pcmk__copy_node_table(source_rsc->allowed_nodes); @@ -1694,15 +1721,15 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, } else { const bool pos = pcmk_is_set(flags, pcmk__coloc_select_nonnegative); - pe_rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)", - log_id, (pos? "positive" : "all"), source_rsc->id, factor); + pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)", + log_id, (pos? "positive" : "all"), source_rsc->id, factor); work = pcmk__copy_node_table(*nodes); add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation, factor, pos); } if (work == NULL) { - pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); return; } @@ -1711,16 +1738,16 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) { colocations = pcmk__this_with_colocations(source_rsc); - pe_rsc_trace(source_rsc, - "Checking additional %d optional '%s with' " - "constraints", - g_list_length(colocations), source_rsc->id); + pcmk__rsc_trace(source_rsc, + "Checking additional %d optional '%s with' " + "constraints", + g_list_length(colocations), source_rsc->id); } else { colocations = pcmk__with_this_colocations(source_rsc); - pe_rsc_trace(source_rsc, - "Checking additional %d optional 'with %s' " - "constraints", - g_list_length(colocations), source_rsc->id); + pcmk__rsc_trace(source_rsc, + "Checking additional %d optional 'with %s' " + "constraints", + g_list_length(colocations), source_rsc->id); } flags |= pcmk__coloc_select_active; @@ -1728,7 +1755,8 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, pcmk__colocation_t *constraint = iter->data; pcmk_resource_t *other = NULL; - float other_factor = factor * constraint->score / (float) INFINITY; + float other_factor = factor * constraint->score + / (float) PCMK_SCORE_INFINITY; if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) { other = constraint->primary; @@ -1738,11 +1766,11 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, other = constraint->dependent; } - pe_rsc_trace(source_rsc, - "Optionally merging score of '%s' constraint " - "(%s with %s)", - constraint->id, constraint->dependent->id, - constraint->primary->id); + pcmk__rsc_trace(source_rsc, + "Optionally merging score of '%s' constraint " + "(%s with %s)", + constraint->id, constraint->dependent->id, + constraint->primary->id); other->cmds->add_colocated_node_scores(other, target_rsc, log_id, &work, constraint, other_factor, flags); @@ -1751,10 +1779,10 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, g_list_free(colocations); } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) { - pe_rsc_info(source_rsc, "%s: Rolling back optional scores from %s", - log_id, source_rsc->id); + pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s", + log_id, source_rsc->id); g_hash_table_destroy(work); - pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); return; } @@ -1776,7 +1804,7 @@ pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, } *nodes = work; - pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); } /*! @@ -1793,19 +1821,19 @@ pcmk__add_dependent_scores(gpointer data, gpointer user_data) pcmk_resource_t *target_rsc = user_data; pcmk_resource_t *source_rsc = colocation->dependent; - const float factor = colocation->score / (float) INFINITY; + const float factor = colocation->score / (float) PCMK_SCORE_INFINITY; uint32_t flags = pcmk__coloc_select_active; if (!pcmk__colocation_has_influence(colocation, NULL)) { return; } - if (target_rsc->variant == pcmk_rsc_variant_clone) { + if (pcmk__is_clone(target_rsc)) { flags |= pcmk__coloc_select_nonnegative; } - pe_rsc_trace(target_rsc, - "%s: Incorporating attenuated %s assignment scores due " - "to colocation %s", - target_rsc->id, source_rsc->id, colocation->id); + pcmk__rsc_trace(target_rsc, + "%s: Incorporating attenuated %s assignment scores due " + "to colocation %s", + target_rsc->id, source_rsc->id, colocation->id); source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc, source_rsc->id, &target_rsc->allowed_nodes, @@ -1848,22 +1876,22 @@ pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent, primary_node = pe_find_node_id(primary_nodes, dependent_node->details->id); if (primary_node == NULL) { - dependent_node->weight = -INFINITY; - pe_rsc_trace(dependent, - "Banning %s from %s (no primary instance) for %s", - dependent->id, pe__node_name(dependent_node), - colocation->id); + dependent_node->weight = -PCMK_SCORE_INFINITY; + pcmk__rsc_trace(dependent, + "Banning %s from %s (no primary instance) for %s", + dependent->id, pcmk__node_name(dependent_node), + colocation->id); } else if (merge_scores) { dependent_node->weight = pcmk__add_scores(dependent_node->weight, primary_node->weight); - pe_rsc_trace(dependent, - "Added %s's score %s to %s's score for %s (now %s) " - "for colocation %s", - primary->id, pcmk_readable_score(primary_node->weight), - dependent->id, pe__node_name(dependent_node), - pcmk_readable_score(dependent_node->weight), - colocation->id); + pcmk__rsc_trace(dependent, + "Added %s's score %s to %s's score for %s (now %s) " + "for colocation %s", + primary->id, pcmk_readable_score(primary_node->weight), + dependent->id, pcmk__node_name(dependent_node), + pcmk_readable_score(dependent_node->weight), + colocation->id); } } } diff --git a/lib/pacemaker/pcmk_sched_constraints.c b/lib/pacemaker/pcmk_sched_constraints.c index 0d1beb9..3c817d2 100644 --- a/lib/pacemaker/pcmk_sched_constraints.c +++ b/lib/pacemaker/pcmk_sched_constraints.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. * @@ -17,7 +17,6 @@ #include <crm/crm.h> #include <crm/cib.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/iso8601.h> @@ -30,10 +29,15 @@ static bool evaluate_lifetime(xmlNode *lifetime, pcmk_scheduler_t *scheduler) { - bool result = FALSE; + bool result = false; crm_time_t *next_change = crm_time_new_undefined(); + pcmk_rule_input_t rule_input = { + .now = scheduler->now, + }; + + result = (pcmk__evaluate_rules(lifetime, &rule_input, + next_change) == pcmk_rc_ok); - result = pe_evaluate_rules(lifetime, NULL, scheduler->now, next_change); if (crm_time_is_defined(next_change)) { time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change); @@ -56,49 +60,48 @@ void pcmk__unpack_constraints(pcmk_scheduler_t *scheduler) { xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input, - XML_CIB_TAG_CONSTRAINTS); + PCMK_XE_CONSTRAINTS); - for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints); + for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints, NULL, NULL, + NULL); xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { 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); const char *tag = (const char *) xml_obj->name; if (id == NULL) { pcmk__config_err("Ignoring <%s> constraint without " - XML_ATTR_ID, tag); + PCMK_XA_ID, tag); continue; } crm_trace("Unpacking %s constraint '%s'", tag, id); - lifetime = first_named_child(xml_obj, "lifetime"); + lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL); if (lifetime != NULL) { - pcmk__config_warn("Support for 'lifetime' attribute (in %s) is " - "deprecated (the rules it contains should " - "instead be direct descendants of the " - "constraint object)", id); + pcmk__config_warn("Support for '" PCMK__XE_LIFETIME "' element " + "(in %s) is deprecated and will be dropped " + "in a later release", id); } if ((lifetime != NULL) && !evaluate_lifetime(lifetime, scheduler)) { crm_info("Constraint %s %s is not active", tag, id); - } else if (pcmk__str_eq(XML_CONS_TAG_RSC_ORDER, tag, pcmk__str_none)) { + } else if (pcmk__str_eq(PCMK_XE_RSC_ORDER, tag, pcmk__str_none)) { pcmk__unpack_ordering(xml_obj, scheduler); - } else if (pcmk__str_eq(XML_CONS_TAG_RSC_DEPEND, tag, pcmk__str_none)) { + } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) { pcmk__unpack_colocation(xml_obj, scheduler); - } else if (pcmk__str_eq(XML_CONS_TAG_RSC_LOCATION, tag, - pcmk__str_none)) { + } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) { pcmk__unpack_location(xml_obj, scheduler); - } else if (pcmk__str_eq(XML_CONS_TAG_RSC_TICKET, tag, pcmk__str_none)) { + } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) { pcmk__unpack_rsc_ticket(xml_obj, scheduler); } else { - pe_err("Unsupported constraint type: %s", tag); + pcmk__config_err("Unsupported constraint type: %s", tag); } } } @@ -148,7 +151,7 @@ find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id, if (g_hash_table_lookup_extended(scheduler->template_rsc_sets, id, NULL, (gpointer *) tag)) { if (*tag == NULL) { - crm_warn("No resource is derived from template '%s'", id); + crm_notice("No resource is derived from template '%s'", id); return false; } return true; @@ -158,13 +161,13 @@ find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id, if (g_hash_table_lookup_extended(scheduler->tags, id, NULL, (gpointer *) tag)) { if (*tag == NULL) { - crm_warn("No resource is tagged with '%s'", id); + crm_notice("No resource is tagged with '%s'", id); return false; } return true; } - crm_warn("No template or tag named '%s'", id); + pcmk__config_warn("No resource, template, or tag named '%s'", id); return false; } @@ -201,11 +204,13 @@ pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, /*! * \internal - * \brief Replace any resource tags with equivalent resource_ref entries + * \brief Replace any resource tags with equivalent \C PCMK_XE_RESOURCE_REF + * entries * - * If a given constraint has resource sets, check each set for resource_ref - * entries that list tags rather than resource IDs, and replace any found with - * resource_ref entries for the corresponding resource IDs. + * If a given constraint has resource sets, check each set for + * \c PCMK_XE_RESOURCE_REF entries that list tags rather than resource IDs, and + * replace any found with \c PCMK_XE_RESOURCE_REF entries for the corresponding + * resource IDs. * * \param[in,out] xml_obj Constraint XML * \param[in] scheduler Scheduler data @@ -220,29 +225,32 @@ pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler) bool any_refs = false; // Short-circuit if there are no sets - if (first_named_child(xml_obj, XML_CONS_TAG_RSC_SET) == NULL) { + if (pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, + NULL) == NULL) { return NULL; } - new_xml = copy_xml(xml_obj); + new_xml = pcmk__xml_copy(NULL, xml_obj); - for (xmlNode *set = first_named_child(new_xml, XML_CONS_TAG_RSC_SET); - set != NULL; set = crm_next_same_xml(set)) { + for (xmlNode *set = pcmk__xe_first_child(new_xml, PCMK_XE_RESOURCE_SET, + NULL, NULL); + set != NULL; set = pcmk__xe_next_same(set)) { GList *tag_refs = NULL; GList *iter = NULL; - for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { pcmk_resource_t *rsc = NULL; pcmk_tag_t *tag = NULL; - if (!pcmk__valid_resource_or_tag(scheduler, ID(xml_rsc), &rsc, - &tag)) { + if (!pcmk__valid_resource_or_tag(scheduler, pcmk__xe_id(xml_rsc), + &rsc, &tag)) { pcmk__config_err("Ignoring resource sets for constraint '%s' " "because '%s' is not a valid resource or tag", - ID(xml_obj), ID(xml_rsc)); + pcmk__xe_id(xml_obj), pcmk__xe_id(xml_rsc)); free_xml(new_xml); return NULL; @@ -250,7 +258,9 @@ pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler) continue; } else if (tag) { - // resource_ref under resource_set references template or tag + /* PCMK_XE_RESOURCE_REF under PCMK_XE_RESOURCE_SET references + * template or tag + */ xmlNode *last_ref = xml_rsc; /* For example, given the original XML: @@ -278,9 +288,9 @@ pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler) new_rsc_ref = xmlNewDocRawNode(set->doc, NULL, (pcmkXmlStr) - XML_TAG_RESOURCE_REF, + PCMK_XE_RESOURCE_REF, NULL); - crm_xml_add(new_rsc_ref, XML_ATTR_ID, obj_ref); + crm_xml_add(new_rsc_ref, PCMK_XA_ID, obj_ref); xmlAddNextSibling(last_ref, new_rsc_ref); last_ref = new_rsc_ref; @@ -345,9 +355,9 @@ pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false); - cons_id = ID(xml_obj); + cons_id = pcmk__xe_id(xml_obj); if (cons_id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return false; } @@ -364,35 +374,35 @@ pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, } else if (tag) { /* The "attr" attribute (for a resource in a constraint) specifies a - * template or tag. Add the corresponding resource_set containing the - * resources derived from or tagged with it. + * template or tag. Add the corresponding PCMK_XE_RESOURCE_SET + * containing the resources derived from or tagged with it. */ - *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET); - crm_xml_add(*rsc_set, XML_ATTR_ID, id); + *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET); + crm_xml_add(*rsc_set, PCMK_XA_ID, id); for (GList *iter = tag->refs; iter != NULL; iter = iter->next) { const char *obj_ref = iter->data; xmlNode *rsc_ref = NULL; - rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF); - crm_xml_add(rsc_ref, XML_ATTR_ID, obj_ref); + rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF); + crm_xml_add(rsc_ref, PCMK_XA_ID, obj_ref); } - /* Set sequential="false" for the resource_set */ - pcmk__xe_set_bool_attr(*rsc_set, "sequential", false); + // Set PCMK_XA_SEQUENTIAL=PCMK_VALUE_FALSE for the PCMK_XE_RESOURCE_SET + pcmk__xe_set_bool_attr(*rsc_set, PCMK_XA_SEQUENTIAL, false); } else if ((rsc != NULL) && convert_rsc) { /* Even if a regular resource is referenced by "attr", convert it into a - * resource_set, because the other resource reference in the constraint - * could be a template or tag. + * PCMK_XE_RESOURCE_SET, because the other resource reference in the + * constraint could be a template or tag. */ xmlNode *rsc_ref = NULL; - *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET); - crm_xml_add(*rsc_set, XML_ATTR_ID, id); + *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET); + crm_xml_add(*rsc_set, PCMK_XA_ID, id); - rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF); - crm_xml_add(rsc_ref, XML_ATTR_ID, id); + rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF); + crm_xml_add(rsc_ref, PCMK_XA_ID, id); } else { return true; @@ -400,7 +410,7 @@ pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, /* Remove the "attr" attribute referencing the template/tag */ if (*rsc_set != NULL) { - xml_remove_prop(xml_obj, attr); + pcmk__xe_remove_attr(xml_obj, attr); } return true; diff --git a/lib/pacemaker/pcmk_sched_fencing.c b/lib/pacemaker/pcmk_sched_fencing.c index 3fe9ebc..ab8d189 100644 --- a/lib/pacemaker/pcmk_sched_fencing.c +++ b/lib/pacemaker/pcmk_sched_fencing.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. * @@ -31,8 +31,8 @@ rsc_is_known_on(const pcmk_resource_t *rsc, const pcmk_node_t *node) if (g_hash_table_lookup(rsc->known_on, node->details->id) != NULL) { return TRUE; - } else if ((rsc->variant == pcmk_rsc_variant_primitive) - && pe_rsc_is_anon_clone(rsc->parent) + } else if (pcmk__is_primitive(rsc) + && pcmk__is_anonymous_clone(rsc->parent) && (g_hash_table_lookup(rsc->parent->known_on, node->details->id) != NULL)) { /* We check only the parent, not the uber-parent, because we cannot @@ -87,8 +87,8 @@ order_start_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) * The most likely explanation is that the DC died and took * its status with it. */ - pe_rsc_debug(rsc, "Ordering %s after %s recovery", - action->uuid, pe__node_name(target)); + pcmk__rsc_debug(rsc, "Ordering %s after %s recovery", + action->uuid, pcmk__node_name(target)); order_actions(stonith_op, action, pcmk__ar_ordered |pcmk__ar_unrunnable_first_blocks); @@ -129,7 +129,7 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) * because guest node "fencing" is actually just a resource stop. */ if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing) - || pe__is_guest_node(target)) { + || pcmk__is_guest_or_bundle_node(target)) { order_implicit = true; } @@ -143,7 +143,7 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) pcmk_action_t *action = iter->data; // The stop would never complete, so convert it into a pseudo-action. - pe__set_action_flags(action, pcmk_action_pseudo|pcmk_action_runnable); + pcmk__set_action_flags(action, pcmk_action_pseudo|pcmk_action_runnable); if (order_implicit) { /* Order the stonith before the parent stop (if any). @@ -158,7 +158,7 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) * cluster and thus immune to that check (and is irrelevant if * target is not a guest). */ - if (!pe_rsc_is_bundled(rsc)) { + if (!pcmk__is_bundled(rsc)) { order_actions(stonith_op, action, pcmk__ar_guest_allowed); } order_actions(stonith_op, parent_stop, pcmk__ar_guest_allowed); @@ -167,11 +167,11 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { crm_notice("Stop of failed resource %s is implicit %s %s is fenced", rsc->id, (order_implicit? "after" : "because"), - pe__node_name(target)); + pcmk__node_name(target)); } else { crm_info("%s is implicit %s %s is fenced", action->uuid, (order_implicit? "after" : "because"), - pe__node_name(target)); + pcmk__node_name(target)); } if (pcmk_is_set(rsc->flags, pcmk_rsc_notify)) { @@ -198,7 +198,7 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) * resources instead of the above. */ crm_info("Moving healthy resource %s off %s before fencing", - rsc->id, pe__node_name(node)); + rsc->id, pcmk__node_name(node)); pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, strdup(PCMK_ACTION_STONITH), stonith_op, pcmk__ar_ordered, rsc->cluster); @@ -217,22 +217,22 @@ order_stop_vs_fencing(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) || pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { - pe_rsc_info(rsc, - "Demote of failed resource %s is implicit " - "after %s is fenced", - rsc->id, pe__node_name(target)); + pcmk__rsc_info(rsc, + "Demote of failed resource %s is implicit " + "after %s is fenced", + rsc->id, pcmk__node_name(target)); } else { - pe_rsc_info(rsc, "%s is implicit after %s is fenced", - action->uuid, pe__node_name(target)); + pcmk__rsc_info(rsc, "%s is implicit after %s is fenced", + action->uuid, pcmk__node_name(target)); } /* The demote would never complete and is now implied by the * fencing, so convert it into a pseudo-action. */ - pe__set_action_flags(action, - pcmk_action_pseudo|pcmk_action_runnable); + pcmk__set_action_flags(action, + pcmk_action_pseudo|pcmk_action_runnable); - if (pe_rsc_is_bundled(rsc)) { + if (pcmk__is_bundled(rsc)) { // Recovery will be ordered as usual after parent's implied stop } else if (order_implicit) { @@ -263,9 +263,9 @@ rsc_stonith_ordering(pcmk_resource_t *rsc, pcmk_action_t *stonith_op) } } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, - "Skipping fencing constraints for unmanaged resource: %s", - rsc->id); + pcmk__rsc_trace(rsc, + "Skipping fencing constraints for unmanaged resource: " + "%s", rsc->id); } else { order_start_vs_fencing(rsc, stonith_op); @@ -381,7 +381,7 @@ pcmk__fence_guest(pcmk_node_t *node) */ stonith_op = pe_fence_op(node, fence_action, FALSE, "guest is unclean", FALSE, node->details->data_set); - pe__set_action_flags(stonith_op, pcmk_action_pseudo|pcmk_action_runnable); + pcmk__set_action_flags(stonith_op, pcmk_action_pseudo|pcmk_action_runnable); /* We want to imply stops/demotes after the guest is stopped, not wait until * it is restarted, so we always order pseudo-fencing after stop, not start @@ -393,8 +393,8 @@ pcmk__fence_guest(pcmk_node_t *node) node->details->data_set); crm_info("Implying guest %s is down (action %d) after %s fencing", - pe__node_name(node), stonith_op->id, - pe__node_name(stop->node)); + pcmk__node_name(node), stonith_op->id, + pcmk__node_name(stop->node)); order_actions(parent_stonith_op, stonith_op, pcmk__ar_unrunnable_first_blocks |pcmk__ar_first_implies_then); @@ -405,7 +405,7 @@ pcmk__fence_guest(pcmk_node_t *node) |pcmk__ar_first_implies_then); crm_info("Implying guest %s is down (action %d) " "after container %s is stopped (action %d)", - pe__node_name(node), stonith_op->id, + pcmk__node_name(node), stonith_op->id, container->id, stop->id); } else { /* If we're fencing the guest node but there's no stop for the guest @@ -422,13 +422,13 @@ pcmk__fence_guest(pcmk_node_t *node) order_actions(stop, stonith_op, pcmk__ar_ordered); crm_info("Implying guest %s is down (action %d) " "after connection is stopped (action %d)", - pe__node_name(node), stonith_op->id, stop->id); + pcmk__node_name(node), stonith_op->id, stop->id); } else { /* Not sure why we're fencing, but everything must already be * cleanly stopped. */ crm_info("Implying guest %s is down (action %d) ", - pe__node_name(node), stonith_op->id); + pcmk__node_name(node), stonith_op->id); } } @@ -448,7 +448,8 @@ pcmk__fence_guest(pcmk_node_t *node) bool pcmk__node_unfenced(const pcmk_node_t *node) { - const char *unfenced = pe_node_attribute_raw(node, CRM_ATTR_UNFENCED); + const char *unfenced = pcmk__node_attr(node, CRM_ATTR_UNFENCED, NULL, + pcmk__rsc_node_current); return !pcmk__str_eq(unfenced, "0", pcmk__str_null_matches); } diff --git a/lib/pacemaker/pcmk_sched_group.c b/lib/pacemaker/pcmk_sched_group.c index 9983c1f..74b5efa 100644 --- a/lib/pacemaker/pcmk_sched_group.c +++ b/lib/pacemaker/pcmk_sched_group.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. * @@ -11,7 +11,7 @@ #include <stdbool.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -41,24 +41,24 @@ pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, pcmk_node_t *first_assigned_node = NULL; pcmk_resource_t *first_member = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return rsc->allocated_to; // Assignment already done } if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { - pe_rsc_debug(rsc, "Assignment dependency loop detected involving %s", - rsc->id); + pcmk__rsc_debug(rsc, "Assignment dependency loop detected involving %s", + rsc->id); return NULL; } if (rsc->children == NULL) { // No members to assign - pe__clear_resource_flags(rsc, pcmk_rsc_unassigned); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned); return NULL; } - pe__set_resource_flags(rsc, pcmk_rsc_assigning); + pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); first_member = (pcmk_resource_t *) rsc->children->data; rsc->role = first_member->role; @@ -70,8 +70,8 @@ pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, pcmk_resource_t *member = (pcmk_resource_t *) iter->data; pcmk_node_t *node = NULL; - pe_rsc_trace(rsc, "Assigning group %s member %s", - rsc->id, member->id); + pcmk__rsc_trace(rsc, "Assigning group %s member %s", + rsc->id, member->id); node = member->cmds->assign(member, prefer, stop_if_fail); if (first_assigned_node == NULL) { first_assigned_node = node; @@ -79,7 +79,7 @@ pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, } pe__set_next_role(rsc, first_member->next_role, "first group member"); - pe__clear_resource_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned); if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) { return NULL; @@ -101,7 +101,8 @@ create_group_pseudo_op(pcmk_resource_t *group, const char *action) { pcmk_action_t *op = custom_action(group, pcmk__op_key(group->id, action, 0), action, NULL, TRUE, group->cluster); - pe__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable); + + pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable); return op; } @@ -114,9 +115,9 @@ create_group_pseudo_op(pcmk_resource_t *group, const char *action) void pcmk__group_create_actions(pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); - pe_rsc_trace(rsc, "Creating actions for group %s", rsc->id); + pcmk__rsc_trace(rsc, "Creating actions for group %s", rsc->id); // Create actions for individual group members for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { @@ -130,7 +131,7 @@ pcmk__group_create_actions(pcmk_resource_t *rsc) create_group_pseudo_op(rsc, PCMK_ACTION_RUNNING); create_group_pseudo_op(rsc, PCMK_ACTION_STOP); create_group_pseudo_op(rsc, PCMK_ACTION_STOPPED); - if (crm_is_true(g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_PROMOTABLE))) { + if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_PROMOTABLE))) { create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTE); create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTED); create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTE); @@ -174,7 +175,7 @@ member_internal_constraints(gpointer data, gpointer user_data) if (member_data->previous_member == NULL) { // This is first member if (member_data->ordered) { - pe__set_order_flags(down_flags, pcmk__ar_ordered); + pcmk__set_relation_flags(down_flags, pcmk__ar_ordered); post_down_flags = pcmk__ar_first_implies_then; } @@ -186,8 +187,9 @@ member_internal_constraints(gpointer data, gpointer user_data) } // Colocate this member with the previous one - pcmk__new_colocation("#group-members", NULL, INFINITY, member, - member_data->previous_member, NULL, NULL, flags); + pcmk__new_colocation("#group-members", NULL, PCMK_SCORE_INFINITY, + member, member_data->previous_member, NULL, NULL, + flags); } if (member_data->promotable) { @@ -308,7 +310,7 @@ pcmk__group_internal_constraints(pcmk_resource_t *rsc) struct member_data member_data = { false, }; const pcmk_resource_t *top = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); /* Order group pseudo-actions relative to each other for restarting: * stop group -> group is stopped -> start group -> group is started @@ -353,8 +355,8 @@ colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary, return; } - pe_rsc_trace(primary, "Processing %s (group %s with %s) for dependent", - colocation->id, dependent->id, primary->id); + pcmk__rsc_trace(primary, "Processing %s (group %s with %s) for dependent", + colocation->id, dependent->id, primary->id); if (pe__group_flag_is_set(dependent, pcmk__group_colocated)) { // Colocate first member (internal colocations will handle the rest) @@ -363,7 +365,7 @@ colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary, return; } - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { pcmk__config_err("%s: Cannot perform mandatory colocation between " "non-colocated group and %s", dependent->id, primary->id); @@ -395,9 +397,9 @@ colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary, { const pcmk_resource_t *member = NULL; - pe_rsc_trace(primary, - "Processing colocation %s (%s with group %s) for primary", - colocation->id, dependent->id, primary->id); + pcmk__rsc_trace(primary, + "Processing colocation %s (%s with group %s) for primary", + colocation->id, dependent->id, primary->id); if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) { return; @@ -405,7 +407,7 @@ colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary, if (pe__group_flag_is_set(primary, pcmk__group_colocated)) { - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { /* For mandatory colocations, the entire group must be assignable * (and in the specified role if any), so apply the colocation based * on the last member. @@ -426,7 +428,7 @@ colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary, return; } - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { pcmk__config_err("%s: Cannot perform mandatory colocation with" " non-colocated group %s", dependent->id, primary->id); @@ -468,7 +470,7 @@ pcmk__group_apply_coloc_score(pcmk_resource_t *dependent, } else { // Method should only be called for primitive dependents - CRM_ASSERT(dependent->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(dependent)); colocate_with_group(dependent, primary, colocation); } @@ -499,7 +501,7 @@ pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node) // Check whether member has the same action enum action_tasks task = get_complex_task(member, action->task); - const char *task_s = task2text(task); + const char *task_s = pcmk_action_text(task); pcmk_action_t *member_action = find_first_action(member->actions, NULL, task_s, node); @@ -510,11 +512,11 @@ pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node) // Group action is mandatory if any member action is if (pcmk_is_set(flags, pcmk_action_optional) && !pcmk_is_set(member_flags, pcmk_action_optional)) { - pe_rsc_trace(action->rsc, "%s is mandatory because %s is", - action->uuid, member_action->uuid); - pe__clear_raw_action_flags(flags, "group action", - pcmk_action_optional); - pe__clear_action_flags(action, pcmk_action_optional); + pcmk__rsc_trace(action->rsc, "%s is mandatory because %s is", + action->uuid, member_action->uuid); + pcmk__clear_raw_action_flags(flags, "group action", + pcmk_action_optional); + pcmk__clear_action_flags(action, pcmk_action_optional); } // Group action is unrunnable if any member action is @@ -522,22 +524,22 @@ pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node) && pcmk_is_set(flags, pcmk_action_runnable) && !pcmk_is_set(member_flags, pcmk_action_runnable)) { - pe_rsc_trace(action->rsc, "%s is unrunnable because %s is", - action->uuid, member_action->uuid); - pe__clear_raw_action_flags(flags, "group action", - pcmk_action_runnable); - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__rsc_trace(action->rsc, "%s is unrunnable because %s is", + action->uuid, member_action->uuid); + pcmk__clear_raw_action_flags(flags, "group action", + pcmk_action_runnable); + pcmk__clear_action_flags(action, pcmk_action_runnable); } /* Group (pseudo-)actions other than stop or demote are unrunnable * unless every member will do it. */ } else if ((task != pcmk_action_stop) && (task != pcmk_action_demote)) { - pe_rsc_trace(action->rsc, - "%s is not runnable because %s will not %s", - action->uuid, member->id, task_s); - pe__clear_raw_action_flags(flags, "group action", - pcmk_action_runnable); + pcmk__rsc_trace(action->rsc, + "%s is not runnable because %s will not %s", + action->uuid, member->id, task_s); + pcmk__clear_raw_action_flags(flags, "group action", + pcmk_action_runnable); } } @@ -607,16 +609,15 @@ pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, * \param[in,out] location Location constraint to apply */ void -pcmk__group_apply_location(pcmk_resource_t *rsc, pe__location_t *location) +pcmk__group_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { GList *node_list_orig = NULL; GList *node_list_copy = NULL; bool reset_scores = true; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group) - && (location != NULL)); + CRM_ASSERT(pcmk__is_group(rsc) && (location != NULL)); - node_list_orig = location->node_list_rh; + node_list_orig = location->nodes; node_list_copy = pcmk__copy_node_list(node_list_orig, true); reset_scores = pe__group_flag_is_set(rsc, pcmk__group_colocated); @@ -635,11 +636,11 @@ pcmk__group_apply_location(pcmk_resource_t *rsc, pe__location_t *location) * the first member's scores already incorporate theirs. */ reset_scores = false; - location->node_list_rh = node_list_copy; + location->nodes = node_list_copy; } } - location->node_list_rh = node_list_orig; + location->nodes = node_list_orig; g_list_free_full(node_list_copy, free); } @@ -651,14 +652,14 @@ pcmk__group_colocated_resources(const pcmk_resource_t *rsc, { const pcmk_resource_t *member = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); if (orig_rsc == NULL) { orig_rsc = rsc; } if (pe__group_flag_is_set(rsc, pcmk__group_colocated) - || pe_rsc_is_clone(rsc->parent)) { + || pcmk__is_clone(rsc->parent)) { /* This group has colocated members and/or is cloned -- either way, * add every child's colocated resources to the list. The first and last * members will include the group's own colocations. @@ -689,8 +690,7 @@ pcmk__with_group_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group) - && (orig_rsc != NULL) && (list != NULL)); + CRM_ASSERT((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc)); // Ignore empty groups if (rsc->children == NULL) { @@ -705,8 +705,8 @@ pcmk__with_group_colocations(const pcmk_resource_t *rsc, return; } - pe_rsc_trace(rsc, "Adding 'with %s' colocations to list for %s", - rsc->id, orig_rsc->id); + pcmk__rsc_trace(rsc, "Adding 'with %s' colocations to list for %s", + rsc->id, orig_rsc->id); // Add the group's own colocations pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc); @@ -739,8 +739,7 @@ pcmk__group_with_colocations(const pcmk_resource_t *rsc, { const pcmk_resource_t *member = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group) - && (orig_rsc != NULL) && (list != NULL)); + CRM_ASSERT((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc)); // Ignore empty groups if (rsc->children == NULL) { @@ -752,8 +751,8 @@ pcmk__group_with_colocations(const pcmk_resource_t *rsc, */ if ((rsc == orig_rsc) || (orig_rsc == (const pcmk_resource_t *) rsc->children->data)) { - pe_rsc_trace(rsc, "Adding '%s with' colocations to list for %s", - rsc->id, orig_rsc->id); + pcmk__rsc_trace(rsc, "Adding '%s with' colocations to list for %s", + rsc->id, orig_rsc->id); // Add the group's own colocations pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc); @@ -800,7 +799,7 @@ pcmk__group_with_colocations(const pcmk_resource_t *rsc, const pcmk__colocation_t *colocation = NULL; colocation = (const pcmk__colocation_t *) cons_iter->data; - if (colocation->score == INFINITY) { + if (colocation->score == PCMK_SCORE_INFINITY) { pcmk__add_this_with(list, colocation, orig_rsc); } } @@ -849,9 +848,7 @@ pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, { pcmk_resource_t *member = NULL; - CRM_ASSERT((source_rsc != NULL) - && (source_rsc->variant == pcmk_rsc_variant_group) - && (nodes != NULL) + CRM_ASSERT(pcmk__is_group(source_rsc) && (nodes != NULL) && ((colocation != NULL) || ((target_rsc == NULL) && (*nodes == NULL)))); @@ -861,11 +858,11 @@ pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, // Avoid infinite recursion if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) { - pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s", - log_id, source_rsc->id); + pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s", + log_id, source_rsc->id); return; } - pe__set_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); // Ignore empty groups (only possible with schema validation disabled) if (source_rsc->children == NULL) { @@ -887,11 +884,11 @@ pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, } else { member = source_rsc->children->data; } - pe_rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s " - "(at %.6f)", log_id, source_rsc->id, member->id, factor); + pcmk__rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s " + "(at %.6f)", log_id, source_rsc->id, member->id, factor); member->cmds->add_colocated_node_scores(member, target_rsc, log_id, nodes, colocation, factor, flags); - pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes); + pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes); } // Group implementation of pcmk_assignment_methods_t:add_utilization() @@ -902,17 +899,17 @@ pcmk__group_add_utilization(const pcmk_resource_t *rsc, { pcmk_resource_t *member = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group) - && (orig_rsc != NULL) && (utilization != NULL)); + CRM_ASSERT((orig_rsc != NULL) && (utilization != NULL) + && pcmk__is_group(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } - pe_rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization", - orig_rsc->id, rsc->id); + pcmk__rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization", + orig_rsc->id, rsc->id); if (pe__group_flag_is_set(rsc, pcmk__group_colocated) - || pe_rsc_is_clone(rsc->parent)) { + || pcmk__is_clone(rsc->parent)) { // Every group member will be on same node, so sum all members for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { member = (pcmk_resource_t *) iter->data; @@ -940,7 +937,7 @@ pcmk__group_add_utilization(const pcmk_resource_t *rsc, void pcmk__group_shutdown_lock(pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk_resource_t *member = (pcmk_resource_t *) iter->data; diff --git a/lib/pacemaker/pcmk_sched_instances.c b/lib/pacemaker/pcmk_sched_instances.c index 4667845..16ea386 100644 --- a/lib/pacemaker/pcmk_sched_instances.c +++ b/lib/pacemaker/pcmk_sched_instances.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. * @@ -12,7 +12,7 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -33,42 +33,43 @@ can_run_instance(const pcmk_resource_t *instance, const pcmk_node_t *node, pcmk_node_t *allowed_node = NULL; if (pcmk_is_set(instance->flags, pcmk_rsc_removed)) { - pe_rsc_trace(instance, "%s cannot run on %s: orphaned", - instance->id, pe__node_name(node)); + pcmk__rsc_trace(instance, "%s cannot run on %s: orphaned", + instance->id, pcmk__node_name(node)); return false; } if (!pcmk__node_available(node, false, false)) { - pe_rsc_trace(instance, - "%s cannot run on %s: node cannot run resources", - instance->id, pe__node_name(node)); + pcmk__rsc_trace(instance, + "%s cannot run on %s: node cannot run resources", + instance->id, pcmk__node_name(node)); return false; } allowed_node = pcmk__top_allowed_node(instance, node); if (allowed_node == NULL) { crm_warn("%s cannot run on %s: node not allowed", - instance->id, pe__node_name(node)); + instance->id, pcmk__node_name(node)); return false; } if (allowed_node->weight < 0) { - pe_rsc_trace(instance, "%s cannot run on %s: parent score is %s there", - instance->id, pe__node_name(node), - pcmk_readable_score(allowed_node->weight)); + pcmk__rsc_trace(instance, + "%s cannot run on %s: parent score is %s there", + instance->id, pcmk__node_name(node), + pcmk_readable_score(allowed_node->weight)); return false; } if (allowed_node->count >= max_per_node) { - pe_rsc_trace(instance, - "%s cannot run on %s: node already has %d instance%s", - instance->id, pe__node_name(node), max_per_node, - pcmk__plural_s(max_per_node)); + pcmk__rsc_trace(instance, + "%s cannot run on %s: node already has %d instance%s", + instance->id, pcmk__node_name(node), max_per_node, + pcmk__plural_s(max_per_node)); return false; } - pe_rsc_trace(instance, "%s can run on %s (%d already running)", - instance->id, pe__node_name(node), allowed_node->count); + pcmk__rsc_trace(instance, "%s can run on %s (%d already running)", + instance->id, pcmk__node_name(node), allowed_node->count); return true; } @@ -89,9 +90,9 @@ ban_unavailable_allowed_nodes(pcmk_resource_t *instance, int max_per_node) g_hash_table_iter_init(&iter, instance->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (!can_run_instance(instance, node, max_per_node)) { - pe_rsc_trace(instance, "Banning %s from unavailable node %s", - instance->id, pe__node_name(node)); - node->weight = -INFINITY; + pcmk__rsc_trace(instance, "Banning %s from unavailable node %s", + instance->id, pcmk__node_name(node)); + node->weight = -PCMK_SCORE_INFINITY; for (GList *child_iter = instance->children; child_iter != NULL; child_iter = child_iter->next) { pcmk_resource_t *child = child_iter->data; @@ -100,12 +101,12 @@ ban_unavailable_allowed_nodes(pcmk_resource_t *instance, int max_per_node) child_node = g_hash_table_lookup(child->allowed_nodes, node->details->id); if (child_node != NULL) { - pe_rsc_trace(instance, - "Banning %s child %s " - "from unavailable node %s", - instance->id, child->id, - pe__node_name(node)); - child_node->weight = -INFINITY; + pcmk__rsc_trace(instance, + "Banning %s child %s " + "from unavailable node %s", + instance->id, child->id, + pcmk__node_name(node)); + child_node->weight = -PCMK_SCORE_INFINITY; } } } @@ -148,7 +149,7 @@ apply_parent_colocations(const pcmk_resource_t *rsc, GHashTable **nodes) for (const GList *iter = colocations; iter != NULL; iter = iter->next) { const pcmk__colocation_t *colocation = iter->data; pcmk_resource_t *other = colocation->primary; - float factor = colocation->score / (float) INFINITY; + float factor = colocation->score / (float) PCMK_SCORE_INFINITY; other->cmds->add_colocated_node_scores(other, rsc, rsc->id, nodes, colocation, factor, @@ -160,7 +161,7 @@ apply_parent_colocations(const pcmk_resource_t *rsc, GHashTable **nodes) for (const GList *iter = colocations; iter != NULL; iter = iter->next) { const pcmk__colocation_t *colocation = iter->data; pcmk_resource_t *other = colocation->dependent; - float factor = colocation->score / (float) INFINITY; + float factor = colocation->score / (float) PCMK_SCORE_INFINITY; if (!pcmk__colocation_has_influence(colocation, rsc)) { continue; @@ -194,8 +195,8 @@ cmp_instance_by_colocation(const pcmk_resource_t *instance1, int rc = 0; pcmk_node_t *node1 = NULL; pcmk_node_t *node2 = NULL; - pcmk_node_t *current_node1 = pe__current_node(instance1); - pcmk_node_t *current_node2 = pe__current_node(instance2); + pcmk_node_t *current_node1 = pcmk__current_node(instance1); + pcmk_node_t *current_node2 = pcmk__current_node(instance2); GHashTable *colocated_scores1 = NULL; GHashTable *colocated_scores2 = NULL; @@ -218,14 +219,14 @@ cmp_instance_by_colocation(const pcmk_resource_t *instance1, // Compare nodes by updated scores if (node1->weight < node2->weight) { crm_trace("Assign %s (%d on %s) after %s (%d on %s)", - instance1->id, node1->weight, pe__node_name(node1), - instance2->id, node2->weight, pe__node_name(node2)); + instance1->id, node1->weight, pcmk__node_name(node1), + instance2->id, node2->weight, pcmk__node_name(node2)); rc = 1; } else if (node1->weight > node2->weight) { crm_trace("Assign %s (%d on %s) before %s (%d on %s)", - instance1->id, node1->weight, pe__node_name(node1), - instance2->id, node2->weight, pe__node_name(node2)); + instance1->id, node1->weight, pcmk__node_name(node1), + instance2->id, node2->weight, pcmk__node_name(node2)); rc = -1; } @@ -273,8 +274,8 @@ node_is_allowed(const pcmk_resource_t *rsc, pcmk_node_t **node) (*node)->details->id); if ((allowed == NULL) || (allowed->weight < 0)) { - pe_rsc_trace(rsc, "%s: current location (%s) is unavailable", - rsc->id, pe__node_name(*node)); + pcmk__rsc_trace(rsc, "%s: current location (%s) is unavailable", + rsc->id, pcmk__node_name(*node)); *node = NULL; return false; } @@ -557,13 +558,13 @@ assign_instance(pcmk_resource_t *instance, const pcmk_node_t *prefer, { pcmk_node_t *chosen = NULL; - pe_rsc_trace(instance, "Assigning %s (preferring %s)", instance->id, - ((prefer == NULL)? "no node" : prefer->details->uname)); + pcmk__rsc_trace(instance, "Assigning %s (preferring %s)", instance->id, + ((prefer == NULL)? "no node" : prefer->details->uname)); if (pcmk_is_set(instance->flags, pcmk_rsc_assigning)) { - pe_rsc_debug(instance, - "Assignment loop detected involving %s colocations", - instance->id); + pcmk__rsc_debug(instance, + "Assignment loop detected involving %s colocations", + instance->id); return NULL; } ban_unavailable_allowed_nodes(instance, max_per_node); @@ -600,15 +601,15 @@ assign_instance_early(const pcmk_resource_t *rsc, pcmk_resource_t *instance, GHashTable *allowed_orig_parent = parent->allowed_nodes; const pcmk_node_t *allowed_node = NULL; - pe_rsc_trace(instance, "Trying to assign %s to its current node %s", - instance->id, pe__node_name(current)); + pcmk__rsc_trace(instance, "Trying to assign %s to its current node %s", + instance->id, pcmk__node_name(current)); allowed_node = g_hash_table_lookup(instance->allowed_nodes, current->details->id); if (!pcmk__node_available(allowed_node, true, false)) { - pe_rsc_info(instance, - "Not assigning %s to current node %s: unavailable", - instance->id, pe__node_name(current)); + pcmk__rsc_info(instance, + "Not assigning %s to current node %s: unavailable", + instance->id, pcmk__node_name(current)); return false; } @@ -638,43 +639,44 @@ assign_instance_early(const pcmk_resource_t *rsc, pcmk_resource_t *instance, while (reserved < available) { chosen = assign_instance(instance, current, max_per_node); - if (pe__same_node(chosen, current)) { + if (pcmk__same_node(chosen, current)) { // Successfully assigned to current node break; } // Assignment updates scores, so restore to original state - pe_rsc_debug(instance, "Rolling back node scores for %s", instance->id); + pcmk__rsc_debug(instance, "Rolling back node scores for %s", + instance->id); pcmk__restore_node_tables(instance, allowed_orig); if (chosen == NULL) { // Assignment failed, so give up - pe_rsc_info(instance, - "Not assigning %s to current node %s: unavailable", - instance->id, pe__node_name(current)); - pe__set_resource_flags(instance, pcmk_rsc_unassigned); + pcmk__rsc_info(instance, + "Not assigning %s to current node %s: unavailable", + instance->id, pcmk__node_name(current)); + pcmk__set_rsc_flags(instance, pcmk_rsc_unassigned); break; } // We prefer more strongly to assign an instance to the chosen node - pe_rsc_debug(instance, - "Not assigning %s to current node %s: %s is better", - instance->id, pe__node_name(current), - pe__node_name(chosen)); + pcmk__rsc_debug(instance, + "Not assigning %s to current node %s: %s is better", + instance->id, pcmk__node_name(current), + pcmk__node_name(chosen)); // Reserve one instance for the chosen node and try again if (++reserved >= available) { - pe_rsc_info(instance, - "Not assigning %s to current node %s: " - "other assignments are more important", - instance->id, pe__node_name(current)); + pcmk__rsc_info(instance, + "Not assigning %s to current node %s: " + "other assignments are more important", + instance->id, pcmk__node_name(current)); } else { - pe_rsc_debug(instance, - "Reserved an instance of %s for %s. Retrying " - "assignment of %s to %s", - rsc->id, pe__node_name(chosen), instance->id, - pe__node_name(current)); + pcmk__rsc_debug(instance, + "Reserved an instance of %s for %s. Retrying " + "assignment of %s to %s", + rsc->id, pcmk__node_name(chosen), instance->id, + pcmk__node_name(current)); } // Clear this assignment (frees chosen); leave instance counts in parent @@ -692,8 +694,8 @@ assign_instance_early(const pcmk_resource_t *rsc, pcmk_resource_t *instance, // Couldn't assign instance to current node return false; } - pe_rsc_trace(instance, "Assigned %s to current node %s", - instance->id, pe__node_name(current)); + pcmk__rsc_trace(instance, "Assigned %s to current node %s", + instance->id, pcmk__node_name(current)); increment_parent_count(instance, chosen); return true; } @@ -746,20 +748,20 @@ preferred_node(const pcmk_resource_t *instance, int optimal_per_node) } // Check whether instance's current node can run resources - node = pe__current_node(instance); + node = pcmk__current_node(instance); if (!pcmk__node_available(node, true, false)) { - pe_rsc_trace(instance, "Not assigning %s to %s early (unavailable)", - instance->id, pe__node_name(node)); + pcmk__rsc_trace(instance, "Not assigning %s to %s early (unavailable)", + instance->id, pcmk__node_name(node)); return NULL; } // Check whether node already has optimal number of instances assigned parent_node = pcmk__top_allowed_node(instance, node); if ((parent_node != NULL) && (parent_node->count >= optimal_per_node)) { - pe_rsc_trace(instance, - "Not assigning %s to %s early " - "(optimal instances already assigned)", - instance->id, pe__node_name(node)); + pcmk__rsc_trace(instance, + "Not assigning %s to %s early " + "(optimal instances already assigned)", + instance->id, pcmk__node_name(node)); return NULL; } @@ -795,12 +797,12 @@ pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, optimal_per_node = 1; } - pe_rsc_debug(collective, - "Assigning up to %d %s instance%s to up to %u node%s " - "(at most %d per host, %d optimal)", - max_total, collective->id, pcmk__plural_s(max_total), - available_nodes, pcmk__plural_s(available_nodes), - max_per_node, optimal_per_node); + pcmk__rsc_debug(collective, + "Assigning up to %d %s instance%s to up to %u node%s " + "(at most %d per host, %d optimal)", + max_total, collective->id, pcmk__plural_s(max_total), + available_nodes, pcmk__plural_s(available_nodes), + max_per_node, optimal_per_node); // Assign as many instances as possible to their current location for (iter = instances; (iter != NULL) && (assigned < max_total); @@ -820,8 +822,8 @@ pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, } } - pe_rsc_trace(collective, "Assigned %d of %d instance%s to current node", - assigned, max_total, pcmk__plural_s(max_total)); + pcmk__rsc_trace(collective, "Assigned %d of %d instance%s to current node", + assigned, max_total, pcmk__plural_s(max_total)); for (iter = instances; iter != NULL; iter = iter->next) { instance = (pcmk_resource_t *) iter->data; @@ -831,7 +833,7 @@ pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, } if (instance->running_on != NULL) { - current = pe__current_node(instance); + current = pcmk__current_node(instance); if (pcmk__top_allowed_node(instance, current) == NULL) { const char *unmanaged = ""; @@ -839,16 +841,16 @@ pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, unmanaged = "Unmanaged resource "; } crm_notice("%s%s is running on %s which is no longer allowed", - unmanaged, instance->id, pe__node_name(current)); + unmanaged, instance->id, pcmk__node_name(current)); } } if (assigned >= max_total) { - pe_rsc_debug(collective, - "Not assigning %s because maximum %d instances " - "already assigned", - instance->id, max_total); - resource_location(instance, NULL, -INFINITY, + pcmk__rsc_debug(collective, + "Not assigning %s because maximum %d instances " + "already assigned", + instance->id, max_total); + resource_location(instance, NULL, -PCMK_SCORE_INFINITY, "collective_limit_reached", collective->cluster); } else if (assign_instance(instance, NULL, max_per_node) != NULL) { @@ -856,9 +858,9 @@ pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, } } - pe_rsc_debug(collective, "Assigned %d of %d possible instance%s of %s", - assigned, max_total, pcmk__plural_s(max_total), - collective->id); + pcmk__rsc_debug(collective, "Assigned %d of %d possible instance%s of %s", + assigned, max_total, pcmk__plural_s(max_total), + collective->id); } enum instance_state { @@ -926,13 +928,13 @@ check_instance_state(const pcmk_resource_t *instance, uint32_t *state) if (!optional && pcmk_is_set(action->flags, pcmk_action_runnable)) { - pe_rsc_trace(instance, "Instance is starting due to %s", - action->uuid); + pcmk__rsc_trace(instance, "Instance is starting due to %s", + action->uuid); instance_state |= instance_starting; } else { - pe_rsc_trace(instance, "%s doesn't affect %s state (%s)", - action->uuid, instance->id, - (optional? "optional" : "unrunnable")); + pcmk__rsc_trace(instance, "%s doesn't affect %s state (%s)", + action->uuid, instance->id, + (optional? "optional" : "unrunnable")); } } else if (pcmk__str_eq(PCMK_ACTION_STOP, action->task, @@ -944,13 +946,13 @@ check_instance_state(const pcmk_resource_t *instance, uint32_t *state) if (!optional && pcmk_any_flags_set(action->flags, pcmk_action_pseudo |pcmk_action_runnable)) { - pe_rsc_trace(instance, "Instance is stopping due to %s", - action->uuid); + pcmk__rsc_trace(instance, "Instance is stopping due to %s", + action->uuid); instance_state |= instance_stopping; } else { - pe_rsc_trace(instance, "%s doesn't affect %s state (%s)", - action->uuid, instance->id, - (optional? "optional" : "unrunnable")); + pcmk__rsc_trace(instance, "%s doesn't affect %s state (%s)", + action->uuid, instance->id, + (optional? "optional" : "unrunnable")); } } } @@ -980,8 +982,8 @@ pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances) pcmk_action_t *start = NULL; pcmk_action_t *started = NULL; - pe_rsc_trace(collective, "Creating collective instance actions for %s", - collective->id); + pcmk__rsc_trace(collective, "Creating collective instance actions for %s", + collective->id); // Create actions for each instance appropriate to its variant for (GList *iter = instances; iter != NULL; iter = iter->next) { @@ -998,9 +1000,9 @@ pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances) started = pe__new_rsc_pseudo_action(collective, PCMK_ACTION_RUNNING, !pcmk_is_set(state, instance_starting), false); - started->priority = INFINITY; + started->priority = PCMK_SCORE_INFINITY; if (pcmk_any_flags_set(state, instance_active|instance_starting)) { - pe__set_action_flags(started, pcmk_action_runnable); + pcmk__set_action_flags(started, pcmk_action_runnable); } // Create pseudo-actions for rsc stop and stopped @@ -1010,12 +1012,12 @@ pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances) stopped = pe__new_rsc_pseudo_action(collective, PCMK_ACTION_STOPPED, !pcmk_is_set(state, instance_stopping), true); - stopped->priority = INFINITY; + stopped->priority = PCMK_SCORE_INFINITY; if (!pcmk_is_set(state, instance_restarting)) { - pe__set_action_flags(stop, pcmk_action_migratable); + pcmk__set_action_flags(stop, pcmk_action_migratable); } - if (collective->variant == pcmk_rsc_variant_clone) { + if (pcmk__is_clone(collective)) { pe__create_clone_notif_pseudo_ops(collective, start, started, stop, stopped); } @@ -1035,7 +1037,7 @@ pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances) static inline GList * get_instance_list(const pcmk_resource_t *rsc) { - if (rsc->variant == pcmk_rsc_variant_bundle) { + if (pcmk__is_bundle(rsc)) { return pe__bundle_containers(rsc); } else { return rsc->children; @@ -1080,9 +1082,9 @@ pcmk__instance_matches(const pcmk_resource_t *instance, const pcmk_node_t *node, if ((role != pcmk_role_unknown) && (role != instance->fns->state(instance, current))) { - pe_rsc_trace(instance, - "%s is not a compatible instance (role is not %s)", - instance->id, role2text(role)); + pcmk__rsc_trace(instance, + "%s is not a compatible instance (role is not %s)", + instance->id, pcmk_role_text(role)); return false; } @@ -1092,23 +1094,28 @@ pcmk__instance_matches(const pcmk_resource_t *instance, const pcmk_node_t *node, } if (instance_node == NULL) { - pe_rsc_trace(instance, - "%s is not a compatible instance (not assigned to a node)", - instance->id); + pcmk__rsc_trace(instance, + "%s is not a compatible instance " + "(not assigned to a node)", + instance->id); return false; } - if (!pe__same_node(instance_node, node)) { - pe_rsc_trace(instance, - "%s is not a compatible instance (assigned to %s not %s)", - instance->id, pe__node_name(instance_node), - pe__node_name(node)); + if (!pcmk__same_node(instance_node, node)) { + pcmk__rsc_trace(instance, + "%s is not a compatible instance " + "(assigned to %s not %s)", + instance->id, pcmk__node_name(instance_node), + pcmk__node_name(node)); return false; } return true; } +#define display_role(r) \ + (((r) == pcmk_role_unknown)? "matching" : pcmk_role_text(r)) + /*! * \internal * \brief Find an instance that matches a given resource by node and role @@ -1135,20 +1142,20 @@ find_compatible_instance_on_node(const pcmk_resource_t *match_rsc, pcmk_resource_t *instance = (pcmk_resource_t *) iter->data; if (pcmk__instance_matches(instance, node, role, current)) { - pe_rsc_trace(match_rsc, - "Found %s %s instance %s compatible with %s on %s", - role == pcmk_role_unknown? "matching" : role2text(role), - rsc->id, instance->id, match_rsc->id, - pe__node_name(node)); + pcmk__rsc_trace(match_rsc, + "Found %s %s instance %s compatible with %s on %s", + display_role(role), rsc->id, instance->id, + match_rsc->id, pcmk__node_name(node)); free_instance_list(rsc, instances); // Only frees list, not contents return instance; } } free_instance_list(rsc, instances); - pe_rsc_trace(match_rsc, "No %s %s instance found compatible with %s on %s", - ((role == pcmk_role_unknown)? "matching" : role2text(role)), - rsc->id, match_rsc->id, pe__node_name(node)); + pcmk__rsc_trace(match_rsc, + "No %s %s instance found compatible with %s on %s", + display_role(role), rsc->id, match_rsc->id, + pcmk__node_name(node)); return NULL; } @@ -1192,8 +1199,8 @@ pcmk__find_compatible_instance(const pcmk_resource_t *match_rsc, } if (instance == NULL) { - pe_rsc_debug(rsc, "No %s instance found compatible with %s", - rsc->id, match_rsc->id); + pcmk__rsc_debug(rsc, "No %s instance found compatible with %s", + rsc->id, match_rsc->id); } g_list_free(nodes); return instance; @@ -1218,20 +1225,20 @@ unassign_if_mandatory(const pcmk_action_t *first, const pcmk_action_t *then, { // Allow "then" instance to go down even without an interleave match if (current) { - pe_rsc_trace(then->rsc, - "%s has no instance to order before stopping " - "or demoting %s", - first->rsc->id, then_instance->id); + pcmk__rsc_trace(then->rsc, + "%s has no instance to order before stopping " + "or demoting %s", + first->rsc->id, then_instance->id); /* If the "first" action must be runnable, but there is no "first" * instance, the "then" instance must not be allowed to come up. */ } else if (pcmk_any_flags_set(type, pcmk__ar_unrunnable_first_blocks |pcmk__ar_first_implies_then)) { - pe_rsc_info(then->rsc, - "Inhibiting %s from being active " - "because there is no %s instance to interleave", - then_instance->id, first->rsc->id); + pcmk__rsc_info(then->rsc, + "Inhibiting %s from being active " + "because there is no %s instance to interleave", + then_instance->id, first->rsc->id); return pcmk__assign_resource(then_instance, NULL, true, true); } return false; @@ -1337,15 +1344,15 @@ orig_action_name(const pcmk_action_t *action) PCMK_ACTION_NOTIFIED, NULL)) { // action->uuid is RSC_(confirmed-){pre,post}_notify_ACTION_INTERVAL CRM_CHECK(parse_op_key(action->uuid, NULL, &action_type, NULL), - return task2text(pcmk_action_unspecified)); + return pcmk_action_text(pcmk_action_unspecified)); action_name = strstr(action_type, "_notify_"); CRM_CHECK(action_name != NULL, - return task2text(pcmk_action_unspecified)); + return pcmk_action_text(pcmk_action_unspecified)); action_name += strlen("_notify_"); } orig_task = get_complex_task(instance, action_name); free(action_type); - return task2text(orig_task); + return pcmk_action_text(orig_task); } /*! @@ -1473,9 +1480,10 @@ can_interleave_actions(const pcmk_action_t *first, const pcmk_action_t *then) } interleave = crm_is_true(g_hash_table_lookup(rsc->meta, - XML_RSC_ATTR_INTERLEAVE)); - pe_rsc_trace(rsc, "'%s then %s' will %sbe interleaved (based on %s)", - first->uuid, then->uuid, (interleave? "" : "not "), rsc->id); + PCMK_META_INTERLEAVE)); + pcmk__rsc_trace(rsc, "'%s then %s' will %sbe interleaved (based on %s)", + first->uuid, then->uuid, (interleave? "" : "not "), + rsc->id); return interleave; } @@ -1633,32 +1641,32 @@ pcmk__collective_action_flags(pcmk_action_t *action, const GList *instances, uint32_t instance_flags; // Node is relevant only to primitive instances - if (instance->variant == pcmk_rsc_variant_primitive) { + if (pcmk__is_primitive(instance)) { instance_node = node; } instance_action = find_first_action(instance->actions, NULL, action_name, instance_node); if (instance_action == NULL) { - pe_rsc_trace(action->rsc, "%s has no %s action on %s", - instance->id, action_name, pe__node_name(node)); + pcmk__rsc_trace(action->rsc, "%s has no %s action on %s", + instance->id, action_name, pcmk__node_name(node)); continue; } - pe_rsc_trace(action->rsc, "%s has %s for %s on %s", - instance->id, instance_action->uuid, action_name, - pe__node_name(node)); + pcmk__rsc_trace(action->rsc, "%s has %s for %s on %s", + instance->id, instance_action->uuid, action_name, + pcmk__node_name(node)); instance_flags = instance->cmds->action_flags(instance_action, node); // If any instance action is mandatory, so is the collective action if (pcmk_is_set(flags, pcmk_action_optional) && !pcmk_is_set(instance_flags, pcmk_action_optional)) { - pe_rsc_trace(instance, "%s is mandatory because %s is", - action->uuid, instance_action->uuid); + pcmk__rsc_trace(instance, "%s is mandatory because %s is", + action->uuid, instance_action->uuid); pe__clear_action_summary_flags(flags, action, pcmk_action_optional); - pe__clear_action_flags(action, pcmk_action_optional); + pcmk__clear_action_flags(action, pcmk_action_optional); } // If any instance action is runnable, so is the collective action @@ -1668,12 +1676,12 @@ pcmk__collective_action_flags(pcmk_action_t *action, const GList *instances, } if (!any_runnable) { - pe_rsc_trace(action->rsc, - "%s is not runnable because no instance can run %s", - action->uuid, action_name); + pcmk__rsc_trace(action->rsc, + "%s is not runnable because no instance can run %s", + action->uuid, action_name); pe__clear_action_summary_flags(flags, action, pcmk_action_runnable); if (node == NULL) { - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__clear_action_flags(action, pcmk_action_runnable); } } diff --git a/lib/pacemaker/pcmk_sched_location.c b/lib/pacemaker/pcmk_sched_location.c index eab9481..94914f0 100644 --- a/lib/pacemaker/pcmk_sched_location.c +++ b/lib/pacemaker/pcmk_sched_location.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. * @@ -13,6 +13,7 @@ #include <glib.h> #include <crm/crm.h> +#include <crm/common/rules_internal.h> #include <crm/pengine/status.h> #include <crm/pengine/rules.h> #include <pacemaker-internal.h> @@ -26,89 +27,167 @@ get_node_score(const char *rule, const char *score, bool raw, int score_f = 0; if (score == NULL) { - pe_err("Rule %s: no score specified. Assuming 0.", rule); + pcmk__config_warn("Rule %s: no score specified (assuming 0)", rule); } else if (raw) { score_f = char2score(score); } else { + const char *target = NULL; const char *attr_score = NULL; - attr_score = pe__node_attribute_calculated(node, score, rsc, - pcmk__rsc_node_current, - false); + target = g_hash_table_lookup(rsc->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); + attr_score = pcmk__node_attr(node, score, target, + pcmk__rsc_node_current); if (attr_score == NULL) { crm_debug("Rule %s: %s did not have a value for %s", - rule, pe__node_name(node), score); - score_f = -INFINITY; + rule, pcmk__node_name(node), score); + score_f = -PCMK_SCORE_INFINITY; } else { crm_debug("Rule %s: %s had value %s for %s", - rule, pe__node_name(node), attr_score, score); + rule, pcmk__node_name(node), attr_score, score); score_f = char2score(attr_score); } } return score_f; } -static pe__location_t * +/*! + * \internal + * \brief Parse a role configuration for a location constraint + * + * \param[in] role_spec Role specification + * \param[out] role Where to store parsed role + * + * \return true if role specification is valid, otherwise false + */ +static bool +parse_location_role(const char *role_spec, enum rsc_role_e *role) +{ + if (role_spec == NULL) { + *role = pcmk_role_unknown; + return true; + } + + *role = pcmk_parse_role(role_spec); + switch (*role) { + case pcmk_role_unknown: + return false; + + case pcmk_role_started: + case pcmk_role_unpromoted: + /* Any promotable clone instance cannot be promoted without being in + * the unpromoted role first. Therefore, any constraint for the + * started or unpromoted role applies to every role. + */ + *role = pcmk_role_unknown; + break; + + default: + break; + } + return true; +} + +/*! + * \internal + * \brief Generate a location constraint from a rule + * + * \param[in,out] rsc Resource that constraint is for + * \param[in] rule_xml Rule XML (sub-element of location constraint) + * \param[in] discovery Value of \c PCMK_XA_RESOURCE_DISCOVERY for + * constraint + * \param[out] next_change Where to set when rule evaluation will change + * \param[in,out] rule_input Values used to evaluate rule criteria + * (node-specific values will be overwritten by + * this function) + * + * \return true if rule is valid, otherwise false + */ +static bool generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml, const char *discovery, crm_time_t *next_change, - pe_re_match_data_t *re_match_data) + pcmk_rule_input_t *rule_input) { const char *rule_id = NULL; const char *score = NULL; const char *boolean = NULL; - const char *role = NULL; + const char *role_spec = NULL; GList *iter = NULL; - GList *nodes = NULL; - bool do_and = true; - bool accept = true; bool raw_score = true; bool score_allocated = false; - pe__location_t *location_rule = NULL; + pcmk__location_t *location_rule = NULL; + enum rsc_role_e role = pcmk_role_unknown; + enum pcmk__combine combine = pcmk__combine_unknown; rule_xml = expand_idref(rule_xml, rsc->cluster->input); if (rule_xml == NULL) { - return NULL; + return false; // Error already logged } - rule_id = crm_element_value(rule_xml, XML_ATTR_ID); - boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP); - role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE); + rule_id = crm_element_value(rule_xml, PCMK_XA_ID); + if (rule_id == NULL) { + pcmk__config_err("Ignoring " PCMK_XE_RULE " without " PCMK_XA_ID + " in location constraint"); + return false; + } - crm_trace("Processing rule: %s", rule_id); + boolean = crm_element_value(rule_xml, PCMK_XA_BOOLEAN_OP); + role_spec = crm_element_value(rule_xml, PCMK_XA_ROLE); - if ((role != NULL) && (text2role(role) == pcmk_role_unknown)) { - pe_err("Bad role specified for %s: %s", rule_id, role); - return NULL; + if (parse_location_role(role_spec, &role)) { + crm_trace("Setting rule %s role filter to %s", rule_id, role_spec); + } else { + pcmk__config_err("Ignoring rule %s: Invalid " PCMK_XA_ROLE " '%s'", + rule_id, role_spec); + return false; } - score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE); + crm_trace("Processing location constraint rule %s", rule_id); + + score = crm_element_value(rule_xml, PCMK_XA_SCORE); if (score == NULL) { - score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE); + score = crm_element_value(rule_xml, PCMK_XA_SCORE_ATTRIBUTE); if (score != NULL) { raw_score = false; } } - if (pcmk__str_eq(boolean, "or", pcmk__str_casei)) { - do_and = false; + + combine = pcmk__parse_combine(boolean); + switch (combine) { + case pcmk__combine_and: + case pcmk__combine_or: + break; + + default: + /* @COMPAT When we can break behavioral backward compatibility, + * return false + */ + pcmk__config_warn("Location constraint rule %s has invalid " + PCMK_XA_BOOLEAN_OP " value '%s', using default " + "'" PCMK_VALUE_AND "'", + rule_id, boolean); + combine = pcmk__combine_and; + break; } location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL); + CRM_CHECK(location_rule != NULL, return NULL); - if (location_rule == NULL) { - return NULL; - } + location_rule->role_filter = role; - if ((re_match_data != NULL) && (re_match_data->nregs > 0) - && (re_match_data->pmatch[0].rm_so != -1) && !raw_score) { + if ((rule_input->rsc_id != NULL) && (rule_input->rsc_id_nmatches > 0) + && !raw_score) { - char *result = pe_expand_re_matches(score, re_match_data); + char *result = pcmk__replace_submatches(score, rule_input->rsc_id, + rule_input->rsc_id_submatches, + rule_input->rsc_id_nmatches); if (result != NULL) { score = result; @@ -116,69 +195,21 @@ generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml, } } - if (role != NULL) { - crm_trace("Setting role filter: %s", role); - location_rule->role_filter = text2role(role); - if (location_rule->role_filter == pcmk_role_unpromoted) { - /* Any promotable clone cannot be promoted without being in the - * unpromoted role first. Ergo, any constraint for the unpromoted - * role applies to every role. - */ - location_rule->role_filter = pcmk_role_unknown; - } - } - if (do_and) { - nodes = pcmk__copy_node_list(rsc->cluster->nodes, true); - for (iter = nodes; iter != NULL; iter = iter->next) { - pcmk_node_t *node = iter->data; - - node->weight = get_node_score(rule_id, score, raw_score, node, rsc); - } - } - for (iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) { - int score_f = 0; pcmk_node_t *node = iter->data; - pe_match_data_t match_data = { - .re = re_match_data, - .params = pe_rsc_params(rsc, node, rsc->cluster), - .meta = rsc->meta, - }; - accept = pe_test_rule(rule_xml, node->details->attrs, pcmk_role_unknown, - rsc->cluster->now, next_change, &match_data); + rule_input->node_attrs = node->details->attrs; + rule_input->rsc_params = pe_rsc_params(rsc, node, rsc->cluster); - crm_trace("Rule %s %s on %s", ID(rule_xml), accept? "passed" : "failed", - pe__node_name(node)); - - score_f = get_node_score(rule_id, score, raw_score, node, rsc); - - if (accept) { - pcmk_node_t *local = pe_find_node_id(nodes, node->details->id); - - if ((local == NULL) && do_and) { - continue; - - } else if (local == NULL) { - local = pe__copy_node(node); - nodes = g_list_append(nodes, local); - } + if (pcmk_evaluate_rule(rule_xml, rule_input, + next_change) == pcmk_rc_ok) { + pcmk_node_t *local = pe__copy_node(node); - if (!do_and) { - local->weight = pcmk__add_scores(local->weight, score_f); - } - crm_trace("%s has score %s after %s", pe__node_name(node), + location_rule->nodes = g_list_prepend(location_rule->nodes, local); + local->weight = get_node_score(rule_id, score, raw_score, node, + rsc); + crm_trace("%s has score %s after %s", pcmk__node_name(node), pcmk_readable_score(local->weight), rule_id); - - } else if (do_and && !accept) { - // Remove it - pcmk_node_t *delete = pe_find_node_id(nodes, node->details->id); - - if (delete != NULL) { - nodes = g_list_remove(nodes, delete); - crm_trace("%s did not match", pe__node_name(node)); - } - free(delete); } } @@ -186,27 +217,26 @@ generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml, free((char *)score); } - location_rule->node_list_rh = nodes; - if (location_rule->node_list_rh == NULL) { - crm_trace("No matching nodes for rule %s", rule_id); - return NULL; + if (location_rule->nodes == NULL) { + crm_trace("No matching nodes for location constraint rule %s", rule_id); + } else { + crm_trace("Location constraint rule %s matched %d nodes", + rule_id, g_list_length(location_rule->nodes)); } - - crm_trace("%s: %d nodes matched", - rule_id, g_list_length(location_rule->node_list_rh)); - return location_rule; + return true; } static void -unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc, const char *role, - const char *score, pe_re_match_data_t *re_match_data) +unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc, + const char *role_spec, const char *score, + char *rsc_id_match, int rsc_id_nmatches, + regmatch_t *rsc_id_submatches) { - pe__location_t *location = NULL; - const char *rsc_id = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE); - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *node = crm_element_value(xml_obj, XML_CIB_TAG_NODE); + const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC); + const char *id = crm_element_value(xml_obj, PCMK_XA_ID); + const char *node = crm_element_value(xml_obj, PCMK_XE_NODE); const char *discovery = crm_element_value(xml_obj, - XML_LOCATION_ATTR_DISCOVERY); + PCMK_XA_RESOURCE_DISCOVERY); if (rsc == NULL) { pcmk__config_warn("Ignoring constraint '%s' because resource '%s' " @@ -215,37 +245,86 @@ unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc, const char *role, } if (score == NULL) { - score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + score = crm_element_value(xml_obj, PCMK_XA_SCORE); } if ((node != NULL) && (score != NULL)) { int score_i = char2score(score); - pcmk_node_t *match = pe_find_node(rsc->cluster->nodes, node); - - if (!match) { + pcmk_node_t *match = pcmk_find_node(rsc->cluster, node); + enum rsc_role_e role = pcmk_role_unknown; + pcmk__location_t *location = NULL; + + if (match == NULL) { + crm_info("Ignoring location constraint %s " + "because '%s' is not a known node", + pcmk__s(id, "without ID"), node); return; } + + if (role_spec == NULL) { + role_spec = crm_element_value(xml_obj, PCMK_XA_ROLE); + } + if (parse_location_role(role_spec, &role)) { + crm_trace("Setting location constraint %s role filter: %s", + id, role_spec); + } else { + /* @COMPAT The previous behavior of creating the constraint ignoring + * the role is retained for now, but we should ignore the entire + * constraint when we can break backward compatibility. + */ + pcmk__config_err("Ignoring role in constraint %s: " + "Invalid value '%s'", id, role_spec); + } + location = pcmk__new_location(id, rsc, score_i, discovery, match); + if (location == NULL) { + return; // Error already logged + } + location->role_filter = role; } else { bool empty = true; crm_time_t *next_change = crm_time_new_undefined(); + pcmk_rule_input_t rule_input = { + .now = rsc->cluster->now, + .rsc_meta = rsc->meta, + .rsc_id = rsc_id_match, + .rsc_id_submatches = rsc_id_submatches, + .rsc_id_nmatches = rsc_id_nmatches, + }; - /* This loop is logically parallel to pe_evaluate_rules(), except + /* This loop is logically parallel to pcmk__evaluate_rules(), except * instead of checking whether any rule is active, we set up location * constraints for each active rule. + * + * @COMPAT When we can break backward compatibility, limit location + * constraints to a single rule, for consistency with other contexts. + * Since a rule may contain other rules, this does not prohibit any + * existing use cases. */ - for (xmlNode *rule_xml = first_named_child(xml_obj, XML_TAG_RULE); - rule_xml != NULL; rule_xml = crm_next_same_xml(rule_xml)) { - empty = false; - crm_trace("Unpacking %s/%s", id, ID(rule_xml)); - generate_location_rule(rsc, rule_xml, discovery, next_change, - re_match_data); + for (xmlNode *rule_xml = pcmk__xe_first_child(xml_obj, PCMK_XE_RULE, + NULL, NULL); + rule_xml != NULL; rule_xml = pcmk__xe_next_same(rule_xml)) { + + if (generate_location_rule(rsc, rule_xml, discovery, next_change, + &rule_input)) { + if (empty) { + empty = false; + continue; + } + pcmk__warn_once(pcmk__wo_location_rules, + "Support for multiple " PCMK_XE_RULE + " elements in a location constraint is " + "deprecated and will be removed in a future " + "release (use a single new rule combining the " + "previous rules with " PCMK_XA_BOOLEAN_OP + " set to '" PCMK_VALUE_OR "' instead)"); + } } if (empty) { pcmk__config_err("Ignoring constraint '%s' because it contains " - "no rules", id); + "no valid rules", id); } /* If there is a point in the future when the evaluation of a rule will @@ -258,51 +337,25 @@ unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc, const char *role, "location rule evaluation"); } crm_time_free(next_change); - return; - } - - if (role == NULL) { - role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE); - } - - if ((location != NULL) && (role != NULL)) { - if (text2role(role) == pcmk_role_unknown) { - pe_err("Invalid constraint %s: Bad role %s", id, role); - return; - - } else { - enum rsc_role_e r = text2role(role); - switch (r) { - case pcmk_role_unknown: - case pcmk_role_started: - case pcmk_role_unpromoted: - /* Applies to all */ - location->role_filter = pcmk_role_unknown; - break; - default: - location->role_filter = r; - break; - } - } } } static void unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) { - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE); + const char *id = crm_element_value(xml_obj, PCMK_XA_ID); + const char *value = crm_element_value(xml_obj, PCMK_XA_RSC); if (value) { pcmk_resource_t *rsc; rsc = pcmk__find_constraint_resource(scheduler->resources, value); - unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL); + unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL, 0, NULL); } - value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE_PATTERN); + value = crm_element_value(xml_obj, PCMK_XA_RSC_PATTERN); if (value) { - regex_t *r_patt = calloc(1, sizeof(regex_t)); + regex_t regex; bool invert = false; if (value[0] == '!') { @@ -310,11 +363,10 @@ unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) invert = true; } - if (regcomp(r_patt, value, REG_EXTENDED) != 0) { + if (regcomp(®ex, value, REG_EXTENDED) != 0) { pcmk__config_err("Ignoring constraint '%s' because " - XML_LOC_ATTR_SOURCE_PATTERN + PCMK_XA_RSC_PATTERN " has invalid value '%s'", id, value); - free(r_patt); return; } @@ -326,29 +378,24 @@ unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) regmatch_t *pmatch = NULL; int status; - if (r_patt->re_nsub > 0) { - nregs = r_patt->re_nsub + 1; + if (regex.re_nsub > 0) { + nregs = regex.re_nsub + 1; } else { nregs = 1; } - pmatch = calloc(nregs, sizeof(regmatch_t)); + pmatch = pcmk__assert_alloc(nregs, sizeof(regmatch_t)); - status = regexec(r_patt, r->id, nregs, pmatch, 0); + status = regexec(®ex, r->id, nregs, pmatch, 0); if (!invert && (status == 0)) { - pe_re_match_data_t re_match_data = { - .string = r->id, - .nregs = nregs, - .pmatch = pmatch - }; - crm_debug("'%s' matched '%s' for %s", r->id, value, id); - unpack_rsc_location(xml_obj, r, NULL, NULL, &re_match_data); + unpack_rsc_location(xml_obj, r, NULL, NULL, r->id, nregs, + pmatch); } else if (invert && (status != 0)) { crm_debug("'%s' is an inverted match of '%s' for %s", r->id, value, id); - unpack_rsc_location(xml_obj, r, NULL, NULL, NULL); + unpack_rsc_location(xml_obj, r, NULL, NULL, NULL, 0, NULL); } else { crm_trace("'%s' does not match '%s' for %s", r->id, value, id); @@ -357,8 +404,7 @@ unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) free(pmatch); } - regfree(r_patt); - free(r_patt); + regfree(®ex); } } @@ -378,9 +424,9 @@ unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml, CRM_CHECK(xml_obj != NULL, return EINVAL); - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return pcmk_rc_unpack_error; } @@ -388,11 +434,11 @@ unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml, // Check whether there are any resource sets with template or tag references *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler); if (*expanded_xml != NULL) { - crm_log_xml_trace(*expanded_xml, "Expanded rsc_location"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION); return pcmk_rc_ok; } - rsc_id = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE); + rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC); if (rsc_id == NULL) { return pcmk_rc_ok; } @@ -407,12 +453,14 @@ unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml, return pcmk_rc_ok; } - state = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE); + state = crm_element_value(xml_obj, PCMK_XA_ROLE); - *expanded_xml = copy_xml(xml_obj); + *expanded_xml = pcmk__xml_copy(NULL, xml_obj); - // Convert any template or tag reference into constraint resource_set - if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_LOC_ATTR_SOURCE, + /* Convert any template or tag reference into constraint + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC, false, scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; @@ -421,11 +469,13 @@ unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (rsc_set != NULL) { if (state != NULL) { - // Move "rsc-role" into converted resource_set as "role" attribute - crm_xml_add(rsc_set, "role", state); - xml_remove_prop(*expanded_xml, XML_RULE_ATTR_ROLE); + /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as + * PCMK_XA_ROLE attribute + */ + crm_xml_add(rsc_set, PCMK_XA_ROLE, state); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_ROLE); } - crm_log_xml_trace(*expanded_xml, "Expanded rsc_location"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION); } else { // No sets @@ -449,29 +499,30 @@ unpack_location_set(xmlNode *location, xmlNode *set, CRM_CHECK(set != NULL, return EINVAL); - set_id = ID(set); + set_id = pcmk__xe_id(set); if (set_id == NULL) { - pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET " without " - XML_ATTR_ID " in constraint '%s'", - pcmk__s(ID(location), "(missing ID)")); + pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET " without " + PCMK_XA_ID " in constraint '%s'", + pcmk__s(pcmk__xe_id(location), "(missing ID)")); return pcmk_rc_unpack_error; } - role = crm_element_value(set, "role"); - local_score = crm_element_value(set, XML_RULE_ATTR_SCORE); + role = crm_element_value(set, PCMK_XA_ROLE); + local_score = crm_element_value(set, PCMK_XA_SCORE); - for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { resource = pcmk__find_constraint_resource(scheduler->resources, - ID(xml_rsc)); + pcmk__xe_id(xml_rsc)); if (resource == NULL) { pcmk__config_err("%s: No resource found for %s", - set_id, ID(xml_rsc)); + set_id, pcmk__xe_id(xml_rsc)); return pcmk_rc_unpack_error; } - unpack_rsc_location(location, resource, role, local_score, NULL); + unpack_rsc_location(location, resource, role, local_score, NULL, 0, + NULL); } return pcmk_rc_ok; @@ -495,8 +546,8 @@ pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) xml_obj = expanded_xml; } - for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL; - set = crm_next_same_xml(set)) { + for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL); + set != NULL; set = pcmk__xe_next_same(set)) { any_sets = true; set = expand_idref(set, scheduler->input); @@ -530,63 +581,63 @@ pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) * \param[in] discover_mode Resource discovery option for constraint * \param[in] node Node in constraint (or NULL if rule-based) * - * \return Newly allocated location constraint + * \return Newly allocated location constraint on success, otherwise NULL * \note The result will be added to the cluster (via \p rsc) and should not be * freed separately. */ -pe__location_t * +pcmk__location_t * pcmk__new_location(const char *id, pcmk_resource_t *rsc, int node_score, const char *discover_mode, pcmk_node_t *node) { - pe__location_t *new_con = NULL; + pcmk__location_t *new_con = NULL; + + CRM_CHECK((node != NULL) || (node_score == 0), return NULL); if (id == NULL) { - pe_err("Invalid constraint: no ID specified"); + pcmk__config_err("Invalid constraint: no ID specified"); return NULL; + } - } else if (rsc == NULL) { - pe_err("Invalid constraint %s: no resource specified", id); + if (rsc == NULL) { + pcmk__config_err("Invalid constraint %s: no resource specified", id); return NULL; - - } else if (node == NULL) { - CRM_CHECK(node_score == 0, return NULL); } - new_con = calloc(1, sizeof(pe__location_t)); - if (new_con != NULL) { - new_con->id = strdup(id); - new_con->rsc_lh = rsc; - new_con->node_list_rh = NULL; - new_con->role_filter = pcmk_role_unknown; + new_con = pcmk__assert_alloc(1, sizeof(pcmk__location_t)); + new_con->id = pcmk__str_copy(id); + new_con->rsc = rsc; + new_con->nodes = NULL; + new_con->role_filter = pcmk_role_unknown; - if (pcmk__str_eq(discover_mode, "always", - pcmk__str_null_matches|pcmk__str_casei)) { - new_con->discover_mode = pcmk_probe_always; + if (pcmk__str_eq(discover_mode, PCMK_VALUE_ALWAYS, + pcmk__str_null_matches|pcmk__str_casei)) { + new_con->discover_mode = pcmk_probe_always; - } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) { - new_con->discover_mode = pcmk_probe_never; + } else if (pcmk__str_eq(discover_mode, PCMK_VALUE_NEVER, + pcmk__str_casei)) { + new_con->discover_mode = pcmk_probe_never; - } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) { - new_con->discover_mode = pcmk_probe_exclusive; - rsc->exclusive_discover = TRUE; + } else if (pcmk__str_eq(discover_mode, PCMK_VALUE_EXCLUSIVE, + pcmk__str_casei)) { + new_con->discover_mode = pcmk_probe_exclusive; + rsc->exclusive_discover = TRUE; - } else { - pe_err("Invalid " XML_LOCATION_ATTR_DISCOVERY " value %s " - "in location constraint", discover_mode); - } - - if (node != NULL) { - pcmk_node_t *copy = pe__copy_node(node); + } else { + pcmk__config_err("Invalid " PCMK_XA_RESOURCE_DISCOVERY " value %s " + "in location constraint", discover_mode); + } - copy->weight = node_score; - new_con->node_list_rh = g_list_prepend(NULL, copy); - } + if (node != NULL) { + pcmk_node_t *copy = pe__copy_node(node); - rsc->cluster->placement_constraints = g_list_prepend( - rsc->cluster->placement_constraints, new_con); - rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con); + copy->weight = node_score; + new_con->nodes = g_list_prepend(NULL, copy); } + rsc->cluster->placement_constraints = g_list_prepend( + rsc->cluster->placement_constraints, new_con); + rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con); + return new_con; } @@ -601,9 +652,9 @@ pcmk__apply_locations(pcmk_scheduler_t *scheduler) { for (GList *iter = scheduler->placement_constraints; iter != NULL; iter = iter->next) { - pe__location_t *location = iter->data; + pcmk__location_t *location = iter->data; - location->rsc_lh->cmds->apply_location(location->rsc_lh, location); + location->rsc->cmds->apply_location(location->rsc, location); } } @@ -618,7 +669,7 @@ pcmk__apply_locations(pcmk_scheduler_t *scheduler) * apply_location() method should be used instead in most cases. */ void -pcmk__apply_location(pcmk_resource_t *rsc, pe__location_t *location) +pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *location) { bool need_role = false; @@ -627,40 +678,39 @@ pcmk__apply_location(pcmk_resource_t *rsc, pe__location_t *location) // If a role was specified, ensure constraint is applicable need_role = (location->role_filter > pcmk_role_unknown); if (need_role && (location->role_filter != rsc->next_role)) { - pe_rsc_trace(rsc, - "Not applying %s to %s because role will be %s not %s", - location->id, rsc->id, role2text(rsc->next_role), - role2text(location->role_filter)); + pcmk__rsc_trace(rsc, + "Not applying %s to %s because role will be %s not %s", + location->id, rsc->id, pcmk_role_text(rsc->next_role), + pcmk_role_text(location->role_filter)); return; } - if (location->node_list_rh == NULL) { - pe_rsc_trace(rsc, "Not applying %s to %s because no nodes match", - location->id, rsc->id); + if (location->nodes == NULL) { + pcmk__rsc_trace(rsc, "Not applying %s to %s because no nodes match", + location->id, rsc->id); return; } - pe_rsc_trace(rsc, "Applying %s%s%s to %s", location->id, - (need_role? " for role " : ""), - (need_role? role2text(location->role_filter) : ""), rsc->id); - - for (GList *iter = location->node_list_rh; - iter != NULL; iter = iter->next) { + pcmk__rsc_trace(rsc, "Applying %s%s%s to %s", location->id, + (need_role? " for role " : ""), + (need_role? pcmk_role_text(location->role_filter) : ""), + rsc->id); + for (GList *iter = location->nodes; iter != NULL; iter = iter->next) { pcmk_node_t *node = iter->data; pcmk_node_t *allowed_node = g_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (allowed_node == NULL) { - pe_rsc_trace(rsc, "* = %d on %s", - node->weight, pe__node_name(node)); + pcmk__rsc_trace(rsc, "* = %d on %s", + node->weight, pcmk__node_name(node)); allowed_node = pe__copy_node(node); g_hash_table_insert(rsc->allowed_nodes, (gpointer) allowed_node->details->id, allowed_node); } else { - pe_rsc_trace(rsc, "* + %d on %s", - node->weight, pe__node_name(node)); + pcmk__rsc_trace(rsc, "* + %d on %s", + node->weight, pcmk__node_name(node)); allowed_node->weight = pcmk__add_scores(allowed_node->weight, node->weight); } diff --git a/lib/pacemaker/pcmk_sched_migration.c b/lib/pacemaker/pcmk_sched_migration.c index 5231bf7..65faa3d 100644 --- a/lib/pacemaker/pcmk_sched_migration.c +++ b/lib/pacemaker/pcmk_sched_migration.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. * @@ -11,7 +11,7 @@ #include <stdbool.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -28,11 +28,11 @@ static void add_migration_meta(pcmk_action_t *action, const pcmk_node_t *source, const pcmk_node_t *target) { - add_hash_param(action->meta, XML_LRM_ATTR_MIGRATE_SOURCE, - source->details->uname); + pcmk__insert_meta(action, PCMK__META_MIGRATE_SOURCE, + source->details->uname); - add_hash_param(action->meta, XML_LRM_ATTR_MIGRATE_TARGET, - target->details->uname); + pcmk__insert_meta(action, PCMK__META_MIGRATE_TARGET, + target->details->uname); } /*! @@ -50,10 +50,10 @@ pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current) pcmk_action_t *start = NULL; pcmk_action_t *stop = NULL; - pe_rsc_trace(rsc, "Creating actions to %smigrate %s from %s to %s", - ((rsc->partial_migration_target == NULL)? "" : "partially "), - rsc->id, pe__node_name(current), - pe__node_name(rsc->allocated_to)); + pcmk__rsc_trace(rsc, "Creating actions to %smigrate %s from %s to %s", + ((rsc->partial_migration_target == NULL)? "" : "partially "), + rsc->id, pcmk__node_name(current), + pcmk__node_name(rsc->allocated_to)); start = start_action(rsc, rsc->allocated_to, TRUE); stop = stop_action(rsc, current, TRUE); @@ -68,15 +68,15 @@ pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current) PCMK_ACTION_MIGRATE_FROM, rsc->allocated_to, TRUE, rsc->cluster); - pe__set_action_flags(start, pcmk_action_migratable); - pe__set_action_flags(stop, pcmk_action_migratable); + pcmk__set_action_flags(start, pcmk_action_migratable); + pcmk__set_action_flags(stop, pcmk_action_migratable); // This is easier than trying to delete it from the graph - pe__set_action_flags(start, pcmk_action_pseudo); + pcmk__set_action_flags(start, pcmk_action_pseudo); if (rsc->partial_migration_target == NULL) { - pe__set_action_flags(migrate_from, pcmk_action_migratable); - pe__set_action_flags(migrate_to, pcmk_action_migratable); + pcmk__set_action_flags(migrate_from, pcmk_action_migratable); + pcmk__set_action_flags(migrate_to, pcmk_action_migratable); migrate_to->needs = start->needs; // Probe -> migrate_to -> migrate_from @@ -93,7 +93,7 @@ pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current) pcmk__ar_ordered|pcmk__ar_unmigratable_then_blocks, rsc->cluster); } else { - pe__set_action_flags(migrate_from, pcmk_action_migratable); + pcmk__set_action_flags(migrate_from, pcmk_action_migratable); migrate_from->needs = start->needs; // Probe -> migrate_from (migrate_to already completed) @@ -132,10 +132,11 @@ pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current) * * However we know Pacemaker Remote connection resources don't * require this, so we skip this for them. (Although it wouldn't - * hurt, and now that record-pending defaults to true, skipping it - * matters even less.) + * hurt, and now that PCMK_META_RECORD_PENDING defaults to true, + * skipping it matters even less.) */ - add_hash_param(migrate_to->meta, XML_OP_ATTR_PENDING, "true"); + pcmk__insert_meta(migrate_to, + PCMK_META_RECORD_PENDING, PCMK_VALUE_TRUE); } } @@ -159,12 +160,12 @@ pcmk__abort_dangling_migration(void *data, void *user_data) bool cleanup = pcmk_is_set(rsc->cluster->flags, pcmk_sched_remove_after_stop); - pe_rsc_trace(rsc, - "Scheduling stop%s for %s on %s due to dangling migration", - (cleanup? " and cleanup" : ""), rsc->id, - pe__node_name(dangling_source)); + pcmk__rsc_trace(rsc, + "Scheduling stop%s for %s on %s due to dangling migration", + (cleanup? " and cleanup" : ""), rsc->id, + pcmk__node_name(dangling_source)); stop = stop_action(rsc, dangling_source, FALSE); - pe__set_action_flags(stop, pcmk_action_migration_abort); + pcmk__set_action_flags(stop, pcmk_action_migration_abort); if (cleanup) { pcmk__schedule_cleanup(rsc, dangling_source, false); } @@ -185,41 +186,42 @@ pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current) CRM_CHECK(rsc != NULL, return false); if (!pcmk_is_set(rsc->flags, pcmk_rsc_migratable)) { - pe_rsc_trace(rsc, "%s cannot migrate because " - "the configuration does not allow it", - rsc->id); + pcmk__rsc_trace(rsc, + "%s cannot migrate because " + "the configuration does not allow it", rsc->id); return false; } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, "%s cannot migrate because it is not managed", - rsc->id); + pcmk__rsc_trace(rsc, "%s cannot migrate because it is not managed", + rsc->id); return false; } if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { - pe_rsc_trace(rsc, "%s cannot migrate because it is failed", - rsc->id); + pcmk__rsc_trace(rsc, "%s cannot migrate because it is failed", rsc->id); return false; } if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) { - pe_rsc_trace(rsc, "%s cannot migrate because it has a start pending", - rsc->id); + pcmk__rsc_trace(rsc, "%s cannot migrate because it has a start pending", + rsc->id); return false; } if ((current == NULL) || current->details->unclean) { - pe_rsc_trace(rsc, "%s cannot migrate because " - "its current node (%s) is unclean", - rsc->id, pe__node_name(current)); + pcmk__rsc_trace(rsc, + "%s cannot migrate because " + "its current node (%s) is unclean", + rsc->id, pcmk__node_name(current)); return false; } if ((rsc->allocated_to == NULL) || rsc->allocated_to->details->unclean) { - pe_rsc_trace(rsc, "%s cannot migrate because " - "its next node (%s) is unclean", - rsc->id, pe__node_name(rsc->allocated_to)); + pcmk__rsc_trace(rsc, + "%s cannot migrate because " + "its next node (%s) is unclean", + rsc->id, pcmk__node_name(rsc->allocated_to)); return false; } @@ -241,8 +243,7 @@ task_from_action_or_key(const pcmk_action_t *action, const char *key) char *res = NULL; if (action != NULL) { - res = strdup(action->task); - CRM_ASSERT(res != NULL); + res = pcmk__str_copy(action->task); } else if (key != NULL) { parse_op_key(key, NULL, &res, NULL); } @@ -260,7 +261,7 @@ task_from_action_or_key(const pcmk_action_t *action, const char *key) * \param[in,out] order Ordering constraint to check */ void -pcmk__order_migration_equivalents(pe__ordering_t *order) +pcmk__order_migration_equivalents(pcmk__action_relation_t *order) { char *first_task = NULL; char *then_task = NULL; @@ -268,25 +269,23 @@ pcmk__order_migration_equivalents(pe__ordering_t *order) bool first_migratable; // Only orderings between unrelated resources are relevant - if ((order->lh_rsc == NULL) || (order->rh_rsc == NULL) - || (order->lh_rsc == order->rh_rsc) - || is_parent(order->lh_rsc, order->rh_rsc) - || is_parent(order->rh_rsc, order->lh_rsc)) { + if ((order->rsc1 == NULL) || (order->rsc2 == NULL) + || (order->rsc1 == order->rsc2) + || is_parent(order->rsc1, order->rsc2) + || is_parent(order->rsc2, order->rsc1)) { return; } // Only orderings involving at least one migratable resource are relevant - first_migratable = pcmk_is_set(order->lh_rsc->flags, pcmk_rsc_migratable); - then_migratable = pcmk_is_set(order->rh_rsc->flags, pcmk_rsc_migratable); + first_migratable = pcmk_is_set(order->rsc1->flags, pcmk_rsc_migratable); + then_migratable = pcmk_is_set(order->rsc2->flags, pcmk_rsc_migratable); if (!first_migratable && !then_migratable) { return; } // Check which actions are involved - first_task = task_from_action_or_key(order->lh_action, - order->lh_action_task); - then_task = task_from_action_or_key(order->rh_action, - order->rh_action_task); + first_task = task_from_action_or_key(order->action1, order->task1); + then_task = task_from_action_or_key(order->action2, order->task2); if (pcmk__str_eq(first_task, PCMK_ACTION_START, pcmk__str_none) && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) { @@ -296,31 +295,31 @@ pcmk__order_migration_equivalents(pe__ordering_t *order) if (first_migratable && then_migratable) { /* A start then B start * -> A migrate_from then B migrate_to */ - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_MIGRATE_FROM, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); } if (then_migratable) { if (first_migratable) { - pe__set_order_flags(flags, pcmk__ar_if_first_unmigratable); + pcmk__set_relation_flags(flags, pcmk__ar_if_first_unmigratable); } /* A start then B start * -> A start then B migrate_to (if start is not part of a * migration) */ - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_START, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); } } else if (then_migratable @@ -330,29 +329,29 @@ pcmk__order_migration_equivalents(pe__ordering_t *order) uint32_t flags = pcmk__ar_ordered; if (first_migratable) { - pe__set_order_flags(flags, pcmk__ar_if_first_unmigratable); + pcmk__set_relation_flags(flags, pcmk__ar_if_first_unmigratable); } /* For an ordering "stop A then stop B", if A is moving via restart, and * B is migrating, enforce that B's migrate_to occurs after A's stop. */ - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP, 0), + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_STOP, 0), NULL, - order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); // Also order B's migrate_from after A's stop during partial migrations - if (order->rh_rsc->partial_migration_target) { - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP, + if (order->rsc2->partial_migration_target != NULL) { + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_STOP, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_FROM, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); } } else if (pcmk__str_eq(first_task, PCMK_ACTION_PROMOTE, pcmk__str_none) @@ -363,13 +362,13 @@ pcmk__order_migration_equivalents(pe__ordering_t *order) if (then_migratable) { /* A promote then B start * -> A promote then B migrate_to */ - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_PROMOTE, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); } } else if (pcmk__str_eq(first_task, PCMK_ACTION_DEMOTE, pcmk__str_none) @@ -380,23 +379,23 @@ pcmk__order_migration_equivalents(pe__ordering_t *order) if (then_migratable) { /* A demote then B stop * -> A demote then B migrate_to */ - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_DEMOTE, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); // Order B migrate_from after A demote during partial migrations - if (order->rh_rsc->partial_migration_target) { - pcmk__new_ordering(order->lh_rsc, - pcmk__op_key(order->lh_rsc->id, + if (order->rsc2->partial_migration_target != NULL) { + pcmk__new_ordering(order->rsc1, + pcmk__op_key(order->rsc1->id, PCMK_ACTION_DEMOTE, 0), - NULL, order->rh_rsc, - pcmk__op_key(order->rh_rsc->id, + NULL, order->rsc2, + pcmk__op_key(order->rsc2->id, PCMK_ACTION_MIGRATE_FROM, 0), - NULL, flags, order->lh_rsc->cluster); + NULL, flags, order->rsc1->cluster); } } } diff --git a/lib/pacemaker/pcmk_sched_nodes.c b/lib/pacemaker/pcmk_sched_nodes.c index 9cf5545..bddd23e 100644 --- a/lib/pacemaker/pcmk_sched_nodes.c +++ b/lib/pacemaker/pcmk_sched_nodes.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. * @@ -8,7 +8,7 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <pacemaker-internal.h> #include <pacemaker.h> @@ -41,7 +41,7 @@ pcmk__node_available(const pcmk_node_t *node, bool consider_score, } // @TODO Go through all callers to see which should set consider_guest - if (consider_guest && pe__is_guest_node(node)) { + if (consider_guest && pcmk__is_guest_or_bundle_node(node)) { pcmk_resource_t *guest = node->details->remote_rsc->container; if (guest->fns->location(guest, NULL, FALSE) == NULL) { @@ -205,8 +205,8 @@ compare_nodes(gconstpointer a, gconstpointer b, gpointer data) const pcmk_node_t *node2 = (const pcmk_node_t *) b; const pcmk_node_t *preferred = (const pcmk_node_t *) data; - int node1_score = -INFINITY; - int node2_score = -INFINITY; + int node1_score = -PCMK_SCORE_INFINITY; + int node2_score = -PCMK_SCORE_INFINITY; int result = 0; @@ -228,35 +228,37 @@ compare_nodes(gconstpointer a, gconstpointer b, gpointer data) if (node1_score > node2_score) { crm_trace("%s before %s (score %d > %d)", - pe__node_name(node1), pe__node_name(node2), + pcmk__node_name(node1), pcmk__node_name(node2), node1_score, node2_score); return -1; } if (node1_score < node2_score) { crm_trace("%s after %s (score %d < %d)", - pe__node_name(node1), pe__node_name(node2), + pcmk__node_name(node1), pcmk__node_name(node2), node1_score, node2_score); return 1; } // If appropriate, compare node utilization - if (pcmk__str_eq(node1->details->data_set->placement_strategy, "minimal", - pcmk__str_casei)) { + if (pcmk__str_eq(node1->details->data_set->placement_strategy, + PCMK_VALUE_MINIMAL, pcmk__str_casei)) { goto equal; } - if (pcmk__str_eq(node1->details->data_set->placement_strategy, "balanced", - pcmk__str_casei)) { + if (pcmk__str_eq(node1->details->data_set->placement_strategy, + PCMK_VALUE_BALANCED, pcmk__str_casei)) { + result = pcmk__compare_node_capacities(node1, node2); if (result < 0) { crm_trace("%s before %s (greater capacity by %d attributes)", - pe__node_name(node1), pe__node_name(node2), result * -1); + pcmk__node_name(node1), pcmk__node_name(node2), + result * -1); return -1; } else if (result > 0) { crm_trace("%s after %s (lower capacity by %d attributes)", - pe__node_name(node1), pe__node_name(node2), result); + pcmk__node_name(node1), pcmk__node_name(node2), result); return 1; } } @@ -265,13 +267,13 @@ compare_nodes(gconstpointer a, gconstpointer b, gpointer data) if (node1->details->num_resources < node2->details->num_resources) { crm_trace("%s before %s (%d resources < %d)", - pe__node_name(node1), pe__node_name(node2), + pcmk__node_name(node1), pcmk__node_name(node2), node1->details->num_resources, node2->details->num_resources); return -1; } else if (node1->details->num_resources > node2->details->num_resources) { crm_trace("%s after %s (%d resources > %d)", - pe__node_name(node1), pe__node_name(node2), + pcmk__node_name(node1), pcmk__node_name(node2), node1->details->num_resources, node2->details->num_resources); return 1; } @@ -279,13 +281,13 @@ compare_nodes(gconstpointer a, gconstpointer b, gpointer data) // Check whether one node is already running desired resource if (preferred != NULL) { - if (pe__same_node(preferred, node1)) { + if (pcmk__same_node(preferred, node1)) { crm_trace("%s before %s (preferred node)", - pe__node_name(node1), pe__node_name(node2)); + pcmk__node_name(node1), pcmk__node_name(node2)); return -1; - } else if (pe__same_node(preferred, node2)) { + } else if (pcmk__same_node(preferred, node2)) { crm_trace("%s after %s (not preferred node)", - pe__node_name(node1), pe__node_name(node2)); + pcmk__node_name(node1), pcmk__node_name(node2)); return 1; } } @@ -295,15 +297,15 @@ equal: result = strcmp(node1->details->uname, node2->details->uname); if (result < 0) { crm_trace("%s before %s (name)", - pe__node_name(node1), pe__node_name(node2)); + pcmk__node_name(node1), pcmk__node_name(node2)); return -1; } else if (result > 0) { crm_trace("%s after %s (name)", - pe__node_name(node1), pe__node_name(node2)); + pcmk__node_name(node1), pcmk__node_name(node2)); return 1; } - crm_trace("%s == %s", pe__node_name(node1), pe__node_name(node2)); + crm_trace("%s == %s", pcmk__node_name(node1), pcmk__node_name(node2)); return 0; } @@ -360,8 +362,9 @@ pcmk__apply_node_health(pcmk_scheduler_t *scheduler) { int base_health = 0; enum pcmk__health_strategy strategy; - const char *strategy_str = pe_pref(scheduler->config_hash, - PCMK__OPT_NODE_HEALTH_STRATEGY); + const char *strategy_str = + pcmk__cluster_option(scheduler->config_hash, + PCMK_OPT_NODE_HEALTH_STRATEGY); strategy = pcmk__parse_health_strategy(strategy_str); if (strategy == pcmk__health_strategy_none) { @@ -371,7 +374,7 @@ pcmk__apply_node_health(pcmk_scheduler_t *scheduler) // The progressive strategy can use a base health score if (strategy == pcmk__health_strategy_progressive) { - base_health = pe__health_score(PCMK__OPT_NODE_HEALTH_BASE, scheduler); + base_health = pe__health_score(PCMK_OPT_NODE_HEALTH_BASE, scheduler); } for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) { @@ -383,7 +386,7 @@ pcmk__apply_node_health(pcmk_scheduler_t *scheduler) continue; } crm_info("Overall system health of %s is %d", - pe__node_name(node), health); + pcmk__node_name(node), health); // Use node health as a location score for each resource on the node for (GList *r = scheduler->resources; r != NULL; r = r->next) { @@ -393,16 +396,16 @@ pcmk__apply_node_health(pcmk_scheduler_t *scheduler) if (health < 0) { /* Negative health scores do not apply to resources with - * allow-unhealthy-nodes=true. + * PCMK_META_ALLOW_UNHEALTHY_NODES=true. */ constrain = !crm_is_true(g_hash_table_lookup(rsc->meta, - PCMK__META_ALLOW_UNHEALTHY_NODES)); + PCMK_META_ALLOW_UNHEALTHY_NODES)); } if (constrain) { pcmk__new_location(strategy_str, rsc, health, NULL, node); } else { - pe_rsc_trace(rsc, "%s is immune from health ban on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "%s is immune from health ban on %s", + rsc->id, pcmk__node_name(node)); } } } diff --git a/lib/pacemaker/pcmk_sched_ordering.c b/lib/pacemaker/pcmk_sched_ordering.c index e589692..fdb49e2 100644 --- a/lib/pacemaker/pcmk_sched_ordering.c +++ b/lib/pacemaker/pcmk_sched_ordering.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. * @@ -65,7 +65,8 @@ invert_action(const char *action) } else if (pcmk__str_eq(action, PCMK_ACTION_STOPPED, pcmk__str_none)) { return PCMK_ACTION_RUNNING; } - crm_warn("Unknown action '%s' specified in order constraint", action); + pcmk__config_warn("Unknown action '%s' specified in order constraint", + action); return NULL; } @@ -73,10 +74,10 @@ static enum pe_order_kind get_ordering_type(const xmlNode *xml_obj) { enum pe_order_kind kind_e = pe_order_kind_mandatory; - const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND); + const char *kind = crm_element_value(xml_obj, PCMK_XA_KIND); if (kind == NULL) { - const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE); kind_e = pe_order_kind_mandatory; @@ -87,25 +88,26 @@ get_ordering_type(const xmlNode *xml_obj) if (score_i == 0) { kind_e = pe_order_kind_optional; } - pe_warn_once(pcmk__wo_order_score, - "Support for 'score' in rsc_order is deprecated " - "and will be removed in a future release " - "(use 'kind' instead)"); + pcmk__warn_once(pcmk__wo_order_score, + "Support for '" PCMK_XA_SCORE "' in " + PCMK_XE_RSC_ORDER " is deprecated and will be " + "removed in a future release " + "(use '" PCMK_XA_KIND "' instead)"); } - } else if (pcmk__str_eq(kind, "Mandatory", pcmk__str_none)) { + } else if (pcmk__str_eq(kind, PCMK_VALUE_MANDATORY, pcmk__str_none)) { kind_e = pe_order_kind_mandatory; - } else if (pcmk__str_eq(kind, "Optional", pcmk__str_none)) { + } else if (pcmk__str_eq(kind, PCMK_VALUE_OPTIONAL, pcmk__str_none)) { kind_e = pe_order_kind_optional; - } else if (pcmk__str_eq(kind, "Serialize", pcmk__str_none)) { + } else if (pcmk__str_eq(kind, PCMK_VALUE_SERIALIZE, pcmk__str_none)) { kind_e = pe_order_kind_serialize; } else { - pcmk__config_err("Resetting '" XML_ORDER_ATTR_KIND "' for constraint " - "%s to 'Mandatory' because '%s' is not valid", - pcmk__s(ID(xml_obj), "missing ID"), kind); + pcmk__config_err("Resetting '" PCMK_XA_KIND "' for constraint %s to " + "'" PCMK_VALUE_MANDATORY "' because '%s' is not valid", + pcmk__s(pcmk__xe_id(xml_obj), "missing ID"), kind); } return kind_e; } @@ -116,7 +118,8 @@ get_ordering_type(const xmlNode *xml_obj) * * \param[in] xml_obj Ordering XML * \param[in] parent_kind Default ordering kind - * \param[in] parent_symmetrical_s Parent element's symmetrical setting, if any + * \param[in] parent_symmetrical_s Parent element's \c PCMK_XA_SYMMETRICAL + * setting, if any * * \retval ordering_symmetric Ordering is symmetric * \retval ordering_asymmetric Ordering is asymmetric @@ -130,13 +133,13 @@ get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind, enum pe_order_kind kind = parent_kind; // Default to parent's kind // Check ordering XML for explicit kind - if ((crm_element_value(xml_obj, XML_ORDER_ATTR_KIND) != NULL) - || (crm_element_value(xml_obj, XML_RULE_ATTR_SCORE) != NULL)) { + if ((crm_element_value(xml_obj, PCMK_XA_KIND) != NULL) + || (crm_element_value(xml_obj, PCMK_XA_SCORE) != NULL)) { kind = get_ordering_type(xml_obj); } - // Check ordering XML (and parent) for explicit symmetrical setting - rc = pcmk__xe_get_bool_attr(xml_obj, XML_CONS_ATTR_SYMMETRICAL, &symmetric); + // Check ordering XML (and parent) for explicit PCMK_XA_SYMMETRICAL setting + rc = pcmk__xe_get_bool_attr(xml_obj, PCMK_XA_SYMMETRICAL, &symmetric); if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) { symmetric = crm_is_true(parent_symmetrical_s); @@ -146,10 +149,10 @@ get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind, if (rc == pcmk_rc_ok) { if (symmetric) { if (kind == pe_order_kind_serialize) { - pcmk__config_warn("Ignoring " XML_CONS_ATTR_SYMMETRICAL + pcmk__config_warn("Ignoring " PCMK_XA_SYMMETRICAL " for '%s' because not valid with " - XML_ORDER_ATTR_KIND " of 'Serialize'", - ID(xml_obj)); + PCMK_XA_KIND " of '" PCMK_VALUE_SERIALIZE "'", + pcmk__xe_id(xml_obj)); } else { return ordering_symmetric; } @@ -182,7 +185,7 @@ ordering_flags_for_kind(enum pe_order_kind kind, const char *first, switch (kind) { case pe_order_kind_optional: - pe__set_order_flags(flags, pcmk__ar_ordered); + pcmk__set_relation_flags(flags, pcmk__ar_ordered); break; case pe_order_kind_serialize: @@ -190,27 +193,29 @@ ordering_flags_for_kind(enum pe_order_kind kind, const char *first, * will not match an equality comparison against pcmk__ar_none or * pcmk__ar_ordered. */ - pe__set_order_flags(flags, pcmk__ar_serialize); + pcmk__set_relation_flags(flags, pcmk__ar_serialize); break; case pe_order_kind_mandatory: - pe__set_order_flags(flags, pcmk__ar_ordered); + pcmk__set_relation_flags(flags, pcmk__ar_ordered); switch (symmetry) { case ordering_asymmetric: - pe__set_order_flags(flags, pcmk__ar_asymmetric); + pcmk__set_relation_flags(flags, pcmk__ar_asymmetric); break; case ordering_symmetric: - pe__set_order_flags(flags, pcmk__ar_first_implies_then); + pcmk__set_relation_flags(flags, + pcmk__ar_first_implies_then); if (pcmk__strcase_any_of(first, PCMK_ACTION_START, PCMK_ACTION_PROMOTE, NULL)) { - pe__set_order_flags(flags, - pcmk__ar_unrunnable_first_blocks); + pcmk__set_relation_flags(flags, + pcmk__ar_unrunnable_first_blocks); } break; case ordering_symmetric_inverse: - pe__set_order_flags(flags, pcmk__ar_then_implies_first); + pcmk__set_relation_flags(flags, + pcmk__ar_then_implies_first); break; } break; @@ -243,34 +248,34 @@ get_ordering_resource(const xmlNode *xml, const char *resource_attr, if (rsc_id == NULL) { pcmk__config_err("Ignoring constraint '%s' without %s", - ID(xml), resource_attr); + pcmk__xe_id(xml), resource_attr); return NULL; } rsc = pcmk__find_constraint_resource(scheduler->resources, rsc_id); if (rsc == NULL) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " - "does not exist", ID(xml), rsc_id); + "does not exist", pcmk__xe_id(xml), rsc_id); return NULL; } if (instance_id != NULL) { - pe_warn_once(pcmk__wo_order_inst, - "Support for " XML_ORDER_ATTR_FIRST_INSTANCE " and " - XML_ORDER_ATTR_THEN_INSTANCE " is deprecated and will be " - "removed in a future release."); + pcmk__warn_once(pcmk__wo_order_inst, + "Support for " PCMK__XA_FIRST_INSTANCE " and " + PCMK__XA_THEN_INSTANCE " is deprecated and will be " + "removed in a future release."); - if (!pe_rsc_is_clone(rsc)) { + if (!pcmk__is_clone(rsc)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", - ID(xml), rsc_id, instance_id); + pcmk__xe_id(xml), rsc_id, instance_id); return NULL; } rsc = find_clone_instance(rsc, instance_id); if (rsc == NULL) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "does not have an instance '%s'", - "'%s'", ID(xml), rsc_id, instance_id); + pcmk__xe_id(xml), rsc_id, instance_id); return NULL; } } @@ -292,7 +297,7 @@ get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml) const char *clone_min = NULL; bool require_all = false; - if (!pe_rsc_is_clone(rsc)) { + if (!pcmk__is_clone(rsc)) { return 0; } @@ -305,13 +310,16 @@ get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml) } /* @COMPAT 1.1.13: - * require-all=false is deprecated equivalent of clone-min=1 + * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE is deprecated equivalent of + * PCMK_META_CLONE_MIN=1 */ - if (pcmk__xe_get_bool_attr(xml, "require-all", &require_all) != ENODATA) { - pe_warn_once(pcmk__wo_require_all, - "Support for require-all in ordering constraints " - "is deprecated and will be removed in a future release" - " (use clone-min clone meta-attribute instead)"); + if (pcmk__xe_get_bool_attr(xml, PCMK_XA_REQUIRE_ALL, + &require_all) != ENODATA) { + pcmk__warn_once(pcmk__wo_require_all, + "Support for " PCMK_XA_REQUIRE_ALL " in ordering " + "constraints is deprecated and will be removed in a " + "future release (use " PCMK_META_CLONE_MIN " clone " + "meta-attribute instead)"); if (!require_all) { return 1; } @@ -322,7 +330,7 @@ get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml) /*! * \internal - * \brief Create orderings for a constraint with clone-min > 0 + * \brief Create orderings for a constraint with \c PCMK_META_CLONE_MIN > 0 * * \param[in] id Ordering ID * \param[in,out] rsc_first 'First' resource in ordering (a clone) @@ -348,7 +356,7 @@ clone_min_ordering(const char *id, * considered runnable before allowing the pseudo-action to be runnable. */ clone_min_met->required_runnable_before = clone_min; - pe__set_action_flags(clone_min_met, pcmk_action_min_runnable); + pcmk__set_action_flags(clone_min_met, pcmk_action_min_runnable); // Order the actions for each clone instance before the pseudo-action for (GList *iter = rsc_first->children; iter != NULL; iter = iter->next) { @@ -377,14 +385,14 @@ clone_min_ordering(const char *id, * \param[in] flag Ordering flag to set (when applicable) * \param[in,out] flags Ordering flag set to update * - * \compat The restart-type resource meta-attribute is deprecated. Eventually, - * it will be removed, and pe_restart_ignore will be the only behavior, - * at which time this can just be removed entirely. + * \compat The \c PCMK__META_RESTART_TYPE resource meta-attribute is deprecated. + * Eventually, it will be removed, and \c pe_restart_ignore will be the + * only behavior, at which time this can just be removed entirely. */ #define handle_restart_type(rsc, kind, flag, flags) do { \ if (((kind) == pe_order_kind_optional) \ && ((rsc)->restart_type == pe_restart_restart)) { \ - pe__set_order_flags((flags), (flag)); \ + pcmk__set_relation_flags((flags), (flag)); \ } \ } while (0) @@ -435,33 +443,31 @@ unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) CRM_CHECK(xml_obj != NULL, return); - id = crm_element_value(xml_obj, XML_ATTR_ID); + id = crm_element_value(xml_obj, PCMK_XA_ID); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return; } - rsc_first = get_ordering_resource(xml_obj, XML_ORDER_ATTR_FIRST, - XML_ORDER_ATTR_FIRST_INSTANCE, - scheduler); + rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST, + PCMK__XA_FIRST_INSTANCE, scheduler); if (rsc_first == NULL) { return; } - rsc_then = get_ordering_resource(xml_obj, XML_ORDER_ATTR_THEN, - XML_ORDER_ATTR_THEN_INSTANCE, - scheduler); + rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN, + PCMK__XA_THEN_INSTANCE, scheduler); if (rsc_then == NULL) { return; } - action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION); + action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION); if (action_first == NULL) { action_first = PCMK_ACTION_START; } - action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION); + action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION); if (action_then == NULL) { action_then = action_first; } @@ -527,7 +533,7 @@ pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task, char *then_action_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched) { - pe__ordering_t *order = NULL; + pcmk__action_relation_t *order = NULL; // One of action or resource must be specified for each side CRM_CHECK(((first_action != NULL) || (first_rsc != NULL)) @@ -541,38 +547,37 @@ pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task, then_rsc = then_action->rsc; } - order = calloc(1, sizeof(pe__ordering_t)); - CRM_ASSERT(order != NULL); + order = pcmk__assert_alloc(1, sizeof(pcmk__action_relation_t)); order->id = sched->order_id++; order->flags = flags; - order->lh_rsc = first_rsc; - order->rh_rsc = then_rsc; - order->lh_action = first_action; - order->rh_action = then_action; - order->lh_action_task = first_action_task; - order->rh_action_task = then_action_task; + order->rsc1 = first_rsc; + order->rsc2 = then_rsc; + order->action1 = first_action; + order->action2 = then_action; + order->task1 = first_action_task; + order->task2 = then_action_task; - if ((order->lh_action_task == NULL) && (first_action != NULL)) { - order->lh_action_task = strdup(first_action->uuid); + if ((order->task1 == NULL) && (first_action != NULL)) { + order->task1 = strdup(first_action->uuid); } - if ((order->rh_action_task == NULL) && (then_action != NULL)) { - order->rh_action_task = strdup(then_action->uuid); + if ((order->task2 == NULL) && (then_action != NULL)) { + order->task2 = strdup(then_action->uuid); } - if ((order->lh_rsc == NULL) && (first_action != NULL)) { - order->lh_rsc = first_action->rsc; + if ((order->rsc1 == NULL) && (first_action != NULL)) { + order->rsc1 = first_action->rsc; } - if ((order->rh_rsc == NULL) && (then_action != NULL)) { - order->rh_rsc = then_action->rsc; + if ((order->rsc2 == NULL) && (then_action != NULL)) { + order->rsc2 = then_action->rsc; } - pe_rsc_trace(first_rsc, "Created ordering %d for %s then %s", - (sched->order_id - 1), - pcmk__s(order->lh_action_task, "an underspecified action"), - pcmk__s(order->rh_action_task, "an underspecified action")); + pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s", + (sched->order_id - 1), + pcmk__s(order->task1, "an underspecified action"), + pcmk__s(order->task2, "an underspecified action")); sched->ordering_constraints = g_list_prepend(sched->ordering_constraints, order); @@ -583,8 +588,10 @@ pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task, * \brief Unpack a set in an ordering constraint * * \param[in] set Set XML to unpack - * \param[in] parent_kind rsc_order XML "kind" attribute - * \param[in] parent_symmetrical_s rsc_order XML "symmetrical" attribute + * \param[in] parent_kind \c PCMK_XE_RSC_ORDER XML \c PCMK_XA_KIND + * attribute + * \param[in] parent_symmetrical_s \c PCMK_XE_RSC_ORDER XML + * \c PCMK_XA_SYMMETRICAL attribute * \param[in,out] scheduler Scheduler data * * \return Standard Pacemaker return code @@ -605,10 +612,10 @@ unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind, enum ordering_symmetry symmetry; char *key = NULL; - const char *id = ID(set); - const char *action = crm_element_value(set, "action"); - const char *sequential_s = crm_element_value(set, "sequential"); - const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND); + const char *id = pcmk__xe_id(set); + const char *action = crm_element_value(set, PCMK_XA_ACTION); + const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL); + const char *kind_s = crm_element_value(set, PCMK_XA_KIND); if (action == NULL) { action = PCMK_ACTION_START; @@ -626,10 +633,12 @@ unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind, symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s); flags = ordering_flags_for_kind(local_kind, action, symmetry); - for (const xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (const xmlNode *xml_rsc = pcmk__xe_first_child(set, + PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - EXPAND_CONSTRAINT_IDREF(id, resource, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc)); resources = g_list_append(resources, resource); } @@ -719,14 +728,14 @@ order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, pcmk_resource_t *rsc_1 = NULL; pcmk_resource_t *rsc_2 = NULL; - const char *action_1 = crm_element_value(set1, "action"); - const char *action_2 = crm_element_value(set2, "action"); + const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION); + const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION); uint32_t flags = pcmk__ar_none; bool require_all = true; - (void) pcmk__xe_get_bool_attr(set1, "require-all", &require_all); + (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all); if (action_1 == NULL) { action_1 = PCMK_ACTION_START; @@ -757,16 +766,18 @@ order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, * irrelevant in regards to set2. */ if (!require_all) { - char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s", ID(set1)); + char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s", + pcmk__xe_id(set1)); pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler); free(task); - pe__set_action_flags(unordered_action, pcmk_action_min_runnable); + pcmk__set_action_flags(unordered_action, pcmk_action_min_runnable); - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc)); /* Add an ordering constraint between every element in set1 and the * pseudo action. If any action in set1 is runnable the pseudo @@ -778,10 +789,11 @@ order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, |pcmk__ar_first_implies_then_graphed, scheduler); } - for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc_2 != NULL; xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) { + for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2)); + EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2)); /* Add an ordering constraint between the pseudo-action and every * element in set2. If the pseudo-action is runnable, every action @@ -796,44 +808,48 @@ order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, return pcmk_rc_ok; } - if (pcmk__xe_attr_is_true(set1, "sequential")) { + if (pcmk__xe_attr_is_true(set1, PCMK_XA_SEQUENTIAL)) { if (symmetry == ordering_symmetric_inverse) { // Get the first one - xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); + xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); if (xml_rsc != NULL) { - EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc)); } } else { // Get the last one const char *rid = NULL; - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - rid = ID(xml_rsc); + rid = pcmk__xe_id(xml_rsc); } EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid); } } - if (pcmk__xe_attr_is_true(set2, "sequential")) { + if (pcmk__xe_attr_is_true(set2, PCMK_XA_SEQUENTIAL)) { if (symmetry == ordering_symmetric_inverse) { // Get the last one const char *rid = NULL; - for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - rid = ID(xml_rsc); + rid = pcmk__xe_id(xml_rsc); } EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid); } else { // Get the first one - xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF); + xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, + NULL); if (xml_rsc != NULL) { - EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc)); } } } @@ -842,34 +858,38 @@ order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2, pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags); } else if (rsc_1 != NULL) { - for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc)); pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags); } } else if (rsc_2 != NULL) { - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc)); pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags); } } else { - for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, + NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc)); + EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc)); - for (xmlNode *xml_rsc_2 = first_named_child(set2, - XML_TAG_RESOURCE_REF); - xml_rsc_2 != NULL; xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) { + for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2, + PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) { - EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2)); + EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2)); pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags); } @@ -911,12 +931,12 @@ unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml, // Check whether there are any resource sets with template or tag references *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler); if (*expanded_xml != NULL) { - crm_log_xml_trace(*expanded_xml, "Expanded rsc_order"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER); return pcmk_rc_ok; } - id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST); - id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN); + id_first = crm_element_value(xml_obj, PCMK_XA_FIRST); + id_then = crm_element_value(xml_obj, PCMK_XA_THEN); if ((id_first == NULL) || (id_then == NULL)) { return pcmk_rc_ok; } @@ -924,14 +944,16 @@ unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first, &tag_first)) { pcmk__config_err("Ignoring constraint '%s' because '%s' is not a " - "valid resource or tag", ID(xml_obj), id_first); + "valid resource or tag", + pcmk__xe_id(xml_obj), id_first); return pcmk_rc_unpack_error; } if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then, &tag_then)) { pcmk__config_err("Ignoring constraint '%s' because '%s' is not a " - "valid resource or tag", ID(xml_obj), id_then); + "valid resource or tag", + pcmk__xe_id(xml_obj), id_then); return pcmk_rc_unpack_error; } @@ -940,14 +962,16 @@ unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml, return pcmk_rc_ok; } - action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION); - action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION); + action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION); + action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION); - *expanded_xml = copy_xml(xml_obj); + *expanded_xml = pcmk__xml_copy(NULL, xml_obj); - // Convert template/tag reference in "first" into constraint resource_set - if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, XML_ORDER_ATTR_FIRST, - true, scheduler)) { + /* Convert template/tag reference in PCMK_XA_FIRST into constraint + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true, + scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; @@ -955,16 +979,20 @@ unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (rsc_set_first != NULL) { if (action_first != NULL) { - // Move "first-action" into converted resource_set as "action" - crm_xml_add(rsc_set_first, "action", action_first); - xml_remove_prop(*expanded_xml, XML_ORDER_ATTR_FIRST_ACTION); + /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as + * PCMK_XA_ACTION + */ + crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_FIRST_ACTION); } any_sets = true; } - // Convert template/tag reference in "then" into constraint resource_set - if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, XML_ORDER_ATTR_THEN, - true, scheduler)) { + /* Convert template/tag reference in PCMK_XA_THEN into constraint + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true, + scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; @@ -972,15 +1000,17 @@ unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (rsc_set_then != NULL) { if (action_then != NULL) { - // Move "then-action" into converted resource_set as "action" - crm_xml_add(rsc_set_then, "action", action_then); - xml_remove_prop(*expanded_xml, XML_ORDER_ATTR_THEN_ACTION); + /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as + * PCMK_XA_ACTION + */ + crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_THEN_ACTION); } any_sets = true; } if (any_sets) { - crm_log_xml_trace(*expanded_xml, "Expanded rsc_order"); + crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER); } else { free_xml(*expanded_xml); *expanded_xml = NULL; @@ -1005,8 +1035,8 @@ pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) xmlNode *orig_xml = NULL; xmlNode *expanded_xml = NULL; - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); + const char *id = crm_element_value(xml_obj, PCMK_XA_ID); + const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL); enum pe_order_kind kind = get_ordering_type(xml_obj); enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind, @@ -1022,8 +1052,8 @@ pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) } // If the constraint has resource sets, unpack them - for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); - set != NULL; set = crm_next_same_xml(set)) { + for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL); + set != NULL; set = pcmk__xe_next_same(set)) { set = expand_idref(set, scheduler->input); if ((set == NULL) // Configuration error, message already logged @@ -1079,8 +1109,8 @@ ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input) && (input->action->rsc != NULL) && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) { - crm_warn("Invalid ordering constraint between %s and %s", - input->action->rsc->id, action->rsc->id); + pcmk__config_warn("Invalid ordering constraint between %s and %s", + input->action->rsc->id, action->rsc->id); return true; } @@ -1135,7 +1165,7 @@ pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op) pcmk_action_t *action = (pcmk_action_t *) iter->data; // Only stops on the node shutting down are relevant - if (!pe__same_node(action->node, node) + if (!pcmk__same_node(action->node, node) || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) { continue; } @@ -1143,17 +1173,17 @@ pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op) // Resources and nodes in maintenance mode won't be touched if (pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)) { - pe_rsc_trace(action->rsc, - "Not ordering %s before shutdown of %s because " - "resource in maintenance mode", - action->uuid, pe__node_name(node)); + pcmk__rsc_trace(action->rsc, + "Not ordering %s before shutdown of %s because " + "resource in maintenance mode", + action->uuid, pcmk__node_name(node)); continue; } else if (node->details->maintenance) { - pe_rsc_trace(action->rsc, - "Not ordering %s before shutdown of %s because " - "node in maintenance mode", - action->uuid, pe__node_name(node)); + pcmk__rsc_trace(action->rsc, + "Not ordering %s before shutdown of %s because " + "node in maintenance mode", + action->uuid, pcmk__node_name(node)); continue; } @@ -1163,16 +1193,16 @@ pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op) */ if (!pcmk_any_flags_set(action->rsc->flags, pcmk_rsc_managed|pcmk_rsc_blocked)) { - pe_rsc_trace(action->rsc, - "Not ordering %s before shutdown of %s because " - "resource is unmanaged or blocked", - action->uuid, pe__node_name(node)); + pcmk__rsc_trace(action->rsc, + "Not ordering %s before shutdown of %s because " + "resource is unmanaged or blocked", + action->uuid, pcmk__node_name(node)); continue; } - pe_rsc_trace(action->rsc, "Ordering %s before shutdown of %s", - action->uuid, pe__node_name(node)); - pe__clear_action_flags(action, pcmk_action_optional); + pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s", + action->uuid, pcmk__node_name(node)); + pcmk__clear_action_flags(action, pcmk_action_optional); pcmk__new_ordering(action->rsc, NULL, action, NULL, strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op, pcmk__ar_ordered|pcmk__ar_unrunnable_first_blocks, @@ -1202,14 +1232,12 @@ find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key) char *task = NULL; guint interval_ms = 0; - if (parse_op_key(original_key, NULL, &task, &interval_ms)) { - key = pcmk__op_key(rsc->id, task, interval_ms); - list = find_actions(rsc->actions, key, NULL); - free(key); - free(task); - } else { - crm_err("Invalid operation key (bug?): %s", original_key); - } + CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms), + return NULL); + key = pcmk__op_key(rsc->id, task, interval_ms); + list = find_actions(rsc->actions, key, NULL); + free(key); + free(task); } return list; } @@ -1224,7 +1252,8 @@ find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key) */ static void order_resource_actions_after(pcmk_action_t *first_action, - const pcmk_resource_t *rsc, pe__ordering_t *order) + const pcmk_resource_t *rsc, + pcmk__action_relation_t *order) { GList *then_actions = NULL; uint32_t flags = pcmk__ar_none; @@ -1232,37 +1261,37 @@ order_resource_actions_after(pcmk_action_t *first_action, CRM_CHECK((rsc != NULL) && (order != NULL), return); flags = order->flags; - pe_rsc_trace(rsc, "Applying ordering %d for 'then' resource %s", - order->id, rsc->id); + pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s", + order->id, rsc->id); - if (order->rh_action != NULL) { - then_actions = g_list_prepend(NULL, order->rh_action); + if (order->action2 != NULL) { + then_actions = g_list_prepend(NULL, order->action2); } else { - then_actions = find_actions_by_task(rsc, order->rh_action_task); + then_actions = find_actions_by_task(rsc, order->task2); } if (then_actions == NULL) { - pe_rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s", - order->id, order->rh_action_task, rsc->id); + pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s", + order->id, order->task2, rsc->id); return; } if ((first_action != NULL) && (first_action->rsc == rsc) && pcmk_is_set(first_action->flags, pcmk_action_migration_abort)) { - pe_rsc_trace(rsc, - "Detected dangling migration ordering (%s then %s %s)", - first_action->uuid, order->rh_action_task, rsc->id); - pe__clear_order_flags(flags, pcmk__ar_first_implies_then); + pcmk__rsc_trace(rsc, + "Detected dangling migration ordering (%s then %s %s)", + first_action->uuid, order->task2, rsc->id); + pcmk__clear_relation_flags(flags, pcmk__ar_first_implies_then); } if ((first_action == NULL) && !pcmk_is_set(flags, pcmk__ar_first_implies_then)) { - pe_rsc_debug(rsc, - "Ignoring ordering %d for %s: No first action found", - order->id, rsc->id); + pcmk__rsc_debug(rsc, + "Ignoring ordering %d for %s: No first action found", + order->id, rsc->id); g_list_free(then_actions); return; } @@ -1273,10 +1302,10 @@ order_resource_actions_after(pcmk_action_t *first_action, if (first_action != NULL) { order_actions(first_action, then_action_iter, flags); } else { - pe__clear_action_flags(then_action_iter, pcmk_action_runnable); + pcmk__clear_action_flags(then_action_iter, pcmk_action_runnable); crm_warn("%s of %s is unrunnable because there is no %s of %s " "to order it after", then_action_iter->task, rsc->id, - order->lh_action_task, order->lh_rsc->id); + order->task1, order->rsc1->id); } } @@ -1284,56 +1313,58 @@ order_resource_actions_after(pcmk_action_t *first_action, } static void -rsc_order_first(pcmk_resource_t *first_rsc, pe__ordering_t *order) +rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order) { GList *first_actions = NULL; - pcmk_action_t *first_action = order->lh_action; - pcmk_resource_t *then_rsc = order->rh_rsc; + pcmk_action_t *first_action = order->action1; + pcmk_resource_t *then_rsc = order->rsc2; CRM_ASSERT(first_rsc != NULL); - pe_rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)", - order->id, first_rsc->id); + pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)", + order->id, first_rsc->id); if (first_action != NULL) { first_actions = g_list_prepend(NULL, first_action); } else { - first_actions = find_actions_by_task(first_rsc, order->lh_action_task); + first_actions = find_actions_by_task(first_rsc, order->task1); } if ((first_actions == NULL) && (first_rsc == then_rsc)) { - pe_rsc_trace(first_rsc, - "Ignoring constraint %d: first (%s for %s) not found", - order->id, order->lh_action_task, first_rsc->id); + pcmk__rsc_trace(first_rsc, + "Ignoring constraint %d: first (%s for %s) not found", + order->id, order->task1, first_rsc->id); } else if (first_actions == NULL) { char *key = NULL; char *op_type = NULL; guint interval_ms = 0; - parse_op_key(order->lh_action_task, NULL, &op_type, &interval_ms); + parse_op_key(order->task1, NULL, &op_type, &interval_ms); key = pcmk__op_key(first_rsc->id, op_type, interval_ms); if ((first_rsc->fns->state(first_rsc, TRUE) == pcmk_role_stopped) && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) { free(key); - pe_rsc_trace(first_rsc, - "Ignoring constraint %d: first (%s for %s) not found", - order->id, order->lh_action_task, first_rsc->id); + pcmk__rsc_trace(first_rsc, + "Ignoring constraint %d: first (%s for %s) " + "not found", + order->id, order->task1, first_rsc->id); } else if ((first_rsc->fns->state(first_rsc, TRUE) == pcmk_role_unpromoted) && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE, pcmk__str_none)) { free(key); - pe_rsc_trace(first_rsc, - "Ignoring constraint %d: first (%s for %s) not found", - order->id, order->lh_action_task, first_rsc->id); + pcmk__rsc_trace(first_rsc, + "Ignoring constraint %d: first (%s for %s) " + "not found", + order->id, order->task1, first_rsc->id); } else { - pe_rsc_trace(first_rsc, - "Creating first (%s for %s) for constraint %d ", - order->lh_action_task, first_rsc->id, order->id); + pcmk__rsc_trace(first_rsc, + "Creating first (%s for %s) for constraint %d ", + order->task1, first_rsc->id, order->id); first_action = custom_action(first_rsc, key, op_type, NULL, TRUE, first_rsc->cluster); first_actions = g_list_prepend(NULL, first_action); @@ -1343,18 +1374,18 @@ rsc_order_first(pcmk_resource_t *first_rsc, pe__ordering_t *order) } if (then_rsc == NULL) { - if (order->rh_action == NULL) { - pe_rsc_trace(first_rsc, "Ignoring constraint %d: then not found", - order->id); + if (order->action2 == NULL) { + pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found", + order->id); return; } - then_rsc = order->rh_action->rsc; + then_rsc = order->action2->rsc; } for (GList *iter = first_actions; iter != NULL; iter = iter->next) { first_action = iter->data; if (then_rsc == NULL) { - order_actions(first_action, order->rh_action, order->flags); + order_actions(first_action, order->action2, order->flags); } else { order_resource_actions_after(first_action, then_rsc, order); @@ -1407,22 +1438,22 @@ pcmk__apply_orderings(pcmk_scheduler_t *sched) for (GList *iter = sched->ordering_constraints; iter != NULL; iter = iter->next) { - pe__ordering_t *order = iter->data; - pcmk_resource_t *rsc = order->lh_rsc; + pcmk__action_relation_t *order = iter->data; + pcmk_resource_t *rsc = order->rsc1; if (rsc != NULL) { rsc_order_first(rsc, order); continue; } - rsc = order->rh_rsc; + rsc = order->rsc2; if (rsc != NULL) { - order_resource_actions_after(order->lh_action, rsc, order); + order_resource_actions_after(order->action1, rsc, order); } else { crm_trace("Applying ordering constraint %d (non-resource actions)", order->id); - order_actions(order->lh_action, order->rh_action, order->flags); + order_actions(order->action1, order->action2, order->flags); } } @@ -1454,8 +1485,8 @@ pcmk__order_after_each(pcmk_action_t *after, GList *list) const char *before_desc = before->task? before->task : before->uuid; crm_debug("Ordering %s on %s before %s on %s", - before_desc, pe__node_name(before->node), - after_desc, pe__node_name(after->node)); + before_desc, pcmk__node_name(before->node), + after_desc, pcmk__node_name(after->node)); order_actions(before, after, pcmk__ar_ordered); } } diff --git a/lib/pacemaker/pcmk_sched_primitive.c b/lib/pacemaker/pcmk_sched_primitive.c index 96acf1c..a113cca 100644 --- a/lib/pacemaker/pcmk_sched_primitive.c +++ b/lib/pacemaker/pcmk_sched_primitive.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. * @@ -12,7 +12,7 @@ #include <stdbool.h> #include <stdint.h> // uint8_t, uint32_t -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -137,7 +137,7 @@ sorted_allowed_nodes(const pcmk_resource_t *rsc) GList *nodes = g_hash_table_get_values(rsc->allowed_nodes); if (nodes != NULL) { - return pcmk__sort_nodes(nodes, pe__current_node(rsc)); + return pcmk__sort_nodes(nodes, pcmk__current_node(rsc)); } } return NULL; @@ -191,8 +191,8 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, chosen = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id); if (chosen == NULL) { - pe_rsc_trace(rsc, "Preferred node %s for %s was unknown", - pe__node_name(prefer), rsc->id); + pcmk__rsc_trace(rsc, "Preferred node %s for %s was unknown", + pcmk__node_name(prefer), rsc->id); /* Favor the preferred node as long as its score is at least as good as * the best allowed node's. @@ -201,20 +201,21 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, * node is better, when the best node's score is less than INFINITY. */ } else if (chosen->weight < best->weight) { - pe_rsc_trace(rsc, "Preferred node %s for %s was unsuitable", - pe__node_name(chosen), rsc->id); + pcmk__rsc_trace(rsc, "Preferred node %s for %s was unsuitable", + pcmk__node_name(chosen), rsc->id); chosen = NULL; } else if (!pcmk__node_available(chosen, true, false)) { - pe_rsc_trace(rsc, "Preferred node %s for %s was unavailable", - pe__node_name(chosen), rsc->id); + pcmk__rsc_trace(rsc, "Preferred node %s for %s was unavailable", + pcmk__node_name(chosen), rsc->id); chosen = NULL; } else { - pe_rsc_trace(rsc, - "Chose preferred node %s for %s " - "(ignoring %d candidates)", - pe__node_name(chosen), rsc->id, g_list_length(nodes)); + pcmk__rsc_trace(rsc, + "Chose preferred node %s for %s " + "(ignoring %d candidates)", + pcmk__node_name(chosen), rsc->id, + g_list_length(nodes)); } } @@ -225,7 +226,7 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, chosen = best; - if (!pe_rsc_is_unique_clone(rsc->parent) + if (!pcmk__is_unique_clone(rsc->parent) && (chosen->weight > 0) // Zero not acceptable && pcmk__node_available(chosen, false, false)) { /* If the resource is already running on a node, prefer that node if @@ -237,15 +238,15 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, * remaining unassigned instances to prefer a node that's already * running another instance. */ - pcmk_node_t *running = pe__current_node(rsc); + pcmk_node_t *running = pcmk__current_node(rsc); if (running == NULL) { // Nothing to do } else if (!pcmk__node_available(running, true, false)) { - pe_rsc_trace(rsc, - "Current node for %s (%s) can't run resources", - rsc->id, pe__node_name(running)); + pcmk__rsc_trace(rsc, + "Current node for %s (%s) can't run resources", + rsc->id, pcmk__node_name(running)); } else { int nodes_with_best_score = 1; @@ -257,7 +258,7 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, // The nodes are sorted by score, so no more are equal break; } - if (pe__same_node(allowed, running)) { + if (pcmk__same_node(allowed, running)) { // Scores are equal, so prefer the current node chosen = allowed; } @@ -267,20 +268,20 @@ assign_best_node(pcmk_resource_t *rsc, const pcmk_node_t *prefer, if (nodes_with_best_score > 1) { uint8_t log_level = LOG_INFO; - if (chosen->weight >= INFINITY) { + if (chosen->weight >= PCMK_SCORE_INFINITY) { log_level = LOG_WARNING; } do_crm_log(log_level, "Chose %s for %s from %d nodes with score %s", - pe__node_name(chosen), rsc->id, + pcmk__node_name(chosen), rsc->id, nodes_with_best_score, pcmk_readable_score(chosen->weight)); } } } - pe_rsc_trace(rsc, "Chose %s for %s from %d candidates", - pe__node_name(chosen), rsc->id, g_list_length(nodes)); + pcmk__rsc_trace(rsc, "Chose %s for %s from %d candidates", + pcmk__node_name(chosen), rsc->id, g_list_length(nodes)); } pcmk__assign_resource(rsc, chosen, false, stop_if_fail); @@ -303,16 +304,18 @@ apply_this_with(pcmk__colocation_t *colocation, pcmk_resource_t *rsc) // In certain cases, we will need to revert the node scores if ((colocation->dependent_role >= pcmk_role_promoted) - || ((colocation->score < 0) && (colocation->score > -INFINITY))) { + || ((colocation->score < 0) + && (colocation->score > -PCMK_SCORE_INFINITY))) { archive = pcmk__copy_node_table(rsc->allowed_nodes); } if (pcmk_is_set(other->flags, pcmk_rsc_unassigned)) { - pe_rsc_trace(rsc, - "%s: Assigning colocation %s primary %s first" - "(score=%d role=%s)", - rsc->id, colocation->id, other->id, - colocation->score, role2text(colocation->dependent_role)); + pcmk__rsc_trace(rsc, + "%s: Assigning colocation %s primary %s first" + "(score=%d role=%s)", + rsc->id, colocation->id, other->id, + colocation->score, + pcmk_role_text(colocation->dependent_role)); other->cmds->assign(other, NULL, true); } @@ -320,10 +323,10 @@ apply_this_with(pcmk__colocation_t *colocation, pcmk_resource_t *rsc) rsc->cmds->apply_coloc_score(rsc, other, colocation, true); if ((archive != NULL) && !pcmk__any_node_available(rsc->allowed_nodes)) { - pe_rsc_info(rsc, - "%s: Reverting scores from colocation with %s " - "because no nodes allowed", - rsc->id, other->id); + pcmk__rsc_info(rsc, + "%s: Reverting scores from colocation with %s " + "because no nodes allowed", + rsc->id, other->id); g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = archive; archive = NULL; @@ -342,8 +345,8 @@ apply_this_with(pcmk__colocation_t *colocation, pcmk_resource_t *rsc) static void remote_connection_assigned(const pcmk_resource_t *connection) { - pcmk_node_t *remote_node = pe_find_node(connection->cluster->nodes, - connection->id); + pcmk_node_t *remote_node = pcmk_find_node(connection->cluster, + connection->id); CRM_CHECK(remote_node != NULL, return); @@ -363,7 +366,7 @@ remote_connection_assigned(const pcmk_resource_t *connection) "(%sassigned connection's next role is %s)", remote_node->details->id, ((connection->allocated_to == NULL)? "un" : ""), - role2text(connection->next_role)); + pcmk_role_text(connection->next_role)); remote_node->details->shutdown = TRUE; } } @@ -395,13 +398,13 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, GList *iter = NULL; pcmk__colocation_t *colocation = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT(pcmk__is_primitive(rsc)); // Never assign a child without parent being assigned first if ((rsc->parent != NULL) && !pcmk_is_set(rsc->parent->flags, pcmk_rsc_assigning)) { - pe_rsc_debug(rsc, "%s: Assigning parent %s first", - rsc->id, rsc->parent->id); + pcmk__rsc_debug(rsc, "%s: Assigning parent %s first", + rsc->id, rsc->parent->id); rsc->parent->cmds->assign(rsc->parent, prefer, stop_if_fail); } @@ -410,18 +413,18 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, const char *node_name = "no node"; if (rsc->allocated_to != NULL) { - node_name = pe__node_name(rsc->allocated_to); + node_name = pcmk__node_name(rsc->allocated_to); } - pe_rsc_debug(rsc, "%s: pre-assigned to %s", rsc->id, node_name); + pcmk__rsc_debug(rsc, "%s: pre-assigned to %s", rsc->id, node_name); return rsc->allocated_to; } // Ensure we detect assignment loops if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) { - pe_rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); + pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id); return NULL; } - pe__set_resource_flags(rsc, pcmk_rsc_assigning); + pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning); pe__show_node_scores(true, rsc, "Pre-assignment", rsc->allowed_nodes, rsc->cluster); @@ -433,16 +436,16 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, for (iter = this_with_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; - if ((colocation->score <= -CRM_SCORE_INFINITY) - || (colocation->score >= CRM_SCORE_INFINITY)) { + if ((colocation->score <= -PCMK_SCORE_INFINITY) + || (colocation->score >= PCMK_SCORE_INFINITY)) { apply_this_with(colocation, rsc); } } for (iter = with_this_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; - if ((colocation->score <= -CRM_SCORE_INFINITY) - || (colocation->score >= CRM_SCORE_INFINITY)) { + if ((colocation->score <= -PCMK_SCORE_INFINITY) + || (colocation->score >= PCMK_SCORE_INFINITY)) { pcmk__add_dependent_scores(colocation, rsc); } } @@ -454,16 +457,16 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, for (iter = this_with_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; - if ((colocation->score > -CRM_SCORE_INFINITY) - && (colocation->score < CRM_SCORE_INFINITY)) { + if ((colocation->score > -PCMK_SCORE_INFINITY) + && (colocation->score < PCMK_SCORE_INFINITY)) { apply_this_with(colocation, rsc); } } for (iter = with_this_colocations; iter != NULL; iter = iter->next) { colocation = iter->data; - if ((colocation->score > -CRM_SCORE_INFINITY) - && (colocation->score < CRM_SCORE_INFINITY)) { + if ((colocation->score > -PCMK_SCORE_INFINITY) + && (colocation->score < PCMK_SCORE_INFINITY)) { pcmk__add_dependent_scores(colocation, rsc); } } @@ -472,19 +475,21 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, g_list_free(with_this_colocations); if (rsc->next_role == pcmk_role_stopped) { - pe_rsc_trace(rsc, - "Banning %s from all nodes because it will be stopped", - rsc->id); - resource_location(rsc, NULL, -INFINITY, XML_RSC_ATTR_TARGET_ROLE, - rsc->cluster); + pcmk__rsc_trace(rsc, + "Banning %s from all nodes because it will be stopped", + rsc->id); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + PCMK_META_TARGET_ROLE, rsc->cluster); } else if ((rsc->next_role > rsc->role) && !pcmk_is_set(rsc->cluster->flags, pcmk_sched_quorate) && (rsc->cluster->no_quorum_policy == pcmk_no_quorum_freeze)) { crm_notice("Resource %s cannot be elevated from %s to %s due to " - "no-quorum-policy=freeze", - rsc->id, role2text(rsc->role), role2text(rsc->next_role)); - pe__set_next_role(rsc, rsc->role, "no-quorum-policy=freeze"); + PCMK_OPT_NO_QUORUM_POLICY "=" PCMK_VALUE_FREEZE, + rsc->id, pcmk_role_text(rsc->role), + pcmk_role_text(rsc->next_role)); + pe__set_next_role(rsc, rsc->role, + PCMK_OPT_NO_QUORUM_POLICY "=" PCMK_VALUE_FREEZE); } pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags, @@ -494,7 +499,7 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, // Unmanage resource if fencing is enabled but no device is configured if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled) && !pcmk_is_set(rsc->cluster->flags, pcmk_sched_have_fencing)) { - pe__clear_resource_flags(rsc, pcmk_rsc_managed); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { @@ -503,7 +508,7 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, pcmk_node_t *assign_to = NULL; pe__set_next_role(rsc, rsc->role, "unmanaged"); - assign_to = pe__current_node(rsc); + assign_to = pcmk__current_node(rsc); if (assign_to == NULL) { reason = "inactive"; } else if (rsc->role == pcmk_role_promoted) { @@ -513,28 +518,30 @@ pcmk__primitive_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, } else { reason = "active"; } - pe_rsc_info(rsc, "Unmanaged resource %s assigned to %s: %s", rsc->id, - (assign_to? assign_to->details->uname : "no node"), reason); + pcmk__rsc_info(rsc, "Unmanaged resource %s assigned to %s: %s", rsc->id, + (assign_to? assign_to->details->uname : "no node"), + reason); pcmk__assign_resource(rsc, assign_to, true, stop_if_fail); } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_stop_all)) { // Must stop at some point, but be consistent with stop_if_fail if (stop_if_fail) { - pe_rsc_debug(rsc, "Forcing %s to stop: stop-all-resources", - rsc->id); + pcmk__rsc_debug(rsc, + "Forcing %s to stop: " PCMK_OPT_STOP_ALL_RESOURCES, + rsc->id); } pcmk__assign_resource(rsc, NULL, true, stop_if_fail); } else if (!assign_best_node(rsc, prefer, stop_if_fail)) { // Assignment failed if (!pcmk_is_set(rsc->flags, pcmk_rsc_removed)) { - pe_rsc_info(rsc, "Resource %s cannot run anywhere", rsc->id); + pcmk__rsc_info(rsc, "Resource %s cannot run anywhere", rsc->id); } else if ((rsc->running_on != NULL) && stop_if_fail) { - pe_rsc_info(rsc, "Stopping orphan resource %s", rsc->id); + pcmk__rsc_info(rsc, "Stopping removed resource %s", rsc->id); } } - pe__clear_resource_flags(rsc, pcmk_rsc_assigning); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning); if (rsc->is_remote_node) { remote_connection_assigned(rsc); @@ -562,14 +569,14 @@ schedule_restart_actions(pcmk_resource_t *rsc, pcmk_node_t *current, enum rsc_role_e next_role; rsc_transition_fn fn = NULL; - pe__set_resource_flags(rsc, pcmk_rsc_restarting); + pcmk__set_rsc_flags(rsc, pcmk_rsc_restarting); // Bring resource down to a stop on its current node while (role != pcmk_role_stopped) { next_role = rsc_state_matrix[role][pcmk_role_stopped]; - pe_rsc_trace(rsc, "Creating %s action to take %s down from %s to %s", - (need_stop? "required" : "optional"), rsc->id, - role2text(role), role2text(next_role)); + pcmk__rsc_trace(rsc, "Creating %s action to take %s down from %s to %s", + (need_stop? "required" : "optional"), rsc->id, + pcmk_role_text(role), pcmk_role_text(next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; @@ -587,9 +594,9 @@ schedule_restart_actions(pcmk_resource_t *rsc, pcmk_node_t *current, if ((next_role == pcmk_role_promoted) && need_promote) { required = true; } - pe_rsc_trace(rsc, "Creating %s action to take %s up from %s to %s", - (required? "required" : "optional"), rsc->id, - role2text(role), role2text(next_role)); + pcmk__rsc_trace(rsc, "Creating %s action to take %s up from %s to %s", + (required? "required" : "optional"), rsc->id, + pcmk_role_text(role), pcmk_role_text(next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; @@ -598,7 +605,7 @@ schedule_restart_actions(pcmk_resource_t *rsc, pcmk_node_t *current, role = next_role; } - pe__clear_resource_flags(rsc, pcmk_rsc_restarting); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_restarting); } /*! @@ -635,11 +642,11 @@ create_pending_start(pcmk_resource_t *rsc) { pcmk_action_t *start = NULL; - pe_rsc_trace(rsc, - "Creating action for %s to represent already pending start", - rsc->id); + pcmk__rsc_trace(rsc, + "Creating action for %s to represent already pending start", + rsc->id); start = start_action(rsc, rsc->allocated_to, TRUE); - pe__set_action_flags(start, pcmk_action_always_in_graph); + pcmk__set_action_flags(start, pcmk_action_always_in_graph); } /*! @@ -657,10 +664,12 @@ schedule_role_transition_actions(pcmk_resource_t *rsc) enum rsc_role_e next_role = rsc_state_matrix[role][rsc->next_role]; rsc_transition_fn fn = NULL; - pe_rsc_trace(rsc, - "Creating action to take %s from %s to %s (ending at %s)", - rsc->id, role2text(role), role2text(next_role), - role2text(rsc->next_role)); + pcmk__rsc_trace(rsc, + "Creating action to take %s from %s to %s " + "(ending at %s)", + rsc->id, pcmk_role_text(role), + pcmk_role_text(next_role), + pcmk_role_text(rsc->next_role)); fn = rsc_action_matrix[role][next_role]; if (fn == NULL) { break; @@ -690,14 +699,15 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) unsigned int num_clean_active = 0; const char *next_role_source = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT(pcmk__is_primitive(rsc)); next_role_source = set_default_next_role(rsc); - pe_rsc_trace(rsc, - "Creating all actions for %s transition from %s to %s " - "(%s) on %s", - rsc->id, role2text(rsc->role), role2text(rsc->next_role), - next_role_source, pe__node_name(rsc->allocated_to)); + pcmk__rsc_trace(rsc, + "Creating all actions for %s transition from %s to %s " + "(%s) on %s", + rsc->id, pcmk_role_text(rsc->role), + pcmk_role_text(rsc->next_role), next_role_source, + pcmk__node_name(rsc->allocated_to)); current = rsc->fns->active_node(rsc, &num_all_active, &num_clean_active); @@ -705,12 +715,12 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) rsc); if ((current != NULL) && (rsc->allocated_to != NULL) - && !pe__same_node(current, rsc->allocated_to) + && !pcmk__same_node(current, rsc->allocated_to) && (rsc->next_role >= pcmk_role_started)) { - pe_rsc_trace(rsc, "Moving %s from %s to %s", - rsc->id, pe__node_name(current), - pe__node_name(rsc->allocated_to)); + pcmk__rsc_trace(rsc, "Moving %s from %s to %s", + rsc->id, pcmk__node_name(current), + pcmk__node_name(rsc->allocated_to)); is_moving = true; allow_migrate = pcmk__rsc_can_migrate(rsc, current); @@ -722,14 +732,15 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) if ((rsc->partial_migration_source != NULL) && (rsc->partial_migration_target != NULL) && allow_migrate && (num_all_active == 2) - && pe__same_node(current, rsc->partial_migration_source) - && pe__same_node(rsc->allocated_to, rsc->partial_migration_target)) { + && pcmk__same_node(current, rsc->partial_migration_source) + && pcmk__same_node(rsc->allocated_to, rsc->partial_migration_target)) { /* A partial migration is in progress, and the migration target remains * the same as when the migration began. */ - pe_rsc_trace(rsc, "Partial migration of %s from %s to %s will continue", - rsc->id, pe__node_name(rsc->partial_migration_source), - pe__node_name(rsc->partial_migration_target)); + pcmk__rsc_trace(rsc, + "Partial migration of %s from %s to %s will continue", + rsc->id, pcmk__node_name(rsc->partial_migration_source), + pcmk__node_name(rsc->partial_migration_target)); } else if ((rsc->partial_migration_source != NULL) || (rsc->partial_migration_target != NULL)) { @@ -739,14 +750,14 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) // The resource is migrating *and* multiply active! crm_notice("Forcing recovery of %s because it is migrating " "from %s to %s and possibly active elsewhere", - rsc->id, pe__node_name(rsc->partial_migration_source), - pe__node_name(rsc->partial_migration_target)); + rsc->id, pcmk__node_name(rsc->partial_migration_source), + pcmk__node_name(rsc->partial_migration_target)); } else { // The migration source or target isn't available crm_notice("Forcing recovery of %s because it can no longer " "migrate from %s to %s", - rsc->id, pe__node_name(rsc->partial_migration_source), - pe__node_name(rsc->partial_migration_target)); + rsc->id, pcmk__node_name(rsc->partial_migration_source), + pcmk__node_name(rsc->partial_migration_target)); } need_stop = true; rsc->partial_migration_source = rsc->partial_migration_target = NULL; @@ -755,25 +766,26 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) { multiply_active = (num_all_active > 1); } else { - /* If a resource has "requires" set to nothing or quorum, don't consider - * it active on unclean nodes (similar to how all resources behave when - * stonith-enabled is false). We can start such resources elsewhere - * before fencing completes, and if we considered the resource active on - * the failed node, we would attempt recovery for being active on - * multiple nodes. + /* If a resource has PCMK_META_REQUIRES set to PCMK_VALUE_NOTHING or + * PCMK_VALUE_QUORUM, don't consider it active on unclean nodes (similar + * to how all resources behave when PCMK_OPT_STONITH_ENABLED is false). + * We can start such resources elsewhere before fencing completes, and + * if we considered the resource active on the failed node, we would + * attempt recovery for being active on multiple nodes. */ multiply_active = (num_clean_active > 1); } if (multiply_active) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); // Resource was (possibly) incorrectly multiply active - pe_proc_err("%s resource %s might be active on %u nodes (%s)", - pcmk__s(class, "Untyped"), rsc->id, num_all_active, - recovery2text(rsc->recovery_type)); - crm_notice("See https://wiki.clusterlabs.org/wiki/FAQ" - "#Resource_is_Too_Active for more information"); + pcmk__sched_err("%s resource %s might be active on %u nodes (%s)", + pcmk__s(class, "Untyped"), rsc->id, num_all_active, + pcmk__multiply_active_text(rsc->recovery_type)); + crm_notice("For more information, see \"What are multiply active " + "resources?\" at " + "https://projects.clusterlabs.org/w/clusterlabs/faq/"); switch (rsc->recovery_type) { case pcmk_multiply_active_restart: @@ -781,14 +793,14 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) break; case pcmk_multiply_active_unexpected: need_stop = true; // stop_resource() will skip expected node - pe__set_resource_flags(rsc, pcmk_rsc_stop_unexpected); + pcmk__set_rsc_flags(rsc, pcmk_rsc_stop_unexpected); break; default: break; } } else { - pe__clear_resource_flags(rsc, pcmk_rsc_stop_unexpected); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_stop_unexpected); } if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) { @@ -801,28 +813,28 @@ pcmk__primitive_create_actions(pcmk_resource_t *rsc) } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_stop_if_failed)) { need_stop = true; - pe_rsc_trace(rsc, "Recovering %s", rsc->id); + pcmk__rsc_trace(rsc, "Recovering %s", rsc->id); } else { - pe_rsc_trace(rsc, "Recovering %s by demotion", rsc->id); + pcmk__rsc_trace(rsc, "Recovering %s by demotion", rsc->id); if (rsc->next_role == pcmk_role_promoted) { need_promote = true; } } } else if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) { - pe_rsc_trace(rsc, "Blocking further actions on %s", rsc->id); + pcmk__rsc_trace(rsc, "Blocking further actions on %s", rsc->id); need_stop = true; } else if ((rsc->role > pcmk_role_started) && (current != NULL) && (rsc->allocated_to != NULL)) { pcmk_action_t *start = NULL; - pe_rsc_trace(rsc, "Creating start action for promoted resource %s", - rsc->id); + pcmk__rsc_trace(rsc, "Creating start action for promoted resource %s", + rsc->id); start = start_action(rsc, rsc->allocated_to, TRUE); if (!pcmk_is_set(start->flags, pcmk_action_optional)) { // Recovery of a promoted resource - pe_rsc_trace(rsc, "%s restart is required for recovery", rsc->id); + pcmk__rsc_trace(rsc, "%s restart is required for recovery", rsc->id); need_stop = true; } } @@ -855,7 +867,7 @@ rsc_avoids_remote_nodes(const pcmk_resource_t *rsc) g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (node->details->remote_rsc != NULL) { - node->weight = -INFINITY; + node->weight = -PCMK_SCORE_INFINITY; } } } @@ -902,12 +914,12 @@ pcmk__primitive_internal_constraints(pcmk_resource_t *rsc) bool check_unfencing = false; bool check_utilization = false; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, - "Skipping implicit constraints for unmanaged resource %s", - rsc->id); + pcmk__rsc_trace(rsc, + "Skipping implicit constraints for unmanaged resource " + "%s", rsc->id); return; } @@ -920,7 +932,7 @@ pcmk__primitive_internal_constraints(pcmk_resource_t *rsc) // Whether a non-default placement strategy is used check_utilization = (g_hash_table_size(rsc->utilization) > 0) && !pcmk__str_eq(rsc->cluster->placement_strategy, - "default", pcmk__str_casei); + PCMK_VALUE_DEFAULT, pcmk__str_casei); // Order stops before starts (i.e. restart) pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), NULL, @@ -1014,7 +1026,7 @@ pcmk__primitive_internal_constraints(pcmk_resource_t *rsc) pcmk_node_t *node = item->data; if (node->details->remote_rsc != remote_rsc) { - node->weight = -INFINITY; + node->weight = -PCMK_SCORE_INFINITY; } } @@ -1049,7 +1061,7 @@ pcmk__primitive_internal_constraints(pcmk_resource_t *rsc) if (pcmk_is_set(rsc->flags, pcmk_rsc_remote_nesting_allowed)) { score = 10000; /* Highly preferred but not essential */ } else { - score = INFINITY; /* Force them to run on the same host */ + score = PCMK_SCORE_INFINITY; // Force to run on same host } pcmk__new_colocation("#resource-with-container", NULL, score, rsc, rsc->container, NULL, NULL, @@ -1099,10 +1111,11 @@ pcmk__primitive_apply_coloc_score(pcmk_resource_t *dependent, filter_results = pcmk__colocation_affects(dependent, primary, colocation, false); - pe_rsc_trace(dependent, "%s %s with %s (%s, score=%d, filter=%d)", - ((colocation->score > 0)? "Colocating" : "Anti-colocating"), - dependent->id, primary->id, colocation->id, colocation->score, - filter_results); + pcmk__rsc_trace(dependent, "%s %s with %s (%s, score=%d, filter=%d)", + ((colocation->score > 0)? "Colocating" : "Anti-colocating"), + dependent->id, primary->id, colocation->id, + colocation->score, + filter_results); switch (filter_results) { case pcmk__coloc_affects_role: @@ -1123,8 +1136,7 @@ void pcmk__with_primitive_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive) - && (list != NULL)); + CRM_ASSERT(pcmk__is_primitive(rsc) && (list != NULL)); if (rsc == orig_rsc) { /* For the resource itself, add all of its own colocations and relevant @@ -1153,8 +1165,7 @@ void pcmk__primitive_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive) - && (list != NULL)); + CRM_ASSERT(pcmk__is_primitive(rsc) && (list != NULL)); if (rsc == orig_rsc) { /* For the resource itself, add all of its own colocations and relevant @@ -1199,8 +1210,9 @@ pcmk__primitive_action_flags(pcmk_action_t *action, const pcmk_node_t *node) * \param[in] rsc Resource to check * \param[in] node Node to check * - * \return true if \p rsc is multiply active with multiple-active set to - * stop_unexpected, and \p node is the node where it will remain active + * \return \c true if \p rsc is multiply active with + * \c PCMK_META_MULTIPLE_ACTIVE set to \c PCMK_VALUE_STOP_UNEXPECTED, + * and \p node is the node where it will remain active * \note This assumes that the resource's next role cannot be changed to stopped * after this is called, which should be reasonable if status has already * been unpacked and resources have been assigned to nodes. @@ -1211,7 +1223,7 @@ is_expected_node(const pcmk_resource_t *rsc, const pcmk_node_t *node) return pcmk_all_flags_set(rsc->flags, pcmk_rsc_stop_unexpected|pcmk_rsc_restarting) && (rsc->next_role > pcmk_role_stopped) - && pe__same_node(rsc->allocated_to, node); + && pcmk__same_node(rsc->allocated_to, node); } /*! @@ -1231,36 +1243,36 @@ stop_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) if (is_expected_node(rsc, current)) { /* We are scheduling restart actions for a multiply active resource - * with multiple-active=stop_unexpected, and this is where it should - * not be stopped. + * with PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_STOP_UNEXPECTED, and + * this is where it should not be stopped. */ - pe_rsc_trace(rsc, - "Skipping stop of multiply active resource %s " - "on expected node %s", - rsc->id, pe__node_name(current)); + pcmk__rsc_trace(rsc, + "Skipping stop of multiply active resource %s " + "on expected node %s", + rsc->id, pcmk__node_name(current)); continue; } if (rsc->partial_migration_target != NULL) { // Continue migration if node originally was and remains target - if (pe__same_node(current, rsc->partial_migration_target) - && pe__same_node(current, rsc->allocated_to)) { - pe_rsc_trace(rsc, - "Skipping stop of %s on %s " - "because partial migration there will continue", - rsc->id, pe__node_name(current)); + if (pcmk__same_node(current, rsc->partial_migration_target) + && pcmk__same_node(current, rsc->allocated_to)) { + pcmk__rsc_trace(rsc, + "Skipping stop of %s on %s " + "because partial migration there will continue", + rsc->id, pcmk__node_name(current)); continue; } else { - pe_rsc_trace(rsc, - "Forcing stop of %s on %s " - "because migration target changed", - rsc->id, pe__node_name(current)); + pcmk__rsc_trace(rsc, + "Forcing stop of %s on %s " + "because migration target changed", + rsc->id, pcmk__node_name(current)); optional = false; } } - pe_rsc_trace(rsc, "Scheduling stop of %s on %s", - rsc->id, pe__node_name(current)); + pcmk__rsc_trace(rsc, "Scheduling stop of %s on %s", + rsc->id, pcmk__node_name(current)); stop = stop_action(rsc, current, optional); if (rsc->allocated_to == NULL) { @@ -1275,7 +1287,7 @@ stop_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe__clear_action_flags(stop, pcmk_action_runnable); + pcmk__clear_action_flags(stop, pcmk_action_runnable); } if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_remove_after_stop)) { @@ -1288,8 +1300,8 @@ stop_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) order_actions(stop, unfence, pcmk__ar_then_implies_first); if (!pcmk__node_unfenced(current)) { - pe_proc_err("Stopping %s until %s can be unfenced", - rsc->id, pe__node_name(current)); + pcmk__sched_err("Stopping %s until %s can be unfenced", + rsc->id, pcmk__node_name(current)); } } } @@ -1310,26 +1322,26 @@ start_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) CRM_ASSERT(node != NULL); - pe_rsc_trace(rsc, "Scheduling %s start of %s on %s (score %d)", - (optional? "optional" : "required"), rsc->id, - pe__node_name(node), node->weight); + pcmk__rsc_trace(rsc, "Scheduling %s start of %s on %s (score %d)", + (optional? "optional" : "required"), rsc->id, + pcmk__node_name(node), node->weight); start = start_action(rsc, node, TRUE); pcmk__order_vs_unfence(rsc, node, start, pcmk__ar_first_implies_then); if (pcmk_is_set(start->flags, pcmk_action_runnable) && !optional) { - pe__clear_action_flags(start, pcmk_action_optional); + pcmk__clear_action_flags(start, pcmk_action_optional); } if (is_expected_node(rsc, node)) { /* This could be a problem if the start becomes necessary for other * reasons later. */ - pe_rsc_trace(rsc, - "Start of multiply active resouce %s " - "on expected node %s will be a pseudo-action", - rsc->id, pe__node_name(node)); - pe__set_action_flags(start, pcmk_action_pseudo); + pcmk__rsc_trace(rsc, + "Start of multiply active resouce %s " + "on expected node %s will be a pseudo-action", + rsc->id, pcmk__node_name(node)); + pcmk__set_action_flags(start, pcmk_action_pseudo); } } @@ -1364,29 +1376,29 @@ promote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) if (runnable) { pcmk_action_t *promote = promote_action(rsc, node, optional); - pe_rsc_trace(rsc, "Scheduling %s promotion of %s on %s", - (optional? "optional" : "required"), rsc->id, - pe__node_name(node)); + pcmk__rsc_trace(rsc, "Scheduling %s promotion of %s on %s", + (optional? "optional" : "required"), rsc->id, + pcmk__node_name(node)); if (is_expected_node(rsc, node)) { /* This could be a problem if the promote becomes necessary for * other reasons later. */ - pe_rsc_trace(rsc, - "Promotion of multiply active resouce %s " - "on expected node %s will be a pseudo-action", - rsc->id, pe__node_name(node)); - pe__set_action_flags(promote, pcmk_action_pseudo); + pcmk__rsc_trace(rsc, + "Promotion of multiply active resouce %s " + "on expected node %s will be a pseudo-action", + rsc->id, pcmk__node_name(node)); + pcmk__set_action_flags(promote, pcmk_action_pseudo); } } else { - pe_rsc_trace(rsc, "Not promoting %s on %s: start unrunnable", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Not promoting %s on %s: start unrunnable", + rsc->id, pcmk__node_name(node)); action_list = pe__resource_actions(rsc, node, PCMK_ACTION_PROMOTE, true); for (iter = action_list; iter != NULL; iter = iter->next) { pcmk_action_t *promote = (pcmk_action_t *) iter->data; - pe__clear_action_flags(promote, pcmk_action_runnable); + pcmk__clear_action_flags(promote, pcmk_action_runnable); } g_list_free(action_list); } @@ -1412,14 +1424,14 @@ demote_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool optional) pcmk_node_t *current = (pcmk_node_t *) iter->data; if (is_expected_node(rsc, current)) { - pe_rsc_trace(rsc, - "Skipping demote of multiply active resource %s " - "on expected node %s", - rsc->id, pe__node_name(current)); + pcmk__rsc_trace(rsc, + "Skipping demote of multiply active resource %s " + "on expected node %s", + rsc->id, pcmk__node_name(current)); } else { - pe_rsc_trace(rsc, "Scheduling %s demotion of %s on %s", - (optional? "optional" : "required"), rsc->id, - pe__node_name(current)); + pcmk__rsc_trace(rsc, "Scheduling %s demotion of %s on %s", + (optional? "optional" : "required"), rsc->id, + pcmk__node_name(current)); demote_action(rsc, current, optional); } } @@ -1453,18 +1465,19 @@ pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node, CRM_CHECK((rsc != NULL) && (node != NULL), return); if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { - pe_rsc_trace(rsc, "Skipping clean-up of %s on %s: resource failed", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping clean-up of %s on %s: resource failed", + rsc->id, pcmk__node_name(node)); return; } if (node->details->unclean || !node->details->online) { - pe_rsc_trace(rsc, "Skipping clean-up of %s on %s: node unavailable", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping clean-up of %s on %s: node unavailable", + rsc->id, pcmk__node_name(node)); return; } - crm_notice("Scheduling clean-up of %s on %s", rsc->id, pe__node_name(node)); + crm_notice("Scheduling clean-up of %s on %s", + rsc->id, pcmk__node_name(node)); delete_action(rsc, node, optional); // stop -> clean-up -> start @@ -1488,24 +1501,23 @@ pcmk__primitive_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) char *value = NULL; const pcmk_resource_t *parent = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive) - && (xml != NULL)); + CRM_ASSERT(pcmk__is_primitive(rsc) && (xml != NULL)); /* Clone instance numbers get set internally as meta-attributes, and are * needed in the transition graph (for example, to tell unique clone * instances apart). */ - value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION); + value = g_hash_table_lookup(rsc->meta, PCMK__META_CLONE); if (value != NULL) { - name = crm_meta_name(XML_RSC_ATTR_INCARNATION); + name = crm_meta_name(PCMK__META_CLONE); crm_xml_add(xml, name, value); free(name); } // Not sure if this one is really needed ... - value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_REMOTE_NODE); + value = g_hash_table_lookup(rsc->meta, PCMK_META_REMOTE_NODE); if (value != NULL) { - name = crm_meta_name(XML_RSC_ATTR_REMOTE_NODE); + name = crm_meta_name(PCMK_META_REMOTE_NODE); crm_xml_add(xml, name, value); free(name); } @@ -1516,7 +1528,7 @@ pcmk__primitive_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml) */ for (parent = rsc; parent != NULL; parent = parent->parent) { if (parent->container != NULL) { - crm_xml_add(xml, CRM_META "_" XML_RSC_ATTR_CONTAINER, + crm_xml_add(xml, CRM_META "_" PCMK__META_CONTAINER, parent->container->id); } } @@ -1537,15 +1549,16 @@ pcmk__primitive_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive) + CRM_ASSERT(pcmk__is_primitive(rsc) && (orig_rsc != NULL) && (utilization != NULL)); if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { return; } - pe_rsc_trace(orig_rsc, "%s: Adding primitive %s as colocated utilization", - orig_rsc->id, rsc->id); + pcmk__rsc_trace(orig_rsc, + "%s: Adding primitive %s as colocated utilization", + orig_rsc->id, rsc->id); pcmk__release_node_capacity(utilization, rsc); } @@ -1560,7 +1573,8 @@ pcmk__primitive_add_utilization(const pcmk_resource_t *rsc, static time_t shutdown_time(pcmk_node_t *node) { - const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN); + const char *shutdown = pcmk__node_attr(node, PCMK__NODE_ATTR_SHUTDOWN, NULL, + pcmk__rsc_node_current); time_t result = 0; if (shutdown != NULL) { @@ -1587,8 +1601,8 @@ ban_if_not_locked(gpointer data, gpointer user_data) pcmk_resource_t *rsc = (pcmk_resource_t *) user_data; if (strcmp(node->details->uname, rsc->lock_node->details->uname) != 0) { - resource_location(rsc, node, -CRM_SCORE_INFINITY, - XML_CONFIG_ATTR_SHUTDOWN_LOCK, rsc->cluster); + resource_location(rsc, node, -PCMK_SCORE_INFINITY, + PCMK_OPT_SHUTDOWN_LOCK, rsc->cluster); } } @@ -1598,13 +1612,13 @@ pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc) { const char *class = NULL; - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT(pcmk__is_primitive(rsc)); - class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + class = crm_element_value(rsc->xml, PCMK_XA_CLASS); // Fence devices and remote connections can't be locked if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_null_matches) - || pe__resource_is_remote_conn(rsc)) { + || rsc->is_remote_node) { return; } @@ -1616,9 +1630,9 @@ pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc) * considered locked. This shouldn't be possible, but as a * failsafe, we don't want to disturb the resource now. */ - pe_rsc_info(rsc, - "Cancelling shutdown lock because %s is already active", - rsc->id); + pcmk__rsc_info(rsc, + "Cancelling shutdown lock " + "because %s is already active", rsc->id); pe__clear_resource_history(rsc, rsc->lock_node); rsc->lock_node = NULL; rsc->lock_time = 0; @@ -1630,8 +1644,9 @@ pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc) if (node->details->shutdown) { if (node->details->unclean) { - pe_rsc_debug(rsc, "Not locking %s to unclean %s for shutdown", - rsc->id, pe__node_name(node)); + pcmk__rsc_debug(rsc, + "Not locking %s to unclean %s for shutdown", + rsc->id, pcmk__node_name(node)); } else { rsc->lock_node = node; rsc->lock_time = shutdown_time(node); @@ -1647,14 +1662,14 @@ pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc) if (rsc->cluster->shutdown_lock > 0) { time_t lock_expiration = rsc->lock_time + rsc->cluster->shutdown_lock; - pe_rsc_info(rsc, "Locking %s to %s due to shutdown (expires @%lld)", - rsc->id, pe__node_name(rsc->lock_node), - (long long) lock_expiration); + pcmk__rsc_info(rsc, "Locking %s to %s due to shutdown (expires @%lld)", + rsc->id, pcmk__node_name(rsc->lock_node), + (long long) lock_expiration); pe__update_recheck_time(++lock_expiration, rsc->cluster, "shutdown lock expiration"); } else { - pe_rsc_info(rsc, "Locking %s to %s due to shutdown", - rsc->id, pe__node_name(rsc->lock_node)); + pcmk__rsc_info(rsc, "Locking %s to %s due to shutdown", + rsc->id, pcmk__node_name(rsc->lock_node)); } // If resource is locked to one node, ban it from all other nodes diff --git a/lib/pacemaker/pcmk_sched_probes.c b/lib/pacemaker/pcmk_sched_probes.c index e31e8d2..6335ab0 100644 --- a/lib/pacemaker/pcmk_sched_probes.c +++ b/lib/pacemaker/pcmk_sched_probes.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. * @@ -111,7 +111,7 @@ guest_resource_will_stop(const pcmk_node_t *node) // Guest is moving || ((guest_rsc->role > pcmk_role_stopped) && (guest_rsc->allocated_to != NULL) - && (pe_find_node(guest_rsc->running_on, + && (pcmk__find_node_in_list(guest_rsc->running_on, guest_rsc->allocated_to->details->uname) == NULL)); } @@ -131,11 +131,11 @@ probe_action(pcmk_resource_t *rsc, pcmk_node_t *node) char *key = pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0); crm_debug("Scheduling probe of %s %s on %s", - role2text(rsc->role), rsc->id, pe__node_name(node)); + pcmk_role_text(rsc->role), rsc->id, pcmk__node_name(node)); probe = custom_action(rsc, key, PCMK_ACTION_MONITOR, node, FALSE, rsc->cluster); - pe__clear_action_flags(probe, pcmk_action_optional); + pcmk__clear_action_flags(probe, pcmk_action_optional); pcmk__order_vs_unfence(rsc, node, probe, pcmk__ar_ordered); add_expected_result(probe, rsc, node); @@ -169,14 +169,14 @@ pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node) goto no_probe; } - if (pe__is_guest_or_remote_node(node)) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + if (pcmk__is_pacemaker_remote_node(node)) { + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_none)) { reason = "Pacemaker Remote nodes cannot run stonith agents"; goto no_probe; - } else if (pe__is_guest_node(node) + } else if (pcmk__is_guest_or_bundle_node(node) && pe__resource_contains_guest_node(rsc->cluster, rsc)) { reason = "guest nodes cannot run resources containing guest nodes"; goto no_probe; @@ -232,7 +232,7 @@ pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node) goto no_probe; } - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { pcmk_resource_t *guest = node->details->remote_rsc->container; if (guest->role == pcmk_role_stopped) { @@ -262,7 +262,7 @@ pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node) * just the instance. Otherwise, the start or reload is for the resource * itself. */ - if (!pe_rsc_is_clone(top)) { + if (!pcmk__is_clone(top)) { top = rsc; } @@ -271,7 +271,7 @@ pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node) */ if (!pcmk_is_set(probe->flags, pcmk_action_runnable) && (top->running_on == NULL)) { - pe__set_order_flags(flags, pcmk__ar_unrunnable_first_blocks); + pcmk__set_relation_flags(flags, pcmk__ar_unrunnable_first_blocks); } // Start or reload after probing the resource @@ -284,9 +284,9 @@ pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node) return true; no_probe: - pe_rsc_trace(rsc, - "Skipping probe for %s on %s because %s", - rsc->id, node->details->id, reason); + pcmk__rsc_trace(rsc, + "Skipping probe for %s on %s because %s", + rsc->id, node->details->id, reason); return false; } @@ -305,8 +305,9 @@ probe_needed_before_action(const pcmk_action_t *probe, { // Probes on a node are performed after unfencing it, not before if (pcmk__str_eq(then->task, PCMK_ACTION_STONITH, pcmk__str_none) - && pe__same_node(probe->node, then->node)) { - const char *op = g_hash_table_lookup(then->meta, "stonith_action"); + && pcmk__same_node(probe->node, then->node)) { + const char *op = g_hash_table_lookup(then->meta, + PCMK__META_STONITH_ACTION); if (pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) { return false; @@ -316,7 +317,7 @@ probe_needed_before_action(const pcmk_action_t *probe, // Probes should be done on a node before shutting it down if (pcmk__str_eq(then->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none) && (probe->node != NULL) && (then->node != NULL) - && !pe__same_node(probe->node, then->node)) { + && !pcmk__same_node(probe->node, then->node)) { return false; } @@ -343,7 +344,7 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) for (GList *iter = scheduler->ordering_constraints; iter != NULL; iter = iter->next) { - pe__ordering_t *order = iter->data; + pcmk__action_relation_t *order = iter->data; uint32_t order_flags = pcmk__ar_ordered; GList *probes = NULL; GList *then_actions = NULL; @@ -356,15 +357,15 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) } // Skip non-resource orderings, and orderings for the same resource - if ((order->lh_rsc == NULL) || (order->lh_rsc == order->rh_rsc)) { + if ((order->rsc1 == NULL) || (order->rsc1 == order->rsc2)) { continue; } // Skip invalid orderings (shouldn't be possible) - first = order->lh_action; - then = order->rh_action; - if (((first == NULL) && (order->lh_action_task == NULL)) - || ((then == NULL) && (order->rh_action_task == NULL))) { + first = order->action1; + then = order->action2; + if (((first == NULL) && (order->task1 == NULL)) + || ((then == NULL) && (order->task2 == NULL))) { continue; } @@ -373,7 +374,7 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) pcmk__str_none)) { continue; } else if ((first == NULL) - && !pcmk__ends_with(order->lh_action_task, + && !pcmk__ends_with(order->task1, "_" PCMK_ACTION_STOP "_0")) { continue; } @@ -382,14 +383,13 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) * container. Otherwise, it might introduce a transition loop, since a * probe could be scheduled after the container starts again. */ - if ((order->rh_rsc != NULL) - && (order->lh_rsc->container == order->rh_rsc)) { + if ((order->rsc2 != NULL) && (order->rsc1->container == order->rsc2)) { if ((then != NULL) && pcmk__str_eq(then->task, PCMK_ACTION_STOP, pcmk__str_none)) { continue; } else if ((then == NULL) - && pcmk__ends_with(order->rh_action_task, + && pcmk__ends_with(order->task2, "_" PCMK_ACTION_STOP "_0")) { continue; } @@ -397,10 +397,11 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) // Preserve certain order options for future filtering if (pcmk_is_set(order->flags, pcmk__ar_if_first_unmigratable)) { - pe__set_order_flags(order_flags, pcmk__ar_if_first_unmigratable); + pcmk__set_relation_flags(order_flags, + pcmk__ar_if_first_unmigratable); } if (pcmk_is_set(order->flags, pcmk__ar_if_on_same_node)) { - pe__set_order_flags(order_flags, pcmk__ar_if_on_same_node); + pcmk__set_relation_flags(order_flags, pcmk__ar_if_on_same_node); } // Preserve certain order types for future filtering @@ -410,7 +411,7 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) } // List all scheduled probes for the first resource - probes = pe__resource_actions(order->lh_rsc, NULL, PCMK_ACTION_MONITOR, + probes = pe__resource_actions(order->rsc1, NULL, PCMK_ACTION_MONITOR, FALSE); if (probes == NULL) { // There aren't any continue; @@ -420,9 +421,9 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) if (then != NULL) { then_actions = g_list_prepend(NULL, then); - } else if (order->rh_rsc != NULL) { - then_actions = find_actions(order->rh_rsc->actions, - order->rh_action_task, NULL); + } else if (order->rsc2 != NULL) { + then_actions = find_actions(order->rsc2->actions, order->task2, + NULL); if (then_actions == NULL) { // There aren't any g_list_free(probes); continue; @@ -431,8 +432,8 @@ add_probe_orderings_for_stops(pcmk_scheduler_t *scheduler) crm_trace("Implying 'probe then' orderings for '%s then %s' " "(id=%d, type=%.6x)", - ((first == NULL)? order->lh_action_task : first->uuid), - ((then == NULL)? order->rh_action_task : then->uuid), + ((first == NULL)? order->task1 : first->uuid), + ((then == NULL)? order->task2 : then->uuid), order->id, order->flags); for (GList *probe_iter = probes; probe_iter != NULL; @@ -494,8 +495,8 @@ add_start_orderings_for_probe(pcmk_action_t *probe, crm_trace("Adding probe start orderings for 'unrunnable %s@%s " "then instances of %s@%s'", - probe->uuid, pe__node_name(probe->node), - after->action->uuid, pe__node_name(after->action->node)); + probe->uuid, pcmk__node_name(probe->node), + after->action->uuid, pcmk__node_name(after->action->node)); for (GList *then_iter = after->action->actions_after; then_iter != NULL; then_iter = then_iter->next) { @@ -512,8 +513,9 @@ add_start_orderings_for_probe(pcmk_action_t *probe, crm_trace("Adding probe start ordering for 'unrunnable %s@%s " "then %s@%s' (type=%#.6x)", - probe->uuid, pe__node_name(probe->node), - then->action->uuid, pe__node_name(then->action->node), flags); + probe->uuid, pcmk__node_name(probe->node), + then->action->uuid, pcmk__node_name(then->action->node), + flags); /* Prevent the instance from starting if the instance can't, but don't * cause any other intances to stop if already active. @@ -544,8 +546,7 @@ add_restart_orderings_for_probe(pcmk_action_t *probe, pcmk_action_t *after) pcmk_resource_t *compatible_rsc = NULL; // Validate that this is a resource probe followed by some action - if ((after == NULL) || (probe == NULL) || (probe->rsc == NULL) - || (probe->rsc->variant != pcmk_rsc_variant_primitive) + if ((after == NULL) || (probe == NULL) || !pcmk__is_primitive(probe->rsc) || !pcmk__str_eq(probe->task, PCMK_ACTION_MONITOR, pcmk__str_none)) { return; } @@ -554,18 +555,16 @@ add_restart_orderings_for_probe(pcmk_action_t *probe, pcmk_action_t *after) if (pcmk_is_set(after->flags, pcmk_action_detect_loop)) { return; } - pe__set_action_flags(after, pcmk_action_detect_loop); + pcmk__set_action_flags(after, pcmk_action_detect_loop); crm_trace("Adding probe restart orderings for '%s@%s then %s@%s'", - probe->uuid, pe__node_name(probe->node), - after->uuid, pe__node_name(after->node)); + probe->uuid, pcmk__node_name(probe->node), + after->uuid, pcmk__node_name(after->node)); /* Add restart orderings if "then" is for a different primitive. * Orderings for collective resources will be added later. */ - if ((after->rsc != NULL) - && (after->rsc->variant == pcmk_rsc_variant_primitive) - && (probe->rsc != after->rsc)) { + if (pcmk__is_primitive(after->rsc) && (probe->rsc != after->rsc)) { GList *then_actions = NULL; @@ -596,7 +595,7 @@ add_restart_orderings_for_probe(pcmk_action_t *probe, pcmk_action_t *after) if ((after->rsc != NULL) && (after->rsc->variant > pcmk_rsc_variant_group)) { const char *interleave_s = g_hash_table_lookup(after->rsc->meta, - XML_RSC_ATTR_INTERLEAVE); + PCMK_META_INTERLEAVE); interleave = crm_is_true(interleave_s); if (interleave) { @@ -652,9 +651,9 @@ add_restart_orderings_for_probe(pcmk_action_t *probe, pcmk_action_t *after) crm_trace("Recursively adding probe restart orderings for " "'%s@%s then %s@%s' (type=%#.6x)", - after->uuid, pe__node_name(after->node), + after->uuid, pcmk__node_name(after->node), after_wrapper->action->uuid, - pe__node_name(after_wrapper->action->node), + pcmk__node_name(after_wrapper->action->node), after_wrapper->type); add_restart_orderings_for_probe(probe, after_wrapper->action); @@ -673,7 +672,7 @@ clear_actions_tracking_flag(pcmk_scheduler_t *scheduler) for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) { pcmk_action_t *action = iter->data; - pe__clear_action_flags(action, pcmk_action_detect_loop); + pcmk__clear_action_flags(action, pcmk_action_detect_loop); } } @@ -691,7 +690,7 @@ add_start_restart_orderings_for_rsc(gpointer data, gpointer user_data) GList *probes = NULL; // For collective resources, order each instance recursively - if (rsc->variant != pcmk_rsc_variant_primitive) { + if (!pcmk__is_primitive(rsc)) { g_list_foreach(rsc->children, add_start_restart_orderings_for_rsc, NULL); return; @@ -774,7 +773,7 @@ order_then_probes(pcmk_scheduler_t *scheduler) } if (start == NULL) { - crm_err("No start action for %s", rsc->id); + crm_debug("No start action for %s", rsc->id); continue; } @@ -816,21 +815,21 @@ order_then_probes(pcmk_scheduler_t *scheduler) crm_trace("Same parent %s for %s", first_rsc->id, start->uuid); continue; - } else if (!pe_rsc_is_clone(pe__const_top_resource(first_rsc, - false))) { + } else if (!pcmk__is_clone(pe__const_top_resource(first_rsc, + false))) { crm_trace("Not a clone %s for %s", first_rsc->id, start->uuid); continue; } - crm_err("Applying %s before %s %d", first->uuid, start->uuid, - pe__const_top_resource(first_rsc, false)->variant); + crm_debug("Applying %s before %s %d", first->uuid, start->uuid, + pe__const_top_resource(first_rsc, false)->variant); for (GList *probe_iter = probes; probe_iter != NULL; probe_iter = probe_iter->next) { pcmk_action_t *probe = (pcmk_action_t *) probe_iter->data; - crm_err("Ordering %s before %s", first->uuid, probe->uuid); + crm_debug("Ordering %s before %s", first->uuid, probe->uuid); order_actions(first, probe, pcmk__ar_ordered); } } @@ -885,7 +884,8 @@ pcmk__schedule_probes(pcmk_scheduler_t *scheduler) * for processing old saved CIBs (< 1.1.14), including the * reprobe-target_rc regression test. */ - probed = pe_node_attribute_raw(node, CRM_OP_PROBED); + probed = pcmk__node_attr(node, CRM_OP_PROBED, NULL, + pcmk__rsc_node_current); if (probed != NULL && crm_is_true(probed) == FALSE) { pcmk_action_t *probe_op = NULL; @@ -893,8 +893,7 @@ pcmk__schedule_probes(pcmk_scheduler_t *scheduler) crm_strdup_printf("%s-%s", CRM_OP_REPROBE, node->details->uname), CRM_OP_REPROBE, node, FALSE, scheduler); - add_hash_param(probe_op->meta, XML_ATTR_TE_NOWAIT, - XML_BOOLEAN_TRUE); + pcmk__insert_meta(probe_op, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE); continue; } diff --git a/lib/pacemaker/pcmk_sched_promotable.c b/lib/pacemaker/pcmk_sched_promotable.c index 7f81a13..fb8bb9a 100644 --- a/lib/pacemaker/pcmk_sched_promotable.c +++ b/lib/pacemaker/pcmk_sched_promotable.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,7 +9,7 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -130,25 +130,25 @@ apply_promoted_locations(pcmk_resource_t *child, const pcmk_node_t *chosen) { for (const GList *iter = location_constraints; iter; iter = iter->next) { - const pe__location_t *location = iter->data; + const pcmk__location_t *location = iter->data; const pcmk_node_t *constraint_node = NULL; if (location->role_filter == pcmk_role_promoted) { - constraint_node = pe_find_node_id(location->node_list_rh, + constraint_node = pe_find_node_id(location->nodes, chosen->details->id); } if (constraint_node != NULL) { int new_priority = pcmk__add_scores(child->priority, constraint_node->weight); - pe_rsc_trace(child, - "Applying location %s to %s promotion priority on %s: " - "%s + %s = %s", - location->id, child->id, - pe__node_name(constraint_node), - pcmk_readable_score(child->priority), - pcmk_readable_score(constraint_node->weight), - pcmk_readable_score(new_priority)); + pcmk__rsc_trace(child, + "Applying location %s to %s promotion priority on " + "%s: %s + %s = %s", + location->id, child->id, + pcmk__node_name(constraint_node), + pcmk_readable_score(child->priority), + pcmk_readable_score(constraint_node->weight), + pcmk_readable_score(new_priority)); child->priority = new_priority; } } @@ -174,39 +174,40 @@ node_to_be_promoted_on(const pcmk_resource_t *rsc) pcmk_resource_t *child = (pcmk_resource_t *) iter->data; if (node_to_be_promoted_on(child) == NULL) { - pe_rsc_trace(rsc, - "%s can't be promoted because member %s can't", - rsc->id, child->id); + pcmk__rsc_trace(rsc, + "%s can't be promoted because member %s can't", + rsc->id, child->id); return NULL; } } node = rsc->fns->location(rsc, NULL, FALSE); if (node == NULL) { - pe_rsc_trace(rsc, "%s can't be promoted because it won't be active", - rsc->id); + pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active", + rsc->id); return NULL; } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { if (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted) { crm_notice("Unmanaged instance %s will be left promoted on %s", - rsc->id, pe__node_name(node)); + rsc->id, pcmk__node_name(node)); } else { - pe_rsc_trace(rsc, "%s can't be promoted because it is unmanaged", - rsc->id); + pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged", + rsc->id); return NULL; } } else if (rsc->priority < 0) { - pe_rsc_trace(rsc, - "%s can't be promoted because its promotion priority %d " - "is negative", - rsc->id, rsc->priority); + pcmk__rsc_trace(rsc, + "%s can't be promoted because its promotion priority " + "%d is negative", + rsc->id, rsc->priority); return NULL; } else if (!pcmk__node_available(node, false, true)) { - pe_rsc_trace(rsc, "%s can't be promoted because %s can't run resources", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s can't be promoted because %s can't run resources", + rsc->id, pcmk__node_name(node)); return NULL; } @@ -219,18 +220,18 @@ node_to_be_promoted_on(const pcmk_resource_t *rsc) * have a fail-safe. */ if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - crm_warn("%s can't be promoted because %s is not allowed on %s " - "(scheduler bug?)", - rsc->id, parent->id, pe__node_name(node)); + pcmk__sched_err("%s can't be promoted because %s is not allowed " + "on %s (scheduler bug?)", + rsc->id, parent->id, pcmk__node_name(node)); } // else the instance is unmanaged and already promoted return NULL; } else if ((local_node->count >= pe__clone_promoted_node_max(parent)) && pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, - "%s can't be promoted because %s has " - "maximum promoted instances already", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s can't be promoted because %s has " + "maximum promoted instances already", + rsc->id, pcmk__node_name(node)); return NULL; } @@ -261,16 +262,16 @@ cmp_promotable_instance(gconstpointer a, gconstpointer b) // Check sort index set by pcmk__set_instance_roles() if (rsc1->sort_index > rsc2->sort_index) { - pe_rsc_trace(rsc1, - "%s has higher promotion priority than %s " - "(sort index %d > %d)", - rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); + pcmk__rsc_trace(rsc1, + "%s has higher promotion priority than %s " + "(sort index %d > %d)", + rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); return -1; } else if (rsc1->sort_index < rsc2->sort_index) { - pe_rsc_trace(rsc1, - "%s has lower promotion priority than %s " - "(sort index %d < %d)", - rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); + pcmk__rsc_trace(rsc1, + "%s has lower promotion priority than %s " + "(sort index %d < %d)", + rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index); return 1; } @@ -278,16 +279,16 @@ cmp_promotable_instance(gconstpointer a, gconstpointer b) role1 = rsc1->fns->state(rsc1, TRUE); role2 = rsc2->fns->state(rsc2, TRUE); if (role1 > role2) { - pe_rsc_trace(rsc1, - "%s has higher promotion priority than %s " - "(higher current role)", - rsc1->id, rsc2->id); + pcmk__rsc_trace(rsc1, + "%s has higher promotion priority than %s " + "(higher current role)", + rsc1->id, rsc2->id); return -1; } else if (role1 < role2) { - pe_rsc_trace(rsc1, - "%s has lower promotion priority than %s " - "(lower current role)", - rsc1->id, rsc2->id); + pcmk__rsc_trace(rsc1, + "%s has lower promotion priority than %s " + "(lower current role)", + rsc1->id, rsc2->id); return 1; } @@ -316,13 +317,15 @@ add_sort_index_to_node_score(gpointer data, gpointer user_data) const pcmk_node_t *chosen = NULL; if (child->sort_index < 0) { - pe_rsc_trace(clone, "Not adding sort index of %s: negative", child->id); + pcmk__rsc_trace(clone, "Not adding sort index of %s: negative", + child->id); return; } chosen = child->fns->location(child, NULL, FALSE); if (chosen == NULL) { - pe_rsc_trace(clone, "Not adding sort index of %s: inactive", child->id); + pcmk__rsc_trace(clone, "Not adding sort index of %s: inactive", + child->id); return; } @@ -330,10 +333,11 @@ add_sort_index_to_node_score(gpointer data, gpointer user_data) CRM_ASSERT(node != NULL); node->weight = pcmk__add_scores(child->sort_index, node->weight); - pe_rsc_trace(clone, - "Added cumulative priority of %s (%s) to score on %s (now %s)", - child->id, pcmk_readable_score(child->sort_index), - pe__node_name(node), pcmk_readable_score(node->weight)); + pcmk__rsc_trace(clone, + "Added cumulative priority of %s (%s) to score on %s " + "(now %s)", + child->id, pcmk_readable_score(child->sort_index), + pcmk__node_name(node), pcmk_readable_score(node->weight)); } /*! @@ -350,18 +354,18 @@ apply_coloc_to_dependent(gpointer data, gpointer user_data) pcmk_resource_t *clone = user_data; pcmk_resource_t *primary = colocation->primary; uint32_t flags = pcmk__coloc_select_default; - float factor = colocation->score / (float) INFINITY; + float factor = colocation->score / (float) PCMK_SCORE_INFINITY; if (colocation->dependent_role != pcmk_role_promoted) { return; } - if (colocation->score < INFINITY) { + if (colocation->score < PCMK_SCORE_INFINITY) { flags = pcmk__coloc_select_active; } - pe_rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s", - colocation->id, colocation->dependent->id, - colocation->primary->id, - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s", + colocation->id, colocation->dependent->id, + colocation->primary->id, + pcmk_readable_score(colocation->score)); primary->cmds->add_colocated_node_scores(primary, clone, clone->id, &clone->allowed_nodes, colocation, factor, flags); @@ -380,7 +384,7 @@ apply_coloc_to_primary(gpointer data, gpointer user_data) pcmk__colocation_t *colocation = data; pcmk_resource_t *clone = user_data; pcmk_resource_t *dependent = colocation->dependent; - const float factor = colocation->score / (float) INFINITY; + const float factor = colocation->score / (float) PCMK_SCORE_INFINITY; const uint32_t flags = pcmk__coloc_select_active |pcmk__coloc_select_nonnegative; @@ -389,10 +393,10 @@ apply_coloc_to_primary(gpointer data, gpointer user_data) return; } - pe_rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s", - colocation->id, colocation->dependent->id, - colocation->primary->id, - pcmk_readable_score(colocation->score)); + pcmk__rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s", + colocation->id, colocation->dependent->id, + colocation->primary->id, + pcmk_readable_score(colocation->score)); dependent->cmds->add_colocated_node_scores(dependent, clone, clone->id, &clone->allowed_nodes, colocation, factor, flags); @@ -415,15 +419,16 @@ set_sort_index_to_node_score(gpointer data, gpointer user_data) if (!pcmk_is_set(child->flags, pcmk_rsc_managed) && (child->next_role == pcmk_role_promoted)) { - child->sort_index = INFINITY; - pe_rsc_trace(clone, - "Final sort index for %s is INFINITY (unmanaged promoted)", - child->id); + child->sort_index = PCMK_SCORE_INFINITY; + pcmk__rsc_trace(clone, + "Final sort index for %s is INFINITY " + "(unmanaged promoted)", + child->id); } else if ((chosen == NULL) || (child->sort_index < 0)) { - pe_rsc_trace(clone, - "Final sort index for %s is %d (ignoring node score)", - child->id, child->sort_index); + pcmk__rsc_trace(clone, + "Final sort index for %s is %d (ignoring node score)", + child->id, child->sort_index); } else { const pcmk_node_t *node = g_hash_table_lookup(clone->allowed_nodes, @@ -431,9 +436,9 @@ set_sort_index_to_node_score(gpointer data, gpointer user_data) CRM_ASSERT(node != NULL); child->sort_index = node->weight; - pe_rsc_trace(clone, - "Adding scores for %s: final sort index for %s is %d", - clone->id, child->id, child->sort_index); + pcmk__rsc_trace(clone, + "Adding scores for %s: final sort index for %s is %d", + clone->id, child->id, child->sort_index); } } @@ -452,14 +457,14 @@ sort_promotable_instances(pcmk_resource_t *clone) == pcmk_rc_already) { return; } - pe__set_resource_flags(clone, pcmk_rsc_updating_nodes); + pcmk__set_rsc_flags(clone, pcmk_rsc_updating_nodes); for (GList *iter = clone->children; iter != NULL; iter = iter->next) { pcmk_resource_t *child = (pcmk_resource_t *) iter->data; - pe_rsc_trace(clone, - "Adding scores for %s: initial sort index for %s is %d", - clone->id, child->id, child->sort_index); + pcmk__rsc_trace(clone, + "Adding scores for %s: initial sort index for %s is %d", + clone->id, child->id, child->sort_index); } pe__show_node_scores(true, clone, "Before", clone->allowed_nodes, clone->cluster); @@ -485,7 +490,7 @@ sort_promotable_instances(pcmk_resource_t *clone) // Finally, sort instances in descending order of promotion priority clone->children = g_list_sort(clone->children, cmp_promotable_instance); - pe__clear_resource_flags(clone, pcmk_rsc_updating_nodes); + pcmk__clear_rsc_flags(clone, pcmk_rsc_updating_nodes); } /*! @@ -619,23 +624,25 @@ promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node) || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) { reason = "known"; } else { - pe_rsc_trace(rsc, - "Ignoring %s promotion score (for %s) on %s: not probed", - rsc->id, id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "Ignoring %s promotion score (for %s) on %s: " + "not probed", + rsc->id, id, pcmk__node_name(node)); free(id); return false; } check_allowed: if (is_allowed(rsc, node)) { - pe_rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s", - rsc->id, id, pe__node_name(node), reason); + pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s", + rsc->id, id, pcmk__node_name(node), reason); free(id); return true; } - pe_rsc_trace(rsc, "Ignoring %s promotion score (for %s) on %s: not allowed", - rsc->id, id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "Ignoring %s promotion score (for %s) on %s: not allowed", + rsc->id, id, pcmk__node_name(node)); free(id); return false; } @@ -656,15 +663,17 @@ promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node, { char *attr_name = NULL; const char *attr_value = NULL; + const char *target = NULL; enum pcmk__rsc_node node_type = pcmk__rsc_node_assigned; if (pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) { // Not assigned yet node_type = pcmk__rsc_node_current; } + target = g_hash_table_lookup(rsc->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); attr_name = pcmk_promotion_score_name(name); - attr_value = pe__node_attribute_calculated(node, attr_name, rsc, node_type, - false); + attr_value = pcmk__node_attr(node, attr_name, target, node_type); free(attr_name); return attr_value; } @@ -725,8 +734,9 @@ promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node, attr_value = promotion_attr_value(rsc, node, name); if (attr_value != NULL) { - pe_rsc_trace(rsc, "Promotion score for %s on %s = %s", - name, pe__node_name(node), pcmk__s(attr_value, "(unset)")); + pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s", + name, pcmk__node_name(node), + pcmk__s(attr_value, "(unset)")); } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { /* If we don't have any resource history yet, we won't have clone_name. * In that case, for anonymous clones, try the resource name without @@ -735,9 +745,9 @@ promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node, name = clone_strip(rsc->id); if (strcmp(rsc->id, name) != 0) { attr_value = promotion_attr_value(rsc, node, name); - pe_rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s", - name, pe__node_name(node), rsc->id, - pcmk__s(attr_value, "(unset)")); + pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s", + name, pcmk__node_name(node), rsc->id, + pcmk__s(attr_value, "(unset)")); } free(name); } @@ -787,19 +797,20 @@ pcmk__add_promotion_scores(pcmk_resource_t *rsc) new_score = pcmk__add_scores(node->weight, score); if (new_score != node->weight) { // Could remain INFINITY node->weight = new_score; - pe_rsc_trace(rsc, - "Added %s promotion priority (%s) to score " - "on %s (now %s)", - child_rsc->id, pcmk_readable_score(score), - pe__node_name(node), - pcmk_readable_score(new_score)); + pcmk__rsc_trace(rsc, + "Added %s promotion priority (%s) to score " + "on %s (now %s)", + child_rsc->id, pcmk_readable_score(score), + pcmk__node_name(node), + pcmk_readable_score(new_score)); } } if (score > child_rsc->priority) { - pe_rsc_trace(rsc, - "Updating %s priority to promotion score (%d->%d)", - child_rsc->id, child_rsc->priority, score); + pcmk__rsc_trace(rsc, + "Updating %s priority to promotion score " + "(%d->%d)", + child_rsc->id, child_rsc->priority, score); child_rsc->priority = score; } } @@ -885,12 +896,12 @@ show_promotion_score(pcmk_resource_t *instance) out->message(out, "promotion-score", instance, chosen, pcmk_readable_score(instance->sort_index)); } else { - pe_rsc_debug(pe__const_top_resource(instance, false), - "%s promotion score on %s: sort=%s priority=%s", - instance->id, - ((chosen == NULL)? "none" : pe__node_name(chosen)), - pcmk_readable_score(instance->sort_index), - pcmk_readable_score(instance->priority)); + pcmk__rsc_debug(pe__const_top_resource(instance, false), + "%s promotion score on %s: sort=%s priority=%s", + instance->id, + ((chosen == NULL)? "none" : pcmk__node_name(chosen)), + pcmk_readable_score(instance->sort_index), + pcmk_readable_score(instance->priority)); } } @@ -910,8 +921,8 @@ set_instance_priority(gpointer data, gpointer user_data) enum rsc_role_e next_role = pcmk_role_unknown; GList *list = NULL; - pe_rsc_trace(clone, "Assigning priority for %s: %s", instance->id, - role2text(instance->next_role)); + pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id, + pcmk_role_text(instance->next_role)); if (instance->fns->state(instance, TRUE) == pcmk_role_started) { set_current_role_unpromoted(instance, NULL); @@ -939,12 +950,11 @@ set_instance_priority(gpointer data, gpointer user_data) instance->priority = promotion_score(instance, chosen, &is_default); if (is_default) { - /* - * Default to -1 if no value is set. This allows - * instances eligible for promotion to be specified - * based solely on rsc_location constraints, but - * prevents any instance from being promoted if neither - * a constraint nor a promotion score is present + /* Default to -1 if no value is set. This allows instances + * eligible for promotion to be specified based solely on + * PCMK_XE_RSC_LOCATION constraints, but prevents any + * instance from being promoted if neither a constraint nor + * a promotion score is present. */ instance->priority = -1; } @@ -954,7 +964,7 @@ set_instance_priority(gpointer data, gpointer user_data) case pcmk_role_unpromoted: case pcmk_role_stopped: // Instance can't be promoted - instance->priority = -INFINITY; + instance->priority = -PCMK_SCORE_INFINITY; break; case pcmk_role_promoted: @@ -981,10 +991,10 @@ set_instance_priority(gpointer data, gpointer user_data) instance->sort_index = instance->priority; if (next_role == pcmk_role_promoted) { - instance->sort_index = INFINITY; + instance->sort_index = PCMK_SCORE_INFINITY; } - pe_rsc_trace(clone, "Assigning %s priority = %d", - instance->id, instance->priority); + pcmk__rsc_trace(clone, "Assigning %s priority = %d", + instance->id, instance->priority); } /*! @@ -1006,8 +1016,8 @@ set_instance_role(gpointer data, gpointer user_data) show_promotion_score(instance); if (instance->sort_index < 0) { - pe_rsc_trace(clone, "Not supposed to promote instance %s", - instance->id); + pcmk__rsc_trace(clone, "Not supposed to promote instance %s", + instance->id); } else if ((*count < pe__clone_promoted_max(instance)) || !pcmk_is_set(clone->flags, pcmk_rsc_managed)) { @@ -1029,9 +1039,9 @@ set_instance_role(gpointer data, gpointer user_data) } chosen->count++; - pe_rsc_info(clone, "Choosing %s (%s) on %s for promotion", - instance->id, role2text(instance->role), - pe__node_name(chosen)); + pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion", + instance->id, pcmk_role_text(instance->role), + pcmk__node_name(chosen)); set_next_role_promoted(instance, NULL); (*count)++; } @@ -1061,8 +1071,8 @@ pcmk__set_instance_roles(pcmk_resource_t *rsc) // Choose the first N eligible instances to be promoted g_list_foreach(rsc->children, set_instance_role, &promoted); - pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d", - rsc->id, promoted, pe__clone_promoted_max(rsc)); + pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d", + rsc->id, promoted, pe__clone_promoted_max(rsc)); } /*! @@ -1175,17 +1185,17 @@ update_dependent_allowed_nodes(pcmk_resource_t *dependent, const char *primary_value = NULL; const char *attr = colocation->node_attribute; - if (colocation->score >= INFINITY) { + if (colocation->score >= PCMK_SCORE_INFINITY) { return; // Colocation is mandatory, so allowed node scores don't matter } primary_value = pcmk__colocation_node_attr(primary_node, attr, primary); - pe_rsc_trace(colocation->primary, - "Applying %s (%s with %s on %s by %s @%d) to %s", - colocation->id, colocation->dependent->id, - colocation->primary->id, pe__node_name(primary_node), attr, - colocation->score, dependent->id); + pcmk__rsc_trace(colocation->primary, + "Applying %s (%s with %s on %s by %s @%d) to %s", + colocation->id, colocation->dependent->id, + colocation->primary->id, pcmk__node_name(primary_node), + attr, colocation->score, dependent->id); g_hash_table_iter_init(&iter, dependent->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { @@ -1194,11 +1204,12 @@ update_dependent_allowed_nodes(pcmk_resource_t *dependent, if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) { node->weight = pcmk__add_scores(node->weight, colocation->score); - pe_rsc_trace(colocation->primary, - "Added %s score (%s) to %s (now %s)", - colocation->id, pcmk_readable_score(colocation->score), - pe__node_name(node), - pcmk_readable_score(node->weight)); + pcmk__rsc_trace(colocation->primary, + "Added %s score (%s) to %s (now %s)", + colocation->id, + pcmk_readable_score(colocation->score), + pcmk__node_name(node), + pcmk_readable_score(node->weight)); } } } @@ -1242,14 +1253,14 @@ pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary, * However, skip this for promoted-with-promoted colocations, otherwise * inactive dependent instances can't start (in the unpromoted role). */ - if ((colocation->score >= INFINITY) + if ((colocation->score >= PCMK_SCORE_INFINITY) && ((colocation->dependent_role != pcmk_role_promoted) || (colocation->primary_role != pcmk_role_promoted))) { - pe_rsc_trace(colocation->primary, - "Applying %s (mandatory %s with %s) to %s", - colocation->id, colocation->dependent->id, - colocation->primary->id, dependent->id); + pcmk__rsc_trace(colocation->primary, + "Applying %s (mandatory %s with %s) to %s", + colocation->id, colocation->dependent->id, + colocation->primary->id, dependent->id); pcmk__colocation_intersect_nodes(dependent, primary, colocation, affected_nodes, true); } @@ -1281,21 +1292,22 @@ pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, int new_priority = pcmk__add_scores(dependent->priority, colocation->score); - pe_rsc_trace(colocation->primary, - "Applying %s (%s with %s) to %s priority (%s + %s = %s)", - colocation->id, colocation->dependent->id, - colocation->primary->id, dependent->id, - pcmk_readable_score(dependent->priority), - pcmk_readable_score(colocation->score), - pcmk_readable_score(new_priority)); + pcmk__rsc_trace(colocation->primary, + "Applying %s (%s with %s) to %s priority " + "(%s + %s = %s)", + colocation->id, colocation->dependent->id, + colocation->primary->id, dependent->id, + pcmk_readable_score(dependent->priority), + pcmk_readable_score(colocation->score), + pcmk_readable_score(new_priority)); dependent->priority = new_priority; - } else if (colocation->score >= INFINITY) { + } else if (colocation->score >= PCMK_SCORE_INFINITY) { // Mandatory colocation, but primary won't be here - pe_rsc_trace(colocation->primary, - "Applying %s (%s with %s) to %s: can't be promoted", - colocation->id, colocation->dependent->id, - colocation->primary->id, dependent->id); - dependent->priority = -INFINITY; + pcmk__rsc_trace(colocation->primary, + "Applying %s (%s with %s) to %s: can't be promoted", + colocation->id, colocation->dependent->id, + colocation->primary->id, dependent->id); + dependent->priority = -PCMK_SCORE_INFINITY; } } diff --git a/lib/pacemaker/pcmk_sched_recurring.c b/lib/pacemaker/pcmk_sched_recurring.c index 9210fab..6a861b7 100644 --- a/lib/pacemaker/pcmk_sched_recurring.c +++ b/lib/pacemaker/pcmk_sched_recurring.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. * @@ -11,7 +11,7 @@ #include <stdbool.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/scheduler_internal.h> #include <pacemaker-internal.h> @@ -40,8 +40,11 @@ struct op_history { static guint xe_interval(const xmlNode *xml) { - return crm_parse_interval_spec(crm_element_value(xml, - XML_LRM_ATTR_INTERVAL)); + guint interval_ms = 0U; + + pcmk_parse_interval_spec(crm_element_value(xml, PCMK_META_INTERVAL), + &interval_ms); + return interval_ms; } /*! @@ -60,25 +63,27 @@ is_op_dup(const pcmk_resource_t *rsc, const char *name, guint interval_ms) { const char *id = NULL; - for (xmlNode *op = first_named_child(rsc->ops_xml, "op"); - op != NULL; op = crm_next_same_xml(op)) { + for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL, + NULL); + op != NULL; op = pcmk__xe_next_same(op)) { // Check whether action name and interval match - if (!pcmk__str_eq(crm_element_value(op, "name"), name, pcmk__str_none) + if (!pcmk__str_eq(crm_element_value(op, PCMK_XA_NAME), name, + pcmk__str_none) || (xe_interval(op) != interval_ms)) { continue; } - if (ID(op) == NULL) { + if (pcmk__xe_id(op) == NULL) { continue; // Shouldn't be possible } if (id == NULL) { - id = ID(op); // First matching op + id = pcmk__xe_id(op); // First matching op } else { pcmk__config_err("Operation %s is duplicate of %s (do not use " "same name and interval combination more " - "than once per resource)", ID(op), id); + "than once per resource)", pcmk__xe_id(op), id); return true; } } @@ -132,13 +137,13 @@ is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml, return false; // Not recurring } - op->id = ID(xml); + op->id = pcmk__xe_id(xml); if (pcmk__str_empty(op->id)) { pcmk__config_err("Ignoring resource history entry without ID"); return false; // Shouldn't be possible (unless CIB was manually edited) } - op->name = crm_element_value(xml, "name"); + op->name = crm_element_value(xml, PCMK_XA_NAME); if (op_cannot_recur(op->name)) { pcmk__config_err("Ignoring %s because %s action cannot be recurring", op->id, pcmk__s(op->name, "unnamed")); @@ -151,13 +156,13 @@ is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml, } // Ensure role is valid if specified - role = crm_element_value(xml, "role"); + role = crm_element_value(xml, PCMK_XA_ROLE); if (role == NULL) { op->role = pcmk_role_unknown; } else { - op->role = text2role(role); + op->role = pcmk_parse_role(role); if (op->role == pcmk_role_unknown) { - pcmk__config_err("Ignoring %s because %s is not a valid role", + pcmk__config_err("Ignoring %s role because %s is not a valid role", op->id, role); return false; } @@ -166,11 +171,11 @@ is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml, // Only actions that are still configured and enabled matter if (pcmk__find_action_config(rsc, op->name, op->interval_ms, false) == NULL) { - pe_rsc_trace(rsc, - "Ignoring %s (%s-interval %s for %s) because it is " - "disabled or no longer in configuration", - op->id, pcmk__readable_interval(op->interval_ms), op->name, - rsc->id); + pcmk__rsc_trace(rsc, + "Ignoring %s (%s-interval %s for %s) because it is " + "disabled or no longer in configuration", + op->id, pcmk__readable_interval(op->interval_ms), + op->name, rsc->id); return false; } @@ -197,22 +202,24 @@ active_recurring_should_be_optional(const pcmk_resource_t *rsc, GList *possible_matches = NULL; if (node == NULL) { // Should only be possible if unmanaged and stopped - pe_rsc_trace(rsc, "%s will be mandatory because resource is unmanaged", - key); + pcmk__rsc_trace(rsc, + "%s will be mandatory because resource is unmanaged", + key); return false; } if (!pcmk_is_set(rsc->cmds->action_flags(start, NULL), pcmk_action_optional)) { - pe_rsc_trace(rsc, "%s will be mandatory because %s is", - key, start->uuid); + pcmk__rsc_trace(rsc, "%s will be mandatory because %s is", + key, start->uuid); return false; } possible_matches = find_actions_exact(rsc->actions, key, node); if (possible_matches == NULL) { - pe_rsc_trace(rsc, "%s will be mandatory because it is not active on %s", - key, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s will be mandatory because it is not active on %s", + key, pcmk__node_name(node)); return false; } @@ -222,9 +229,9 @@ active_recurring_should_be_optional(const pcmk_resource_t *rsc, const pcmk_action_t *op = (const pcmk_action_t *) iter->data; if (pcmk_is_set(op->flags, pcmk_action_reschedule)) { - pe_rsc_trace(rsc, - "%s will be mandatory because " - "it needs to be rescheduled", key); + pcmk__rsc_trace(rsc, + "%s will be mandatory because " + "it needs to be rescheduled", key); g_list_free(possible_matches); return false; } @@ -249,20 +256,26 @@ recurring_op_for_active(pcmk_resource_t *rsc, pcmk_action_t *start, { pcmk_action_t *mon = NULL; bool is_optional = true; - const bool is_default_role = (op->role == pcmk_role_unknown); + bool role_match = false; + enum rsc_role_e monitor_role = op->role; // We're only interested in recurring actions for active roles - if (op->role == pcmk_role_stopped) { + if (monitor_role == pcmk_role_stopped) { return; } is_optional = active_recurring_should_be_optional(rsc, node, op->key, start); - if ((!is_default_role && (rsc->next_role != op->role)) - || (is_default_role && (rsc->next_role == pcmk_role_promoted))) { - // Configured monitor role doesn't match role resource will have + // Check whether monitor's role matches role resource will have + if (monitor_role == pcmk_role_unknown) { + monitor_role = pcmk_role_unpromoted; + role_match = (rsc->next_role != pcmk_role_promoted); + } else { + role_match = (rsc->next_role == monitor_role); + } + if (!role_match) { if (is_optional) { // It's running, so cancel it char *after_key = NULL; pcmk_action_t *cancel_op = pcmk__new_cancel_action(rsc, op->name, @@ -298,34 +311,34 @@ recurring_op_for_active(pcmk_resource_t *rsc, pcmk_action_t *start, "%s recurring action %s because %s configured for %s role " "(not %s)", (is_optional? "Cancelling" : "Ignoring"), op->key, op->id, - role2text(is_default_role? pcmk_role_unpromoted : op->role), - role2text(rsc->next_role)); + pcmk_role_text(monitor_role), + pcmk_role_text(rsc->next_role)); return; } - pe_rsc_trace(rsc, - "Creating %s recurring action %s for %s (%s %s on %s)", - (is_optional? "optional" : "mandatory"), op->key, - op->id, rsc->id, role2text(rsc->next_role), - pe__node_name(node)); + pcmk__rsc_trace(rsc, + "Creating %s recurring action %s for %s (%s %s on %s)", + (is_optional? "optional" : "mandatory"), op->key, + op->id, rsc->id, pcmk_role_text(rsc->next_role), + pcmk__node_name(node)); mon = custom_action(rsc, strdup(op->key), op->name, node, is_optional, rsc->cluster); if (!pcmk_is_set(start->flags, pcmk_action_runnable)) { - pe_rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid); - pe__clear_action_flags(mon, pcmk_action_runnable); + pcmk__rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid); + pcmk__clear_action_flags(mon, pcmk_action_runnable); } else if ((node == NULL) || !node->details->online || node->details->unclean) { - pe_rsc_trace(rsc, "%s is unrunnable because no node is available", - mon->uuid); - pe__clear_action_flags(mon, pcmk_action_runnable); + pcmk__rsc_trace(rsc, "%s is unrunnable because no node is available", + mon->uuid); + pcmk__clear_action_flags(mon, pcmk_action_runnable); } else if (!pcmk_is_set(mon->flags, pcmk_action_optional)) { - pe_rsc_info(rsc, "Start %s-interval %s for %s on %s", - pcmk__readable_interval(op->interval_ms), mon->task, - rsc->id, pe__node_name(node)); + pcmk__rsc_info(rsc, "Start %s-interval %s for %s on %s", + pcmk__readable_interval(op->interval_ms), mon->task, + rsc->id, pcmk__node_name(node)); } if (rsc->next_role == pcmk_role_promoted) { @@ -402,11 +415,11 @@ cancel_if_running(pcmk_resource_t *rsc, const pcmk_node_t *node, default: break; } - pe_rsc_info(rsc, - "Cancelling %s-interval %s action for %s on %s because " - "configured for " PCMK__ROLE_STOPPED " role (not %s)", - pcmk__readable_interval(interval_ms), name, rsc->id, - pe__node_name(node), role2text(rsc->next_role)); + pcmk__rsc_info(rsc, + "Cancelling %s-interval %s action for %s on %s because " + "configured for " PCMK_ROLE_STOPPED " role (not %s)", + pcmk__readable_interval(interval_ms), name, rsc->id, + pcmk__node_name(node), pcmk_role_text(rsc->next_role)); } /*! @@ -450,15 +463,15 @@ order_after_stops(pcmk_resource_t *rsc, const pcmk_node_t *node, if (!pcmk_is_set(stop->flags, pcmk_action_optional) && !pcmk_is_set(action->flags, pcmk_action_optional) && !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, "%s optional on %s: unmanaged", - action->uuid, pe__node_name(node)); - pe__set_action_flags(action, pcmk_action_optional); + pcmk__rsc_trace(rsc, "%s optional on %s: unmanaged", + action->uuid, pcmk__node_name(node)); + pcmk__set_action_flags(action, pcmk_action_optional); } if (!pcmk_is_set(stop->flags, pcmk_action_runnable)) { crm_debug("%s unrunnable on %s: stop is unrunnable", - action->uuid, pe__node_name(node)); - pe__clear_action_flags(action, pcmk_action_runnable); + action->uuid, pcmk__node_name(node)); + pcmk__clear_action_flags(action, pcmk_action_runnable); } if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { @@ -492,13 +505,14 @@ recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node, } if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { - crm_notice("Ignoring %s (recurring monitors for " PCMK__ROLE_STOPPED + crm_notice("Ignoring %s (recurring monitors for " PCMK_ROLE_STOPPED " role are not supported for anonymous clones)", op->id); return; // @TODO add support } - pe_rsc_trace(rsc, "Creating recurring action %s for %s on nodes " - "where it should not be running", op->id, rsc->id); + pcmk__rsc_trace(rsc, + "Creating recurring action %s for %s on nodes " + "where it should not be running", op->id, rsc->id); for (GList *iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) { pcmk_node_t *stop_node = (pcmk_node_t *) iter->data; @@ -519,11 +533,11 @@ recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node, is_optional = (possible_matches != NULL); g_list_free(possible_matches); - pe_rsc_trace(rsc, - "Creating %s recurring action %s for %s (%s " - PCMK__ROLE_STOPPED " on %s)", - (is_optional? "optional" : "mandatory"), - op->key, op->id, rsc->id, pe__node_name(stop_node)); + pcmk__rsc_trace(rsc, + "Creating %s recurring action %s for %s (%s " + PCMK_ROLE_STOPPED " on %s)", + (is_optional? "optional" : "mandatory"), + op->key, op->id, rsc->id, pcmk__node_name(stop_node)); stopped_mon = custom_action(rsc, strdup(op->key), op->name, stop_node, is_optional, rsc->cluster); @@ -540,17 +554,17 @@ recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node, order_after_stops(rsc, stop_node, stopped_mon); if (!stop_node->details->online || stop_node->details->unclean) { - pe_rsc_debug(rsc, "%s unrunnable on %s: node unavailable)", - stopped_mon->uuid, pe__node_name(stop_node)); - pe__clear_action_flags(stopped_mon, pcmk_action_runnable); + pcmk__rsc_debug(rsc, "%s unrunnable on %s: node unavailable)", + stopped_mon->uuid, pcmk__node_name(stop_node)); + pcmk__clear_action_flags(stopped_mon, pcmk_action_runnable); } if (pcmk_is_set(stopped_mon->flags, pcmk_action_runnable) && !pcmk_is_set(stopped_mon->flags, pcmk_action_optional)) { crm_notice("Start recurring %s-interval %s for " - PCMK__ROLE_STOPPED " %s on %s", + PCMK_ROLE_STOPPED " %s on %s", pcmk__readable_interval(op->interval_ms), - stopped_mon->task, rsc->id, pe__node_name(stop_node)); + stopped_mon->task, rsc->id, pcmk__node_name(stop_node)); } } } @@ -567,14 +581,16 @@ pcmk__create_recurring_actions(pcmk_resource_t *rsc) pcmk_action_t *start = NULL; if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) { - pe_rsc_trace(rsc, "Skipping recurring actions for blocked resource %s", - rsc->id); + pcmk__rsc_trace(rsc, + "Skipping recurring actions for blocked resource %s", + rsc->id); return; } if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) { - pe_rsc_trace(rsc, "Skipping recurring actions for %s " - "in maintenance mode", rsc->id); + pcmk__rsc_trace(rsc, + "Skipping recurring actions for %s " + "in maintenance mode", rsc->id); return; } @@ -582,10 +598,10 @@ pcmk__create_recurring_actions(pcmk_resource_t *rsc) // Recurring actions for active roles not needed } else if (rsc->allocated_to->details->maintenance) { - pe_rsc_trace(rsc, - "Skipping recurring actions for %s on %s " - "in maintenance mode", - rsc->id, pe__node_name(rsc->allocated_to)); + pcmk__rsc_trace(rsc, + "Skipping recurring actions for %s on %s " + "in maintenance mode", + rsc->id, pcmk__node_name(rsc->allocated_to)); } else if ((rsc->next_role != pcmk_role_stopped) || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { @@ -593,10 +609,12 @@ pcmk__create_recurring_actions(pcmk_resource_t *rsc) start = start_action(rsc, rsc->allocated_to, TRUE); } - pe_rsc_trace(rsc, "Creating any recurring actions needed for %s", rsc->id); + pcmk__rsc_trace(rsc, "Creating any recurring actions needed for %s", + rsc->id); - for (xmlNode *op = first_named_child(rsc->ops_xml, "op"); - op != NULL; op = crm_next_same_xml(op)) { + for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL, + NULL); + op != NULL; op = pcmk__xe_next_same(op)) { struct op_history op_history = { NULL, }; @@ -634,18 +652,20 @@ pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *task, CRM_ASSERT((rsc != NULL) && (task != NULL) && (node != NULL)); - // @TODO dangerous if possible to schedule another action with this key key = pcmk__op_key(rsc->id, task, interval_ms); + /* This finds an existing action by key, so custom_action() does not change + * cancel_op->task. + */ cancel_op = custom_action(rsc, key, PCMK_ACTION_CANCEL, node, FALSE, rsc->cluster); - pcmk__str_update(&cancel_op->task, PCMK_ACTION_CANCEL); - pcmk__str_update(&cancel_op->cancel_task, task); + pcmk__str_update(&(cancel_op->task), PCMK_ACTION_CANCEL); + pcmk__str_update(&(cancel_op->cancel_task), task); interval_ms_s = crm_strdup_printf("%u", interval_ms); - add_hash_param(cancel_op->meta, XML_LRM_ATTR_TASK, task); - add_hash_param(cancel_op->meta, XML_LRM_ATTR_INTERVAL_MS, interval_ms_s); + pcmk__insert_meta(cancel_op, PCMK_XA_OPERATION, task); + pcmk__insert_meta(cancel_op, PCMK_META_INTERVAL, interval_ms_s); free(interval_ms_s); return cancel_op; @@ -675,9 +695,9 @@ pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id, crm_info("Recurring %s-interval %s for %s will be stopped on %s: %s", pcmk__readable_interval(interval_ms), task, rsc->id, - pe__node_name(node), reason); + pcmk__node_name(node), reason); cancel = pcmk__new_cancel_action(rsc, task, interval_ms, node); - add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id); + pcmk__insert_meta(cancel, PCMK__XA_CALL_ID, call_id); // Cancellations happen after stops pcmk__new_ordering(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, @@ -703,7 +723,7 @@ pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task, NULL, rsc->cluster); op = custom_action(rsc, pcmk__op_key(rsc->id, task, interval_ms), task, node, TRUE, rsc->cluster); - pe__set_action_flags(op, pcmk_action_reschedule); + pcmk__set_action_flags(op, pcmk_action_reschedule); } /*! @@ -719,8 +739,7 @@ pcmk__action_is_recurring(const pcmk_action_t *action) { guint interval_ms = 0; - if (pcmk__guint_from_hash(action->meta, - XML_LRM_ATTR_INTERVAL_MS, 0, + if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0, &interval_ms) != pcmk_rc_ok) { return false; } diff --git a/lib/pacemaker/pcmk_sched_remote.c b/lib/pacemaker/pcmk_sched_remote.c index c915389..80afe9f 100644 --- a/lib/pacemaker/pcmk_sched_remote.c +++ b/lib/pacemaker/pcmk_sched_remote.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. * @@ -13,7 +13,6 @@ #include <crm/crm.h> #include <crm/cib.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> @@ -94,7 +93,7 @@ get_remote_node_state(const pcmk_node_t *node) remote_rsc = node->details->remote_rsc; CRM_ASSERT(remote_rsc != NULL); - cluster_node = pe__current_node(remote_rsc); + cluster_node = pcmk__current_node(remote_rsc); /* If the cluster node the remote connection resource resides on * is unclean or went offline, we can't process any operations @@ -169,7 +168,7 @@ static void apply_remote_ordering(pcmk_action_t *action) { pcmk_resource_t *remote_rsc = NULL; - enum action_tasks task = text2task(action->task); + enum action_tasks task = pcmk_parse_action(action->task); enum remote_connection_state state = get_remote_node_state(action->node); uint32_t order_opts = pcmk__ar_none; @@ -178,7 +177,7 @@ apply_remote_ordering(pcmk_action_t *action) return; } - CRM_ASSERT(pe__is_guest_or_remote_node(action->node)); + CRM_ASSERT(pcmk__is_pacemaker_remote_node(action->node)); remote_rsc = action->node->details->remote_rsc; CRM_ASSERT(remote_rsc != NULL); @@ -203,7 +202,8 @@ apply_remote_ordering(pcmk_action_t *action) if (state == remote_state_failed) { /* Force recovery, by making this action required */ - pe__set_order_flags(order_opts, pcmk__ar_first_implies_then); + pcmk__set_relation_flags(order_opts, + pcmk__ar_first_implies_then); } /* Ensure connection is up before running this action */ @@ -266,7 +266,7 @@ apply_remote_ordering(pcmk_action_t *action) pcmk__ar_first_implies_then); } else { - pcmk_node_t *cluster_node = pe__current_node(remote_rsc); + pcmk_node_t *cluster_node = pcmk__current_node(remote_rsc); if ((task == pcmk_action_monitor) && (state == remote_state_failed)) { /* We would only be here if we do not know the state of the @@ -306,11 +306,11 @@ apply_container_ordering(pcmk_action_t *action) */ pcmk_resource_t *remote_rsc = NULL; pcmk_resource_t *container = NULL; - enum action_tasks task = text2task(action->task); + enum action_tasks task = pcmk_parse_action(action->task); CRM_ASSERT(action->rsc != NULL); CRM_ASSERT(action->node != NULL); - CRM_ASSERT(pe__is_guest_or_remote_node(action->node)); + CRM_ASSERT(pcmk__is_pacemaker_remote_node(action->node)); remote_rsc = action->node->details->remote_rsc; CRM_ASSERT(remote_rsc != NULL); @@ -434,7 +434,7 @@ pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler) continue; } - if (!pe__is_guest_or_remote_node(action->node)) { + if (!pcmk__is_pacemaker_remote_node(action->node)) { continue; } @@ -464,7 +464,7 @@ pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler) item = item->next) { pcmk_action_t *rsc_action = item->data; - if (!pe__same_node(rsc_action->node, action->node) + if (!pcmk__same_node(rsc_action->node, action->node) && pcmk__str_eq(rsc_action->task, PCMK_ACTION_STOP, pcmk__str_none)) { pcmk__new_ordering(remote, start_key(remote), NULL, @@ -480,8 +480,8 @@ pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler) * * This is somewhat brittle in that we need to make sure the results of * this ordering are compatible with the result of get_router_node(). - * It would probably be better to add XML_LRM_ATTR_ROUTER_NODE as part - * of this logic rather than create_graph_action(). + * It would probably be better to add PCMK__XA_ROUTER_NODE as part of + * this logic rather than create_graph_action(). */ if (remote->container) { crm_trace("Container ordering for %s", action->uuid); @@ -505,7 +505,7 @@ pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler) bool pcmk__is_failed_remote_node(const pcmk_node_t *node) { - return pe__is_remote_node(node) && (node->details->remote_rsc != NULL) + return pcmk__is_remote_node(node) && (node->details->remote_rsc != NULL) && (get_remote_node_state(node) == remote_state_failed); } @@ -551,13 +551,13 @@ pcmk__connection_host_for_action(const pcmk_action_t *action) const char *task = action->task; if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_none) - || !pe__is_guest_or_remote_node(action->node)) { + || !pcmk__is_pacemaker_remote_node(action->node)) { return NULL; } CRM_ASSERT(action->node->details->remote_rsc != NULL); - began_on = pe__current_node(action->node->details->remote_rsc); + began_on = pcmk__current_node(action->node->details->remote_rsc); ended_on = action->node->details->remote_rsc->allocated_to; if (action->node->details->remote_rsc && (action->node->details->remote_rsc->container == NULL) @@ -583,7 +583,7 @@ pcmk__connection_host_for_action(const pcmk_action_t *action) return began_on; } - if (pe__same_node(began_on, ended_on)) { + if (pcmk__same_node(began_on, ended_on)) { crm_trace("Routing %s for %s through remote connection's " "current node %s (not moving)%s", action->task, (action->rsc? action->rsc->id : "no resource"), @@ -654,45 +654,48 @@ pcmk__connection_host_for_action(const pcmk_action_t *action) void pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params) { - const char *remote_addr = g_hash_table_lookup(params, - XML_RSC_ATTR_REMOTE_RA_ADDR); + const char *remote_addr = g_hash_table_lookup(params, PCMK_REMOTE_RA_ADDR); if (pcmk__str_eq(remote_addr, "#uname", pcmk__str_none)) { GHashTable *base = pe_rsc_params(rsc, NULL, rsc->cluster); - remote_addr = g_hash_table_lookup(base, XML_RSC_ATTR_REMOTE_RA_ADDR); + remote_addr = g_hash_table_lookup(base, PCMK_REMOTE_RA_ADDR); if (remote_addr != NULL) { - g_hash_table_insert(params, strdup(XML_RSC_ATTR_REMOTE_RA_ADDR), - strdup(remote_addr)); + pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, remote_addr); } } } /*! - * \brief Add special bundle meta-attributes to XML + * \brief Add special guest node meta-attributes to XML * - * If a given action will be executed on a guest node (including a bundle), - * add the special bundle meta-attribute "container-attribute-target" and - * environment variable "physical_host" as XML attributes (using meta-attribute - * naming). + * If a given action will be executed on a guest node, add the following as XML + * attributes (using meta-attribute naming): + * * The resource's \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET meta-attribute + * (usually set only for bundles), as \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET + * * The guest's physical host (current host for "down" actions, next host for + * "up" actions), as \c PCMK__META_PHYSICAL_HOST + * + * If the guest node has no physical host, then don't add either attribute. * * \param[in,out] args_xml XML to add attributes to * \param[in] action Action to check */ void -pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action) +pcmk__add_guest_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action) { const pcmk_node_t *guest = action->node; const pcmk_node_t *host = NULL; enum action_tasks task; - if (!pe__is_guest_node(guest)) { + if (!pcmk__is_guest_or_bundle_node(guest)) { return; } - task = text2task(action->task); + task = pcmk_parse_action(action->task); if ((task == pcmk_action_notify) || (task == pcmk_action_notified)) { - task = text2task(g_hash_table_lookup(action->meta, "notify_operation")); + task = pcmk_parse_action(g_hash_table_lookup(action->meta, + "notify_operation")); } switch (task) { @@ -701,7 +704,7 @@ pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action) case pcmk_action_demote: case pcmk_action_demoted: // "Down" actions take place on guest's current host - host = pe__current_node(guest->details->remote_rsc->container); + host = pcmk__current_node(guest->details->remote_rsc->container); break; case pcmk_action_start: @@ -718,11 +721,14 @@ pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action) } if (host != NULL) { - hash2metafield((gpointer) XML_RSC_ATTR_TARGET, - (gpointer) g_hash_table_lookup(action->rsc->meta, - XML_RSC_ATTR_TARGET), + gpointer target = + g_hash_table_lookup(action->rsc->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); + + hash2metafield((gpointer) PCMK_META_CONTAINER_ATTRIBUTE_TARGET, + target, (gpointer) args_xml); - hash2metafield((gpointer) PCMK__ENV_PHYSICAL_HOST, + hash2metafield((gpointer) PCMK__META_PHYSICAL_HOST, (gpointer) host->details->uname, (gpointer) args_xml); } diff --git a/lib/pacemaker/pcmk_sched_resource.c b/lib/pacemaker/pcmk_sched_resource.c index 908c434..0791994 100644 --- a/lib/pacemaker/pcmk_sched_resource.c +++ b/lib/pacemaker/pcmk_sched_resource.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the Pacemaker project contributors + * Copyright 2014-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -11,7 +11,7 @@ #include <stdlib.h> #include <string.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -113,9 +113,9 @@ pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node, { bool changed = false; const char *attr_list[] = { - XML_ATTR_TYPE, - XML_AGENT_ATTR_CLASS, - XML_AGENT_ATTR_PROVIDER + PCMK_XA_TYPE, + PCMK_XA_CLASS, + PCMK_XA_PROVIDER, }; for (int i = 0; i < PCMK__NELEM(attr_list); i++) { @@ -129,7 +129,7 @@ pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node, if (active_on_node) { crm_notice("Forcing restart of %s on %s " "because %s changed from '%s' to '%s'", - rsc->id, pe__node_name(node), attr_list[i], + rsc->id, pcmk__node_name(node), attr_list[i], pcmk__s(old_value, ""), pcmk__s(value, "")); } } @@ -138,7 +138,7 @@ pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node, // Make sure the resource is restarted custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE, rsc->cluster); - pe__set_resource_flags(rsc, pcmk_rsc_start_pending); + pcmk__set_rsc_flags(rsc, pcmk_rsc_start_pending); } return changed; } @@ -254,8 +254,8 @@ pcmk__colocated_resources(const pcmk_resource_t *rsc, return colocated_rscs; } - pe_rsc_trace(orig_rsc, "%s is in colocation chain with %s", - rsc->id, orig_rsc->id); + pcmk__rsc_trace(orig_rsc, "%s is in colocation chain with %s", + rsc->id, orig_rsc->id); colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc); // Follow colocations where this resource is the dependent resource @@ -268,7 +268,7 @@ pcmk__colocated_resources(const pcmk_resource_t *rsc, continue; // Break colocation loop } - if ((constraint->score == INFINITY) && + if ((constraint->score == PCMK_SCORE_INFINITY) && (pcmk__colocation_affects(rsc, primary, constraint, true) == pcmk__coloc_affects_location)) { add_colocated_resources(primary, orig_rsc, &colocated_rscs); @@ -286,11 +286,11 @@ pcmk__colocated_resources(const pcmk_resource_t *rsc, continue; // Break colocation loop } - if (pe_rsc_is_clone(rsc) && !pe_rsc_is_clone(dependent)) { + if (pcmk__is_clone(rsc) && !pcmk__is_clone(dependent)) { continue; // We can't be sure whether dependent will be colocated } - if ((constraint->score == INFINITY) && + if ((constraint->score == PCMK_SCORE_INFINITY) && (pcmk__colocation_affects(dependent, rsc, constraint, true) == pcmk__coloc_affects_location)) { add_colocated_resources(dependent, orig_rsc, &colocated_rscs); @@ -334,7 +334,7 @@ pcmk__output_resource_actions(pcmk_resource_t *rsc) next = rsc->allocated_to; if (rsc->running_on) { - current = pe__current_node(rsc); + current = pcmk__current_node(rsc); if (rsc->role == pcmk_role_stopped) { /* This can occur when resources are being recovered because * the current role can change in pcmk__primitive_create_actions() @@ -424,14 +424,14 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, && ((node->weight < 0) // Allow graph to assume that guest node connections will come up || (!pcmk__node_available(node, true, false) - && !pe__is_guest_node(node)))) { + && !pcmk__is_guest_or_bundle_node(node)))) { - pe_rsc_debug(rsc, - "All nodes for resource %s are unavailable, unclean or " - "shutting down (%s can%s run resources, with score %s)", - rsc->id, pe__node_name(node), - (pcmk__node_available(node, true, false)? "" : "not"), - pcmk_readable_score(node->weight)); + pcmk__rsc_debug(rsc, + "All nodes for resource %s are unavailable, unclean or " + "shutting down (%s can%s run resources, with score %s)", + rsc->id, pcmk__node_name(node), + (pcmk__node_available(node, true, false)? "" : "not"), + pcmk_readable_score(node->weight)); if (stop_if_fail) { pe__set_next_role(rsc, pcmk_role_stopped, "node availability"); @@ -440,17 +440,17 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, } if (rsc->allocated_to != NULL) { - changed = !pe__same_node(rsc->allocated_to, node); + changed = !pcmk__same_node(rsc->allocated_to, node); } else { changed = (node != NULL); } pcmk__unassign_resource(rsc); - pe__clear_resource_flags(rsc, pcmk_rsc_unassigned); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned); if (node == NULL) { char *rc_stopped = NULL; - pe_rsc_debug(rsc, "Could not assign %s to a node", rsc->id); + pcmk__rsc_debug(rsc, "Could not assign %s to a node", rsc->id); if (!stop_if_fail) { return changed; @@ -460,15 +460,15 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, for (GList *iter = rsc->actions; iter != NULL; iter = iter->next) { pcmk_action_t *op = (pcmk_action_t *) iter->data; - pe_rsc_debug(rsc, "Updating %s for %s assignment failure", - op->uuid, rsc->id); + pcmk__rsc_debug(rsc, "Updating %s for %s assignment failure", + op->uuid, rsc->id); if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) { - pe__clear_action_flags(op, pcmk_action_optional); + pcmk__clear_action_flags(op, pcmk_action_optional); } else if (pcmk__str_eq(op->task, PCMK_ACTION_START, pcmk__str_none)) { - pe__clear_action_flags(op, pcmk_action_runnable); + pcmk__clear_action_flags(op, pcmk_action_runnable); } else { // Cancel recurring actions, unless for stopped state @@ -476,9 +476,9 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, const char *target_rc_s = NULL; interval_ms_s = g_hash_table_lookup(op->meta, - XML_LRM_ATTR_INTERVAL_MS); + PCMK_META_INTERVAL); target_rc_s = g_hash_table_lookup(op->meta, - XML_ATTR_TE_TARGET_RC); + PCMK__META_OP_TARGET_RC); if (rc_stopped == NULL) { rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING); } @@ -486,7 +486,7 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches) && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) { - pe__clear_action_flags(op, pcmk_action_runnable); + pcmk__clear_action_flags(op, pcmk_action_runnable); } } } @@ -494,7 +494,7 @@ pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, return changed; } - pe_rsc_debug(rsc, "Assigning %s to %s", rsc->id, pe__node_name(node)); + pcmk__rsc_debug(rsc, "Assigning %s to %s", rsc->id, pcmk__node_name(node)); rsc->allocated_to = pe__copy_node(node); add_assigned_resource(node, rsc); @@ -529,10 +529,10 @@ pcmk__unassign_resource(pcmk_resource_t *rsc) if (old == NULL) { crm_info("Unassigning %s", rsc->id); } else { - crm_info("Unassigning %s from %s", rsc->id, pe__node_name(old)); + crm_info("Unassigning %s from %s", rsc->id, pcmk__node_name(old)); } - pe__set_resource_flags(rsc, pcmk_rsc_unassigned); + pcmk__set_rsc_flags(rsc, pcmk_rsc_unassigned); if (rsc->children == NULL) { if (old == NULL) { @@ -600,11 +600,12 @@ pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node, remaining_tries = rsc->migration_threshold - fail_count; if (remaining_tries <= 0) { - crm_warn("%s cannot run on %s due to reaching migration threshold " - "(clean up resource to allow again)" - CRM_XS " failures=%d migration-threshold=%d", - rsc_to_ban->id, pe__node_name(node), fail_count, - rsc->migration_threshold); + pcmk__sched_warn("%s cannot run on %s due to reaching migration " + "threshold (clean up resource to allow again)" + CRM_XS " failures=%d " + PCMK_META_MIGRATION_THRESHOLD "=%d", + rsc_to_ban->id, pcmk__node_name(node), fail_count, + rsc->migration_threshold); if (failed != NULL) { *failed = rsc_to_ban; } @@ -614,7 +615,7 @@ pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node, crm_info("%s can fail %d more time%s on " "%s before reaching migration threshold (%d)", rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries), - pe__node_name(node), rsc->migration_threshold); + pcmk__node_name(node), rsc->migration_threshold); return false; } @@ -635,7 +636,7 @@ get_node_score(const pcmk_node_t *node, GHashTable *nodes) if ((node != NULL) && (nodes != NULL)) { found_node = g_hash_table_lookup(nodes, node->details->id); } - return (found_node == NULL)? -INFINITY : found_node->weight; + return (found_node == NULL)? -PCMK_SCORE_INFINITY : found_node->weight; } /*! @@ -661,8 +662,8 @@ cmp_resources(gconstpointer a, gconstpointer b, gpointer data) const GList *nodes = data; int rc = 0; - int r1_score = -INFINITY; - int r2_score = -INFINITY; + int r1_score = -PCMK_SCORE_INFINITY; + int r2_score = -PCMK_SCORE_INFINITY; pcmk_node_t *r1_node = NULL; pcmk_node_t *r2_node = NULL; GHashTable *r1_nodes = NULL; @@ -703,10 +704,10 @@ cmp_resources(gconstpointer a, gconstpointer b, gpointer data) // The resource with highest score on its current node goes first reason = "current location"; if (resource1->running_on != NULL) { - r1_node = pe__current_node(resource1); + r1_node = pcmk__current_node(resource1); } if (resource2->running_on != NULL) { - r2_node = pe__current_node(resource2); + r2_node = pcmk__current_node(resource2); } r1_score = get_node_score(r1_node, r1_nodes); r2_score = get_node_score(r2_node, r2_nodes); diff --git a/lib/pacemaker/pcmk_sched_tickets.c b/lib/pacemaker/pcmk_sched_tickets.c index f61b371..a22ed45 100644 --- a/lib/pacemaker/pcmk_sched_tickets.c +++ b/lib/pacemaker/pcmk_sched_tickets.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. * @@ -50,8 +50,8 @@ ticket_role_matches(const pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket) || (rsc_ticket->role == rsc->role)) { return true; } - pe_rsc_trace(rsc, "Skipping constraint: \"%s\" state filter", - role2text(rsc_ticket->role)); + pcmk__rsc_trace(rsc, "Skipping constraint: \"%s\" state filter", + pcmk_role_text(rsc_ticket->role)); return false; } @@ -73,29 +73,29 @@ constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket) } if (rsc->children) { - pe_rsc_trace(rsc, "Processing ticket dependencies from %s", rsc->id); + pcmk__rsc_trace(rsc, "Processing ticket dependencies from %s", rsc->id); for (iter = rsc->children; iter != NULL; iter = iter->next) { constraints_for_ticket((pcmk_resource_t *) iter->data, rsc_ticket); } return; } - pe_rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)", - rsc->id, rsc_ticket->ticket->id, rsc_ticket->id, - role2text(rsc_ticket->role)); + pcmk__rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)", + rsc->id, rsc_ticket->ticket->id, rsc_ticket->id, + pcmk_role_text(rsc_ticket->role)); if (!rsc_ticket->ticket->granted && (rsc->running_on != NULL)) { switch (rsc_ticket->loss_policy) { case loss_ticket_stop: - resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__", - rsc->cluster); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + "__loss_of_ticket__", rsc->cluster); break; case loss_ticket_demote: // Promotion score will be set to -INFINITY in promotion_order() if (rsc_ticket->role != pcmk_role_promoted) { - resource_location(rsc, NULL, -INFINITY, + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, "__loss_of_ticket__", rsc->cluster); } break; @@ -105,8 +105,8 @@ constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket) return; } - resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__", - rsc->cluster); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + "__loss_of_ticket__", rsc->cluster); for (iter = rsc->running_on; iter != NULL; iter = iter->next) { pe_fence_node(rsc->cluster, (pcmk_node_t *) iter->data, @@ -119,8 +119,8 @@ constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket) return; } if (rsc->running_on != NULL) { - pe__clear_resource_flags(rsc, pcmk_rsc_managed); - pe__set_resource_flags(rsc, pcmk_rsc_blocked); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked); } break; } @@ -129,16 +129,16 @@ constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket) if ((rsc_ticket->role != pcmk_role_promoted) || (rsc_ticket->loss_policy == loss_ticket_stop)) { - resource_location(rsc, NULL, -INFINITY, "__no_ticket__", - rsc->cluster); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + "__no_ticket__", rsc->cluster); } } else if (rsc_ticket->ticket->standby) { if ((rsc_ticket->role != pcmk_role_promoted) || (rsc_ticket->loss_policy == loss_ticket_stop)) { - resource_location(rsc, NULL, -INFINITY, "__ticket_standby__", - rsc->cluster); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + "__ticket_standby__", rsc->cluster); } } } @@ -160,7 +160,7 @@ rsc_ticket_new(const char *id, pcmk_resource_t *rsc, pcmk_ticket_t *ticket, return; } - if (pcmk__str_eq(state, PCMK__ROLE_STARTED, + if (pcmk__str_eq(state, PCMK_ROLE_STARTED, pcmk__str_null_matches|pcmk__str_casei)) { state = PCMK__ROLE_UNKNOWN; } @@ -168,59 +168,59 @@ rsc_ticket_new(const char *id, pcmk_resource_t *rsc, pcmk_ticket_t *ticket, new_rsc_ticket->id = id; new_rsc_ticket->ticket = ticket; new_rsc_ticket->rsc = rsc; - new_rsc_ticket->role = text2role(state); + new_rsc_ticket->role = pcmk_parse_role(state); - if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) { + if (pcmk__str_eq(loss_policy, PCMK_VALUE_FENCE, pcmk__str_casei)) { if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { new_rsc_ticket->loss_policy = loss_ticket_fence; } else { - pcmk__config_err("Resetting '" XML_TICKET_ATTR_LOSS_POLICY - "' for ticket '%s' to 'stop' " + pcmk__config_err("Resetting '" PCMK_XA_LOSS_POLICY "' " + "for ticket '%s' to '" PCMK_VALUE_STOP "' " "because fencing is not configured", ticket->id); - loss_policy = "stop"; + loss_policy = PCMK_VALUE_STOP; } } if (new_rsc_ticket->loss_policy == loss_ticket_fence) { crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); - } else if (pcmk__str_eq(loss_policy, "freeze", pcmk__str_casei)) { + } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_FREEZE, pcmk__str_casei)) { crm_debug("On loss of ticket '%s': Freeze %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); new_rsc_ticket->loss_policy = loss_ticket_freeze; - } else if (pcmk__str_eq(loss_policy, PCMK_ACTION_DEMOTE, pcmk__str_casei)) { + } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_DEMOTE, pcmk__str_casei)) { crm_debug("On loss of ticket '%s': Demote %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); new_rsc_ticket->loss_policy = loss_ticket_demote; - } else if (pcmk__str_eq(loss_policy, "stop", pcmk__str_casei)) { + } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_STOP, pcmk__str_casei)) { crm_debug("On loss of ticket '%s': Stop %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); new_rsc_ticket->loss_policy = loss_ticket_stop; } else { if (new_rsc_ticket->role == pcmk_role_promoted) { crm_debug("On loss of ticket '%s': Default to demote %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); new_rsc_ticket->loss_policy = loss_ticket_demote; } else { crm_debug("On loss of ticket '%s': Default to stop %s (%s)", new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id, - role2text(new_rsc_ticket->role)); + pcmk_role_text(new_rsc_ticket->role)); new_rsc_ticket->loss_policy = loss_ticket_stop; } } - pe_rsc_trace(rsc, "%s (%s) ==> %s", - rsc->id, role2text(new_rsc_ticket->role), ticket->id); + pcmk__rsc_trace(rsc, "%s (%s) ==> %s", + rsc->id, pcmk_role_text(new_rsc_ticket->role), ticket->id); rsc->rsc_tickets = g_list_append(rsc->rsc_tickets, new_rsc_ticket); @@ -243,29 +243,30 @@ unpack_rsc_ticket_set(xmlNode *set, pcmk_ticket_t *ticket, CRM_CHECK(set != NULL, return EINVAL); CRM_CHECK(ticket != NULL, return EINVAL); - set_id = ID(set); + set_id = pcmk__xe_id(set); if (set_id == NULL) { - pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without " - XML_ATTR_ID); + pcmk__config_err("Ignoring <" PCMK_XE_RESOURCE_SET "> without " + PCMK_XA_ID); return pcmk_rc_unpack_error; } - role = crm_element_value(set, "role"); + role = crm_element_value(set, PCMK_XA_ROLE); - for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF); - xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) { + for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, + NULL, NULL); + xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { pcmk_resource_t *resource = NULL; resource = pcmk__find_constraint_resource(scheduler->resources, - ID(xml_rsc)); + pcmk__xe_id(xml_rsc)); if (resource == NULL) { pcmk__config_err("%s: No resource found for %s", - set_id, ID(xml_rsc)); + set_id, pcmk__xe_id(xml_rsc)); return pcmk_rc_unpack_error; } - pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'", - resource->id, ticket->id); + pcmk__rsc_trace(resource, "Resource '%s' depends on ticket '%s'", + resource->id, ticket->id); rsc_ticket_new(set_id, resource, ticket, role, loss_policy); } @@ -276,33 +277,30 @@ static void unpack_simple_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) { const char *id = NULL; - const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET); - const char *loss_policy = crm_element_value(xml_obj, - XML_TICKET_ATTR_LOSS_POLICY); + const char *ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET); + const char *loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY); pcmk_ticket_t *ticket = NULL; - const char *rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE); - const char *state = crm_element_value(xml_obj, - XML_COLOC_ATTR_SOURCE_ROLE); + const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC); + const char *state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); // @COMPAT: Deprecated since 2.1.5 - const char *instance = crm_element_value(xml_obj, - XML_COLOC_ATTR_SOURCE_INSTANCE); + const char *instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE); pcmk_resource_t *rsc = NULL; if (instance != NULL) { - pe_warn_once(pcmk__wo_coloc_inst, - "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is " - "deprecated and will be removed in a future release."); + pcmk__warn_once(pcmk__wo_coloc_inst, + "Support for " PCMK__XA_RSC_INSTANCE " is deprecated " + "and will be removed in a future release"); } CRM_CHECK(xml_obj != NULL, return); - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return; } @@ -333,7 +331,7 @@ unpack_simple_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) "does not exist", id, rsc_id); return; - } else if ((instance != NULL) && !pe_rsc_is_clone(rsc)) { + } else if ((instance != NULL) && !pcmk__is_clone(rsc)) { pcmk__config_err("Ignoring constraint '%s' because resource '%s' " "is not a clone but instance '%s' was requested", id, rsc_id, instance); @@ -371,9 +369,9 @@ unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml, CRM_CHECK(xml_obj != NULL, return EINVAL); - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return pcmk_rc_unpack_error; } @@ -385,7 +383,7 @@ unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml, return pcmk_rc_ok; } - rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE); + rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC); if (rsc_id == NULL) { return pcmk_rc_ok; } @@ -400,13 +398,15 @@ unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml, return pcmk_rc_ok; } - state = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE); + state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE); - *expanded_xml = copy_xml(xml_obj); + *expanded_xml = pcmk__xml_copy(NULL, xml_obj); - // Convert any template or tag reference in "rsc" into ticket resource_set - if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_COLOC_ATTR_SOURCE, - false, scheduler)) { + /* Convert any template or tag reference in "rsc" into ticket + * PCMK_XE_RESOURCE_SET + */ + if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC, false, + scheduler)) { free_xml(*expanded_xml); *expanded_xml = NULL; return pcmk_rc_unpack_error; @@ -414,9 +414,11 @@ unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml, if (rsc_set != NULL) { if (state != NULL) { - // Move "rsc-role" into converted resource_set as a "role" attribute - crm_xml_add(rsc_set, "role", state); - xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE); + /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as a + * PCMK_XA_ROLE attribute + */ + crm_xml_add(rsc_set, PCMK_XA_ROLE, state); + pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE); } } else { @@ -443,9 +445,9 @@ pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) CRM_CHECK(xml_obj != NULL, return); - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return; } @@ -454,7 +456,7 @@ pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) scheduler->tickets = pcmk__strkey_table(free, destroy_ticket); } - ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET); + ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET); if (ticket_str == NULL) { pcmk__config_err("Ignoring constraint '%s' without ticket", id); return; @@ -478,14 +480,14 @@ pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) xml_obj = expanded_xml; } - for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL; - set = crm_next_same_xml(set)) { + for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL); + set != NULL; set = pcmk__xe_next_same(set)) { const char *loss_policy = NULL; any_sets = true; set = expand_idref(set, scheduler->input); - loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY); + loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY); if ((set == NULL) // Configuration error, message already logged || (unpack_rsc_ticket_set(set, ticket, loss_policy, @@ -524,7 +526,7 @@ pcmk__require_promotion_tickets(pcmk_resource_t *rsc) if ((rsc_ticket->role == pcmk_role_promoted) && (!rsc_ticket->ticket->granted || rsc_ticket->ticket->standby)) { - resource_location(rsc, NULL, -INFINITY, + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, "__stateful_without_ticket__", rsc->cluster); } } diff --git a/lib/pacemaker/pcmk_sched_utilization.c b/lib/pacemaker/pcmk_sched_utilization.c index 962a94c..05e3cf6 100644 --- a/lib/pacemaker/pcmk_sched_utilization.c +++ b/lib/pacemaker/pcmk_sched_utilization.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the Pacemaker project contributors + * Copyright 2014-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 <pacemaker-internal.h> #include "libpacemaker_private.h" @@ -31,7 +31,8 @@ utilization_value(const char *s) int value = 0; if ((s != NULL) && (pcmk__scan_min_int(s, &value, INT_MIN) == EINVAL)) { - pe_warn("Using 0 for utilization instead of invalid value '%s'", value); + pcmk__config_warn("Using 0 for utilization instead of " + "invalid value '%s'", value); value = 0; } return value; @@ -229,7 +230,7 @@ check_capacity(gpointer key, gpointer value, gpointer user_data) if (required > remaining) { crm_debug("Remaining capacity for %s on %s (%d) is insufficient " "for resource %s usage (%d)", - (const char *) key, pe__node_name(data->node), remaining, + (const char *) key, pcmk__node_name(data->node), remaining, data->rsc_id, required); data->is_enough = false; } @@ -306,7 +307,7 @@ pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc) CRM_CHECK(rsc != NULL, return NULL); // The default placement strategy ignores utilization - if (pcmk__str_eq(rsc->cluster->placement_strategy, "default", + if (pcmk__str_eq(rsc->cluster->placement_strategy, PCMK_VALUE_DEFAULT, pcmk__str_casei)) { return NULL; } @@ -352,10 +353,10 @@ pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc) if (pcmk__node_available(node, true, false) && !have_enough_capacity(node, rscs_id, unassigned_utilization)) { - pe_rsc_debug(rsc, "%s does not have enough capacity for %s", - pe__node_name(node), rscs_id); - resource_location(rsc, node, -INFINITY, "__limit_utilization__", - rsc->cluster); + pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s", + pcmk__node_name(node), rscs_id); + resource_location(rsc, node, -PCMK_SCORE_INFINITY, + "__limit_utilization__", rsc->cluster); } } most_capable_node = NULL; @@ -366,10 +367,10 @@ pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc) while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (pcmk__node_available(node, true, false) && !have_enough_capacity(node, rsc->id, rsc->utilization)) { - pe_rsc_debug(rsc, "%s does not have enough capacity for %s", - pe__node_name(node), rsc->id); - resource_location(rsc, node, -INFINITY, "__limit_utilization__", - rsc->cluster); + pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s", + pcmk__node_name(node), rsc->id); + resource_location(rsc, node, -PCMK_SCORE_INFINITY, + "__limit_utilization__", rsc->cluster); } } } @@ -401,7 +402,7 @@ new_load_stopped_op(pcmk_node_t *node) if (load_stopped->node == NULL) { load_stopped->node = pe__copy_node(node); - pe__clear_action_flags(load_stopped, pcmk_action_optional); + pcmk__clear_action_flags(load_stopped, pcmk_action_optional); } free(load_stopped_task); return load_stopped; @@ -421,8 +422,9 @@ pcmk__create_utilization_constraints(pcmk_resource_t *rsc, const GList *iter = NULL; pcmk_action_t *load_stopped = NULL; - pe_rsc_trace(rsc, "Creating utilization constraints for %s - strategy: %s", - rsc->id, rsc->cluster->placement_strategy); + pcmk__rsc_trace(rsc, + "Creating utilization constraints for %s - strategy: %s", + rsc->id, rsc->cluster->placement_strategy); // "stop rsc then load_stopped" constraints for current nodes for (iter = rsc->running_on; iter != NULL; iter = iter->next) { diff --git a/lib/pacemaker/pcmk_scheduler.c b/lib/pacemaker/pcmk_scheduler.c index 31b2c36..35bfc9d 100644 --- a/lib/pacemaker/pcmk_scheduler.c +++ b/lib/pacemaker/pcmk_scheduler.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. * @@ -11,7 +11,7 @@ #include <crm/crm.h> #include <crm/cib.h> -#include <crm/msg_xml.h> +#include <crm/cib/internal.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/scheduler_internal.h> @@ -44,7 +44,7 @@ check_params(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *rsc_op, enum pcmk__check_parameters check) { const char *reason = NULL; - op_digest_cache_t *digest_data = NULL; + pcmk__op_digest_t *digest_data = NULL; switch (check) { case pcmk__check_active: @@ -62,7 +62,7 @@ check_params(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *rsc_op, case pcmk__digest_unknown: crm_trace("Resource %s history entry %s on %s has " "no digest to compare", - rsc->id, ID(rsc_op), node->details->id); + rsc->id, pcmk__xe_id(rsc_op), node->details->id); break; case pcmk__digest_match: break; @@ -134,8 +134,8 @@ check_failure_threshold(gpointer data, gpointer user_data) pcmk_resource_t *failed = NULL; if (pcmk__threshold_reached(rsc, node, &failed)) { - resource_location(failed, node, -INFINITY, "__fail_limit__", - rsc->cluster); + resource_location(failed, node, -PCMK_SCORE_INFINITY, + "__fail_limit__", rsc->cluster); } } } @@ -144,10 +144,11 @@ check_failure_threshold(gpointer data, gpointer user_data) * \internal * \brief If resource has exclusive discovery, ban node if not allowed * - * Location constraints have a resource-discovery option that allows users to - * specify where probes are done for the affected resource. If this is set to - * exclusive, probes will only be done on nodes listed in exclusive constraints. - * This function bans the resource from the node if the node is not listed. + * Location constraints have a PCMK_XA_RESOURCE_DISCOVERY option that allows + * users to specify where probes are done for the affected resource. If this is + * set to \c exclusive, probes will only be done on nodes listed in exclusive + * constraints. This function bans the resource from the node if the node is not + * listed. * * \param[in,out] data Resource to check * \param[in] user_data Node to check resource on @@ -168,7 +169,7 @@ apply_exclusive_discovery(gpointer data, gpointer user_data) match = g_hash_table_lookup(rsc->allowed_nodes, node->details->id); if ((match != NULL) && (match->rsc_discover_mode != pcmk_probe_exclusive)) { - match->weight = -INFINITY; + match->weight = -PCMK_SCORE_INFINITY; } } } @@ -210,15 +211,15 @@ apply_stickiness(gpointer data, gpointer user_data) if (!pcmk_is_set(rsc->cluster->flags, pcmk_sched_symmetric_cluster) && (g_hash_table_lookup(rsc->allowed_nodes, node->details->id) == NULL)) { - pe_rsc_debug(rsc, - "Ignoring %s stickiness because the cluster is " - "asymmetric and %s is not explicitly allowed", - rsc->id, pe__node_name(node)); + pcmk__rsc_debug(rsc, + "Ignoring %s stickiness because the cluster is " + "asymmetric and %s is not explicitly allowed", + rsc->id, pcmk__node_name(node)); return; } - pe_rsc_debug(rsc, "Resource %s has %d stickiness on %s", - rsc->id, rsc->stickiness, pe__node_name(node)); + pcmk__rsc_debug(rsc, "Resource %s has %d stickiness on %s", + rsc->id, rsc->stickiness, pcmk__node_name(node)); resource_location(rsc, node, rsc->stickiness, "stickiness", rsc->cluster); } @@ -306,7 +307,7 @@ assign_resources(pcmk_scheduler_t *scheduler) crm_trace("Assigning resources to nodes"); - if (!pcmk__str_eq(scheduler->placement_strategy, "default", + if (!pcmk__str_eq(scheduler->placement_strategy, PCMK_VALUE_DEFAULT, pcmk__str_casei)) { pcmk__sort_resources(scheduler); } @@ -321,8 +322,8 @@ assign_resources(pcmk_scheduler_t *scheduler) pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; if (rsc->is_remote_node) { - pe_rsc_trace(rsc, "Assigning remote connection resource '%s'", - rsc->id); + pcmk__rsc_trace(rsc, "Assigning remote connection resource '%s'", + rsc->id); rsc->cmds->assign(rsc, rsc->partial_migration_target, true); } } @@ -333,8 +334,8 @@ assign_resources(pcmk_scheduler_t *scheduler) pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; if (!rsc->is_remote_node) { - pe_rsc_trace(rsc, "Assigning %s resource '%s'", - rsc->xml->name, rsc->id); + pcmk__rsc_trace(rsc, "Assigning %s resource '%s'", + rsc->xml->name, rsc->id); rsc->cmds->assign(rsc, NULL, true); } } @@ -485,7 +486,7 @@ needs_fencing(const pcmk_node_t *node, bool have_managed) static bool needs_shutdown(const pcmk_node_t *node) { - if (pe__is_guest_or_remote_node(node)) { + if (pcmk__is_pacemaker_remote_node(node)) { /* Do not send shutdown actions for Pacemaker Remote nodes. * @TODO We might come up with a good use for this in the future. */ @@ -532,7 +533,7 @@ schedule_fencing(pcmk_node_t *node) pcmk_action_t *fencing = pe_fence_op(node, NULL, FALSE, "node is unclean", FALSE, node->details->data_set); - pe_warn("Scheduling node %s for fencing", pe__node_name(node)); + pcmk__sched_warn("Scheduling node %s for fencing", pcmk__node_name(node)); pcmk__order_vs_fence(fencing, node->details->data_set); return fencing; } @@ -566,7 +567,7 @@ schedule_fencing_and_shutdowns(pcmk_scheduler_t *scheduler) /* Guest nodes are "fenced" by recovering their container resource, * so handle them separately. */ - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { if (node->details->remote_requires_reset && have_managed && pe_can_fence(scheduler, node)) { pcmk__fence_guest(node); @@ -598,20 +599,21 @@ schedule_fencing_and_shutdowns(pcmk_scheduler_t *scheduler) if ((fencing == NULL) && node->details->unclean) { integrity_lost = true; - pe_warn("Node %s is unclean but cannot be fenced", - pe__node_name(node)); + pcmk__config_warn("Node %s is unclean but cannot be fenced", + pcmk__node_name(node)); } } if (integrity_lost) { if (!pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) { - pe_warn("Resource functionality and data integrity cannot be " - "guaranteed (configure, enable, and test fencing to " - "correct this)"); + pcmk__config_warn("Resource functionality and data integrity " + "cannot be guaranteed (configure, enable, " + "and test fencing to correct this)"); } else if (!pcmk_is_set(scheduler->flags, pcmk_sched_quorate)) { crm_notice("Unclean nodes will not be fenced until quorum is " - "attained or no-quorum-policy is set to ignore"); + "attained or " PCMK_OPT_NO_QUORUM_POLICY " is set to " + PCMK_VALUE_IGNORE); } } @@ -667,7 +669,8 @@ log_resource_details(pcmk_scheduler_t *scheduler) // Log all resources except inactive orphans if (!pcmk_is_set(rsc->flags, pcmk_rsc_removed) || (rsc->role != pcmk_role_stopped)) { - out->message(out, crm_map_element_name(rsc->xml), 0, rsc, all, all); + out->message(out, pcmk__map_element_name(rsc->xml), 0UL, rsc, all, + all); } } @@ -741,7 +744,7 @@ unpack_cib(xmlNode *cib, unsigned long long flags, pcmk_scheduler_t *scheduler) if (pcmk_is_set(scheduler->flags, pcmk_sched_have_status)) { crm_trace("Reusing previously calculated cluster status"); - pe__set_working_set_flags(scheduler, flags); + pcmk__set_scheduler_flags(scheduler, flags); return; } @@ -763,7 +766,7 @@ unpack_cib(xmlNode *cib, unsigned long long flags, pcmk_scheduler_t *scheduler) scheduler->localhost = localhost_save; } - pe__set_working_set_flags(scheduler, flags); + pcmk__set_scheduler_flags(scheduler, flags); scheduler->input = cib; cluster_status(scheduler); // Sets pcmk_sched_have_status } @@ -818,3 +821,73 @@ pcmk__schedule_actions(xmlNode *cib, unsigned long long flags, log_unrunnable_actions(scheduler); } } + +/*! + * \internal + * \brief Initialize scheduler data + * + * Make our own copies of the CIB XML and date/time object, if they're not + * \c NULL. This way we don't have to take ownership of the objects passed via + * the API. + * + * This function is most useful for public API functions that want the caller + * to retain ownership of the CIB object + * + * \param[in,out] out Output object + * \param[in] input The CIB XML to check (if \c NULL, use current CIB) + * \param[in] date Date and time to use in the scheduler (if \c NULL, + * use current date and time). This can be used for + * checking whether a rule is in effect at a certa + * date and time. + * \param[out] scheduler Where to store initialized scheduler data + * + * \return Standard Pacemaker return code + */ +int +pcmk__init_scheduler(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, + pcmk_scheduler_t **scheduler) +{ + // Allows for cleaner syntax than dereferencing the scheduler argument + pcmk_scheduler_t *new_scheduler = NULL; + + new_scheduler = pe_new_working_set(); + if (new_scheduler == NULL) { + return ENOMEM; + } + + pcmk__set_scheduler_flags(new_scheduler, + pcmk_sched_no_counts|pcmk_sched_no_compat); + + // Populate the scheduler data + + // Make our own copy of the given input or fetch the CIB and use that + if (input != NULL) { + new_scheduler->input = pcmk__xml_copy(NULL, input); + if (new_scheduler->input == NULL) { + out->err(out, "Failed to copy input XML"); + pe_free_working_set(new_scheduler); + return ENOMEM; + } + + } else { + int rc = cib__signon_query(out, NULL, &(new_scheduler->input)); + + if (rc != pcmk_rc_ok) { + pe_free_working_set(new_scheduler); + return rc; + } + } + + // Make our own copy of the given crm_time_t object; otherwise + // cluster_status() populates with the current time + if (date != NULL) { + // pcmk_copy_time() guarantees non-NULL + new_scheduler->now = pcmk_copy_time(date); + } + + // Unpack everything + cluster_status(new_scheduler); + *scheduler = new_scheduler; + + return pcmk_rc_ok; +} diff --git a/lib/pacemaker/pcmk_setup.c b/lib/pacemaker/pcmk_setup.c new file mode 100644 index 0000000..a467911 --- /dev/null +++ b/lib/pacemaker/pcmk_setup.c @@ -0,0 +1,78 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> +#include <crm/cib/internal.h> +#include <crm/common/output.h> +#include <crm/common/results.h> +#include <crm/common/scheduler.h> +#include <pacemaker-internal.h> +#include <pacemaker.h> + +#include "libpacemaker_private.h" + +/*! + * \internal + * \brief Set up a pcmk__output_t, (optionally) cib_t, and + * (optionally) pcmk_scheduler_t for use in implementing + * public/private API function pairs + * + * \param[in,out] out Where to store a \c pcmk__output_t object + * \param[in,out] cib Where to store a \c cib_t object + * (may be \c NULL if a CIB is not needed) + * \param[in,out] scheduler Where to store a \c pcmk_scheduler_t object + * (may be \c NULL if a scheduler is not needed) + * \param[in,out] xml Where to write any result XML + * + * \note The \p cib and \p scheduler arguments will only be valid if there + * are no errors in this function. However, \p out will always be + * valid unless there are errors setting it up so that other errors + * may still be reported. + * + * \return Standard Pacemaker return code + */ +int +pcmk__setup_output_cib_sched(pcmk__output_t **out, cib_t **cib, + pcmk_scheduler_t **scheduler, xmlNode **xml) +{ + int rc = pcmk_rc_ok; + + rc = pcmk__xml_output_new(out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + if (cib != NULL) { + *cib = cib_new(); + if (*cib == NULL) { + return pcmk_rc_cib_corrupt; + } + + rc = (*cib)->cmds->signon(*cib, crm_system_name, cib_command); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + cib__clean_up_connection(cib); + return rc; + } + } + + if (scheduler != NULL) { + rc = pcmk__init_scheduler(*out, NULL, NULL, scheduler); + if (rc != pcmk_rc_ok && cib != NULL) { + cib__clean_up_connection(cib); + return rc; + } + + pcmk__unpack_constraints(*scheduler); + } + + pcmk__register_lib_messages(*out); + return rc; +} diff --git a/lib/pacemaker/pcmk_simulate.c b/lib/pacemaker/pcmk_simulate.c index 167f8a5..4146f2d 100644 --- a/lib/pacemaker/pcmk_simulate.c +++ b/lib/pacemaker/pcmk_simulate.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -68,8 +68,7 @@ create_action_name(const pcmk_action_t *action, bool verbose) char *key = NULL; guint interval_ms = 0; - if (pcmk__guint_from_hash(action->meta, - XML_LRM_ATTR_INTERVAL_MS, 0, + if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0, &interval_ms) != pcmk_rc_ok) { interval_ms = 0; } @@ -98,7 +97,8 @@ create_action_name(const pcmk_action_t *action, bool verbose) } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) { - const char *op = g_hash_table_lookup(action->meta, "stonith_action"); + const char *op = g_hash_table_lookup(action->meta, + PCMK__META_STONITH_ACTION); action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host); @@ -153,7 +153,8 @@ print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts, out->begin_list(out, NULL, NULL, "%s", title); out->message(out, "cluster-status", scheduler, state, stonith_rc, NULL, - false, section_opts, show_opts, NULL, all, all); + pcmk__fence_history_none, section_opts, show_opts, NULL, + all, all); out->end_list(out); g_list_free(all); @@ -195,13 +196,13 @@ reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out, scheduler->priv = out; set_effective_date(scheduler, true, use_date); if (pcmk_is_set(flags, pcmk_sim_sanitized)) { - pe__set_working_set_flags(scheduler, pcmk_sched_sanitized); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_sanitized); } if (pcmk_is_set(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(flags, pcmk_sim_show_utilization)) { - pe__set_working_set_flags(scheduler, pcmk_sched_show_utilization); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization); } } @@ -242,7 +243,7 @@ write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file, } if (pcmk_is_set(action->flags, pcmk_action_added_to_graph)) { - style = "bold"; + style = PCMK__VALUE_BOLD; color = "green"; } else if ((action->rsc != NULL) @@ -264,7 +265,7 @@ write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file, CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pcmk_action_runnable)); } - pe__set_action_flags(action, pcmk_action_added_to_graph); + pcmk__set_action_flags(action, pcmk_action_added_to_graph); fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n", action_name, style, color, font); do_not_write: @@ -286,7 +287,7 @@ write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file, if (before->state == pe_link_dumped) { optional = false; - style = "bold"; + style = PCMK__VALUE_BOLD; } else if ((uint32_t) before->type == pcmk__ar_none) { continue; } else if (pcmk_is_set(before->action->flags, @@ -337,19 +338,19 @@ profile_file(const char *xml_file, long long repeat, CRM_ASSERT(out != NULL); - cib_object = filename2xml(xml_file); + cib_object = pcmk__xml_read(xml_file); start = clock(); - if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) { - create_xml_node(cib_object, XML_CIB_TAG_STATUS); + 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) { + if (pcmk_update_configured_schema(&cib_object, false) != pcmk_rc_ok) { free_xml(cib_object); return; } - if (validate_xml(cib_object, NULL, FALSE) != TRUE) { + if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) { free_xml(cib_object); return; } @@ -362,8 +363,11 @@ profile_file(const char *xml_file, long long repeat, } for (int i = 0; i < repeat; ++i) { - xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object); + xmlNode *input = cib_object; + if (repeat > 1) { + input = pcmk__xml_copy(NULL, cib_object); + } scheduler->input = input; set_effective_date(scheduler, false, use_date); pcmk__schedule_actions(input, scheduler_flags, scheduler); @@ -416,14 +420,14 @@ pcmk__profile_dir(const char *dir, long long repeat, /*! * \brief Set the date of the cluster, either to the value given by - * \p use_date, or to the "execution-date" value in the CIB. + * \p use_date, or to the \c PCMK_XA_EXECUTION_DATE value in the CIB. * * \note \p scheduler->priv must have been set to a valid \p pcmk__output_t * object before this function is called. * * \param[in,out] scheduler Scheduler data - * \param[in] print_original If \p true, the "execution-date" should - * also be printed + * \param[in] print_original If \p true, the \c PCMK_XA_EXECUTION_DATE + * should also be printed * \param[in] use_date The date to set the cluster's time to * (may be NULL) */ @@ -436,7 +440,8 @@ set_effective_date(pcmk_scheduler_t *scheduler, bool print_original, CRM_ASSERT(out != NULL); - crm_element_value_epoch(scheduler->input, "execution-date", &original_date); + crm_element_value_epoch(scheduler->input, PCMK_XA_EXECUTION_DATE, + &original_date); if (use_date) { scheduler->now = crm_time_new(use_date); @@ -469,8 +474,8 @@ set_effective_date(pcmk_scheduler_t *scheduler, bool print_original, static int simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) { - const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); - const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); + const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE); + const char *task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY); pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed); out->message(out, "inject-pseudo-action", node, task); @@ -500,18 +505,19 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) const char *resource = NULL; const char *rprovider = NULL; const char *resource_config_name = NULL; - const char *operation = crm_element_value(action->xml, "operation"); + const char *operation = crm_element_value(action->xml, PCMK_XA_OPERATION); const char *target_rc_s = crm_meta_value(action->params, - XML_ATTR_TE_TARGET_RC); + PCMK__META_OP_TARGET_RC); xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; - xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); + xmlNode *action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, + NULL, NULL); - char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); + char *node = crm_element_value_copy(action->xml, PCMK__META_ON_NODE); char *uuid = NULL; const char *router_node = crm_element_value(action->xml, - XML_LRM_ATTR_ROUTER_NODE); + PCMK__XA_ROUTER_NODE); // Certain actions don't need to be displayed or history entries if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) { @@ -530,7 +536,7 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) * (which is preferred when writing history), and if necessary, the instance * name. */ - resource_config_name = crm_element_value(action_rsc, XML_ATTR_ID); + resource_config_name = crm_element_value(action_rsc, PCMK_XA_ID); if (resource_config_name == NULL) { // Shouldn't be possible crm_log_xml_err(action->xml, "No ID"); free(node); @@ -538,7 +544,7 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) } resource = resource_config_name; if (pe_find_resource(fake_resource_list, resource) == NULL) { - const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG); + const char *longname = crm_element_value(action_rsc, PCMK__XA_LONG_ID); if ((longname != NULL) && (pe_find_resource(fake_resource_list, longname) != NULL)) { @@ -554,9 +560,9 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) goto done; // Confirm action and update graph } - rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS); - rtype = crm_element_value(action_rsc, XML_ATTR_TYPE); - rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER); + rclass = crm_element_value(action_rsc, PCMK_XA_CLASS); + rtype = crm_element_value(action_rsc, PCMK_XA_TYPE); + rprovider = crm_element_value(action_rsc, PCMK_XA_PROVIDER); pcmk__scan_min_int(target_rc_s, &target_outcome, 0); @@ -564,7 +570,7 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) cib_sync_call|cib_scope_local) == pcmk_ok); // Ensure the action node is in the CIB - uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID); + uuid = crm_element_value_copy(action->xml, PCMK__META_ON_NODE_UUID); cib_node = pcmk__inject_node(fake_cib, node, ((router_node == NULL)? uuid: node)); free(uuid); @@ -630,15 +636,15 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) out->info(out, "Pretending action %d failed with rc=%d", action->id, op->rc); pcmk__set_graph_action_flags(action, pcmk__graph_action_failed); - graph->abort_priority = INFINITY; - pcmk__inject_failcount(out, cib_node, match_name, op->op_type, + graph->abort_priority = PCMK_SCORE_INFINITY; + pcmk__inject_failcount(out, fake_cib, cib_node, match_name, op->op_type, op->interval_ms, op->rc); break; } pcmk__inject_action_result(cib_resource, op, target_outcome); lrmd_free_event(op); - rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node, + rc = fake_cib->cmds->modify(fake_cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); @@ -662,9 +668,10 @@ simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) static int simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) { - const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); - const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); - xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); + const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE); + const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION); + xmlNode *rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL, + NULL); pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed); out->message(out, "inject-cluster-action", node, task, rsc); @@ -684,8 +691,8 @@ simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) static int simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) { - const char *op = crm_meta_value(action->params, "stonith_action"); - char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); + const char *op = crm_meta_value(action->params, PCMK__META_STONITH_ACTION); + char *target = crm_element_value_copy(action->xml, PCMK__META_ON_NODE); out->message(out, "inject-fencing-action", target, op); @@ -698,24 +705,24 @@ simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) false); CRM_ASSERT(cib_node != NULL); - crm_xml_add(cib_node, XML_ATTR_ORIGIN, __func__); - rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node, + crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, __func__); + rc = fake_cib->cmds->replace(fake_cib, PCMK_XE_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); // Simulate controller clearing node's resource history and attributes pcmk__g_strcat(xpath, - "//" XML_CIB_TAG_STATE - "[@" XML_ATTR_UNAME "='", target, "']/" XML_CIB_TAG_LRM, + "//" PCMK__XE_NODE_STATE + "[@" PCMK_XA_UNAME "='", target, "']/" PCMK__XE_LRM, NULL); fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL, cib_xpath|cib_sync_call|cib_scope_local); g_string_truncate(xpath, 0); pcmk__g_strcat(xpath, - "//" XML_CIB_TAG_STATE - "[@" XML_ATTR_UNAME "='", target, "']" - "/" XML_TAG_TRANSIENT_NODEATTRS, NULL); + "//" PCMK__XE_NODE_STATE + "[@" PCMK_XA_UNAME "='", target, "']" + "/" PCMK__XE_TRANSIENT_ATTRIBUTES, NULL); fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL, cib_xpath|cib_sync_call|cib_scope_local); @@ -866,9 +873,8 @@ pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out, } if (input_file != NULL) { - rc = write_xml_file(input, input_file, FALSE); - if (rc < 0) { - rc = pcmk_legacy2rc(rc); + rc = pcmk__xml_write_file(input, input_file, false, NULL); + if (rc != pcmk_rc_ok) { goto simulate_done; } } @@ -925,8 +931,9 @@ pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out, input = NULL; /* Don't try and free it twice */ if (graph_file != NULL) { - rc = write_xml_file(scheduler->graph, graph_file, FALSE); - if (rc < 0) { + rc = pcmk__xml_write_file(scheduler->graph, graph_file, false, + NULL); + if (rc != pcmk_rc_ok) { rc = pcmk_rc_graph_error; goto simulate_done; } @@ -966,10 +973,10 @@ pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out, set_effective_date(scheduler, true, use_date); if (pcmk_is_set(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(flags, pcmk_sim_show_utilization)) { - pe__set_working_set_flags(scheduler, pcmk_sched_show_utilization); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization); } cluster_status(scheduler); @@ -1001,6 +1008,6 @@ pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler, rc = pcmk__simulate(scheduler, out, injections, flags, section_opts, use_date, input_file, graph_file, dot_file); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); return rc; } diff --git a/lib/pacemaker/pcmk_status.c b/lib/pacemaker/pcmk_status.c index 77b6c90..d12c2bb 100644 --- a/lib/pacemaker/pcmk_status.c +++ b/lib/pacemaker/pcmk_status.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,7 +18,7 @@ #include <crm/common/results.h> #include <crm/fencing/internal.h> #include <crm/pengine/internal.h> -#include <crm/stonith-ng.h> +#include <crm/stonith-ng.h> // stonith__register_messages() #include <pacemaker.h> #include <pacemaker-internal.h> @@ -77,7 +77,7 @@ pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith, cib_t *cib, const char *only_node, const char *only_rsc, const char *neg_location_prefix, bool simple_output) { - xmlNode *cib_copy = copy_xml(current_cib); + xmlNode *cib_copy = pcmk__xml_copy(NULL, current_cib); stonith_history_t *stonith_history = NULL; int history_rc = 0; pcmk_scheduler_t *scheduler = NULL; @@ -86,10 +86,10 @@ pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith, cib_t *cib, int rc = pcmk_rc_ok; - if (cli_config_update(&cib_copy, NULL, FALSE) == FALSE) { + rc = pcmk_update_configured_schema(&cib_copy, false); + if (rc != pcmk_rc_ok) { cib__clean_up_connection(&cib); free_xml(cib_copy); - rc = pcmk_rc_schema_validation; out->err(out, "Upgrade failed: %s", pcmk_rc_str(rc)); return rc; } @@ -101,8 +101,8 @@ pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith, cib_t *cib, } scheduler = pe_new_working_set(); - CRM_ASSERT(scheduler != NULL); - pe__set_working_set_flags(scheduler, pcmk_sched_no_compat); + pcmk__mem_assert(scheduler); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_compat); scheduler->input = cib_copy; scheduler->priv = out; @@ -179,7 +179,7 @@ pcmk_status(xmlNodePtr *xml) rc = pcmk__status(out, cib, pcmk__fence_history_full, pcmk_section_all, show_opts, NULL, NULL, NULL, false, 0); - pcmk__xml_output_finish(out, xml); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); cib_delete(cib); return rc; @@ -329,7 +329,7 @@ pcmk__output_simple_status(pcmk__output_t *out, nodes_online++; } else { pcmk__add_word(&offline_nodes, 1024, "offline node:"); - pcmk__add_word(&offline_nodes, 0, pe__node_name(node)); + pcmk__add_word(&offline_nodes, 0, pcmk__node_name(node)); has_warnings = true; offline = true; } diff --git a/lib/pacemaker/pcmk_ticket.c b/lib/pacemaker/pcmk_ticket.c new file mode 100644 index 0000000..6f15848 --- /dev/null +++ b/lib/pacemaker/pcmk_ticket.c @@ -0,0 +1,553 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/pengine/internal.h> + +#include <pacemaker.h> +#include <pacemaker-internal.h> + +#include "libpacemaker_private.h" + +static int +build_ticket_modify_xml(cib_t *cib, const char *ticket_id, xmlNode **ticket_state_xml, + xmlNode **xml_top) +{ + int rc = pcmk__get_ticket_state(cib, ticket_id, ticket_state_xml); + + if (rc == pcmk_rc_ok || rc == pcmk_rc_duplicate_id) { + /* Ticket(s) found - return their state */ + *xml_top = *ticket_state_xml; + + } else if (rc == ENXIO) { + /* No ticket found - build the XML needed to create it */ + xmlNode *xml_obj = NULL; + + *xml_top = pcmk__xe_create(NULL, PCMK_XE_STATUS); + xml_obj = pcmk__xe_create(*xml_top, PCMK_XE_TICKETS); + *ticket_state_xml = pcmk__xe_create(xml_obj, PCMK__XE_TICKET_STATE); + crm_xml_add(*ticket_state_xml, PCMK_XA_ID, ticket_id); + + rc = pcmk_rc_ok; + + } else { + /* Some other error occurred - clean up and return */ + free_xml(*ticket_state_xml); + } + + return rc; +} + +static void +add_attribute_xml(pcmk_scheduler_t *scheduler, const char *ticket_id, + GHashTable *attr_set, xmlNode **ticket_state_xml) +{ + GHashTableIter hash_iter; + char *key = NULL; + char *value = NULL; + + pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id); + + 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, PCMK__XA_GRANTED, pcmk__str_none) + && (ticket == NULL || ticket->granted == FALSE) + && crm_is_true(value)) { + + char *now = pcmk__ttoa(time(NULL)); + + crm_xml_add(*ticket_state_xml, PCMK_XA_LAST_GRANTED, now); + free(now); + } + } +} + +int +pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state) +{ + int rc = pcmk_rc_ok; + xmlNode *xml_search = NULL; + char *xpath = NULL; + + CRM_ASSERT(cib!= NULL && state != NULL); + *state = NULL; + + if (ticket_id != NULL) { + xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS + "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]", + ticket_id); + } else { + xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS); + } + + rc = cib->cmds->query(cib, xpath, &xml_search, + cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); + + if (rc == pcmk_rc_ok) { + crm_log_xml_debug(xml_search, "Match"); + + if (xml_search->children != NULL && ticket_id != NULL) { + rc = pcmk_rc_duplicate_id; + } + } + + free(xpath); + + *state = xml_search; + return rc; +} + +int +pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id) +{ + int rc = pcmk_rc_ok; + xmlNode *result = NULL; + const char *xpath_base = NULL; + char *xpath = NULL; + + CRM_ASSERT(out != NULL && cib != NULL); + + xpath_base = pcmk_cib_xpath_for(PCMK_XE_CONSTRAINTS); + CRM_ASSERT(xpath_base != NULL); + + if (ticket_id != NULL) { + xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET "[@" PCMK_XA_TICKET "=\"%s\"]", + xpath_base, ticket_id); + } else { + xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET, xpath_base); + } + + rc = cib->cmds->query(cib, (const char *) xpath, &result, + cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); + + if (result != NULL) { + out->message(out, "ticket-constraints", result); + free_xml(result); + } + + free(xpath); + return rc; +} + +int +pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + cib_t *cib = NULL; + + rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_constraints(out, cib, ticket_id); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} + +static int +delete_single_ticket(xmlNode *child, void *userdata) +{ + int rc = pcmk_rc_ok; + cib_t *cib = (cib_t *) userdata; + + rc = cib->cmds->remove(cib, PCMK_XE_STATUS, child, cib_sync_call); + rc = pcmk_legacy2rc(rc); + + return rc; +} + +int +pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, + const char *ticket_id, bool force) +{ + int rc = pcmk_rc_ok; + xmlNode *state = NULL; + + CRM_ASSERT(cib != NULL && scheduler != NULL); + + if (ticket_id == NULL) { + return EINVAL; + } + + if (!force) { + pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id); + + if (ticket == NULL) { + return ENXIO; + } + + if (ticket->granted) { + return EACCES; + } + } + + rc = pcmk__get_ticket_state(cib, ticket_id, &state); + + if (rc == pcmk_rc_duplicate_id) { + out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", + ticket_id); + + } else if (rc == ENXIO) { + return pcmk_rc_ok; + + } else if (rc != pcmk_rc_ok) { + return rc; + } + + crm_log_xml_debug(state, "Delete"); + + if (rc == pcmk_rc_duplicate_id) { + rc = pcmk__xe_foreach_child(state, NULL, delete_single_ticket, cib); + } else { + rc = delete_single_ticket(state, cib); + } + + if (rc == pcmk_rc_ok) { + out->info(out, "Cleaned up %s", ticket_id); + } + + free_xml(state); + return rc; +} + +int +pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + cib_t *cib = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_delete(out, cib, scheduler, ticket_id, force); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + pe_free_working_set(scheduler); + return rc; +} + +int +pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler, + const char *ticket_id, const char *attr_name, + const char *attr_default) +{ + int rc = pcmk_rc_ok; + const char *attr_value = NULL; + pcmk_ticket_t *ticket = NULL; + + CRM_ASSERT(out != NULL && scheduler != NULL); + + if (ticket_id == NULL || attr_name == NULL) { + return EINVAL; + } + + ticket = g_hash_table_lookup(scheduler->tickets, ticket_id); + + if (ticket != NULL) { + attr_value = g_hash_table_lookup(ticket->state, attr_name); + } + + if (attr_value != NULL) { + out->message(out, "ticket-attribute", ticket_id, attr_name, attr_value); + } else if (attr_default != NULL) { + out->message(out, "ticket-attribute", ticket_id, attr_name, attr_default); + } else { + rc = ENXIO; + } + + return rc; +} + +int +pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id, + const char *attr_name, const char *attr_default) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_get_attr(out, scheduler, ticket_id, attr_name, attr_default); + +done: + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + pe_free_working_set(scheduler); + return rc; +} + +int +pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler, + const char *ticket_id, bool details, bool raw) +{ + int rc = pcmk_rc_ok; + + CRM_ASSERT(out != NULL && scheduler != NULL); + + if (ticket_id != NULL) { + GHashTable *tickets = NULL; + pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id); + + if (ticket == NULL) { + return ENXIO; + } + + /* The ticket-list message expects a GHashTable, so we'll construct + * one with just this single item. + */ + tickets = pcmk__strkey_table(free, NULL); + g_hash_table_insert(tickets, strdup(ticket->id), ticket); + out->message(out, "ticket-list", tickets, false, raw, details); + g_hash_table_destroy(tickets); + + } else { + out->message(out, "ticket-list", scheduler->tickets, false, raw, details); + } + + return rc; +} + +int +pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + pe__register_messages(out); + + /* XML output (which is the only format supported by public API functions + * due to the use of pcmk__xml_output_new above) always prints all details, + * so just pass false for the last two arguments. + */ + rc = pcmk__ticket_info(out, scheduler, ticket_id, false, false); + +done: + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + pe_free_working_set(scheduler); + return rc; +} + +int +pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, + const char *ticket_id, GList *attr_delete, bool force) +{ + xmlNode *ticket_state_xml = NULL; + xmlNode *xml_top = NULL; + int rc = pcmk_rc_ok; + + CRM_ASSERT(out != NULL && cib != NULL && scheduler != NULL); + + if (ticket_id == NULL) { + return EINVAL; + } + + /* Nothing to do */ + if (attr_delete == NULL) { + return pcmk_rc_ok; + } + + rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top); + + if (rc == pcmk_rc_duplicate_id) { + out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id); + } else if (rc != pcmk_rc_ok) { + free_xml(ticket_state_xml); + return rc; + } + + for (GList *list_iter = attr_delete; list_iter != NULL; list_iter = list_iter->next) { + const char *key = list_iter->data; + + if (!force && pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)) { + free_xml(ticket_state_xml); + return EACCES; + } + + pcmk__xe_remove_attr(ticket_state_xml, key); + } + + crm_log_xml_debug(xml_top, "Replace"); + rc = cib->cmds->replace(cib, PCMK_XE_STATUS, ticket_state_xml, cib_sync_call); + rc = pcmk_legacy2rc(rc); + + free_xml(xml_top); + return rc; +} + +int +pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + cib_t *cib = NULL; + + rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_remove_attr(out, cib, scheduler, ticket_id, attr_delete, force); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + pe_free_working_set(scheduler); + return rc; +} + +int +pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, + const char *ticket_id, GHashTable *attr_set, bool force) +{ + xmlNode *ticket_state_xml = NULL; + xmlNode *xml_top = NULL; + int rc = pcmk_rc_ok; + + CRM_ASSERT(out != NULL && cib != NULL && scheduler != NULL); + + if (ticket_id == NULL) { + return EINVAL; + } + + /* Nothing to do */ + if (attr_set == NULL || g_hash_table_size(attr_set) == 0) { + return pcmk_rc_ok; + } + + rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top); + + if (rc == pcmk_rc_duplicate_id) { + out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id); + } else if (rc != pcmk_rc_ok) { + free_xml(ticket_state_xml); + return rc; + } + + if (!force && g_hash_table_lookup(attr_set, PCMK__XA_GRANTED)) { + free_xml(ticket_state_xml); + return EACCES; + } + + add_attribute_xml(scheduler, ticket_id, attr_set, &ticket_state_xml); + + crm_log_xml_debug(xml_top, "Update"); + rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call); + rc = pcmk_legacy2rc(rc); + + free_xml(xml_top); + return rc; +} + +int +pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set, + bool force) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + cib_t *cib = NULL; + + rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_set_attr(out, cib, scheduler, ticket_id, attr_set, force); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + pe_free_working_set(scheduler); + return rc; +} + +int +pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id) +{ + xmlNode *state_xml = NULL; + int rc = pcmk_rc_ok; + + CRM_ASSERT(out != NULL && cib != NULL); + + rc = pcmk__get_ticket_state(cib, ticket_id, &state_xml); + + if (rc == pcmk_rc_duplicate_id) { + out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", + ticket_id); + } + + if (state_xml != NULL) { + out->message(out, "ticket-state", state_xml); + free_xml(state_xml); + } + + return rc; +} + +int +pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id) +{ + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + cib_t *cib = NULL; + + rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml); + if (rc != pcmk_rc_ok) { + goto done; + } + + rc = pcmk__ticket_state(out, cib, ticket_id); + +done: + if (cib != NULL) { + cib__clean_up_connection(&cib); + } + + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + return rc; +} diff --git a/lib/pacemaker/pcmk_verify.c b/lib/pacemaker/pcmk_verify.c new file mode 100644 index 0000000..464fff1 --- /dev/null +++ b/lib/pacemaker/pcmk_verify.c @@ -0,0 +1,153 @@ +/* + * Copyright 2023-2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> +#include <crm/cib/internal.h> +#include <crm/common/output.h> +#include <crm/common/results.h> +#include <crm/common/scheduler.h> +#include <pacemaker-internal.h> +#include <pacemaker.h> + +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "libpacemaker_private.h" + +int +pcmk__parse_cib(pcmk__output_t *out, const char *cib_source, xmlNodePtr *cib_object) +{ + // @COMPAT Take an enum for cib_source instead of trying to figure it out? + const char *first = cib_source; + + if (cib_source == NULL) { + crm_info("Reading XML from: live cluster"); + return cib__signon_query(out, NULL, cib_object); + } + + while (isspace(*first)) { + first++; + } + + if (*first == '<') { + *cib_object = pcmk__xml_parse(cib_source); + } else { + *cib_object = pcmk__xml_read(cib_source); + } + + return (*cib_object == NULL)? ENODATA : pcmk_rc_ok; +} + +int +pcmk__verify(pcmk_scheduler_t *scheduler, pcmk__output_t *out, xmlNode *cib_object) +{ + int rc = pcmk_rc_ok; + xmlNode *status = NULL; + xmlNode *cib_object_copy = NULL; + + if (!pcmk__xe_is(cib_object, PCMK_XE_CIB)) { + rc = EBADMSG; + out->err(out, "This tool can only check complete configurations (i.e. those starting with <cib>)."); + goto verify_done; + } + + status = pcmk_find_cib_element(cib_object, PCMK_XE_STATUS); + if (status == NULL) { + pcmk__xe_create(cib_object, PCMK_XE_STATUS); + } + + if (!pcmk__validate_xml(cib_object, NULL, + (xmlRelaxNGValidityErrorFunc) out->err, out)) { + crm_config_error = TRUE; + rc = pcmk_rc_schema_validation; + goto verify_done; + } + + rc = pcmk_update_configured_schema(&cib_object, false); + if (rc != pcmk_rc_ok) { + crm_config_error = TRUE; + 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.", + pcmk__highest_schema_name()); + goto verify_done; + } + + /* 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; + + if (status == NULL) { + // No status available, so do minimal checks + flags |= pcmk_sched_validate_only; + } + cib_object_copy = pcmk__xml_copy(NULL, cib_object); + + /* The scheduler takes ownership of the XML object and potentially + * frees it later. We want the caller of pcmk__verify to retain + * ownership of the passed-in XML object, hence we pass in a copy + * to the scheduler. + */ + pcmk__schedule_actions(cib_object_copy, flags, scheduler); + } + +verify_done: + if (crm_config_error) { + rc = pcmk_rc_schema_validation; + pcmk__config_err("CIB did not pass schema validation"); + } else if (crm_config_warning) { + rc = pcmk_rc_schema_validation; + } + return rc; +} + +int +pcmk_verify(xmlNodePtr *xml, const char *cib_source) +{ + pcmk_scheduler_t *scheduler = NULL; + pcmk__output_t *out = NULL; + int rc = pcmk_rc_ok; + + xmlNode *cib_object = NULL; + + rc = pcmk__xml_output_new(&out, xml); + if (rc != pcmk_rc_ok) { + return rc; + } + + pe__register_messages(out); + pcmk__register_lib_messages(out); + + rc = pcmk__parse_cib(out, cib_source, &cib_object); + if (rc != pcmk_rc_ok) { + out->err(out, "Couldn't parse input"); + goto done; + } + + scheduler = pe_new_working_set(); + if (scheduler == NULL) { + rc = errno; + out->err(out, "Couldn't allocate scheduler data: %s", pcmk_rc_str(rc)); + goto done; + } + + scheduler->priv = out; + rc = pcmk__verify(scheduler, out, cib_object); + +done: + pe_free_working_set(scheduler); + pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); + free_xml(cib_object); + return rc; +} diff --git a/lib/pacemaker/tests/Makefile.am b/lib/pacemaker/tests/Makefile.am new file mode 100644 index 0000000..ad48fdd --- /dev/null +++ b/lib/pacemaker/tests/Makefile.am @@ -0,0 +1,11 @@ +# +# Copyright 2020-2023 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +SUBDIRS = pcmk_resource \ + pcmk_ticket diff --git a/lib/pacemaker/tests/pcmk_resource/Makefile.am b/lib/pacemaker/tests/pcmk_resource/Makefile.am new file mode 100644 index 0000000..bed9a0e --- /dev/null +++ b/lib/pacemaker/tests/pcmk_resource/Makefile.am @@ -0,0 +1,20 @@ +# +# Copyright 2023-2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +LDADD += $(top_builddir)/lib/pacemaker/libpacemaker.la +LDADD += $(top_builddir)/lib/cib/libcib.la + +# Add "_test" to the end of all test program names to simplify .gitignore. + +check_PROGRAMS = pcmk_resource_delete_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c new file mode 100644 index 0000000..e841bfe --- /dev/null +++ b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c @@ -0,0 +1,156 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_resource_delete will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_resource_delete(&xml, "rsc", "primitive"), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("crm_mon.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_input(void **state) +{ + xmlNode *xml = NULL; + + /* There is a primitive resource named "Fencing", so we're just checking + * that it returns EINVAL if both parameters aren't given. + */ + assert_int_equal(pcmk_resource_delete(&xml, "Fencing", NULL), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + assert_int_equal(pcmk_resource_delete(&xml, NULL, "primitive"), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static xmlNode * +find_rsc(const char *rsc) +{ + GString *xpath = g_string_sized_new(1024); + xmlNode *xml_search = NULL; + cib_t *cib = NULL; + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + + pcmk__g_strcat(xpath, + pcmk_cib_xpath_for(PCMK_XE_RESOURCES), + "//*[@" PCMK_XA_ID "=\"", rsc, "\"]", NULL); + + cib->cmds->query(cib, (const char *) xpath->str, &xml_search, + cib_xpath|cib_scope_local); + + g_string_free(xpath, TRUE); + cib__clean_up_connection(&cib); + return xml_search; +} + +static void +incorrect_type(void **state) +{ + xmlNode *xml = NULL; + xmlNode *result = NULL; + + /* cib_process_delete returns pcmk_ok even if given the wrong type so + * we have to do an xpath query of the CIB to make sure it's still + * there. + */ + assert_int_equal(pcmk_resource_delete(&xml, "Fencing", "clone"), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + result = find_rsc("Fencing"); + assert_non_null(result); + + free_xml(result); +} + +static void +correct_type(void **state) +{ + xmlNode *xml = NULL; + xmlNode *result = NULL; + + assert_int_equal(pcmk_resource_delete(&xml, "Fencing", "primitive"), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + result = find_rsc("Fencing"); + assert_null(result); + + free_xml(result); +} + +static void +unknown_resource(void **state) +{ + xmlNode *xml = NULL; + + /* cib_process_delete returns pcmk_ok even if asked to delete something + * that doesn't exist. + */ + assert_int_equal(pcmk_resource_delete(&xml, "no_such_resource", "primitive"), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_input, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(incorrect_type, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(correct_type, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_resource, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/Makefile.am b/lib/pacemaker/tests/pcmk_ticket/Makefile.am new file mode 100644 index 0000000..d9660a0 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/Makefile.am @@ -0,0 +1,27 @@ +# +# Copyright 2024 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# + +include $(top_srcdir)/mk/tap.mk +include $(top_srcdir)/mk/unittest.mk + +LDADD += $(top_builddir)/lib/pacemaker/libpacemaker.la +LDADD += $(top_builddir)/lib/cib/libcib.la + +# Add "_test" to the end of all test program names to simplify .gitignore. + +check_PROGRAMS = pcmk__get_ticket_state_test \ + pcmk_ticket_constraints_test \ + pcmk_ticket_delete_test \ + pcmk_ticket_get_attr_test \ + pcmk_ticket_info_test \ + pcmk_ticket_remove_attr_test \ + pcmk_ticket_set_attr_test \ + pcmk_ticket_state_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk__get_ticket_state_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk__get_ticket_state_test.c new file mode 100644 index 0000000..3f363c2 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk__get_ticket_state_test.c @@ -0,0 +1,178 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +#include <pacemaker-internal.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + cib_t *cib = cib_new(); + + /* Without any special setup, cib_new() here will use the native CIB which + * means IPC calls. But there's nothing listening for those calls, so + * signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk__get_ticket_state(cib, "ticketA", &xml), ENOTCONN); + cib__clean_up_connection(&cib); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + pcmk__assert_asserts(pcmk__get_ticket_state(NULL, "ticketA", &xml)); + pcmk__assert_asserts(pcmk__get_ticket_state(cib, "ticketA", NULL)); + + cib__clean_up_connection(&cib); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + assert_int_equal(pcmk__get_ticket_state(cib, "XYZ", &xml), ENXIO); + + free_xml(xml); + cib__clean_up_connection(&cib); +} + +static void +ticket_exists(void **state) +{ + xmlNode *xml = NULL; + xmlXPathObject *xpath_obj = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + assert_int_equal(pcmk__get_ticket_state(cib, "ticketA", &xml), pcmk_rc_ok); + + /* Verify that the XML result has only one <ticket>, and that its ID is + * what we asked for. + */ + xpath_obj = xpath_search(xml, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]"); + assert_int_equal(numXpathResults(xpath_obj), 1); + + freeXpathObject(xpath_obj); + free_xml(xml); + cib__clean_up_connection(&cib); +} + +static void +multiple_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlNode *ticket_node = NULL; + xmlXPathObject *xpath_obj = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + assert_int_equal(pcmk__get_ticket_state(cib, NULL, &xml), pcmk_rc_ok); + + /* Verify that the XML result has four <ticket> elements, and that their + * IDs are as expected. + */ + xpath_obj = xpath_search(xml, "//" PCMK__XE_TICKET_STATE); + + assert_int_equal(numXpathResults(xpath_obj), 4); + + ticket_node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketA"); + + ticket_node = getXpathResult(xpath_obj, 1); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketB"); + + ticket_node = getXpathResult(xpath_obj, 2); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketC"); + + ticket_node = getXpathResult(xpath_obj, 3); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketC"); + + freeXpathObject(xpath_obj); + free_xml(xml); + cib__clean_up_connection(&cib); +} + +static void +duplicate_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlXPathObject *xpath_obj = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + assert_int_equal(pcmk__get_ticket_state(cib, "ticketC", &xml), pcmk_rc_duplicate_id); + + /* Verify that the XML result has two <ticket> elements, and that their + * IDs are as expected. + */ + xpath_obj = xpath_search(xml, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketC\"]"); + + assert_int_equal(numXpathResults(xpath_obj), 2); + freeXpathObject(xpath_obj); + free_xml(xml); + cib__clean_up_connection(&cib); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test_setup_teardown(cib_not_connected, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(ticket_exists, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(multiple_tickets, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(duplicate_tickets, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_constraints_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_constraints_test.c new file mode 100644 index 0000000..08fe0ea --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_constraints_test.c @@ -0,0 +1,130 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_constraints will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_constraints(&xml, NULL), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +invalid_argument(void **state) +{ + assert_int_equal(pcmk_ticket_constraints(NULL, "ticketA"), EINVAL); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_constraints(&xml, "XYZ"), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +ticket_exists(void **state) +{ + xmlNode *xml = NULL; + xmlXPathObject *xpath_obj = NULL; + + assert_int_equal(pcmk_ticket_constraints(&xml, "ticketA"), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has only one <ticket>, and that its ID is + * what we asked for. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketA\"]"); + + assert_int_equal(numXpathResults(xpath_obj), 1); + freeXpathObject(xpath_obj); + free_xml(xml); +} + +static void +multiple_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlNode *ticket_node = NULL; + xmlXPathObject *xpath_obj = NULL; + + assert_int_equal(pcmk_ticket_constraints(&xml, NULL), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has two <ticket> elements, and that their + * IDs are as expected. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" PCMK_XE_TICKET); + + assert_int_equal(numXpathResults(xpath_obj), 2); + + ticket_node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketA"); + + ticket_node = getXpathResult(xpath_obj, 1); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketB"); + + freeXpathObject(xpath_obj); + free_xml(xml); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(invalid_argument, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(ticket_exists, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(multiple_tickets, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c new file mode 100644 index 0000000..8810039 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c @@ -0,0 +1,170 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_delete will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_delete(&xml, "ticketA", false), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_delete(NULL, "ticketA", false), EINVAL); + + assert_int_equal(pcmk_ticket_delete(&xml, NULL, false), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_delete(&xml, "XYZ", false), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + assert_int_equal(pcmk_ticket_delete(&xml, "XYZ", true), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +ticket_granted(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_delete(&xml, "ticketB", false), EACCES); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +ticket_exists(void **state) +{ + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + assert_int_equal(pcmk_ticket_delete(&xml, "ticketA", false), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify there's no <ticket_state id="ticketA"> */ + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + free_xml(xml); + cib__clean_up_connection(&cib); +} + +static void +force_delete_ticket(void **state) +{ + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + assert_int_equal(pcmk_ticket_delete(&xml, "ticketB", true), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify there's no <ticket_state id="ticketB"> */ + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + free_xml(xml); + cib__clean_up_connection(&cib); +} + +static void +duplicate_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + assert_int_equal(pcmk_ticket_delete(&xml, "ticketC", true), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify there's no <ticket_state id="ticketC"> */ + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketC\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_null(xml_search); + + free_xml(xml); + cib__clean_up_connection(&cib); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(ticket_granted, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(ticket_exists, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(force_delete_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(duplicate_tickets, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_get_attr_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_get_attr_test.c new file mode 100644 index 0000000..0d86127 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_get_attr_test.c @@ -0,0 +1,150 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(NULL, "ticketA", "XYZ", NULL), EINVAL); + + assert_int_equal(pcmk_ticket_get_attr(&xml, NULL, "attrA", NULL), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(&xml, "ticketA", NULL, NULL), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + + /* Both an unknown ticket and an unknown attribute on a known ticket + * return ENXIO so we can't really differentiate between the two here. + * Still, we'd better test both. + */ + assert_int_equal(pcmk_ticket_get_attr(&xml, "XYZ", "attrA", NULL), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(&xml, "ticketA", "XYZ", NULL), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +verify_results(xmlNode *xml, const char *ticket_id, const char *attr_name, + const char *attr_value) +{ + xmlNode *node = NULL; + xmlXPathObject *xpath_obj = NULL; + + /* Verify that the XML result has only one <ticket>, and that its ID is + * what we asked for. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" PCMK_XE_TICKET); + assert_int_equal(numXpathResults(xpath_obj), 1); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_ID), ticket_id); + freeXpathObject(xpath_obj); + + /* Verify that it has an <attribute> child whose name and value are what + * we expect. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" PCMK_XE_TICKET + "/" PCMK_XE_ATTRIBUTE); + assert_int_equal(numXpathResults(xpath_obj), 1); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_NAME), attr_name); + assert_string_equal(crm_element_value(node, PCMK_XA_VALUE), attr_value); + + freeXpathObject(xpath_obj); +} + +static void +attribute_exists(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(&xml, "ticketA", "owner", NULL), pcmk_rc_ok); + pcmk__assert_validates(xml); + + verify_results(xml, "ticketA", "owner", "1"); + + free_xml(xml); +} + +static void +default_no_ticket(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(&xml, "ticketX", "ABC", "DEFAULT"), pcmk_rc_ok); + pcmk__assert_validates(xml); + + verify_results(xml, "ticketX", "ABC", "DEFAULT"); + + free_xml(xml); +} + +static void +default_no_attribute(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_get_attr(&xml, "ticketA", "ABC", "DEFAULT"), pcmk_rc_ok); + pcmk__assert_validates(xml); + + verify_results(xml, "ticketA", "ABC", "DEFAULT"); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(attribute_exists, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(default_no_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(default_no_attribute, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_info_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_info_test.c new file mode 100644 index 0000000..3ecb050 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_info_test.c @@ -0,0 +1,138 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + assert_int_equal(pcmk_ticket_info(NULL, "ticketA"), EINVAL); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_info(&xml, "XYZ"), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +all_tickets(void **state) +{ + xmlNode *node = NULL; + xmlXPathObject *xpath_obj = NULL; + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_info(&xml, NULL), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has three <ticket> elements, with the attributes + * we expect. The input has four tickets, but when they are loaded into the + * scheduler's hash table, the duplicate IDs will collide leaving us with + * three. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" PCMK_XE_TICKET); + assert_int_equal(numXpathResults(xpath_obj), 3); + freeXpathObject(xpath_obj); + + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketA\"]"); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_STATUS), PCMK_VALUE_REVOKED); + assert_string_equal(crm_element_value(node, PCMK__XA_GRANTED), "false"); + assert_string_equal(crm_element_value(node, PCMK_XA_STANDBY), PCMK_VALUE_FALSE); + assert_string_equal(crm_element_value(node, "owner"), "1"); + freeXpathObject(xpath_obj); + + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketB\"]"); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_STATUS), PCMK_VALUE_GRANTED); + assert_string_equal(crm_element_value(node, PCMK__XA_GRANTED), "true"); + assert_string_equal(crm_element_value(node, PCMK_XA_STANDBY), PCMK_VALUE_FALSE); + assert_null(crm_element_value(node, "owner")); + freeXpathObject(xpath_obj); + + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketC\"]"); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_STATUS), PCMK_VALUE_GRANTED); + assert_string_equal(crm_element_value(node, PCMK__XA_GRANTED), "true"); + assert_string_equal(crm_element_value(node, PCMK_XA_STANDBY), PCMK_VALUE_FALSE); + assert_null(crm_element_value(node, "owner")); + + freeXpathObject(xpath_obj); + free_xml(xml); +} + +static void +single_ticket(void **state) +{ + xmlNode *node = NULL; + xmlXPathObject *xpath_obj = NULL; + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_info(&xml, "ticketA"), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has only one <ticket>, with the attributes + * we expect. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketA\"]"); + assert_int_equal(numXpathResults(xpath_obj), 1); + + node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(node, PCMK_XA_STATUS), PCMK_VALUE_REVOKED); + assert_string_equal(crm_element_value(node, PCMK__XA_GRANTED), "false"); + assert_string_equal(crm_element_value(node, PCMK_XA_STANDBY), PCMK_VALUE_FALSE); + assert_string_equal(crm_element_value(node, "owner"), "1"); + + freeXpathObject(xpath_obj); + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(all_tickets, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(single_ticket, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_remove_attr_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_remove_attr_test.c new file mode 100644 index 0000000..c024f9a --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_remove_attr_test.c @@ -0,0 +1,231 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_remove_attr will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_remove_attr(&xml, NULL, NULL, false), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_remove_attr(NULL, "ticketA", NULL, false), EINVAL); + + assert_int_equal(pcmk_ticket_remove_attr(&xml, NULL, NULL, false), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +no_attrs(void **state) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + /* Deleting no attributes on a ticket that doesn't exist is a no-op */ + assert_int_equal(pcmk_ticket_remove_attr(&xml, "XYZ", NULL, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + /* Deleting no attributes on a ticket that exists is also a no-op */ + assert_int_equal(pcmk_ticket_remove_attr(&xml, "ticketA", NULL, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_string_equal("1", crm_element_value(xml_search, "owner")); + free_xml(xml_search); + + /* Another way of specifying no attributes */ + assert_int_equal(pcmk_ticket_remove_attr(&xml, "XYZ", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + g_list_free(attrs); + cib__clean_up_connection(&cib); +} + +static void +remove_missing_attrs(void **state) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + attrs = g_list_append(attrs, strdup("XYZ")); + + /* Deleting an attribute that doesn't exist is a no-op */ + assert_int_equal(pcmk_ticket_remove_attr(&xml, "ticketA", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("1", crm_element_value(xml_search, "owner")); + assert_null(crm_element_value(xml_search, "XYZ")); + + free_xml(xml_search); + g_list_free_full(attrs, free); + cib__clean_up_connection(&cib); +} + +static void +remove_existing_attr(void **state) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + attrs = g_list_append(attrs, strdup("owner")); + + assert_int_equal(pcmk_ticket_remove_attr(&xml, "ticketA", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_null(crm_element_value(xml_search, "owner")); + + free_xml(xml_search); + g_list_free_full(attrs, free); + cib__clean_up_connection(&cib); +} + +static void +remove_granted_without_force(void **state) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + attrs = g_list_append(attrs, strdup(PCMK__XA_GRANTED)); + + assert_int_equal(pcmk_ticket_remove_attr(&xml, "ticketB", attrs, false), EACCES); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("true", crm_element_value(xml_search, PCMK__XA_GRANTED)); + + free_xml(xml_search); + g_list_free_full(attrs, free); + cib__clean_up_connection(&cib); +} + +static void +remove_granted_with_force(void **state) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + attrs = g_list_append(attrs, strdup(PCMK__XA_GRANTED)); + + assert_int_equal(pcmk_ticket_remove_attr(&xml, "ticketB", attrs, true), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_null(crm_element_value(xml_search, PCMK__XA_GRANTED)); + + free_xml(xml_search); + g_list_free_full(attrs, free); + cib__clean_up_connection(&cib); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(no_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(remove_missing_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(remove_existing_attr, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(remove_granted_without_force, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(remove_granted_with_force, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c new file mode 100644 index 0000000..100d05c --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c @@ -0,0 +1,281 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_set_attr will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_set_attr(&xml, NULL, NULL, false), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_set_attr(NULL, "ticketA", NULL, false), EINVAL); + + assert_int_equal(pcmk_ticket_set_attr(&xml, NULL, NULL, false), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +unknown_ticket_no_attrs(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + /* Setting no attributes on a ticket that doesn't exist is a no-op */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", NULL, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + /* Another way of specifying no attributes */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +unknown_ticket_with_attrs(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, "attrA", "123"); + pcmk__insert_dup(attrs, "attrB", "456"); + + /* Setting attributes on a ticket that doesn't exist causes the ticket to + * be created with the given attributes + */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("123", crm_element_value(xml_search, "attrA")); + assert_string_equal("456", crm_element_value(xml_search, "attrB")); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +overwrite_existing_attr(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, "owner", "2"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketA", attrs, false), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("2", crm_element_value(xml_search, "owner")); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +not_granted_to_granted_without_force(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "true"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketA", attrs, false), EACCES); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("false", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +not_granted_to_granted_with_force(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "true"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketA", attrs, true), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("true", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_non_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +granted_to_not_granted_without_force(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "false"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketB", attrs, false), EACCES); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("true", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +granted_to_not_granted_with_force(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "false"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketB", attrs, true), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("false", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket_no_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket_with_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(overwrite_existing_attr, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(not_granted_to_granted_without_force, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(not_granted_to_granted_with_force, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(granted_to_not_granted_without_force, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(granted_to_not_granted_with_force, setup_test, teardown_test)) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_state_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_state_test.c new file mode 100644 index 0000000..4ea018b --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_state_test.c @@ -0,0 +1,156 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <crm/cib/internal.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml.h> +#include <pacemaker.h> + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_state will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_state(&xml, "ticketA"), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + assert_int_equal(pcmk_ticket_state(NULL, "ticketA"), EINVAL); +} + +static void +unknown_ticket(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_state(&xml, "XYZ"), ENXIO); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +ticket_exists(void **state) +{ + xmlNode *xml = NULL; + xmlXPathObject *xpath_obj = NULL; + + assert_int_equal(pcmk_ticket_state(&xml, "ticketA"), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has only one <ticket>, and that its ID is + * what we asked for. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketA\"]"); + + assert_int_equal(numXpathResults(xpath_obj), 1); + freeXpathObject(xpath_obj); + free_xml(xml); +} + +static void +multiple_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlNode *ticket_node = NULL; + xmlXPathObject *xpath_obj = NULL; + + assert_int_equal(pcmk_ticket_state(&xml, NULL), pcmk_rc_ok); + pcmk__assert_validates(xml); + + /* Verify that the XML result has four <ticket> elements, and that their + * IDs are as expected. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" PCMK_XE_TICKET); + + assert_int_equal(numXpathResults(xpath_obj), 4); + + ticket_node = getXpathResult(xpath_obj, 0); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketA"); + + ticket_node = getXpathResult(xpath_obj, 1); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketB"); + + ticket_node = getXpathResult(xpath_obj, 2); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketC"); + + ticket_node = getXpathResult(xpath_obj, 3); + assert_string_equal(crm_element_value(ticket_node, PCMK_XA_ID), "ticketC"); + + freeXpathObject(xpath_obj); + free_xml(xml); +} + +static void +duplicate_tickets(void **state) +{ + xmlNode *xml = NULL; + xmlXPathObject *xpath_obj = NULL; + + assert_int_equal(pcmk_ticket_state(&xml, "ticketC"), pcmk_rc_duplicate_id); + + /* Verify that the XML result has two <ticket> elements, and that their + * IDs are as expected. + */ + xpath_obj = xpath_search(xml, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS + "/" PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"ticketC\"]"); + + assert_int_equal(numXpathResults(xpath_obj), 2); + freeXpathObject(xpath_obj); + free_xml(xml); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(ticket_exists, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(multiple_tickets, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(duplicate_tickets, setup_test, teardown_test)) diff --git a/lib/pengine/Makefile.am b/lib/pengine/Makefile.am index 9ffc745..2bb50da 100644 --- a/lib/pengine/Makefile.am +++ b/lib/pengine/Makefile.am @@ -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. # @@ -17,12 +17,11 @@ SUBDIRS = . tests ## libraries lib_LTLIBRARIES = libpe_rules.la \ libpe_status.la -check_LTLIBRARIES = libpe_rules_test.la \ - libpe_status_test.la +check_LTLIBRARIES = libpe_status_test.la noinst_HEADERS = pe_status_private.h -libpe_rules_la_LDFLAGS = -version-info 30:1:4 +libpe_rules_la_LDFLAGS = -version-info 30:2:4 libpe_rules_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libpe_rules_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) @@ -34,7 +33,7 @@ libpe_rules_la_SOURCES = common.c libpe_rules_la_SOURCES += rules.c libpe_rules_la_SOURCES += rules_alerts.c -libpe_status_la_LDFLAGS = -version-info 35:0:7 +libpe_status_la_LDFLAGS = -version-info 35:1:7 libpe_status_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libpe_status_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) @@ -63,24 +62,12 @@ libpe_status_la_SOURCES += unpack.c libpe_status_la_SOURCES += utils.c # -# libpe_rules_test and libpe_status_test are only used with unit tests, so we can +# libpe_status_test is only used with unit tests, so we can # mock system calls. See lib/common/mock.c for details. # include $(top_srcdir)/mk/tap.mk -libpe_rules_test_la_SOURCES = $(libpe_rules_la_SOURCES) -libpe_rules_test_la_LDFLAGS = $(libpe_rules_la_LDFLAGS) \ - -rpath $(libdir) \ - $(LDFLAGS_WRAP) -# See comments on libcrmcommon_test_la in lib/common/Makefile.am regarding these flags. -libpe_rules_test_la_CFLAGS = $(libpe_rules_la_CFLAGS) \ - -DPCMK__UNIT_TESTING \ - -fno-builtin -fno-inline -libpe_rules_test_la_LIBADD = $(top_builddir)/lib/common/libcrmcommon_test.la \ - -lcmocka \ - -lm - libpe_status_test_la_SOURCES = $(libpe_status_la_SOURCES) libpe_status_test_la_LDFLAGS = $(libpe_status_la_LDFLAGS) \ -rpath $(libdir) \ diff --git a/lib/pengine/bundle.c b/lib/pengine/bundle.c index fd859d5..8967f30 100644 --- a/lib/pengine/bundle.c +++ b/lib/pengine/bundle.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. * @@ -15,7 +15,7 @@ #include <crm/pengine/rules.h> #include <crm/pengine/status.h> #include <crm/pengine/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/output.h> #include <crm/common/xml_internal.h> #include <pe_status_private.h> @@ -71,17 +71,15 @@ typedef struct pe__bundle_variant_data_s { pcmk_resource_t *child; - GList *replicas; // pe__bundle_replica_t * + GList *replicas; // pcmk__bundle_replica_t * GList *ports; // pe__bundle_port_t * GList *mounts; // pe__bundle_mount_t * enum pe__container_agent agent_type; } pe__bundle_variant_data_t; -#define get_bundle_variant_data(data, rsc) \ - CRM_ASSERT(rsc != NULL); \ - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_bundle); \ - CRM_ASSERT(rsc->variant_opaque != NULL); \ +#define get_bundle_variant_data(data, rsc) \ + CRM_ASSERT(pcmk__is_bundle(rsc) && (rsc->variant_opaque != NULL)); \ data = (pe__bundle_variant_data_t *) rsc->variant_opaque; /*! @@ -133,13 +131,13 @@ pe__get_rsc_in_container(const pcmk_resource_t *instance) const pe__bundle_variant_data_t *data = NULL; const pcmk_resource_t *top = pe__const_top_resource(instance, true); - if ((top == NULL) || (top->variant != pcmk_rsc_variant_bundle)) { + if (!pcmk__is_bundle(top)) { return NULL; } get_bundle_variant_data(data, top); for (const GList *iter = data->replicas; iter != NULL; iter = iter->next) { - const pe__bundle_replica_t *replica = iter->data; + const pcmk__bundle_replica_t *replica = iter->data; if (instance == replica->container) { return replica->child; @@ -165,9 +163,9 @@ pe__node_is_bundle_instance(const pcmk_resource_t *bundle, get_bundle_variant_data(bundle_data, bundle); for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) { - pe__bundle_replica_t *replica = iter->data; + pcmk__bundle_replica_t *replica = iter->data; - if (pe__same_node(node, replica->node)) { + if (pcmk__same_node(node, replica->node)) { return true; } } @@ -187,7 +185,7 @@ pcmk_resource_t * pe__first_container(const pcmk_resource_t *bundle) { const pe__bundle_variant_data_t *bundle_data = NULL; - const pe__bundle_replica_t *replica = NULL; + const pcmk__bundle_replica_t *replica = NULL; get_bundle_variant_data(bundle_data, bundle); if (bundle_data->replicas == NULL) { @@ -208,14 +206,14 @@ pe__first_container(const pcmk_resource_t *bundle) */ void pe__foreach_bundle_replica(pcmk_resource_t *bundle, - bool (*fn)(pe__bundle_replica_t *, void *), + bool (*fn)(pcmk__bundle_replica_t *, void *), void *user_data) { const pe__bundle_variant_data_t *bundle_data = NULL; get_bundle_variant_data(bundle_data, bundle); for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) { - if (!fn((pe__bundle_replica_t *) iter->data, user_data)) { + if (!fn((pcmk__bundle_replica_t *) iter->data, user_data)) { break; } } @@ -232,7 +230,7 @@ pe__foreach_bundle_replica(pcmk_resource_t *bundle, */ void pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, - bool (*fn)(const pe__bundle_replica_t *, + bool (*fn)(const pcmk__bundle_replica_t *, void *), void *user_data) { @@ -242,7 +240,7 @@ pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, for (const GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) { - if (!fn((const pe__bundle_replica_t *) iter->data, user_data)) { + if (!fn((const pcmk__bundle_replica_t *) iter->data, user_data)) { break; } } @@ -276,7 +274,7 @@ next_ip(const char *last_ip) } static void -allocate_ip(pe__bundle_variant_data_t *data, pe__bundle_replica_t *replica, +allocate_ip(pe__bundle_variant_data_t *data, pcmk__bundle_replica_t *replica, GString *buffer) { if(data->ip_range_start == NULL) { @@ -318,12 +316,12 @@ allocate_ip(pe__bundle_variant_data_t *data, pe__bundle_replica_t *replica, static xmlNode * create_resource(const char *name, const char *provider, const char *kind) { - xmlNode *rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); + xmlNode *rsc = pcmk__xe_create(NULL, PCMK_XE_PRIMITIVE); - crm_xml_add(rsc, XML_ATTR_ID, name); - crm_xml_add(rsc, XML_AGENT_ATTR_CLASS, PCMK_RESOURCE_CLASS_OCF); - crm_xml_add(rsc, XML_AGENT_ATTR_PROVIDER, provider); - crm_xml_add(rsc, XML_ATTR_TYPE, kind); + crm_xml_add(rsc, PCMK_XA_ID, name); + crm_xml_add(rsc, PCMK_XA_CLASS, PCMK_RESOURCE_CLASS_OCF); + crm_xml_add(rsc, PCMK_XA_PROVIDER, provider); + crm_xml_add(rsc, PCMK_XA_TYPE, kind); return rsc; } @@ -348,10 +346,12 @@ valid_network(pe__bundle_variant_data_t *data) } if(data->control_port) { if(data->nreplicas_per_host > 1) { - pe_err("Specifying the 'control-port' for %s requires 'replicas-per-host=1'", data->prefix); + pcmk__config_err("Specifying the '" PCMK_XA_CONTROL_PORT "' for %s " + "requires '" PCMK_XA_REPLICAS_PER_HOST "=1'", + data->prefix); data->nreplicas_per_host = 1; // @TODO to be sure: - // pe__clear_resource_flags(rsc, pcmk_rsc_unique); + // pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique); } return TRUE; } @@ -360,7 +360,7 @@ valid_network(pe__bundle_variant_data_t *data) static int create_ip_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, - pe__bundle_replica_t *replica) + pcmk__bundle_replica_t *replica) { if(data->ip_range_start) { char *id = NULL; @@ -372,7 +372,7 @@ create_ip_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, xml_ip = create_resource(id, "heartbeat", "IPaddr2"); free(id); - xml_obj = create_xml_node(xml_ip, XML_TAG_ATTR_SETS); + xml_obj = pcmk__xe_create(xml_ip, PCMK_XE_INSTANCE_ATTRIBUTES); crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset); @@ -389,9 +389,9 @@ create_ip_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, crm_create_nvpair_xml(xml_obj, NULL, "cidr_netmask", "32"); } - xml_obj = create_xml_node(xml_ip, "operations"); - crm_create_op_xml(xml_obj, ID(xml_ip), PCMK_ACTION_MONITOR, "60s", - NULL); + xml_obj = pcmk__xe_create(xml_ip, PCMK_XE_OPERATIONS); + crm_create_op_xml(xml_obj, pcmk__xe_id(xml_ip), PCMK_ACTION_MONITOR, + "60s", NULL); // TODO: Other ops? Timeouts and intervals from underlying resource? @@ -421,7 +421,7 @@ container_agent_str(enum pe__container_agent t) static int create_container_resource(pcmk_resource_t *parent, const pe__bundle_variant_data_t *data, - pe__bundle_replica_t *replica) + pcmk__bundle_replica_t *replica) { char *id = NULL; xmlNode *xml_container = NULL; @@ -460,13 +460,13 @@ create_container_resource(pcmk_resource_t *parent, xml_container = create_resource(id, "heartbeat", agent_str); free(id); - xml_obj = create_xml_node(xml_container, XML_TAG_ATTR_SETS); + xml_obj = pcmk__xe_create(xml_container, PCMK_XE_INSTANCE_ATTRIBUTES); crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset); crm_create_nvpair_xml(xml_obj, NULL, "image", data->image); - crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", XML_BOOLEAN_TRUE); - crm_create_nvpair_xml(xml_obj, NULL, "force_kill", XML_BOOLEAN_FALSE); - crm_create_nvpair_xml(xml_obj, NULL, "reuse", XML_BOOLEAN_FALSE); + crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", PCMK_VALUE_TRUE); + crm_create_nvpair_xml(xml_obj, NULL, "force_kill", PCMK_VALUE_FALSE); + crm_create_nvpair_xml(xml_obj, NULL, "reuse", PCMK_VALUE_FALSE); if (data->agent_type == PE__CONTAINER_AGENT_DOCKER) { g_string_append(buffer, " --restart=no"); @@ -544,8 +544,8 @@ create_container_resource(pcmk_resource_t *parent, " -p ", replica->ipaddr, ":", port->source, ":", port->target, NULL); - } else if (!pcmk__str_eq(data->container_network, "host", - pcmk__str_none)) { + } else if (!pcmk__str_eq(data->container_network, + PCMK_VALUE_HOST, pcmk__str_none)) { // No need to do port mapping if net == host pcmk__g_strcat(buffer, " -p ", port->source, ":", port->target, @@ -641,16 +641,16 @@ create_container_resource(pcmk_resource_t *parent, crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true"); } - xml_obj = create_xml_node(xml_container, "operations"); - crm_create_op_xml(xml_obj, ID(xml_container), PCMK_ACTION_MONITOR, "60s", - NULL); + xml_obj = pcmk__xe_create(xml_container, PCMK_XE_OPERATIONS); + crm_create_op_xml(xml_obj, pcmk__xe_id(xml_container), PCMK_ACTION_MONITOR, + "60s", NULL); // TODO: Other ops? Timeouts and intervals from underlying resource? if (pe__unpack_resource(xml_container, &replica->container, parent, parent->cluster) != pcmk_rc_ok) { return pcmk_rc_unpack_error; } - pe__set_resource_flags(replica->container, pcmk_rsc_replica_container); + pcmk__set_rsc_flags(replica->container, pcmk_rsc_replica_container); parent->children = g_list_append(parent->children, replica->container); return pcmk_rc_ok; @@ -668,7 +668,7 @@ disallow_node(pcmk_resource_t *rsc, const char *uname) gpointer match = g_hash_table_lookup(rsc->allowed_nodes, uname); if (match) { - ((pcmk_node_t *) match)->weight = -INFINITY; + ((pcmk_node_t *) match)->weight = -PCMK_SCORE_INFINITY; ((pcmk_node_t *) match)->rsc_discover_mode = pcmk_probe_never; } if (rsc->children) { @@ -678,7 +678,7 @@ disallow_node(pcmk_resource_t *rsc, const char *uname) static int create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, - pe__bundle_replica_t *replica) + pcmk__bundle_replica_t *replica) { if (replica->child && valid_network(data)) { GHashTableIter gIter; @@ -726,18 +726,18 @@ create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, */ free(id); id = NULL; - uname = ID(xml_remote); + uname = pcmk__xe_id(xml_remote); /* Ensure a node has been created for the guest (it may have already * been, if it has a permanent node attribute), and ensure its weight is * -INFINITY so no other resources can run on it. */ - node = pe_find_node(parent->cluster->nodes, uname); + node = pcmk_find_node(parent->cluster, uname); if (node == NULL) { - node = pe_create_node(uname, uname, "remote", "-INFINITY", - parent->cluster); + node = pe_create_node(uname, uname, PCMK_VALUE_REMOTE, + PCMK_VALUE_MINUS_INFINITY, parent->cluster); } else { - node->weight = -INFINITY; + node->weight = -PCMK_SCORE_INFINITY; } node->rsc_discover_mode = pcmk_probe_never; @@ -776,7 +776,7 @@ create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, { pcmk_node_t *copy = pe__copy_node(replica->node); - copy->weight = -INFINITY; + copy->weight = -PCMK_SCORE_INFINITY; g_hash_table_insert(replica->child->parent->allowed_nodes, (gpointer) replica->node->details->id, copy); } @@ -787,22 +787,22 @@ create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, g_hash_table_iter_init(&gIter, replica->remote->allowed_nodes); while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) { - if (pe__is_guest_or_remote_node(node)) { + if (pcmk__is_pacemaker_remote_node(node)) { /* Remote resources can only run on 'normal' cluster node */ - node->weight = -INFINITY; + node->weight = -PCMK_SCORE_INFINITY; } } replica->node->details->remote_rsc = replica->remote; - // Ensure pe__is_guest_node() functions correctly immediately + // Ensure pcmk__is_guest_or_bundle_node() functions correctly replica->remote->container = replica->container; /* A bundle's #kind is closer to "container" (guest node) than the * "remote" set by pe_create_node(). */ - g_hash_table_insert(replica->node->details->attrs, - strdup(CRM_ATTR_KIND), strdup("container")); + pcmk__insert_dup(replica->node->details->attrs, + CRM_ATTR_KIND, "container"); /* One effect of this is that setup_container() will add * replica->remote to replica->container's fillers, which will make @@ -819,8 +819,9 @@ create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, } static int -create_replica_resources(pcmk_resource_t *parent, pe__bundle_variant_data_t *data, - pe__bundle_replica_t *replica) +create_replica_resources(pcmk_resource_t *parent, + pe__bundle_variant_data_t *data, + pcmk__bundle_replica_t *replica) { int rc = pcmk_rc_ok; @@ -840,7 +841,7 @@ create_replica_resources(pcmk_resource_t *parent, pe__bundle_variant_data_t *dat } if ((replica->child != NULL) && (replica->ipaddr != NULL)) { - add_hash_param(replica->child->meta, "external-ip", replica->ipaddr); + pcmk__insert_meta(replica->child, "external-ip", replica->ipaddr); } if (replica->remote != NULL) { @@ -852,8 +853,7 @@ create_replica_resources(pcmk_resource_t *parent, pe__bundle_variant_data_t *dat * containers with pacemaker-remoted inside in order to start * services inside those containers. */ - pe__set_resource_flags(replica->remote, - pcmk_rsc_remote_nesting_allowed); + pcmk__set_rsc_flags(replica->remote, pcmk_rsc_remote_nesting_allowed); } return rc; } @@ -862,12 +862,12 @@ static void mount_add(pe__bundle_variant_data_t *bundle_data, const char *source, const char *target, const char *options, uint32_t flags) { - pe__bundle_mount_t *mount = calloc(1, sizeof(pe__bundle_mount_t)); + pe__bundle_mount_t *mount = pcmk__assert_alloc(1, + sizeof(pe__bundle_mount_t)); - CRM_ASSERT(mount != NULL); - mount->source = strdup(source); - mount->target = strdup(target); - pcmk__str_update(&mount->options, options); + mount->source = pcmk__str_copy(source); + mount->target = pcmk__str_copy(target); + mount->options = pcmk__str_copy(options); mount->flags = flags; bundle_data->mounts = g_list_append(bundle_data->mounts, mount); } @@ -889,7 +889,7 @@ port_free(pe__bundle_port_t *port) free(port); } -static pe__bundle_replica_t * +static pcmk__bundle_replica_t * replica_for_remote(pcmk_resource_t *remote) { pcmk_resource_t *top = remote; @@ -906,7 +906,7 @@ replica_for_remote(pcmk_resource_t *remote) get_bundle_variant_data(bundle_data, top); for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; if (replica->remote == remote) { return replica; @@ -928,20 +928,20 @@ pe__bundle_needs_remote_name(pcmk_resource_t *rsc) // Use NULL node since pcmk__bundle_expand() uses that to set value params = pe_rsc_params(rsc, NULL, rsc->cluster); - value = g_hash_table_lookup(params, XML_RSC_ATTR_REMOTE_RA_ADDR); + value = g_hash_table_lookup(params, PCMK_REMOTE_RA_ADDR); return pcmk__str_eq(value, "#uname", pcmk__str_casei) && xml_contains_remote_node(rsc->xml); } const char * -pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, - xmlNode *xml, const char *field) +pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml, + const char *field) { // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside pcmk_node_t *node = NULL; - pe__bundle_replica_t *replica = NULL; + pcmk__bundle_replica_t *replica = NULL; if (!pe__bundle_needs_remote_name(rsc)) { return NULL; @@ -957,7 +957,7 @@ pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, /* If it won't be running anywhere after the * transition, go with where it's running now. */ - node = pe__current_node(replica->container); + node = pcmk__current_node(replica->container); } if(node == NULL) { @@ -966,7 +966,7 @@ pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, } crm_trace("Setting address for bundle connection %s to bundle host %s", - rsc->id, pe__node_name(node)); + rsc->id, pcmk__node_name(node)); if(xml != NULL && field != NULL) { crm_xml_add(xml, field, node->details->uname); } @@ -976,8 +976,8 @@ pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, #define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set) do { \ flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ - "Bundle mount", ID(mount_xml), flags, \ - (flags_to_set), #flags_to_set); \ + "Bundle mount", pcmk__xe_id(mount_xml), \ + flags, (flags_to_set), #flags_to_set); \ } while (0) gboolean @@ -985,26 +985,32 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) { const char *value = NULL; xmlNode *xml_obj = NULL; + const xmlNode *xml_child = NULL; xmlNode *xml_resource = NULL; pe__bundle_variant_data_t *bundle_data = NULL; bool need_log_mount = TRUE; CRM_ASSERT(rsc != NULL); - pe_rsc_trace(rsc, "Processing resource %s...", rsc->id); + pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id); - bundle_data = calloc(1, sizeof(pe__bundle_variant_data_t)); + bundle_data = pcmk__assert_alloc(1, sizeof(pe__bundle_variant_data_t)); rsc->variant_opaque = bundle_data; bundle_data->prefix = strdup(rsc->id); - xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_DOCKER_S); + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_DOCKER, NULL, NULL); if (xml_obj != NULL) { bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER; } else { - xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_RKT_S); + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK__XE_RKT, NULL, NULL); if (xml_obj != NULL) { + pcmk__warn_once(pcmk__wo_rkt, + "Support for " PCMK__XE_RKT " in bundles " + "(such as %s) is deprecated and will be " + "removed in a future release", rsc->id); bundle_data->agent_type = PE__CONTAINER_AGENT_RKT; } else { - xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_PODMAN_S); + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PODMAN, NULL, + NULL); if (xml_obj != NULL) { bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN; } else { @@ -1013,16 +1019,27 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) } } - // Use 0 for default, minimum, and invalid promoted-max - value = crm_element_value(xml_obj, PCMK_META_PROMOTED_MAX); + // Use 0 for default, minimum, and invalid PCMK_XA_PROMOTED_MAX + value = crm_element_value(xml_obj, PCMK_XA_PROMOTED_MAX); if (value == NULL) { // @COMPAT deprecated since 2.0.0 - value = crm_element_value(xml_obj, "masters"); + value = crm_element_value(xml_obj, PCMK__XA_PROMOTED_MAX_LEGACY); + + if (value != NULL) { + pcmk__warn_once(pcmk__wo_bundle_master, + "Support for the " PCMK__XA_PROMOTED_MAX_LEGACY + " attribute (such as in %s) is deprecated and " + "will be removed in a future release. Use " + PCMK_XA_PROMOTED_MAX " instead.", + rsc->id); + } } pcmk__scan_min_int(value, &bundle_data->promoted_max, 0); - // Default replicas to promoted-max if it was specified and 1 otherwise - value = crm_element_value(xml_obj, "replicas"); + /* Default replicas to PCMK_XA_PROMOTED_MAX if it was specified and 1 + * otherwise + */ + value = crm_element_value(xml_obj, PCMK_XA_REPLICAS); if ((value == NULL) && (bundle_data->promoted_max > 0)) { bundle_data->nreplicas = bundle_data->promoted_max; } else { @@ -1034,39 +1051,49 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) * floating IPs only works if the container is started with: * --userland-proxy=false --ip-masq=false */ - value = crm_element_value(xml_obj, "replicas-per-host"); + value = crm_element_value(xml_obj, PCMK_XA_REPLICAS_PER_HOST); pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1); if (bundle_data->nreplicas_per_host == 1) { - pe__clear_resource_flags(rsc, pcmk_rsc_unique); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique); } - bundle_data->container_command = crm_element_value_copy(xml_obj, "run-command"); - bundle_data->launcher_options = crm_element_value_copy(xml_obj, "options"); - bundle_data->image = crm_element_value_copy(xml_obj, "image"); - bundle_data->container_network = crm_element_value_copy(xml_obj, "network"); + bundle_data->container_command = + crm_element_value_copy(xml_obj, PCMK_XA_RUN_COMMAND); + bundle_data->launcher_options = crm_element_value_copy(xml_obj, + PCMK_XA_OPTIONS); + bundle_data->image = crm_element_value_copy(xml_obj, PCMK_XA_IMAGE); + bundle_data->container_network = crm_element_value_copy(xml_obj, + PCMK_XA_NETWORK); - xml_obj = first_named_child(rsc->xml, "network"); + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_NETWORK, NULL, NULL); if(xml_obj) { - - bundle_data->ip_range_start = crm_element_value_copy(xml_obj, "ip-range-start"); - bundle_data->host_netmask = crm_element_value_copy(xml_obj, "host-netmask"); - bundle_data->host_network = crm_element_value_copy(xml_obj, "host-interface"); - bundle_data->control_port = crm_element_value_copy(xml_obj, "control-port"); - value = crm_element_value(xml_obj, "add-host"); + bundle_data->ip_range_start = + crm_element_value_copy(xml_obj, PCMK_XA_IP_RANGE_START); + bundle_data->host_netmask = + crm_element_value_copy(xml_obj, PCMK_XA_HOST_NETMASK); + bundle_data->host_network = + crm_element_value_copy(xml_obj, PCMK_XA_HOST_INTERFACE); + bundle_data->control_port = + crm_element_value_copy(xml_obj, PCMK_XA_CONTROL_PORT); + value = crm_element_value(xml_obj, PCMK_XA_ADD_HOST); if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) { bundle_data->add_host = TRUE; } - for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL; - xml_child = pcmk__xe_next(xml_child)) { + for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_PORT_MAPPING, + NULL, NULL); + xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) { - pe__bundle_port_t *port = calloc(1, sizeof(pe__bundle_port_t)); - port->source = crm_element_value_copy(xml_child, "port"); + pe__bundle_port_t *port = + pcmk__assert_alloc(1, sizeof(pe__bundle_port_t)); + + port->source = crm_element_value_copy(xml_child, PCMK_XA_PORT); if(port->source == NULL) { - port->source = crm_element_value_copy(xml_child, "range"); + port->source = crm_element_value_copy(xml_child, PCMK_XA_RANGE); } else { - port->target = crm_element_value_copy(xml_child, "internal-port"); + port->target = crm_element_value_copy(xml_child, + PCMK_XA_INTERNAL_PORT); } if(port->source != NULL && strlen(port->source) > 0) { @@ -1076,23 +1103,25 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) bundle_data->ports = g_list_append(bundle_data->ports, port); } else { - pe_err("Invalid port directive %s", ID(xml_child)); + pcmk__config_err("Invalid " PCMK_XA_PORT " directive %s", + pcmk__xe_id(xml_child)); port_free(port); } } } - xml_obj = first_named_child(rsc->xml, "storage"); - for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL; - xml_child = pcmk__xe_next(xml_child)) { + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_STORAGE, NULL, NULL); + for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_STORAGE_MAPPING, + NULL, NULL); + xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) { - const char *source = crm_element_value(xml_child, "source-dir"); - const char *target = crm_element_value(xml_child, "target-dir"); - const char *options = crm_element_value(xml_child, "options"); + const char *source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR); + const char *target = crm_element_value(xml_child, PCMK_XA_TARGET_DIR); + const char *options = crm_element_value(xml_child, PCMK_XA_OPTIONS); int flags = pe__bundle_mount_none; if (source == NULL) { - source = crm_element_value(xml_child, "source-dir-root"); + source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR_ROOT); pe__set_bundle_mount_flags(xml_child, flags, pe__bundle_mount_subdir); } @@ -1103,16 +1132,17 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) need_log_mount = FALSE; } } else { - pe_err("Invalid mount directive %s", ID(xml_child)); + pcmk__config_err("Invalid mount directive %s", + pcmk__xe_id(xml_child)); } } - xml_obj = first_named_child(rsc->xml, "primitive"); + xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PRIMITIVE, NULL, NULL); if (xml_obj && valid_network(bundle_data)) { char *value = NULL; xmlNode *xml_set = NULL; - xml_resource = create_xml_node(NULL, XML_CIB_TAG_INCARNATION); + xml_resource = pcmk__xe_create(NULL, PCMK_XE_CLONE); /* @COMPAT We no longer use the <master> tag, but we need to keep it as * part of the resource name, so that bundles don't restart in a rolling @@ -1122,11 +1152,11 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) (bundle_data->promoted_max? "master" : (const char *)xml_resource->name)); - xml_set = create_xml_node(xml_resource, XML_TAG_META_SETS); + xml_set = pcmk__xe_create(xml_resource, PCMK_XE_META_ATTRIBUTES); crm_xml_set_id(xml_set, "%s-%s-meta", bundle_data->prefix, xml_resource->name); crm_create_nvpair_xml(xml_set, NULL, - XML_RSC_ATTR_ORDERED, XML_BOOLEAN_TRUE); + PCMK_META_ORDERED, PCMK_VALUE_TRUE); value = pcmk__itoa(bundle_data->nreplicas); crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_MAX, value); @@ -1136,24 +1166,25 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_NODE_MAX, value); free(value); - crm_create_nvpair_xml(xml_set, NULL, XML_RSC_ATTR_UNIQUE, + crm_create_nvpair_xml(xml_set, NULL, PCMK_META_GLOBALLY_UNIQUE, pcmk__btoa(bundle_data->nreplicas_per_host > 1)); if (bundle_data->promoted_max) { crm_create_nvpair_xml(xml_set, NULL, - XML_RSC_ATTR_PROMOTABLE, XML_BOOLEAN_TRUE); + PCMK_META_PROMOTABLE, PCMK_VALUE_TRUE); value = pcmk__itoa(bundle_data->promoted_max); crm_create_nvpair_xml(xml_set, NULL, PCMK_META_PROMOTED_MAX, value); free(value); } - //crm_xml_add(xml_obj, XML_ATTR_ID, bundle_data->prefix); - add_node_copy(xml_resource, xml_obj); + //crm_xml_add(xml_obj, PCMK_XA_ID, bundle_data->prefix); + pcmk__xml_copy(xml_resource, xml_obj); } else if(xml_obj) { - pe_err("Cannot control %s inside %s without either ip-range-start or control-port", - rsc->id, ID(xml_obj)); + pcmk__config_err("Cannot control %s inside %s without either " + PCMK_XA_IP_RANGE_START " or " PCMK_XA_CONTROL_PORT, + rsc->id, pcmk__xe_id(xml_obj)); return FALSE; } @@ -1197,7 +1228,7 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) pe__bundle_mount_subdir); } - port = calloc(1, sizeof(pe__bundle_port_t)); + port = pcmk__assert_alloc(1, sizeof(pe__bundle_port_t)); if(bundle_data->control_port) { port->source = strdup(bundle_data->control_port); } else { @@ -1207,7 +1238,8 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) * * However, it gains nothing, since we control both the container * environment and the connection resource parameters, and the user - * can use a different port if desired by setting control-port. + * can use a different port if desired by setting + * PCMK_XA_CONTROL_PORT. */ port->source = pcmk__itoa(DEFAULT_REMOTE_PORT); } @@ -1218,31 +1250,33 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) for (childIter = bundle_data->child->children; childIter != NULL; childIter = childIter->next) { - pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t)); + pcmk__bundle_replica_t *replica = NULL; + replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t)); replica->child = childIter->data; replica->child->exclusive_discover = TRUE; replica->offset = lpc++; // Ensure the child's notify gets set based on the underlying primitive's value if (pcmk_is_set(replica->child->flags, pcmk_rsc_notify)) { - pe__set_resource_flags(bundle_data->child, pcmk_rsc_notify); + pcmk__set_rsc_flags(bundle_data->child, pcmk_rsc_notify); } allocate_ip(bundle_data, replica, buffer); bundle_data->replicas = g_list_append(bundle_data->replicas, replica); - bundle_data->attribute_target = g_hash_table_lookup(replica->child->meta, - XML_RSC_ATTR_TARGET); + bundle_data->attribute_target = + g_hash_table_lookup(replica->child->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); } bundle_data->container_host_options = g_string_free(buffer, FALSE); if (bundle_data->attribute_target) { - g_hash_table_replace(rsc->meta, strdup(XML_RSC_ATTR_TARGET), - strdup(bundle_data->attribute_target)); - g_hash_table_replace(bundle_data->child->meta, - strdup(XML_RSC_ATTR_TARGET), - strdup(bundle_data->attribute_target)); + pcmk__insert_dup(rsc->meta, PCMK_META_CONTAINER_ATTRIBUTE_TARGET, + bundle_data->attribute_target); + pcmk__insert_dup(bundle_data->child->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET, + bundle_data->attribute_target); } } else { @@ -1250,8 +1284,9 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) GString *buffer = g_string_sized_new(1024); for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) { - pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t)); + pcmk__bundle_replica_t *replica = NULL; + replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t)); replica->offset = lpc; allocate_ip(bundle_data, replica, buffer); bundle_data->replicas = g_list_append(bundle_data->replicas, @@ -1262,10 +1297,10 @@ pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) { - pe_err("Failed unpacking resource %s", rsc->id); + pcmk__config_err("Failed unpacking resource %s", rsc->id); rsc->fns->free(rsc); return FALSE; } @@ -1323,7 +1358,7 @@ pe__bundle_active(pcmk_resource_t *rsc, gboolean all) get_bundle_variant_data(bundle_data, rsc); for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) { - pe__bundle_replica_t *replica = iter->data; + pcmk__bundle_replica_t *replica = iter->data; int rsc_active; rsc_active = replica_resource_active(replica->ip, all); @@ -1372,10 +1407,10 @@ pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node) get_bundle_variant_data(bundle_data, bundle); for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; CRM_ASSERT(replica && replica->node); - if (replica->node->details == node->details) { + if (pcmk__same_node(replica->node, node)) { return replica->child; } } @@ -1421,21 +1456,23 @@ bundle_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options, get_bundle_variant_data(bundle_data, rsc); status_print("%s<bundle ", pre_text); - status_print(XML_ATTR_ID "=\"%s\" ", rsc->id); + status_print(PCMK_XA_ID "=\"%s\" ", rsc->id); status_print("type=\"%s\" ", container_agent_str(bundle_data->agent_type)); status_print("image=\"%s\" ", bundle_data->image); - status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_unique)); + status_print("unique=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); status_print("managed=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_managed)); - status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_failed)); + pcmk__flag_text(rsc->flags, pcmk_rsc_managed)); + status_print("failed=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_failed)); status_print(">\n"); for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; CRM_ASSERT(replica); - status_print("%s <replica " XML_ATTR_ID "=\"%d\">\n", + status_print("%s <replica " PCMK_XA_ID "=\"%d\">\n", pre_text, replica->offset); print_rsc_in_list(replica->ip, child_text, options, print_data); print_rsc_in_list(replica->child, child_text, options, print_data); @@ -1476,7 +1513,7 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; char *id = NULL; gboolean print_ip, print_child, print_ctnr, print_remote; @@ -1499,46 +1536,55 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) } if (!printed_header) { + const char *type = container_agent_str(bundle_data->agent_type); + const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique); + const char *maintenance = pcmk__flag_text(rsc->flags, + pcmk_rsc_maintenance); + const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed); + const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed); + printed_header = TRUE; desc = pe__resource_description(rsc, show_opts); - rc = pe__name_and_nvpairs_xml(out, true, "bundle", 8, - "id", rsc->id, - "type", container_agent_str(bundle_data->agent_type), - "image", bundle_data->image, - "unique", pe__rsc_bool_str(rsc, pcmk_rsc_unique), - "maintenance", - pe__rsc_bool_str(rsc, pcmk_rsc_maintenance), - "managed", pe__rsc_bool_str(rsc, pcmk_rsc_managed), - "failed", pe__rsc_bool_str(rsc, pcmk_rsc_failed), - "description", desc); + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_BUNDLE, + PCMK_XA_ID, rsc->id, + PCMK_XA_TYPE, type, + PCMK_XA_IMAGE, bundle_data->image, + PCMK_XA_UNIQUE, unique, + PCMK_XA_MAINTENANCE, maintenance, + PCMK_XA_MANAGED, managed, + PCMK_XA_FAILED, failed, + PCMK_XA_DESCRIPTION, desc, + NULL); CRM_ASSERT(rc == pcmk_rc_ok); } id = pcmk__itoa(replica->offset); - rc = pe__name_and_nvpairs_xml(out, true, "replica", 1, "id", id); + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_REPLICA, + PCMK_XA_ID, id, + NULL); free(id); CRM_ASSERT(rc == pcmk_rc_ok); if (print_ip) { - out->message(out, crm_map_element_name(replica->ip->xml), show_opts, + out->message(out, (const char *) replica->ip->xml->name, show_opts, replica->ip, only_node, only_rsc); } if (print_child) { - out->message(out, crm_map_element_name(replica->child->xml), show_opts, - replica->child, only_node, only_rsc); + out->message(out, (const char *) replica->child->xml->name, + show_opts, replica->child, only_node, only_rsc); } if (print_ctnr) { - out->message(out, crm_map_element_name(replica->container->xml), show_opts, - replica->container, only_node, only_rsc); + out->message(out, (const char *) replica->container->xml->name, + show_opts, replica->container, only_node, only_rsc); } if (print_remote) { - out->message(out, crm_map_element_name(replica->remote->xml), show_opts, - replica->remote, only_node, only_rsc); + out->message(out, (const char *) replica->remote->xml->name, + show_opts, replica->remote, only_node, only_rsc); } pcmk__output_xml_pop_parent(out); // replica @@ -1552,7 +1598,8 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) } static void -pe__bundle_replica_output_html(pcmk__output_t *out, pe__bundle_replica_t *replica, +pe__bundle_replica_output_html(pcmk__output_t *out, + pcmk__bundle_replica_t *replica, pcmk_node_t *node, uint32_t show_opts) { pcmk_resource_t *rsc = replica->child; @@ -1629,7 +1676,7 @@ pe__bundle_html(pcmk__output_t *out, va_list args) for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; gboolean print_ip, print_child, print_ctnr, print_remote; CRM_ASSERT(replica); @@ -1665,23 +1712,26 @@ pe__bundle_html(pcmk__output_t *out, va_list args) } if (print_ip) { - out->message(out, crm_map_element_name(replica->ip->xml), + out->message(out, (const char *) replica->ip->xml->name, new_show_opts, replica->ip, only_node, only_rsc); } if (print_child) { - out->message(out, crm_map_element_name(replica->child->xml), - new_show_opts, replica->child, only_node, only_rsc); + out->message(out, (const char *) replica->child->xml->name, + new_show_opts, replica->child, only_node, + only_rsc); } if (print_ctnr) { - out->message(out, crm_map_element_name(replica->container->xml), - new_show_opts, replica->container, only_node, only_rsc); + out->message(out, (const char *) replica->container->xml->name, + new_show_opts, replica->container, only_node, + only_rsc); } if (print_remote) { - out->message(out, crm_map_element_name(replica->remote->xml), - new_show_opts, replica->remote, only_node, only_rsc); + out->message(out, (const char *) replica->remote->xml->name, + new_show_opts, replica->remote, only_node, + only_rsc); } if (pcmk__list_of_multiple(bundle_data->replicas)) { @@ -1697,7 +1747,8 @@ pe__bundle_html(pcmk__output_t *out, va_list args) desc ? " (" : "", desc ? desc : "", desc ? ")" : "", get_unmanaged_str(rsc)); - pe__bundle_replica_output_html(out, replica, pe__current_node(replica->container), + pe__bundle_replica_output_html(out, replica, + pcmk__current_node(replica->container), show_opts); } } @@ -1707,7 +1758,8 @@ pe__bundle_html(pcmk__output_t *out, va_list args) } static void -pe__bundle_replica_output_text(pcmk__output_t *out, pe__bundle_replica_t *replica, +pe__bundle_replica_output_text(pcmk__output_t *out, + pcmk__bundle_replica_t *replica, pcmk_node_t *node, uint32_t show_opts) { const pcmk_resource_t *rsc = replica->child; @@ -1763,7 +1815,7 @@ pe__bundle_text(pcmk__output_t *out, va_list args) for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; gboolean print_ip, print_child, print_ctnr, print_remote; CRM_ASSERT(replica); @@ -1801,23 +1853,26 @@ pe__bundle_text(pcmk__output_t *out, va_list args) out->begin_list(out, NULL, NULL, NULL); if (print_ip) { - out->message(out, crm_map_element_name(replica->ip->xml), + out->message(out, (const char *) replica->ip->xml->name, new_show_opts, replica->ip, only_node, only_rsc); } if (print_child) { - out->message(out, crm_map_element_name(replica->child->xml), - new_show_opts, replica->child, only_node, only_rsc); + out->message(out, (const char *) replica->child->xml->name, + new_show_opts, replica->child, only_node, + only_rsc); } if (print_ctnr) { - out->message(out, crm_map_element_name(replica->container->xml), - new_show_opts, replica->container, only_node, only_rsc); + out->message(out, (const char *) replica->container->xml->name, + new_show_opts, replica->container, only_node, + only_rsc); } if (print_remote) { - out->message(out, crm_map_element_name(replica->remote->xml), - new_show_opts, replica->remote, only_node, only_rsc); + out->message(out, (const char *) replica->remote->xml->name, + new_show_opts, replica->remote, only_node, + only_rsc); } out->end_list(out); @@ -1831,7 +1886,8 @@ pe__bundle_text(pcmk__output_t *out, va_list args) desc ? " (" : "", desc ? desc : "", desc ? ")" : "", get_unmanaged_str(rsc)); - pe__bundle_replica_output_text(out, replica, pe__current_node(replica->container), + pe__bundle_replica_output_text(out, replica, + pcmk__current_node(replica->container), show_opts); } } @@ -1845,7 +1901,7 @@ pe__bundle_text(pcmk__output_t *out, va_list args) * \deprecated This function will be removed in a future release */ static void -print_bundle_replica(pe__bundle_replica_t *replica, const char *pre_text, +print_bundle_replica(pcmk__bundle_replica_t *replica, const char *pre_text, long options, void *print_data) { pcmk_node_t *node = NULL; @@ -1870,7 +1926,7 @@ print_bundle_replica(pe__bundle_replica_t *replica, const char *pre_text, replica->ipaddr); } - node = pe__current_node(replica->container); + node = pcmk__current_node(replica->container); common_print(rsc, pre_text, buffer, node, options, print_data); } @@ -1909,7 +1965,7 @@ pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options, for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; CRM_ASSERT(replica); if (options & pe_print_html) { @@ -1947,7 +2003,7 @@ pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options, } static void -free_bundle_replica(pe__bundle_replica_t *replica) +free_bundle_replica(pcmk__bundle_replica_t *replica) { if (replica == NULL) { return; @@ -1987,7 +2043,7 @@ pe__free_bundle(pcmk_resource_t *rsc) CRM_CHECK(rsc != NULL, return); get_bundle_variant_data(bundle_data, rsc); - pe_rsc_trace(rsc, "Freeing %s", rsc->id); + pcmk__rsc_trace(rsc, "Freeing %s", rsc->id); free(bundle_data->prefix); free(bundle_data->image); @@ -2031,14 +2087,13 @@ pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current) int pe_bundle_replicas(const pcmk_resource_t *rsc) { - if ((rsc == NULL) || (rsc->variant != pcmk_rsc_variant_bundle)) { - return 0; - } else { + if (pcmk__is_bundle(rsc)) { pe__bundle_variant_data_t *bundle_data = NULL; get_bundle_variant_data(bundle_data, rsc); return bundle_data->nreplicas; } + return 0; } void @@ -2048,7 +2103,7 @@ pe__count_bundle(pcmk_resource_t *rsc) get_bundle_variant_data(bundle_data, rsc); for (GList *item = bundle_data->replicas; item != NULL; item = item->next) { - pe__bundle_replica_t *replica = item->data; + pcmk__bundle_replica_t *replica = item->data; if (replica->ip) { replica->ip->fns->count(replica->ip); @@ -2078,7 +2133,7 @@ pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, get_bundle_variant_data(bundle_data, rsc); for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; + pcmk__bundle_replica_t *replica = gIter->data; if (replica->ip != NULL && !replica->ip->fns->is_filtered(replica->ip, only_rsc, FALSE)) { passes = TRUE; @@ -2117,7 +2172,7 @@ pe__bundle_containers(const pcmk_resource_t *bundle) get_bundle_variant_data(data, bundle); for (GList *iter = data->replicas; iter != NULL; iter = iter->next) { - pe__bundle_replica_t *replica = iter->data; + pcmk__bundle_replica_t *replica = iter->data; containers = g_list_append(containers, replica->container); } @@ -2152,7 +2207,7 @@ pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all, */ get_bundle_variant_data(data, rsc); for (iter = data->replicas; iter != NULL; iter = iter->next) { - pe__bundle_replica_t *replica = iter->data; + pcmk__bundle_replica_t *replica = iter->data; if (replica->container->running_on != NULL) { containers = g_list_append(containers, replica->container); diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c index a92a4b7..596b00e 100644 --- a/lib/pengine/clone.c +++ b/lib/pengine/clone.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. * @@ -15,7 +15,7 @@ #include <crm/pengine/status.h> #include <crm/pengine/internal.h> #include <pe_status_private.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/output.h> #include <crm/common/xml_internal.h> #include <crm/common/scheduler_internal.h> @@ -24,8 +24,8 @@ #define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED_LEGACY "s" #define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED_LEGACY "s" #else -#define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED -#define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED +#define PROMOTED_INSTANCES PCMK_ROLE_PROMOTED +#define UNPROMOTED_INSTANCES PCMK_ROLE_UNPROMOTED #endif typedef struct clone_variant_data_s { @@ -47,8 +47,8 @@ typedef struct clone_variant_data_s { xmlNode *xml_obj_child; } clone_variant_data_t; -#define get_clone_variant_data(data, rsc) \ - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_clone)); \ +#define get_clone_variant_data(data, rsc) \ + CRM_ASSERT(pcmk__is_clone(rsc) && (rsc->variant_opaque != NULL)); \ data = (clone_variant_data_t *) rsc->variant_opaque; /*! @@ -194,13 +194,15 @@ clone_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc, if (attrs != NULL) { PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s", - rsc->id, ID(clone_data->xml_obj_child), + rsc->id, + pcmk__xe_id(clone_data->xml_obj_child), (const char *) attrs->str, desc ? " (" : "", desc ? desc : "", desc ? ")" : ""); g_string_free(attrs, TRUE); } else { PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s", - rsc->id, ID(clone_data->xml_obj_child), + rsc->id, + pcmk__xe_id(clone_data->xml_obj_child), desc ? " (" : "", desc ? desc : "", desc ? ")" : ""); } @@ -210,12 +212,12 @@ void pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid, pcmk_scheduler_t *scheduler) { - if (pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { clone_variant_data_t *clone_data = rsc->variant_opaque; - pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources " - "such as %s can be used only as anonymous clones", - rsc->id, standard, rid); + pcmk__config_warn("Ignoring " PCMK_META_GLOBALLY_UNIQUE " for %s " + "because %s resources such as %s can be used only as " + "anonymous clones", rsc->id, standard, rid); clone_data->clone_node_max = 1; clone_data->clone_max = QB_MIN(clone_data->clone_max, @@ -233,7 +235,7 @@ find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id) get_clone_variant_data(clone_data, rsc); - child_base = ID(clone_data->xml_obj_child); + child_base = pcmk__xe_id(clone_data->xml_obj_child); child_id = crm_strdup_printf("%s:%s", child_base, sub_id); child = pe_find_resource(rsc->children, child_id); @@ -264,9 +266,9 @@ pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) inc_num = pcmk__itoa(clone_data->total_clones); inc_max = pcmk__itoa(clone_data->clone_max); - child_copy = copy_xml(clone_data->xml_obj_child); + child_copy = pcmk__xml_copy(NULL, clone_data->xml_obj_child); - crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num); + crm_xml_add(child_copy, PCMK__META_CLONE, inc_num); if (pe__unpack_resource(child_copy, &child_rsc, rsc, scheduler) != pcmk_rc_ok) { @@ -276,14 +278,15 @@ pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) CRM_ASSERT(child_rsc); clone_data->total_clones += 1; - pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id); + pcmk__rsc_trace(child_rsc, "Setting clone attributes for: %s", + child_rsc->id); rsc->children = g_list_append(rsc->children, child_rsc); if (as_orphan) { pe__set_resource_flags_recursive(child_rsc, pcmk_rsc_removed); } - add_hash_param(child_rsc->meta, PCMK_META_CLONE_MAX, inc_max); - pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id); + pcmk__insert_meta(child_rsc, PCMK_META_CLONE_MAX, inc_max); + pcmk__rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id); bail: free(inc_num); @@ -314,6 +317,26 @@ unpack_meta_int(const pcmk_resource_t *rsc, const char *meta_name, if ((value == NULL) && (deprecated_name != NULL)) { value = g_hash_table_lookup(rsc->meta, deprecated_name); + + if (value != NULL) { + if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_MAX_LEGACY, + pcmk__str_none)) { + pcmk__warn_once(pcmk__wo_clone_master_max, + "Support for the " PCMK__META_PROMOTED_MAX_LEGACY + " meta-attribute (such as in %s) is deprecated " + "and will be removed in a future release. Use the " + PCMK_META_PROMOTED_MAX " meta-attribute instead.", + rsc->id); + } else if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_NODE_MAX_LEGACY, + pcmk__str_none)) { + pcmk__warn_once(pcmk__wo_clone_master_node_max, + "Support for the " PCMK__META_PROMOTED_NODE_MAX_LEGACY + " meta-attribute (such as in %s) is deprecated " + "and will be removed in a future release. Use the " + PCMK_META_PROMOTED_NODE_MAX " meta-attribute instead.", + rsc->id); + } + } } if (value != NULL) { pcmk__scan_min_int(value, &integer, 0); @@ -329,28 +352,25 @@ clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) xmlNode *xml_obj = rsc->xml; clone_variant_data_t *clone_data = NULL; - pe_rsc_trace(rsc, "Processing resource %s...", rsc->id); + pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id); - clone_data = calloc(1, sizeof(clone_variant_data_t)); + clone_data = pcmk__assert_alloc(1, sizeof(clone_variant_data_t)); rsc->variant_opaque = clone_data; if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) { // Use 1 as default but 0 for minimum and invalid - // @COMPAT PCMK_XA_PROMOTED_MAX_LEGACY deprecated since 2.0.0 - clone_data->promoted_max = unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX, - PCMK_XA_PROMOTED_MAX_LEGACY, - 1); + // @COMPAT PCMK__META_PROMOTED_MAX_LEGACY deprecated since 2.0.0 + clone_data->promoted_max = + unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX, + PCMK__META_PROMOTED_MAX_LEGACY, 1); // Use 1 as default but 0 for minimum and invalid - // @COMPAT PCMK_XA_PROMOTED_NODE_MAX_LEGACY deprecated since 2.0.0 + // @COMPAT PCMK__META_PROMOTED_NODE_MAX_LEGACY deprecated since 2.0.0 clone_data->promoted_node_max = unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX, - PCMK_XA_PROMOTED_NODE_MAX_LEGACY, 1); + PCMK__META_PROMOTED_NODE_MAX_LEGACY, 1); } - // Implied by calloc() - /* clone_data->xml_obj_child = NULL; */ - // Use 1 as default but 0 for minimum and invalid clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX, NULL, 1); @@ -361,7 +381,7 @@ clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL, QB_MAX(1, g_list_length(scheduler->nodes))); - if (crm_is_true(g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED))) { + if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_ORDERED))) { clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, "Clone", rsc->id, clone_data->flags, @@ -378,19 +398,20 @@ clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) clone_data->clone_node_max = 1; } - pe_rsc_trace(rsc, "Options for %s", rsc->id); - pe_rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max); - pe_rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max); - pe_rsc_trace(rsc, "\tClone is unique: %s", - pe__rsc_bool_str(rsc, pcmk_rsc_unique)); - pe_rsc_trace(rsc, "\tClone is promotable: %s", - pe__rsc_bool_str(rsc, pcmk_rsc_promotable)); + pcmk__rsc_trace(rsc, "Options for %s", rsc->id); + pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max); + pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max); + pcmk__rsc_trace(rsc, "\tClone is unique: %s", + pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); + pcmk__rsc_trace(rsc, "\tClone is promotable: %s", + pcmk__flag_text(rsc->flags, pcmk_rsc_promotable)); // Clones may contain a single group or primitive - for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL; - a_child = pcmk__xe_next(a_child)) { + for (a_child = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + a_child != NULL; a_child = pcmk__xe_next(a_child)) { - if (pcmk__str_any_of((const char *)a_child->name, XML_CIB_TAG_RESOURCE, XML_CIB_TAG_GROUP, NULL)) { + if (pcmk__str_any_of((const char *) a_child->name, + PCMK_XE_PRIMITIVE, PCMK_XE_GROUP, NULL)) { clone_data->xml_obj_child = a_child; break; } @@ -407,15 +428,16 @@ clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) * This helps ensure clone instances are not shuffled around the cluster * for no benefit in situations when pre-allocation is not appropriate */ - if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) { - add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1"); + if (g_hash_table_lookup(rsc->meta, PCMK_META_RESOURCE_STICKINESS) == NULL) { + pcmk__insert_meta(rsc, PCMK_META_RESOURCE_STICKINESS, "1"); } - /* This ensures that the globally-unique value always exists for children to - * inherit when being unpacked, as well as in resource agents' environment. + /* This ensures that the PCMK_META_GLOBALLY_UNIQUE value always exists for + * children to inherit when being unpacked, as well as in resource agents' + * environment. */ - add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE, - pe__rsc_bool_str(rsc, pcmk_rsc_unique)); + pcmk__insert_meta(rsc, PCMK_META_GLOBALLY_UNIQUE, + pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); if (clone_data->clone_max <= 0) { /* Create one child instance so that unpack_find_resource() will hook up @@ -434,7 +456,8 @@ clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) } } - pe_rsc_trace(rsc, "Added %d children to resource %s...", clone_data->clone_max, rsc->id); + pcmk__rsc_trace(rsc, "Added %d children to resource %s...", + clone_data->clone_max, rsc->id); return TRUE; } @@ -495,13 +518,13 @@ static const char * configured_role_str(pcmk_resource_t * rsc) { const char *target_role = g_hash_table_lookup(rsc->meta, - XML_RSC_ATTR_TARGET_ROLE); + PCMK_META_TARGET_ROLE); if ((target_role == NULL) && rsc->children && rsc->children->data) { pcmk_resource_t *instance = rsc->children->data; // Any instance will do target_role = g_hash_table_lookup(instance->meta, - XML_RSC_ATTR_TARGET_ROLE); + PCMK_META_TARGET_ROLE); } return target_role; } @@ -509,12 +532,17 @@ configured_role_str(pcmk_resource_t * rsc) static enum rsc_role_e configured_role(pcmk_resource_t *rsc) { + enum rsc_role_e role = pcmk_role_unknown; const char *target_role = configured_role_str(rsc); - if (target_role) { - return text2role(target_role); + if (target_role != NULL) { + role = pcmk_parse_role(target_role); + if (role == pcmk_role_unknown) { + pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE + " for resource %s", rsc->id); + } } - return pcmk_role_unknown; + return role; } /*! @@ -530,15 +558,17 @@ clone_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options, GList *gIter = rsc->children; status_print("%s<clone ", pre_text); - status_print(XML_ATTR_ID "=\"%s\" ", rsc->id); + status_print(PCMK_XA_ID "=\"%s\" ", rsc->id); status_print("multi_state=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_promotable)); - status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_unique)); + pcmk__flag_text(rsc->flags, pcmk_rsc_promotable)); + status_print("unique=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_unique)); status_print("managed=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_managed)); - status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_failed)); + pcmk__flag_text(rsc->flags, pcmk_rsc_managed)); + status_print("failed=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_failed)); status_print("failure_ignored=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_ignore_failure)); + pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure)); if (target_role) { status_print("target_role=\"%s\" ", target_role); } @@ -618,7 +648,8 @@ clone_print(pcmk_resource_t *rsc, const char *pre_text, long options, child_text = crm_strdup_printf("%s ", pre_text); status_print("%sClone Set: %s [%s]%s%s%s", - pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child), + pcmk__s(pre_text, ""), rsc->id, + pcmk__xe_id(clone_data->xml_obj_child), pcmk_is_set(rsc->flags, pcmk_rsc_promotable)? " (promotable)" : "", pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "", pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)"); @@ -742,8 +773,8 @@ clone_print(pcmk_resource_t *rsc, const char *pre_text, long options, if (role == pcmk_role_unpromoted) { short_print((const char *) list_text->str, child_text, - UNPROMOTED_INSTANCES " (target-role)", NULL, - options, print_data); + UNPROMOTED_INSTANCES " (" PCMK_META_TARGET_ROLE ")", + NULL, options, print_data); } else { short_print((const char *) list_text->str, child_text, UNPROMOTED_INSTANCES, NULL, options, print_data); @@ -777,8 +808,9 @@ clone_print(pcmk_resource_t *rsc, const char *pre_text, long options, } if (list == NULL) { - /* Clusters with symmetrical=false haven't calculated allowed_nodes yet - * If we've not probed for them yet, the Stopped list will be empty + /* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't + * calculated allowed_nodes yet. If we've not probed for them + * yet, the Stopped list will be empty. */ list = g_hash_table_get_values(rsc->known_on); } @@ -787,7 +819,8 @@ clone_print(pcmk_resource_t *rsc, const char *pre_text, long options, for (nIter = list; nIter != NULL; nIter = nIter->next) { pcmk_node_t *node = (pcmk_node_t *) nIter->data; - if (pe_find_node(rsc->running_on, node->details->uname) == NULL) { + if (pcmk__find_node_in_list(rsc->running_on, + node->details->uname) == NULL) { pcmk__add_word(&stopped_list, 1024, node->details->uname); } } @@ -824,16 +857,12 @@ pe__clone_xml(pcmk__output_t *out, va_list args) GList *only_node = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); - - const char *desc = NULL; GList *gIter = rsc->children; GList *all = NULL; int rc = pcmk_rc_no_output; gboolean printed_header = FALSE; gboolean print_everything = TRUE; - - if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) { return rc; } @@ -855,27 +884,37 @@ pe__clone_xml(pcmk__output_t *out, va_list args) } if (!printed_header) { + const char *multi_state = pcmk__flag_text(rsc->flags, + pcmk_rsc_promotable); + const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique); + const char *maintenance = pcmk__flag_text(rsc->flags, + pcmk_rsc_maintenance); + const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed); + const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc)); + const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed); + const char *ignored = pcmk__flag_text(rsc->flags, + pcmk_rsc_ignore_failure); + const char *target_role = configured_role_str(rsc); + const char *desc = pe__resource_description(rsc, show_opts); + printed_header = TRUE; - desc = pe__resource_description(rsc, show_opts); - rc = pe__name_and_nvpairs_xml(out, true, "clone", 10, - "id", rsc->id, - "multi_state", - pe__rsc_bool_str(rsc, pcmk_rsc_promotable), - "unique", pe__rsc_bool_str(rsc, pcmk_rsc_unique), - "maintenance", - pe__rsc_bool_str(rsc, pcmk_rsc_maintenance), - "managed", pe__rsc_bool_str(rsc, pcmk_rsc_managed), - "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)), - "failed", pe__rsc_bool_str(rsc, pcmk_rsc_failed), - "failure_ignored", - pe__rsc_bool_str(rsc, pcmk_rsc_ignore_failure), - "target_role", configured_role_str(rsc), - "description", desc); + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE, + PCMK_XA_ID, rsc->id, + PCMK_XA_MULTI_STATE, multi_state, + PCMK_XA_UNIQUE, unique, + PCMK_XA_MAINTENANCE, maintenance, + PCMK_XA_MANAGED, managed, + PCMK_XA_DISABLED, disabled, + PCMK_XA_FAILED, failed, + PCMK_XA_FAILURE_IGNORED, ignored, + PCMK_XA_TARGET_ROLE, target_role, + PCMK_XA_DESCRIPTION, desc, + NULL); CRM_ASSERT(rc == pcmk_rc_ok); } - out->message(out, crm_map_element_name(child_rsc->xml), show_opts, + out->message(out, (const char *) child_rsc->xml->name, show_opts, child_rsc, only_node, all); } @@ -963,7 +1002,7 @@ pe__clone_default(pcmk__output_t *out, va_list args) if (stopped == NULL) { stopped = pcmk__strkey_table(free, free); } - g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped")); + pcmk__insert_dup(stopped, child_rsc->id, "Stopped"); } } else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE) @@ -1011,7 +1050,7 @@ pe__clone_default(pcmk__output_t *out, va_list args) /* Print every resource that's a child of this clone. */ all = g_list_prepend(all, (gpointer) "*"); - out->message(out, crm_map_element_name(child_rsc->xml), show_opts, + out->message(out, (const char *) child_rsc->xml->name, show_opts, child_rsc, only_node, all); g_list_free(all); } @@ -1068,7 +1107,8 @@ pe__clone_default(pcmk__output_t *out, va_list args) if (role == pcmk_role_unpromoted) { out->list_item(out, NULL, - UNPROMOTED_INSTANCES " (target-role): [ %s ]", + UNPROMOTED_INSTANCES + " (" PCMK_META_TARGET_ROLE "): [ %s ]", (const char *) list_text->str); } else { out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]", @@ -1099,8 +1139,9 @@ pe__clone_default(pcmk__output_t *out, va_list args) } if (list == NULL) { - /* Clusters with symmetrical=false haven't calculated allowed_nodes yet - * If we've not probed for them yet, the Stopped list will be empty + /* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't + * calculated allowed_nodes yet. If we've not probed for them + * yet, the Stopped list will be empty. */ list = g_hash_table_get_values(rsc->known_on); } @@ -1109,9 +1150,10 @@ pe__clone_default(pcmk__output_t *out, va_list args) for (nIter = list; nIter != NULL; nIter = nIter->next) { pcmk_node_t *node = (pcmk_node_t *) nIter->data; - if (pe_find_node(rsc->running_on, node->details->uname) == NULL && - pcmk__str_in_list(node->details->uname, only_node, - pcmk__str_star_matches|pcmk__str_casei)) { + if ((pcmk__find_node_in_list(rsc->running_on, + node->details->uname) == NULL) + && pcmk__str_in_list(node->details->uname, only_node, + pcmk__str_star_matches|pcmk__str_casei)) { xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname); const char *state = "Stopped"; @@ -1125,12 +1167,13 @@ pe__clone_default(pcmk__output_t *out, va_list args) if (probe_op != NULL) { int rc; - pcmk__scan_min_int(crm_element_value(probe_op, XML_LRM_ATTR_RC), &rc, 0); + pcmk__scan_min_int(crm_element_value(probe_op, + PCMK__XA_RC_CODE), + &rc, 0); g_hash_table_insert(stopped, strdup(node->details->uname), crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc))); } else { - g_hash_table_insert(stopped, strdup(node->details->uname), - strdup(state)); + pcmk__insert_dup(stopped, node->details->uname, state); } } } @@ -1183,13 +1226,13 @@ clone_free(pcmk_resource_t * rsc) get_clone_variant_data(clone_data, rsc); - pe_rsc_trace(rsc, "Freeing %s", rsc->id); + pcmk__rsc_trace(rsc, "Freeing %s", rsc->id); for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data; CRM_ASSERT(child_rsc); - pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id); + pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id); free_xml(child_rsc->xml); child_rsc->xml = NULL; /* There could be a saved unexpanded xml */ @@ -1225,7 +1268,7 @@ clone_resource_state(const pcmk_resource_t * rsc, gboolean current) } } - pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role)); + pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role)); return clone_role; } @@ -1240,7 +1283,7 @@ bool pe__is_universal_clone(const pcmk_resource_t *rsc, const pcmk_scheduler_t *scheduler) { - if (pe_rsc_is_clone(rsc)) { + if (pcmk__is_clone(rsc)) { clone_variant_data_t *clone_data = rsc->variant_opaque; if (clone_data->clone_max == g_list_length(scheduler->nodes)) { @@ -1261,7 +1304,8 @@ pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, passes = TRUE; } else { get_clone_variant_data(clone_data, rsc); - passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches); + passes = pcmk__str_in_list(pcmk__xe_id(clone_data->xml_obj_child), + only_rsc, pcmk__str_star_matches); if (!passes) { for (const GList *iter = rsc->children; @@ -1285,7 +1329,7 @@ pe__clone_child_id(const pcmk_resource_t *rsc) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); - return ID(clone_data->xml_obj_child); + return pcmk__xe_id(clone_data->xml_obj_child); } /*! @@ -1375,7 +1419,7 @@ pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, // Create a "promoted" action for when all promotions are done action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED, !any_promoting, true); - action_complete->priority = INFINITY; + action_complete->priority = PCMK_SCORE_INFINITY; // Create notification pseudo-actions for promotion if (clone_data->promote_notify == NULL) { @@ -1392,7 +1436,7 @@ pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, // Create a "demoted" action for when all demotions are done action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED, !any_demoting, true); - action_complete->priority = INFINITY; + action_complete->priority = PCMK_SCORE_INFINITY; // Create notification pseudo-actions for demotion if (clone_data->demote_notify == NULL) { diff --git a/lib/pengine/common.c b/lib/pengine/common.c index 0fdd5a1..6351bac 100644 --- a/lib/pengine/common.c +++ b/lib/pengine/common.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,7 +9,6 @@ #include <crm_internal.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/util.h> @@ -18,610 +17,46 @@ #include <crm/common/scheduler_internal.h> #include <crm/pengine/internal.h> -gboolean was_processing_error = FALSE; -gboolean was_processing_warning = FALSE; +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START -static bool -check_placement_strategy(const char *value) -{ - return pcmk__strcase_any_of(value, "default", "utilization", "minimal", - "balanced", NULL); -} - -static pcmk__cluster_option_t pe_opts[] = { - /* name, old name, type, allowed values, - * default value, validator, - * short description, - * long description - */ - { - "no-quorum-policy", NULL, "select", "stop, freeze, ignore, demote, suicide", - "stop", pcmk__valid_quorum, - N_("What to do when the cluster does not have quorum"), - NULL - }, - { - "symmetric-cluster", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("Whether resources can run on any node by default"), - NULL - }, - { - "maintenance-mode", NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("Whether the cluster should refrain from monitoring, starting, " - "and stopping resources"), - NULL - }, - { - "start-failure-is-fatal", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("Whether a start failure should prevent a resource from being " - "recovered on the same node"), - N_("When true, the cluster will immediately ban a resource from a node " - "if it fails to start there. When false, the cluster will instead " - "check the resource's fail count against its migration-threshold.") - }, - { - "enable-startup-probes", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("Whether the cluster should check for active resources during start-up"), - NULL - }, - { - XML_CONFIG_ATTR_SHUTDOWN_LOCK, NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("Whether to lock resources to a cleanly shut down node"), - N_("When true, resources active on a node when it is cleanly shut down " - "are kept \"locked\" to that node (not allowed to run elsewhere) " - "until they start again on that node after it rejoins (or for at " - "most shutdown-lock-limit, if set). Stonith resources and " - "Pacemaker Remote connections are never locked. Clone and bundle " - "instances and the promoted role of promotable clones are " - "currently never locked, though support could be added in a future " - "release.") - }, - { - XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT, NULL, "time", NULL, - "0", pcmk__valid_interval_spec, - N_("Do not lock resources to a cleanly shut down node longer than " - "this"), - N_("If shutdown-lock is true and this is set to a nonzero time " - "duration, shutdown locks will expire after this much time has " - "passed since the shutdown was initiated, even if the node has not " - "rejoined.") - }, - - // Fencing-related options - { - "stonith-enabled", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("*** Advanced Use Only *** " - "Whether nodes may be fenced as part of recovery"), - N_("If false, unresponsive nodes are immediately assumed to be harmless, " - "and resources that were active on them may be recovered " - "elsewhere. This can result in a \"split-brain\" situation, " - "potentially leading to data loss and/or service unavailability.") - }, - { - "stonith-action", NULL, "select", "reboot, off, poweroff", - PCMK_ACTION_REBOOT, pcmk__is_fencing_action, - N_("Action to send to fence device when a node needs to be fenced " - "(\"poweroff\" is a deprecated alias for \"off\")"), - NULL - }, - { - "stonith-timeout", NULL, "time", NULL, - "60s", pcmk__valid_interval_spec, - N_("*** Advanced Use Only *** Unused by Pacemaker"), - N_("This value is not used by Pacemaker, but is kept for backward " - "compatibility, and certain legacy fence agents might use it.") - }, - { - XML_ATTR_HAVE_WATCHDOG, NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("Whether watchdog integration is enabled"), - N_("This is set automatically by the cluster according to whether SBD " - "is detected to be in use. User-configured values are ignored. " - "The value `true` is meaningful if diskless SBD is used and " - "`stonith-watchdog-timeout` is nonzero. In that case, if fencing " - "is required, watchdog-based self-fencing will be performed via " - "SBD without requiring a fencing resource explicitly configured.") - }, - { - "concurrent-fencing", NULL, "boolean", NULL, - PCMK__CONCURRENT_FENCING_DEFAULT, pcmk__valid_boolean, - N_("Allow performing fencing operations in parallel"), - NULL - }, - { - "startup-fencing", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("*** Advanced Use Only *** Whether to fence unseen nodes at start-up"), - N_("Setting this to false may lead to a \"split-brain\" situation," - "potentially leading to data loss and/or service unavailability.") - }, - { - XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY, NULL, "time", NULL, - "0", pcmk__valid_interval_spec, - N_("Apply fencing delay targeting the lost nodes with the highest total resource priority"), - N_("Apply specified delay for the fencings that are targeting the lost " - "nodes with the highest total resource priority in case we don't " - "have the majority of the nodes in our cluster partition, so that " - "the more significant nodes potentially win any fencing match, " - "which is especially meaningful under split-brain of 2-node " - "cluster. A promoted resource instance takes the base priority + 1 " - "on calculation if the base priority is not 0. Any static/random " - "delays that are introduced by `pcmk_delay_base/max` configured " - "for the corresponding fencing resources will be added to this " - "delay. This delay should be significantly greater than, safely " - "twice, the maximum `pcmk_delay_base/max`. By default, priority " - "fencing delay is disabled.") - }, - { - XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT, NULL, "time", NULL, - "0", pcmk__valid_interval_spec, - N_("How long to wait for a node that has joined the cluster to join " - "the controller process group"), - N_("Fence nodes that do not join the controller process group within " - "this much time after joining the cluster, to allow the cluster " - "to continue managing resources. A value of 0 means never fence " - "pending nodes. Setting the value to 2h means fence nodes after " - "2 hours.") - }, - { - "cluster-delay", NULL, "time", NULL, - "60s", pcmk__valid_interval_spec, - N_("Maximum time for node-to-node communication"), - N_("The node elected Designated Controller (DC) will consider an action " - "failed if it does not get a response from the node executing the " - "action within this time (after considering the action's own " - "timeout). The \"correct\" value will depend on the speed and " - "load of your network and cluster nodes.") - }, - { - "batch-limit", NULL, "integer", NULL, - "0", pcmk__valid_number, - N_("Maximum number of jobs that the cluster may execute in parallel " - "across all nodes"), - N_("The \"correct\" value will depend on the speed and load of your " - "network and cluster nodes. If set to 0, the cluster will " - "impose a dynamically calculated limit when any node has a " - "high load.") - }, - { - "migration-limit", NULL, "integer", NULL, - "-1", pcmk__valid_number, - N_("The number of live migration actions that the cluster is allowed " - "to execute in parallel on a node (-1 means no limit)") - }, - - /* Orphans and stopping */ - { - "stop-all-resources", NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("Whether the cluster should stop all active resources"), - NULL - }, - { - "stop-orphan-resources", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("Whether to stop resources that were removed from the configuration"), - NULL - }, - { - "stop-orphan-actions", NULL, "boolean", NULL, - "true", pcmk__valid_boolean, - N_("Whether to cancel recurring actions removed from the configuration"), - NULL - }, - { - "remove-after-stop", NULL, "boolean", NULL, - "false", pcmk__valid_boolean, - N_("*** Deprecated *** Whether to remove stopped resources from " - "the executor"), - N_("Values other than default are poorly tested and potentially dangerous." - " This option will be removed in a future release.") - }, +#include <crm/pengine/common_compat.h> - /* Storing inputs */ - { - "pe-error-series-max", NULL, "integer", NULL, - "-1", pcmk__valid_number, - N_("The number of scheduler inputs resulting in errors to save"), - N_("Zero to disable, -1 to store unlimited.") - }, - { - "pe-warn-series-max", NULL, "integer", NULL, - "5000", pcmk__valid_number, - N_("The number of scheduler inputs resulting in warnings to save"), - N_("Zero to disable, -1 to store unlimited.") - }, - { - "pe-input-series-max", NULL, "integer", NULL, - "4000", pcmk__valid_number, - N_("The number of scheduler inputs without errors or warnings to save"), - N_("Zero to disable, -1 to store unlimited.") - }, - - /* Node health */ - { - PCMK__OPT_NODE_HEALTH_STRATEGY, NULL, "select", - PCMK__VALUE_NONE ", " PCMK__VALUE_MIGRATE_ON_RED ", " - PCMK__VALUE_ONLY_GREEN ", " PCMK__VALUE_PROGRESSIVE ", " - PCMK__VALUE_CUSTOM, - PCMK__VALUE_NONE, pcmk__validate_health_strategy, - N_("How cluster should react to node health attributes"), - N_("Requires external entities to create node attributes (named with " - "the prefix \"#health\") with values \"red\", " - "\"yellow\", or \"green\".") - }, - { - PCMK__OPT_NODE_HEALTH_BASE, NULL, "integer", NULL, - "0", pcmk__valid_number, - N_("Base health score assigned to a node"), - N_("Only used when \"node-health-strategy\" is set to \"progressive\".") - }, - { - PCMK__OPT_NODE_HEALTH_GREEN, NULL, "integer", NULL, - "0", pcmk__valid_number, - N_("The score to use for a node health attribute whose value is \"green\""), - N_("Only used when \"node-health-strategy\" is set to \"custom\" or \"progressive\".") - }, - { - PCMK__OPT_NODE_HEALTH_YELLOW, NULL, "integer", NULL, - "0", pcmk__valid_number, - N_("The score to use for a node health attribute whose value is \"yellow\""), - N_("Only used when \"node-health-strategy\" is set to \"custom\" or \"progressive\".") - }, - { - PCMK__OPT_NODE_HEALTH_RED, NULL, "integer", NULL, - "-INFINITY", pcmk__valid_number, - N_("The score to use for a node health attribute whose value is \"red\""), - N_("Only used when \"node-health-strategy\" is set to \"custom\" or \"progressive\".") - }, - - /*Placement Strategy*/ - { - "placement-strategy", NULL, "select", - "default, utilization, minimal, balanced", - "default", check_placement_strategy, - N_("How the cluster should allocate resources to nodes"), - NULL - }, -}; - -void -pe_metadata(pcmk__output_t *out) +const char * +role2text(enum rsc_role_e role) { - const char *desc_short = "Pacemaker scheduler options"; - const char *desc_long = "Cluster options used by Pacemaker's scheduler"; - - gchar *s = pcmk__format_option_metadata("pacemaker-schedulerd", desc_short, - desc_long, pe_opts, - PCMK__NELEM(pe_opts)); - out->output_xml(out, "metadata", s); - g_free(s); + return pcmk_role_text(role); } -void -verify_pe_options(GHashTable * options) +enum rsc_role_e +text2role(const char *role) { - pcmk__validate_cluster_options(options, pe_opts, PCMK__NELEM(pe_opts)); + return pcmk_parse_role(role); } const char * -pe_pref(GHashTable * options, const char *name) +task2text(enum action_tasks task) { - return pcmk__cluster_option(options, pe_opts, PCMK__NELEM(pe_opts), name); -} - -const char * -fail2text(enum action_fail_response fail) -{ - const char *result = "<unknown>"; - - switch (fail) { - case pcmk_on_fail_ignore: - result = "ignore"; - break; - case pcmk_on_fail_demote: - result = "demote"; - break; - case pcmk_on_fail_block: - result = "block"; - break; - case pcmk_on_fail_restart: - result = "recover"; - break; - case pcmk_on_fail_ban: - result = "migrate"; - break; - case pcmk_on_fail_stop: - result = "stop"; - break; - case pcmk_on_fail_fence_node: - result = "fence"; - break; - case pcmk_on_fail_standby_node: - result = "standby"; - break; - case pcmk_on_fail_restart_container: - result = "restart-container"; - break; - case pcmk_on_fail_reset_remote: - result = "reset-remote"; - break; - } - return result; + return pcmk_action_text(task); } enum action_tasks text2task(const char *task) { - if (pcmk__str_eq(task, PCMK_ACTION_STOP, pcmk__str_casei)) { - return pcmk_action_stop; - - } else if (pcmk__str_eq(task, PCMK_ACTION_STOPPED, pcmk__str_casei)) { - return pcmk_action_stopped; - - } else if (pcmk__str_eq(task, PCMK_ACTION_START, pcmk__str_casei)) { - return pcmk_action_start; - - } else if (pcmk__str_eq(task, PCMK_ACTION_RUNNING, pcmk__str_casei)) { - return pcmk_action_started; - - } else if (pcmk__str_eq(task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_casei)) { - return pcmk_action_shutdown; - - } else if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) { - return pcmk_action_fence; - - } else if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)) { - return pcmk_action_monitor; - - } else if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_casei)) { - return pcmk_action_notify; - - } else if (pcmk__str_eq(task, PCMK_ACTION_NOTIFIED, pcmk__str_casei)) { - return pcmk_action_notified; - - } else if (pcmk__str_eq(task, PCMK_ACTION_PROMOTE, pcmk__str_casei)) { - return pcmk_action_promote; - - } else if (pcmk__str_eq(task, PCMK_ACTION_DEMOTE, pcmk__str_casei)) { - return pcmk_action_demote; - - } else if (pcmk__str_eq(task, PCMK_ACTION_PROMOTED, pcmk__str_casei)) { - return pcmk_action_promoted; - - } else if (pcmk__str_eq(task, PCMK_ACTION_DEMOTED, pcmk__str_casei)) { - return pcmk_action_demoted; - } - return pcmk_action_unspecified; + return pcmk_parse_action(task); } const char * -task2text(enum action_tasks task) -{ - const char *result = "<unknown>"; - - switch (task) { - case pcmk_action_unspecified: - result = "no_action"; - break; - case pcmk_action_stop: - result = PCMK_ACTION_STOP; - break; - case pcmk_action_stopped: - result = PCMK_ACTION_STOPPED; - break; - case pcmk_action_start: - result = PCMK_ACTION_START; - break; - case pcmk_action_started: - result = PCMK_ACTION_RUNNING; - break; - case pcmk_action_shutdown: - result = PCMK_ACTION_DO_SHUTDOWN; - break; - case pcmk_action_fence: - result = PCMK_ACTION_STONITH; - break; - case pcmk_action_monitor: - result = PCMK_ACTION_MONITOR; - break; - case pcmk_action_notify: - result = PCMK_ACTION_NOTIFY; - break; - case pcmk_action_notified: - result = PCMK_ACTION_NOTIFIED; - break; - case pcmk_action_promote: - result = PCMK_ACTION_PROMOTE; - break; - case pcmk_action_promoted: - result = PCMK_ACTION_PROMOTED; - break; - case pcmk_action_demote: - result = PCMK_ACTION_DEMOTE; - break; - case pcmk_action_demoted: - result = PCMK_ACTION_DEMOTED; - break; - } - - return result; -} - -const char * -role2text(enum rsc_role_e role) -{ - switch (role) { - case pcmk_role_stopped: - return PCMK__ROLE_STOPPED; - - case pcmk_role_started: - return PCMK__ROLE_STARTED; - - case pcmk_role_unpromoted: -#ifdef PCMK__COMPAT_2_0 - return PCMK__ROLE_UNPROMOTED_LEGACY; -#else - return PCMK__ROLE_UNPROMOTED; -#endif - - case pcmk_role_promoted: -#ifdef PCMK__COMPAT_2_0 - return PCMK__ROLE_PROMOTED_LEGACY; -#else - return PCMK__ROLE_PROMOTED; -#endif - - default: // pcmk_role_unknown - return PCMK__ROLE_UNKNOWN; - } -} - -enum rsc_role_e -text2role(const char *role) +pe_pref(GHashTable * options, const char *name) { - CRM_ASSERT(role != NULL); - if (pcmk__str_eq(role, PCMK__ROLE_STOPPED, pcmk__str_casei)) { - return pcmk_role_stopped; - } else if (pcmk__str_eq(role, PCMK__ROLE_STARTED, pcmk__str_casei)) { - return pcmk_role_started; - } else if (pcmk__strcase_any_of(role, PCMK__ROLE_UNPROMOTED, - PCMK__ROLE_UNPROMOTED_LEGACY, NULL)) { - return pcmk_role_unpromoted; - } else if (pcmk__strcase_any_of(role, PCMK__ROLE_PROMOTED, - PCMK__ROLE_PROMOTED_LEGACY, NULL)) { - return pcmk_role_promoted; - } else if (pcmk__str_eq(role, PCMK__ROLE_UNKNOWN, pcmk__str_casei)) { - return pcmk_role_unknown; - } - crm_err("Unknown role: %s", role); - return pcmk_role_unknown; + return pcmk__cluster_option(options, name); } -void -add_hash_param(GHashTable * hash, const char *name, const char *value) -{ - CRM_CHECK(hash != NULL, return); - - crm_trace("Adding name='%s' value='%s' to hash table", - pcmk__s(name, "<null>"), pcmk__s(value, "<null>")); - if (name == NULL || value == NULL) { - return; - - } else if (pcmk__str_eq(value, "#default", pcmk__str_casei)) { - return; - - } else if (g_hash_table_lookup(hash, name) == NULL) { - g_hash_table_insert(hash, strdup(name), strdup(value)); - } -} - -/*! - * \internal - * \brief Look up an attribute value on the appropriate node - * - * If \p node is a guest node and either the \c XML_RSC_ATTR_TARGET meta - * attribute is set to "host" for \p rsc or \p force_host is \c true, query the - * attribute on the node's host. Otherwise, query the attribute on \p node - * itself. - * - * \param[in] node Node to query attribute value on by default - * \param[in] name Name of attribute to query - * \param[in] rsc Resource on whose behalf we're querying - * \param[in] node_type Type of resource location lookup - * \param[in] force_host Force a lookup on the guest node's host, regardless of - * the \c XML_RSC_ATTR_TARGET value - * - * \return Value of the attribute on \p node or on the host of \p node - * - * \note If \p force_host is \c true, \p node \e must be a guest node. - */ const char * -pe__node_attribute_calculated(const pcmk_node_t *node, const char *name, - const pcmk_resource_t *rsc, - enum pcmk__rsc_node node_type, - bool force_host) +fail2text(enum action_fail_response fail) { - // @TODO: Use pe__is_guest_node() after merging libpe_{rules,status} - bool is_guest = (node != NULL) - && (node->details->type == pcmk_node_variant_remote) - && (node->details->remote_rsc != NULL) - && (node->details->remote_rsc->container != NULL); - const char *source = NULL; - const char *node_type_s = NULL; - const char *reason = NULL; - - const pcmk_resource_t *container = NULL; - const pcmk_node_t *host = NULL; - - CRM_ASSERT((node != NULL) && (name != NULL) && (rsc != NULL) - && (!force_host || is_guest)); - - /* Ignore XML_RSC_ATTR_TARGET if node is not a guest node. This represents a - * user configuration error. - */ - source = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET); - if (!force_host - && (!is_guest || !pcmk__str_eq(source, "host", pcmk__str_casei))) { - - return g_hash_table_lookup(node->details->attrs, name); - } - - container = node->details->remote_rsc->container; - - switch (node_type) { - case pcmk__rsc_node_assigned: - node_type_s = "assigned"; - host = container->allocated_to; - if (host == NULL) { - reason = "not assigned"; - } - break; - - case pcmk__rsc_node_current: - node_type_s = "current"; - - if (container->running_on != NULL) { - host = container->running_on->data; - } - if (host == NULL) { - reason = "inactive"; - } - break; - - default: - // Add support for other enum pcmk__rsc_node values if needed - CRM_ASSERT(false); - break; - } - - if (host != NULL) { - const char *value = g_hash_table_lookup(host->details->attrs, name); - - pe_rsc_trace(rsc, - "%s: Value lookup for %s on %s container host %s %s%s", - rsc->id, name, node_type_s, pe__node_name(host), - ((value != NULL)? "succeeded: " : "failed"), - pcmk__s(value, "")); - return value; - } - pe_rsc_trace(rsc, - "%s: Not looking for %s on %s container host: %s is %s", - rsc->id, name, node_type_s, container->id, reason); - return NULL; + return pcmk_on_fail_text(fail); } -const char * -pe_node_attribute_raw(const pcmk_node_t *node, const char *name) -{ - if(node == NULL) { - return NULL; - } - return g_hash_table_lookup(node->details->attrs, name); -} +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c index 0ab2e04..bd141fb 100644 --- a/lib/pengine/complex.c +++ b/lib/pengine/complex.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. * @@ -11,7 +11,7 @@ #include <crm/pengine/rules.h> #include <crm/pengine/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/common/scheduler_internal.h> @@ -85,30 +85,53 @@ pcmk_rsc_methods_t resource_class_functions[] = { static enum pe_obj_types get_resource_type(const char *name) { - if (pcmk__str_eq(name, XML_CIB_TAG_RESOURCE, pcmk__str_casei)) { + if (pcmk__str_eq(name, PCMK_XE_PRIMITIVE, pcmk__str_casei)) { return pcmk_rsc_variant_primitive; - } else if (pcmk__str_eq(name, XML_CIB_TAG_GROUP, pcmk__str_casei)) { + } else if (pcmk__str_eq(name, PCMK_XE_GROUP, pcmk__str_casei)) { return pcmk_rsc_variant_group; - } else if (pcmk__str_eq(name, XML_CIB_TAG_INCARNATION, pcmk__str_casei)) { + } else if (pcmk__str_eq(name, PCMK_XE_CLONE, pcmk__str_casei)) { return pcmk_rsc_variant_clone; - } else if (pcmk__str_eq(name, PCMK_XE_PROMOTABLE_LEGACY, pcmk__str_casei)) { + } else if (pcmk__str_eq(name, PCMK__XE_PROMOTABLE_LEGACY, + pcmk__str_casei)) { // @COMPAT deprecated since 2.0.0 return pcmk_rsc_variant_clone; - } else if (pcmk__str_eq(name, XML_CIB_TAG_CONTAINER, pcmk__str_casei)) { + } else if (pcmk__str_eq(name, PCMK_XE_BUNDLE, pcmk__str_casei)) { return pcmk_rsc_variant_bundle; } return pcmk_rsc_variant_unknown; } +/*! + * \internal + * \brief Insert a meta-attribute if not already present + * + * \param[in] key Meta-attribute name + * \param[in] value Meta-attribute value to add if not already present + * \param[in,out] table Meta-attribute hash table to insert into + * + * \note This is like pcmk__insert_meta() except it won't overwrite existing + * values. + */ static void dup_attr(gpointer key, gpointer value, gpointer user_data) { - add_hash_param(user_data, key, value); + GHashTable *table = user_data; + + CRM_CHECK((key != NULL) && (table != NULL), return); + if (pcmk__str_eq((const char *) value, "#default", pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting meta-attributes (such as %s) to " + "the explicit value '#default' is deprecated and " + "will be removed in a future release", + (const char *) key); + } else if ((value != NULL) && (g_hash_table_lookup(table, key) == NULL)) { + pcmk__insert_dup(table, (const char *) key, (const char *) value); + } } static void @@ -123,27 +146,21 @@ expand_parents_fixed_nvpairs(pcmk_resource_t *rsc, return ; } - /* Search all parent resources, get the fixed value of "meta_attributes" set only in the original xml, and stack it in the hash table. */ - /* The fixed value of the lower parent resource takes precedence and is not overwritten. */ + /* Search all parent resources, get the fixed value of + * PCMK_XE_META_ATTRIBUTES set only in the original xml, and stack it in the + * hash table. The fixed value of the lower parent resource takes precedence + * and is not overwritten. + */ while(p != NULL) { /* A hash table for comparison is generated, including the id-ref. */ - pe__unpack_dataset_nvpairs(p->xml, XML_TAG_META_SETS, rule_data, + pe__unpack_dataset_nvpairs(p->xml, PCMK_XE_META_ATTRIBUTES, rule_data, parent_orig_meta, NULL, FALSE, scheduler); p = p->parent; } - /* If there is a fixed value of "meta_attributes" of the parent resource, it will be processed. */ if (parent_orig_meta != NULL) { - GHashTableIter iter; - char *key = NULL; - char *value = NULL; - - g_hash_table_iter_init(&iter, parent_orig_meta); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) { - /* Parameters set in the original xml of the parent resource will also try to overwrite the child resource. */ - /* Attributes that already exist in the child lease are not updated. */ - dup_attr(key, value, meta_hash); - } + // This will not overwrite any values already existing for child + g_hash_table_foreach(parent_orig_meta, dup_attr, meta_hash); } if (parent_orig_meta != NULL) { @@ -158,14 +175,13 @@ get_meta_attributes(GHashTable * meta_hash, pcmk_resource_t * rsc, pcmk_node_t *node, pcmk_scheduler_t *scheduler) { pe_rsc_eval_data_t rsc_rule_data = { - .standard = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS), - .provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER), - .agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE) + .standard = crm_element_value(rsc->xml, PCMK_XA_CLASS), + .provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER), + .agent = crm_element_value(rsc->xml, PCMK_XA_TYPE) }; pe_rule_eval_data_t rule_data = { .node_hash = NULL, - .role = pcmk_role_unknown, .now = scheduler->now, .match_data = NULL, .rsc_data = &rsc_rule_data, @@ -173,31 +189,39 @@ get_meta_attributes(GHashTable * meta_hash, pcmk_resource_t * rsc, }; if (node) { + /* @COMPAT Support for node attribute expressions in rules for + * meta-attributes is deprecated. When we can break behavioral backward + * compatibility, drop this block. + */ rule_data.node_hash = node->details->attrs; } for (xmlAttrPtr a = pcmk__xe_first_attr(rsc->xml); a != NULL; a = a->next) { - const char *prop_name = (const char *) a->name; - const char *prop_value = pcmk__xml_attr_value(a); - - add_hash_param(meta_hash, prop_name, prop_value); + if (a->children != NULL) { + dup_attr((gpointer) a->name, (gpointer) a->children->content, + meta_hash); + } } - pe__unpack_dataset_nvpairs(rsc->xml, XML_TAG_META_SETS, &rule_data, + pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_META_ATTRIBUTES, &rule_data, meta_hash, NULL, FALSE, scheduler); - /* Set the "meta_attributes" explicitly set in the parent resource to the hash table of the child resource. */ - /* If it is already explicitly set as a child, it will not be overwritten. */ + /* Set the PCMK_XE_META_ATTRIBUTES explicitly set in the parent resource to + * the hash table of the child resource. If it is already explicitly set as + * a child, it will not be overwritten. + */ if (rsc->parent != NULL) { expand_parents_fixed_nvpairs(rsc, &rule_data, meta_hash, scheduler); } /* check the defaults */ - pe__unpack_dataset_nvpairs(scheduler->rsc_defaults, XML_TAG_META_SETS, + pe__unpack_dataset_nvpairs(scheduler->rsc_defaults, PCMK_XE_META_ATTRIBUTES, &rule_data, meta_hash, NULL, FALSE, scheduler); - /* If there is "meta_attributes" that the parent resource has not explicitly set, set a value that is not set from rsc_default either. */ - /* The values already set up to this point will not be overwritten. */ + /* If there is PCMK_XE_META_ATTRIBUTES that the parent resource has not + * explicitly set, set a value that is not set from PCMK_XE_RSC_DEFAULTS + * either. The values already set up to this point will not be overwritten. + */ if (rsc->parent) { g_hash_table_foreach(rsc->parent->meta, dup_attr, meta_hash); } @@ -209,7 +233,6 @@ get_rsc_attributes(GHashTable *meta_hash, const pcmk_resource_t *rsc, { pe_rule_eval_data_t rule_data = { .node_hash = NULL, - .role = pcmk_role_unknown, .now = scheduler->now, .match_data = NULL, .rsc_data = NULL, @@ -220,30 +243,44 @@ get_rsc_attributes(GHashTable *meta_hash, const pcmk_resource_t *rsc, rule_data.node_hash = node->details->attrs; } - pe__unpack_dataset_nvpairs(rsc->xml, XML_TAG_ATTR_SETS, &rule_data, - meta_hash, NULL, FALSE, scheduler); + pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_INSTANCE_ATTRIBUTES, + &rule_data, meta_hash, NULL, FALSE, scheduler); /* set anything else based on the parent */ if (rsc->parent != NULL) { get_rsc_attributes(meta_hash, rsc->parent, node, scheduler); } else { + if (pcmk__xe_first_child(scheduler->rsc_defaults, + PCMK_XE_INSTANCE_ATTRIBUTES, NULL, + NULL) != NULL) { + /* Not possible with schema validation enabled + * + * @COMPAT Drop support when we can break behavioral + * backward compatibility + */ + pcmk__warn_once(pcmk__wo_instance_defaults, + "Support for " PCMK_XE_INSTANCE_ATTRIBUTES " in " + PCMK_XE_RSC_DEFAULTS " is deprecated and will be " + "removed in a future release"); + } + /* and finally check the defaults */ - pe__unpack_dataset_nvpairs(scheduler->rsc_defaults, XML_TAG_ATTR_SETS, - &rule_data, meta_hash, NULL, FALSE, - scheduler); + pe__unpack_dataset_nvpairs(scheduler->rsc_defaults, + PCMK_XE_INSTANCE_ATTRIBUTES, &rule_data, + meta_hash, NULL, FALSE, scheduler); } } static char * template_op_key(xmlNode * op) { - const char *name = crm_element_value(op, "name"); - const char *role = crm_element_value(op, "role"); + const char *name = crm_element_value(op, PCMK_XA_NAME); + const char *role = crm_element_value(op, PCMK_XA_ROLE); char *key = NULL; if ((role == NULL) - || pcmk__strcase_any_of(role, PCMK__ROLE_STARTED, PCMK__ROLE_UNPROMOTED, + || pcmk__strcase_any_of(role, PCMK_ROLE_STARTED, PCMK_ROLE_UNPROMOTED, PCMK__ROLE_UNPROMOTED_LEGACY, NULL)) { role = PCMK__ROLE_UNKNOWN; } @@ -263,62 +300,59 @@ unpack_template(xmlNode *xml_obj, xmlNode **expanded_xml, xmlNode *rsc_ops = NULL; xmlNode *template_ops = NULL; const char *template_ref = NULL; - const char *clone = NULL; const char *id = NULL; if (xml_obj == NULL) { - pe_err("No resource object for template unpacking"); + pcmk__config_err("No resource object for template unpacking"); return FALSE; } - template_ref = crm_element_value(xml_obj, XML_CIB_TAG_RSC_TEMPLATE); + template_ref = crm_element_value(xml_obj, PCMK_XA_TEMPLATE); if (template_ref == NULL) { return TRUE; } - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pe_err("'%s' object must have a id", xml_obj->name); + pcmk__config_err("'%s' object must have a id", xml_obj->name); return FALSE; } if (pcmk__str_eq(template_ref, id, pcmk__str_none)) { - pe_err("The resource object '%s' should not reference itself", id); + pcmk__config_err("The resource object '%s' should not reference itself", + id); return FALSE; } - cib_resources = get_xpath_object("//" XML_CIB_TAG_RESOURCES, - scheduler->input, LOG_TRACE); + cib_resources = get_xpath_object("//" PCMK_XE_RESOURCES, scheduler->input, + LOG_TRACE); if (cib_resources == NULL) { - pe_err("No resources configured"); + pcmk__config_err("No resources configured"); return FALSE; } - template = pcmk__xe_match(cib_resources, XML_CIB_TAG_RSC_TEMPLATE, - XML_ATTR_ID, template_ref); + template = pcmk__xe_first_child(cib_resources, PCMK_XE_TEMPLATE, + PCMK_XA_ID, template_ref); if (template == NULL) { - pe_err("No template named '%s'", template_ref); + pcmk__config_err("No template named '%s'", template_ref); return FALSE; } - new_xml = copy_xml(template); + new_xml = pcmk__xml_copy(NULL, template); xmlNodeSetName(new_xml, xml_obj->name); - crm_xml_add(new_xml, XML_ATTR_ID, id); - - clone = crm_element_value(xml_obj, XML_RSC_ATTR_INCARNATION); - if(clone) { - crm_xml_add(new_xml, XML_RSC_ATTR_INCARNATION, clone); - } + crm_xml_add(new_xml, PCMK_XA_ID, id); + crm_xml_add(new_xml, PCMK__META_CLONE, + crm_element_value(xml_obj, PCMK__META_CLONE)); - template_ops = find_xml_node(new_xml, "operations", FALSE); + template_ops = pcmk__xe_first_child(new_xml, PCMK_XE_OPERATIONS, NULL, + NULL); - for (child_xml = pcmk__xe_first_child(xml_obj); child_xml != NULL; - child_xml = pcmk__xe_next(child_xml)) { - xmlNode *new_child = NULL; + for (child_xml = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + child_xml != NULL; child_xml = pcmk__xe_next(child_xml)) { - new_child = add_node_copy(new_xml, child_xml); + xmlNode *new_child = pcmk__xml_copy(new_xml, child_xml); - if (pcmk__str_eq((const char *)new_child->name, "operations", pcmk__str_none)) { + if (pcmk__xe_is(new_child, PCMK_XE_OPERATIONS)) { rsc_ops = new_child; } } @@ -327,7 +361,7 @@ unpack_template(xmlNode *xml_obj, xmlNode **expanded_xml, xmlNode *op = NULL; GHashTable *rsc_ops_hash = pcmk__strkey_table(free, NULL); - for (op = pcmk__xe_first_child(rsc_ops); op != NULL; + for (op = pcmk__xe_first_child(rsc_ops, NULL, NULL, NULL); op != NULL; op = pcmk__xe_next(op)) { char *key = template_op_key(op); @@ -335,13 +369,13 @@ unpack_template(xmlNode *xml_obj, xmlNode **expanded_xml, g_hash_table_insert(rsc_ops_hash, key, op); } - for (op = pcmk__xe_first_child(template_ops); op != NULL; - op = pcmk__xe_next(op)) { + for (op = pcmk__xe_first_child(template_ops, NULL, NULL, NULL); + op != NULL; op = pcmk__xe_next(op)) { char *key = template_op_key(op); if (g_hash_table_lookup(rsc_ops_hash, key) == NULL) { - add_node_copy(rsc_ops, op); + pcmk__xml_copy(rsc_ops, op); } free(key); @@ -375,23 +409,25 @@ add_template_rsc(xmlNode *xml_obj, pcmk_scheduler_t *scheduler) const char *id = NULL; if (xml_obj == NULL) { - pe_err("No resource object for processing resource list of template"); + pcmk__config_err("No resource object for processing resource list " + "of template"); return FALSE; } - template_ref = crm_element_value(xml_obj, XML_CIB_TAG_RSC_TEMPLATE); + template_ref = crm_element_value(xml_obj, PCMK_XA_TEMPLATE); if (template_ref == NULL) { return TRUE; } - id = ID(xml_obj); + id = pcmk__xe_id(xml_obj); if (id == NULL) { - pe_err("'%s' object must have a id", xml_obj->name); + pcmk__config_err("'%s' object must have a id", xml_obj->name); return FALSE; } if (pcmk__str_eq(template_ref, id, pcmk__str_none)) { - pe_err("The resource object '%s' should not reference itself", id); + pcmk__config_err("The resource object '%s' should not reference itself", + id); return FALSE; } @@ -406,19 +442,21 @@ static bool detect_promotable(pcmk_resource_t *rsc) { const char *promotable = g_hash_table_lookup(rsc->meta, - XML_RSC_ATTR_PROMOTABLE); + PCMK_META_PROMOTABLE); if (crm_is_true(promotable)) { return TRUE; } // @COMPAT deprecated since 2.0.0 - if (pcmk__xe_is(rsc->xml, PCMK_XE_PROMOTABLE_LEGACY)) { - /* @TODO in some future version, pe_warn_once() here, - * then drop support in even later version - */ - g_hash_table_insert(rsc->meta, strdup(XML_RSC_ATTR_PROMOTABLE), - strdup(XML_BOOLEAN_TRUE)); + if (pcmk__xe_is(rsc->xml, PCMK__XE_PROMOTABLE_LEGACY)) { + pcmk__warn_once(pcmk__wo_master_element, + "Support for <" PCMK__XE_PROMOTABLE_LEGACY "> (such " + "as in %s) is deprecated and will be removed in a " + "future release. Use <" PCMK_XE_CLONE "> with a " + PCMK_META_PROMOTABLE " meta-attribute instead.", + rsc->id); + pcmk__insert_dup(rsc->meta, PCMK_META_PROMOTABLE, PCMK_VALUE_TRUE); return TRUE; } return FALSE; @@ -481,75 +519,75 @@ pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, /*! * \internal - * \brief Unpack a resource's "requires" meta-attribute + * \brief Unpack a resource's \c PCMK_META_REQUIRES meta-attribute * * \param[in,out] rsc Resource being unpacked - * \param[in] value Value of "requires" meta-attribute + * \param[in] value Value of \c PCMK_META_REQUIRES meta-attribute * \param[in] is_default Whether \p value was selected by default */ static void unpack_requires(pcmk_resource_t *rsc, const char *value, bool is_default) { - if (pcmk__str_eq(value, PCMK__VALUE_NOTHING, pcmk__str_casei)) { + if (pcmk__str_eq(value, PCMK_VALUE_NOTHING, pcmk__str_casei)) { - } else if (pcmk__str_eq(value, PCMK__VALUE_QUORUM, pcmk__str_casei)) { - pe__set_resource_flags(rsc, pcmk_rsc_needs_quorum); + } else if (pcmk__str_eq(value, PCMK_VALUE_QUORUM, pcmk__str_casei)) { + pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_quorum); - } else if (pcmk__str_eq(value, PCMK__VALUE_FENCING, pcmk__str_casei)) { - pe__set_resource_flags(rsc, pcmk_rsc_needs_fencing); + } else if (pcmk__str_eq(value, PCMK_VALUE_FENCING, pcmk__str_casei)) { + pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_fencing); if (!pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { pcmk__config_warn("%s requires fencing but fencing is disabled", rsc->id); } - } else if (pcmk__str_eq(value, PCMK__VALUE_UNFENCING, pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_UNFENCING, pcmk__str_casei)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_fence_device)) { - pcmk__config_warn("Resetting \"" XML_RSC_ATTR_REQUIRES "\" for %s " - "to \"" PCMK__VALUE_QUORUM "\" because fencing " + pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s " + "to \"" PCMK_VALUE_QUORUM "\" because fencing " "devices cannot require unfencing", rsc->id); - unpack_requires(rsc, PCMK__VALUE_QUORUM, true); + unpack_requires(rsc, PCMK_VALUE_QUORUM, true); return; } else if (!pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { - pcmk__config_warn("Resetting \"" XML_RSC_ATTR_REQUIRES "\" for %s " - "to \"" PCMK__VALUE_QUORUM "\" because fencing " - "is disabled", rsc->id); - unpack_requires(rsc, PCMK__VALUE_QUORUM, true); + pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s " + "to \"" PCMK_VALUE_QUORUM "\" because fencing is " + "disabled", rsc->id); + unpack_requires(rsc, PCMK_VALUE_QUORUM, true); return; } else { - pe__set_resource_flags(rsc, pcmk_rsc_needs_fencing - |pcmk_rsc_needs_unfencing); + pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_fencing + |pcmk_rsc_needs_unfencing); } } else { const char *orig_value = value; if (pcmk_is_set(rsc->flags, pcmk_rsc_fence_device)) { - value = PCMK__VALUE_QUORUM; + value = PCMK_VALUE_QUORUM; - } else if ((rsc->variant == pcmk_rsc_variant_primitive) + } else if (pcmk__is_primitive(rsc) && xml_contains_remote_node(rsc->xml)) { - value = PCMK__VALUE_QUORUM; + value = PCMK_VALUE_QUORUM; } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_enable_unfencing)) { - value = PCMK__VALUE_UNFENCING; + value = PCMK_VALUE_UNFENCING; } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { - value = PCMK__VALUE_FENCING; + value = PCMK_VALUE_FENCING; } else if (rsc->cluster->no_quorum_policy == pcmk_no_quorum_ignore) { - value = PCMK__VALUE_NOTHING; + value = PCMK_VALUE_NOTHING; } else { - value = PCMK__VALUE_QUORUM; + value = PCMK_VALUE_QUORUM; } if (orig_value != NULL) { - pcmk__config_err("Resetting '" XML_RSC_ATTR_REQUIRES "' for %s " + pcmk__config_err("Resetting '" PCMK_META_REQUIRES "' for %s " "to '%s' because '%s' is not valid", rsc->id, value, orig_value); } @@ -557,30 +595,28 @@ unpack_requires(pcmk_resource_t *rsc, const char *value, bool is_default) return; } - pe_rsc_trace(rsc, "\tRequired to start: %s%s", value, - (is_default? " (default)" : "")); + pcmk__rsc_trace(rsc, "\tRequired to start: %s%s", value, + (is_default? " (default)" : "")); } -#ifndef PCMK__COMPAT_2_0 static void warn_about_deprecated_classes(pcmk_resource_t *rsc) { - const char *std = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *std = crm_element_value(rsc->xml, PCMK_XA_CLASS); if (pcmk__str_eq(std, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_none)) { - pe_warn_once(pcmk__wo_upstart, - "Support for Upstart resources (such as %s) is deprecated " - "and will be removed in a future release of Pacemaker", - rsc->id); + pcmk__warn_once(pcmk__wo_upstart, + "Support for Upstart resources (such as %s) is " + "deprecated and will be removed in a future release", + rsc->id); } else if (pcmk__str_eq(std, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_none)) { - pe_warn_once(pcmk__wo_nagios, - "Support for Nagios resources (such as %s) is deprecated " - "and will be removed in a future release of Pacemaker", - rsc->id); + pcmk__warn_once(pcmk__wo_nagios, + "Support for Nagios resources (such as %s) is " + "deprecated and will be removed in a future release", + rsc->id); } } -#endif /*! * \internal @@ -612,7 +648,6 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, pe_rule_eval_data_t rule_data = { .node_hash = NULL, - .role = pcmk_role_unknown, .now = NULL, .match_data = NULL, .rsc_data = NULL, @@ -628,10 +663,10 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, crm_log_xml_trace(xml_obj, "[raw XML]"); - id = crm_element_value(xml_obj, XML_ATTR_ID); + id = crm_element_value(xml_obj, PCMK_XA_ID); if (id == NULL) { - pe_err("Ignoring <%s> configuration without " XML_ATTR_ID, - xml_obj->name); + pcmk__config_err("Ignoring <%s> configuration without " PCMK_XA_ID, + xml_obj->name); return pcmk_rc_unpack_error; } @@ -641,7 +676,7 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, *rsc = calloc(1, sizeof(pcmk_resource_t)); if (*rsc == NULL) { - crm_crit("Unable to allocate memory for resource '%s'", id); + pcmk__sched_err("Unable to allocate memory for resource '%s'", id); return ENOMEM; } (*rsc)->cluster = scheduler; @@ -660,45 +695,43 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, (*rsc)->parent = parent; - ops = find_xml_node((*rsc)->xml, "operations", FALSE); + ops = pcmk__xe_first_child((*rsc)->xml, PCMK_XE_OPERATIONS, NULL, NULL); (*rsc)->ops_xml = expand_idref(ops, scheduler->input); (*rsc)->variant = get_resource_type((const char *) (*rsc)->xml->name); if ((*rsc)->variant == pcmk_rsc_variant_unknown) { - pe_err("Ignoring resource '%s' of unknown type '%s'", - id, (*rsc)->xml->name); + pcmk__config_err("Ignoring resource '%s' of unknown type '%s'", + id, (*rsc)->xml->name); common_free(*rsc); *rsc = NULL; return pcmk_rc_unpack_error; } -#ifndef PCMK__COMPAT_2_0 - warn_about_deprecated_classes(*rsc); -#endif - (*rsc)->meta = pcmk__strkey_table(free, free); (*rsc)->allowed_nodes = pcmk__strkey_table(NULL, free); (*rsc)->known_on = pcmk__strkey_table(NULL, free); - value = crm_element_value((*rsc)->xml, XML_RSC_ATTR_INCARNATION); + value = crm_element_value((*rsc)->xml, PCMK__META_CLONE); if (value) { (*rsc)->id = crm_strdup_printf("%s:%s", id, value); - add_hash_param((*rsc)->meta, XML_RSC_ATTR_INCARNATION, value); + pcmk__insert_meta(*rsc, PCMK__META_CLONE, value); } else { (*rsc)->id = strdup(id); } + warn_about_deprecated_classes(*rsc); + (*rsc)->fns = &resource_class_functions[(*rsc)->variant]; get_meta_attributes((*rsc)->meta, *rsc, NULL, scheduler); (*rsc)->parameters = pe_rsc_params(*rsc, NULL, scheduler); // \deprecated (*rsc)->flags = 0; - pe__set_resource_flags(*rsc, pcmk_rsc_runnable|pcmk_rsc_unassigned); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_runnable|pcmk_rsc_unassigned); if (!pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) { - pe__set_resource_flags(*rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed); } (*rsc)->rsc_cons = NULL; @@ -709,151 +742,186 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, (*rsc)->recovery_type = pcmk_multiply_active_restart; (*rsc)->stickiness = 0; - (*rsc)->migration_threshold = INFINITY; + (*rsc)->migration_threshold = PCMK_SCORE_INFINITY; (*rsc)->failure_timeout = 0; - value = g_hash_table_lookup((*rsc)->meta, XML_CIB_ATTR_PRIORITY); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_PRIORITY); (*rsc)->priority = char2score(value); - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_CRITICAL); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_CRITICAL); if ((value == NULL) || crm_is_true(value)) { - pe__set_resource_flags(*rsc, pcmk_rsc_critical); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_critical); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_NOTIFY); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_NOTIFY); if (crm_is_true(value)) { - pe__set_resource_flags(*rsc, pcmk_rsc_notify); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_notify); } if (xml_contains_remote_node((*rsc)->xml)) { (*rsc)->is_remote_node = TRUE; - if (g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_CONTAINER)) { + if (g_hash_table_lookup((*rsc)->meta, PCMK__META_CONTAINER)) { guest_node = true; } else { remote_node = true; } } - value = g_hash_table_lookup((*rsc)->meta, XML_OP_ATTR_ALLOW_MIGRATE); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_ALLOW_MIGRATE); if (crm_is_true(value)) { - pe__set_resource_flags(*rsc, pcmk_rsc_migratable); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable); } else if ((value == NULL) && remote_node) { /* By default, we want remote nodes to be able * to float around the cluster without having to stop all the * resources within the remote-node before moving. Allowing * migration support enables this feature. If this ever causes * problems, migration support can be explicitly turned off with - * allow-migrate=false. + * PCMK_META_ALLOW_MIGRATE=false. */ - pe__set_resource_flags(*rsc, pcmk_rsc_migratable); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_MANAGED); - if (value != NULL && !pcmk__str_eq("default", value, pcmk__str_casei)) { - if (crm_is_true(value)) { - pe__set_resource_flags(*rsc, pcmk_rsc_managed); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_IS_MANAGED); + if (value != NULL) { + if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting " PCMK_META_IS_MANAGED + " to the explicit value '" PCMK_VALUE_DEFAULT + "' is deprecated and will be removed in a " + "future release (just leave it unset)"); + } else if (crm_is_true(value)) { + pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed); } else { - pe__clear_resource_flags(*rsc, pcmk_rsc_managed); + pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed); } } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_MAINTENANCE); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MAINTENANCE); if (crm_is_true(value)) { - pe__clear_resource_flags(*rsc, pcmk_rsc_managed); - pe__set_resource_flags(*rsc, pcmk_rsc_maintenance); + pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance); } if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) { - pe__clear_resource_flags(*rsc, pcmk_rsc_managed); - pe__set_resource_flags(*rsc, pcmk_rsc_maintenance); + pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance); } - if (pe_rsc_is_clone(pe__const_top_resource(*rsc, false))) { - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_UNIQUE); + if (pcmk__is_clone(pe__const_top_resource(*rsc, false))) { + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_GLOBALLY_UNIQUE); if (crm_is_true(value)) { - pe__set_resource_flags(*rsc, pcmk_rsc_unique); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique); } if (detect_promotable(*rsc)) { - pe__set_resource_flags(*rsc, pcmk_rsc_promotable); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_promotable); } } else { - pe__set_resource_flags(*rsc, pcmk_rsc_unique); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_RESTART); - if (pcmk__str_eq(value, "restart", pcmk__str_casei)) { + // @COMPAT Deprecated meta-attribute + value = g_hash_table_lookup((*rsc)->meta, PCMK__META_RESTART_TYPE); + if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) { (*rsc)->restart_type = pe_restart_restart; - pe_rsc_trace((*rsc), "%s dependency restart handling: restart", - (*rsc)->id); - pe_warn_once(pcmk__wo_restart_type, - "Support for restart-type is deprecated and will be removed in a future release"); + pcmk__rsc_trace(*rsc, "%s dependency restart handling: restart", + (*rsc)->id); + pcmk__warn_once(pcmk__wo_restart_type, + "Support for " PCMK__META_RESTART_TYPE " is deprecated " + "and will be removed in a future release"); } else { (*rsc)->restart_type = pe_restart_ignore; - pe_rsc_trace((*rsc), "%s dependency restart handling: ignore", - (*rsc)->id); + pcmk__rsc_trace(*rsc, "%s dependency restart handling: ignore", + (*rsc)->id); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_MULTIPLE); - if (pcmk__str_eq(value, "stop_only", pcmk__str_casei)) { + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MULTIPLE_ACTIVE); + if (pcmk__str_eq(value, PCMK_VALUE_STOP_ONLY, pcmk__str_casei)) { (*rsc)->recovery_type = pcmk_multiply_active_stop; - pe_rsc_trace((*rsc), "%s multiple running resource recovery: stop only", - (*rsc)->id); + pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: stop only", + (*rsc)->id); - } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) { (*rsc)->recovery_type = pcmk_multiply_active_block; - pe_rsc_trace((*rsc), "%s multiple running resource recovery: block", - (*rsc)->id); + pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: block", + (*rsc)->id); - } else if (pcmk__str_eq(value, "stop_unexpected", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_STOP_UNEXPECTED, + pcmk__str_casei)) { (*rsc)->recovery_type = pcmk_multiply_active_unexpected; - pe_rsc_trace((*rsc), "%s multiple running resource recovery: " - "stop unexpected instances", - (*rsc)->id); + pcmk__rsc_trace(*rsc, + "%s multiple running resource recovery: " + "stop unexpected instances", + (*rsc)->id); - } else { // "stop_start" - if (!pcmk__str_eq(value, "stop_start", + } else { // PCMK_VALUE_STOP_START + if (!pcmk__str_eq(value, PCMK_VALUE_STOP_START, pcmk__str_casei|pcmk__str_null_matches)) { - pe_warn("%s is not a valid value for " XML_RSC_ATTR_MULTIPLE - ", using default of \"stop_start\"", value); + pcmk__config_warn("%s is not a valid value for " + PCMK_META_MULTIPLE_ACTIVE + ", using default of " + "\"" PCMK_VALUE_STOP_START "\"", + value); } (*rsc)->recovery_type = pcmk_multiply_active_restart; - pe_rsc_trace((*rsc), "%s multiple running resource recovery: " - "stop/start", (*rsc)->id); + pcmk__rsc_trace(*rsc, + "%s multiple running resource recovery: stop/start", + (*rsc)->id); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_STICKINESS); - if (value != NULL && !pcmk__str_eq("default", value, pcmk__str_casei)) { - (*rsc)->stickiness = char2score(value); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_RESOURCE_STICKINESS); + if (value != NULL) { + if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting " + PCMK_META_RESOURCE_STICKINESS + " to the explicit value '" PCMK_VALUE_DEFAULT + "' is deprecated and will be removed in a " + "future release (just leave it unset)"); + } else { + (*rsc)->stickiness = char2score(value); + } } value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MIGRATION_THRESHOLD); - if (value != NULL && !pcmk__str_eq("default", value, pcmk__str_casei)) { - (*rsc)->migration_threshold = char2score(value); - if ((*rsc)->migration_threshold < 0) { - /* @TODO We use 1 here to preserve previous behavior, but this - * should probably use the default (INFINITY) or 0 (to disable) - * instead. - */ - pe_warn_once(pcmk__wo_neg_threshold, - PCMK_META_MIGRATION_THRESHOLD - " must be non-negative, using 1 instead"); - (*rsc)->migration_threshold = 1; + if (value != NULL) { + if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting " + PCMK_META_MIGRATION_THRESHOLD + " to the explicit value '" PCMK_VALUE_DEFAULT + "' is deprecated and will be removed in a " + "future release (just leave it unset)"); + } else { + (*rsc)->migration_threshold = char2score(value); + if ((*rsc)->migration_threshold < 0) { + /* @COMPAT We use 1 here to preserve previous behavior, but this + * should probably use the default (INFINITY) or 0 (to disable) + * instead. + */ + pcmk__warn_once(pcmk__wo_neg_threshold, + PCMK_META_MIGRATION_THRESHOLD + " must be non-negative, using 1 instead"); + (*rsc)->migration_threshold = 1; + } } } - if (pcmk__str_eq(crm_element_value((*rsc)->xml, XML_AGENT_ATTR_CLASS), + if (pcmk__str_eq(crm_element_value((*rsc)->xml, PCMK_XA_CLASS), PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) { - pe__set_working_set_flags(scheduler, pcmk_sched_have_fencing); - pe__set_resource_flags(*rsc, pcmk_rsc_fence_device); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_fencing); + pcmk__set_rsc_flags(*rsc, pcmk_rsc_fence_device); } - value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_REQUIRES); + value = g_hash_table_lookup((*rsc)->meta, PCMK_META_REQUIRES); unpack_requires(*rsc, value, false); value = g_hash_table_lookup((*rsc)->meta, PCMK_META_FAILURE_TIMEOUT); if (value != NULL) { + guint interval_ms = 0U; + // Stored as seconds - (*rsc)->failure_timeout = (int) (crm_parse_interval_spec(value) / 1000); + pcmk_parse_interval_spec(value, &interval_ms); + (*rsc)->failure_timeout = (int) (interval_ms / 1000); } if (remote_node) { @@ -861,24 +929,27 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, /* Grabbing the value now means that any rules based on node attributes * will evaluate to false, so such rules should not be used with - * reconnect_interval. + * PCMK_REMOTE_RA_RECONNECT_INTERVAL. * * @TODO Evaluate per node before using */ - value = g_hash_table_lookup(params, XML_REMOTE_ATTR_RECONNECT_INTERVAL); + value = g_hash_table_lookup(params, PCMK_REMOTE_RA_RECONNECT_INTERVAL); if (value) { /* reconnect delay works by setting failure_timeout and preventing the * connection from starting until the failure is cleared. */ - (*rsc)->remote_reconnect_ms = crm_parse_interval_spec(value); - /* we want to override any default failure_timeout in use when remote - * reconnect_interval is in use. */ + pcmk_parse_interval_spec(value, &((*rsc)->remote_reconnect_ms)); + + /* We want to override any default failure_timeout in use when remote + * PCMK_REMOTE_RA_RECONNECT_INTERVAL is in use. + */ (*rsc)->failure_timeout = (*rsc)->remote_reconnect_ms / 1000; } } get_target_role(*rsc, &((*rsc)->next_role)); - pe_rsc_trace((*rsc), "%s desired next state: %s", (*rsc)->id, - (*rsc)->next_role != pcmk_role_unknown? role2text((*rsc)->next_role) : "default"); + pcmk__rsc_trace(*rsc, "%s desired next state: %s", (*rsc)->id, + ((*rsc)->next_role == pcmk_role_unknown)? + "default" : pcmk_role_text((*rsc)->next_role)); if ((*rsc)->fns->unpack(*rsc, scheduler) == FALSE) { (*rsc)->fns->free(*rsc); @@ -897,12 +968,12 @@ pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, scheduler); } - pe_rsc_trace((*rsc), "%s action notification: %s", (*rsc)->id, - pcmk_is_set((*rsc)->flags, pcmk_rsc_notify)? "required" : "not required"); + pcmk__rsc_trace(*rsc, "%s action notification: %s", (*rsc)->id, + pcmk_is_set((*rsc)->flags, pcmk_rsc_notify)? "required" : "not required"); (*rsc)->utilization = pcmk__strkey_table(free, free); - pe__unpack_dataset_nvpairs((*rsc)->xml, XML_TAG_UTILIZATION, &rule_data, + pe__unpack_dataset_nvpairs((*rsc)->xml, PCMK_XE_UTILIZATION, &rule_data, (*rsc)->utilization, NULL, FALSE, scheduler); if (expanded_xml) { @@ -940,8 +1011,7 @@ uber_parent(pcmk_resource_t *rsc) if (parent == NULL) { return NULL; } - while ((parent->parent != NULL) - && (parent->parent->variant != pcmk_rsc_variant_bundle)) { + while ((parent->parent != NULL) && !pcmk__is_bundle(parent->parent)) { parent = parent->parent; } return parent; @@ -967,8 +1037,7 @@ pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle) return NULL; } while (parent->parent != NULL) { - if (!include_bundle - && (parent->parent->variant == pcmk_rsc_variant_bundle)) { + if (!include_bundle && pcmk__is_bundle(parent->parent)) { break; } parent = parent->parent; @@ -983,7 +1052,7 @@ common_free(pcmk_resource_t * rsc) return; } - pe_rsc_trace(rsc, "Freeing %s %d", rsc->id, rsc->variant); + pcmk__rsc_trace(rsc, "Freeing %s %d", rsc->id, rsc->variant); g_list_free(rsc->rsc_cons); g_list_free(rsc->rsc_cons_lhs); @@ -1031,7 +1100,7 @@ common_free(pcmk_resource_t * rsc) } g_list_free(rsc->fillers); g_list_free(rsc->rsc_location); - pe_rsc_trace(rsc, "Resource freed"); + pcmk__rsc_trace(rsc, "Resource freed"); free(rsc->id); free(rsc->clone_name); free(rsc->allocated_to); @@ -1078,7 +1147,7 @@ pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node, } if (rsc->partial_migration_source != NULL) { - if (node->details == rsc->partial_migration_source->details) { + if (pcmk__same_node(node, rsc->partial_migration_source)) { *active = node; // This is the migration source } else { keep_looking = true; @@ -1124,7 +1193,7 @@ active_node(const pcmk_resource_t *rsc, unsigned int *count_all, /*! * \brief - * \internal Find and count active nodes according to "requires" + * \internal Find and count active nodes according to \c PCMK_META_REQUIRES * * \param[in] rsc Resource to check * \param[out] count If not NULL, will be set to count of active nodes @@ -1133,7 +1202,7 @@ active_node(const pcmk_resource_t *rsc, unsigned int *count_all, * * \note This is a convenience wrapper for active_node() where the count of all * active nodes or only clean active nodes is desired according to the - * "requires" meta-attribute. + * \c PCMK_META_REQUIRES meta-attribute. */ pcmk_node_t * pe__find_active_requires(const pcmk_resource_t *rsc, unsigned int *count) @@ -1185,8 +1254,9 @@ pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why) { CRM_ASSERT((rsc != NULL) && (why != NULL)); if (rsc->next_role != role) { - pe_rsc_trace(rsc, "Resetting next role for %s from %s to %s (%s)", - rsc->id, role2text(rsc->next_role), role2text(role), why); + pcmk__rsc_trace(rsc, "Resetting next role for %s from %s to %s (%s)", + rsc->id, pcmk_role_text(rsc->next_role), + pcmk_role_text(role), why); rsc->next_role = role; } } diff --git a/lib/pengine/failcounts.c b/lib/pengine/failcounts.c index 6990d3d..f33b90f 100644 --- a/lib/pengine/failcounts.c +++ b/lib/pengine/failcounts.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2023 the Pacemaker project contributors + * Copyright 2008-2024 the Pacemaker project contributors * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. @@ -12,7 +12,6 @@ #include <glib.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/util.h> #include <crm/pengine/internal.h> @@ -35,22 +34,20 @@ is_matched_failure(const char *rsc_id, const xmlNode *conf_op_xml, } // Get name and interval from configured op - conf_op_name = crm_element_value(conf_op_xml, "name"); - conf_op_interval_spec = crm_element_value(conf_op_xml, - XML_LRM_ATTR_INTERVAL); - conf_op_interval_ms = crm_parse_interval_spec(conf_op_interval_spec); + conf_op_name = crm_element_value(conf_op_xml, PCMK_XA_NAME); + conf_op_interval_spec = crm_element_value(conf_op_xml, PCMK_META_INTERVAL); + pcmk_parse_interval_spec(conf_op_interval_spec, &conf_op_interval_ms); // Get name and interval from op history entry - lrm_op_task = crm_element_value(lrm_op_xml, XML_LRM_ATTR_TASK); - crm_element_value_ms(lrm_op_xml, XML_LRM_ATTR_INTERVAL_MS, - &lrm_op_interval_ms); + lrm_op_task = crm_element_value(lrm_op_xml, PCMK_XA_OPERATION); + crm_element_value_ms(lrm_op_xml, PCMK_META_INTERVAL, &lrm_op_interval_ms); if ((conf_op_interval_ms != lrm_op_interval_ms) || !pcmk__str_eq(conf_op_name, lrm_op_task, pcmk__str_casei)) { return FALSE; } - lrm_op_id = ID(lrm_op_xml); + lrm_op_id = pcmk__xe_id(lrm_op_xml); last_failure_key = pcmk__op_key(rsc_id, "last_failure", 0); if (pcmk__str_eq(last_failure_key, lrm_op_id, pcmk__str_casei)) { @@ -64,7 +61,7 @@ is_matched_failure(const char *rsc_id, const xmlNode *conf_op_xml, int rc = 0; int target_rc = pe__target_rc_from_xml(lrm_op_xml); - crm_element_value_int(lrm_op_xml, XML_LRM_ATTR_RC, &rc); + crm_element_value_int(lrm_op_xml, PCMK__XA_RC_CODE, &rc); if (rc != target_rc) { matched = TRUE; } @@ -86,16 +83,17 @@ block_failure(const pcmk_node_t *node, pcmk_resource_t *rsc, * to properly detect on-fail in id-ref, operation meta-attributes, or * op_defaults, or evaluate rules. * - * Also, on-fail defaults to block (in unpack_operation()) for stop actions - * when stonith is disabled. + * Also, PCMK_META_ON_FAIL defaults to PCMK_VALUE_BLOCK (in + * unpack_operation()) for stop actions when stonith is disabled. * * Ideally, we'd unpack the operation before this point, and pass in a * meta-attributes table that takes all that into consideration. */ - char *xpath = crm_strdup_printf("//" XML_CIB_TAG_RESOURCE - "[@" XML_ATTR_ID "='%s']" - "//" XML_ATTR_OP - "[@" XML_OP_ATTR_ON_FAIL "='block']", + char *xpath = crm_strdup_printf("//" PCMK_XE_PRIMITIVE + "[@" PCMK_XA_ID "='%s']" + "//" PCMK_XE_OP + "[@" PCMK_META_ON_FAIL + "='" PCMK_VALUE_BLOCK "']", xml_name); xmlXPathObject *xpathObj = xpath_search(rsc->xml, xpath); @@ -124,14 +122,16 @@ block_failure(const pcmk_node_t *node, pcmk_resource_t *rsc, xmlXPathObject *lrm_op_xpathObj = NULL; // Get name and interval from configured op - conf_op_name = crm_element_value(pref, "name"); - conf_op_interval_spec = crm_element_value(pref, XML_LRM_ATTR_INTERVAL); - conf_op_interval_ms = crm_parse_interval_spec(conf_op_interval_spec); + conf_op_name = crm_element_value(pref, PCMK_XA_NAME); + conf_op_interval_spec = crm_element_value(pref, + PCMK_META_INTERVAL); + pcmk_parse_interval_spec(conf_op_interval_spec, + &conf_op_interval_ms); -#define XPATH_FMT "//" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \ - "//" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']" \ - "/" XML_LRM_TAG_RSC_OP "[@" XML_LRM_ATTR_TASK "='%s']" \ - "[@" XML_LRM_ATTR_INTERVAL "='%u']" +#define XPATH_FMT "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']" \ + "//" PCMK__XE_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']" \ + "/" PCMK__XE_LRM_RSC_OP "[@" PCMK_XA_OPERATION "='%s']" \ + "[@" PCMK_META_INTERVAL "='%u']" lrm_op_xpath = crm_strdup_printf(XPATH_FMT, node->details->uname, xml_name, @@ -251,7 +251,7 @@ generate_fail_regexes(const pcmk_resource_t *rsc, int rc = pcmk_rc_ok; char *rsc_name = rsc_fail_name(rsc); const char *version = crm_element_value(rsc->cluster->input, - XML_ATTR_CRM_VERSION); + PCMK_XA_CRM_FEATURE_SET); // @COMPAT Pacemaker <= 1.1.16 used a single fail count per resource gboolean is_legacy = (compare_version(version, "3.0.13") < 0); @@ -302,9 +302,10 @@ update_failcount_for_attr(gpointer key, gpointer value, gpointer user_data) if (regexec(&(fc_data->failcount_re), (const char *) key, 0, NULL, 0) == 0) { fc_data->failcount = pcmk__add_scores(fc_data->failcount, char2score(value)); - pe_rsc_trace(fc_data->rsc, "Added %s (%s) to %s fail count (now %s)", - (const char *) key, (const char *) value, fc_data->rsc->id, - pcmk_readable_score(fc_data->failcount)); + pcmk__rsc_trace(fc_data->rsc, "Added %s (%s) to %s fail count (now %s)", + (const char *) key, (const char *) value, + fc_data->rsc->id, + pcmk_readable_score(fc_data->failcount)); return; } @@ -382,9 +383,10 @@ pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, if ((fc_data.failcount > 0) && (rsc->failure_timeout > 0) && block_failure(node, rsc, xml_op)) { - pe_warn("Ignoring failure timeout %d for %s " - "because it conflicts with on-fail=block", - rsc->failure_timeout, rsc->id); + pcmk__config_warn("Ignoring failure timeout %d for %s " + "because it conflicts with " + PCMK_META_ON_FAIL "=" PCMK_VALUE_BLOCK, + rsc->failure_timeout, rsc->id); rsc->failure_timeout = 0; } @@ -395,8 +397,9 @@ pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, time_t now = get_effective_time(rsc->cluster); if (now > (fc_data.last_failure + rsc->failure_timeout)) { - pe_rsc_debug(rsc, "Failcount for %s on %s expired after %ds", - rsc->id, pe__node_name(node), rsc->failure_timeout); + pcmk__rsc_debug(rsc, "Failcount for %s on %s expired after %ds", + rsc->id, pcmk__node_name(node), + rsc->failure_timeout); fc_data.failcount = 0; } } @@ -412,21 +415,23 @@ pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, * container on the wrong node. */ if (pcmk_is_set(flags, pcmk__fc_fillers) && (rsc->fillers != NULL) - && !pe_rsc_is_bundled(rsc)) { + && !pcmk__is_bundled(rsc)) { g_list_foreach(rsc->fillers, update_failcount_for_filler, &fc_data); if (fc_data.failcount > 0) { - pe_rsc_info(rsc, - "Container %s and the resources within it " - "have failed %s time%s on %s", - rsc->id, pcmk_readable_score(fc_data.failcount), - pcmk__plural_s(fc_data.failcount), pe__node_name(node)); + pcmk__rsc_info(rsc, + "Container %s and the resources within it " + "have failed %s time%s on %s", + rsc->id, pcmk_readable_score(fc_data.failcount), + pcmk__plural_s(fc_data.failcount), + pcmk__node_name(node)); } } else if (fc_data.failcount > 0) { - pe_rsc_info(rsc, "%s has failed %s time%s on %s", - rsc->id, pcmk_readable_score(fc_data.failcount), - pcmk__plural_s(fc_data.failcount), pe__node_name(node)); + pcmk__rsc_info(rsc, "%s has failed %s time%s on %s", + rsc->id, pcmk_readable_score(fc_data.failcount), + pcmk__plural_s(fc_data.failcount), + pcmk__node_name(node)); } if (last_failure != NULL) { @@ -461,8 +466,8 @@ pe__clear_failcount(pcmk_resource_t *rsc, const pcmk_node_t *node, key = pcmk__op_key(rsc->id, PCMK_ACTION_CLEAR_FAILCOUNT, 0); clear = custom_action(rsc, key, PCMK_ACTION_CLEAR_FAILCOUNT, node, FALSE, scheduler); - add_hash_param(clear->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); + pcmk__insert_meta(clear, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE); crm_notice("Clearing failure of %s on %s because %s " CRM_XS " %s", - rsc->id, pe__node_name(node), reason, clear->uuid); + rsc->id, pcmk__node_name(node), reason, clear->uuid); return clear; } diff --git a/lib/pengine/group.c b/lib/pengine/group.c index dad610c..143956b 100644 --- a/lib/pengine/group.c +++ b/lib/pengine/group.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. * @@ -14,7 +14,7 @@ #include <crm/pengine/rules.h> #include <crm/pengine/status.h> #include <crm/pengine/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/output.h> #include <crm/common/strings_internal.h> #include <crm/common/xml_internal.h> @@ -37,8 +37,8 @@ pcmk_resource_t * pe__last_group_member(const pcmk_resource_t *group) { if (group != NULL) { - CRM_CHECK((group->variant == pcmk_rsc_variant_group) - && (group->variant_opaque != NULL), return NULL); + CRM_CHECK(pcmk__is_group(group) && (group->variant_opaque != NULL), + return NULL); return ((group_variant_data_t *) group->variant_opaque)->last_child; } return NULL; @@ -58,8 +58,8 @@ pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags) { group_variant_data_t *group_data = NULL; - CRM_CHECK((group != NULL) && (group->variant == pcmk_rsc_variant_group) - && (group->variant_opaque != NULL), return false); + CRM_CHECK(pcmk__is_group(group) && (group->variant_opaque != NULL), + return false); group_data = (group_variant_data_t *) group->variant_opaque; return pcmk_all_flags_set(group_data->flags, flags); } @@ -89,10 +89,10 @@ set_group_flag(pcmk_resource_t *group, const char *option, uint32_t flag, ((group_variant_data_t *) group->variant_opaque)->flags |= flag; } else { - pe_warn_once(wo_bit, - "Support for the '%s' group meta-attribute is deprecated " - "and will be removed in a future release " - "(use a resource set instead)", option); + pcmk__warn_once(wo_bit, + "Support for the '%s' group meta-attribute is " + "deprecated and will be removed in a future release " + "(use a resource set instead)", option); } } @@ -184,28 +184,28 @@ group_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) group_variant_data_t *group_data = NULL; const char *clone_id = NULL; - pe_rsc_trace(rsc, "Processing resource %s...", rsc->id); + pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id); - group_data = calloc(1, sizeof(group_variant_data_t)); + group_data = pcmk__assert_alloc(1, sizeof(group_variant_data_t)); group_data->last_child = NULL; rsc->variant_opaque = group_data; // @COMPAT These are deprecated since 2.1.5 - set_group_flag(rsc, XML_RSC_ATTR_ORDERED, pcmk__group_ordered, + set_group_flag(rsc, PCMK_META_ORDERED, pcmk__group_ordered, pcmk__wo_group_order); set_group_flag(rsc, "collocated", pcmk__group_colocated, pcmk__wo_group_coloc); - clone_id = crm_element_value(rsc->xml, XML_RSC_ATTR_INCARNATION); + clone_id = crm_element_value(rsc->xml, PCMK__META_CLONE); - for (xml_native_rsc = pcmk__xe_first_child(xml_obj); xml_native_rsc != NULL; + for (xml_native_rsc = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + xml_native_rsc != NULL; xml_native_rsc = pcmk__xe_next(xml_native_rsc)) { - if (pcmk__str_eq((const char *)xml_native_rsc->name, - XML_CIB_TAG_RESOURCE, pcmk__str_none)) { + if (pcmk__xe_is(xml_native_rsc, PCMK_XE_PRIMITIVE)) { pcmk_resource_t *new_rsc = NULL; - crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id); + crm_xml_add(xml_native_rsc, PCMK__META_CLONE, clone_id); if (pe__unpack_resource(xml_native_rsc, &new_rsc, rsc, scheduler) != pcmk_rc_ok) { continue; @@ -213,7 +213,7 @@ group_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) rsc->children = g_list_append(rsc->children, new_rsc); group_data->last_child = new_rsc; - pe_rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id); + pcmk__rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id); } } @@ -268,7 +268,7 @@ group_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options, GList *gIter = rsc->children; char *child_text = crm_strdup_printf("%s ", pre_text); - status_print("%s<group " XML_ATTR_ID "=\"%s\" ", pre_text, rsc->id); + status_print("%s<group " PCMK_XA_ID "=\"%s\" ", pre_text, rsc->id); status_print("number_resources=\"%d\" ", g_list_length(rsc->children)); status_print(">\n"); @@ -369,23 +369,25 @@ pe__group_xml(pcmk__output_t *out, va_list args) if (rc == pcmk_rc_no_output) { char *count = pcmk__itoa(g_list_length(gIter)); - const char *maint_s = pe__rsc_bool_str(rsc, pcmk_rsc_maintenance); - const char *managed_s = pe__rsc_bool_str(rsc, pcmk_rsc_managed); - const char *disabled_s = pcmk__btoa(pe__resource_is_disabled(rsc)); - - rc = pe__name_and_nvpairs_xml(out, true, "group", 5, - XML_ATTR_ID, rsc->id, - "number_resources", count, - "maintenance", maint_s, - "managed", managed_s, - "disabled", disabled_s, - "description", desc); + const char *maintenance = pcmk__flag_text(rsc->flags, + pcmk_rsc_maintenance); + const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed); + const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc)); + + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_GROUP, + PCMK_XA_ID, rsc->id, + PCMK_XA_NUMBER_RESOURCES, count, + PCMK_XA_MAINTENANCE, maintenance, + PCMK_XA_MANAGED, managed, + PCMK_XA_DISABLED, disabled, + PCMK_XA_DESCRIPTION, desc, + NULL); free(count); CRM_ASSERT(rc == pcmk_rc_ok); } - out->message(out, crm_map_element_name(child_rsc->xml), show_opts, child_rsc, - only_node, only_rsc); + out->message(out, (const char *) child_rsc->xml->name, show_opts, + child_rsc, only_node, only_rsc); } if (rc == pcmk_rc_ok) { @@ -442,7 +444,7 @@ pe__group_default(pcmk__output_t *out, va_list args) group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0, pcmk_is_set(show_opts, pcmk_show_inactive_rscs), desc); - out->message(out, crm_map_element_name(child_rsc->xml), show_opts, + out->message(out, (const char *) child_rsc->xml->name, show_opts, child_rsc, only_node, only_rsc); } } @@ -457,17 +459,17 @@ group_free(pcmk_resource_t * rsc) { CRM_CHECK(rsc != NULL, return); - pe_rsc_trace(rsc, "Freeing %s", rsc->id); + pcmk__rsc_trace(rsc, "Freeing %s", rsc->id); for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data; CRM_ASSERT(child_rsc); - pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id); + pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id); child_rsc->fns->free(child_rsc); } - pe_rsc_trace(rsc, "Freeing child list"); + pcmk__rsc_trace(rsc, "Freeing child list"); g_list_free(rsc->children); common_free(rsc); @@ -488,7 +490,7 @@ group_resource_state(const pcmk_resource_t * rsc, gboolean current) } } - pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(group_role)); + pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(group_role)); return group_role; } @@ -534,6 +536,6 @@ pe__group_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, unsigned int pe__group_max_per_node(const pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)); + CRM_ASSERT(pcmk__is_group(rsc)); return 1U; } diff --git a/lib/pengine/native.c b/lib/pengine/native.c index 48b1a6a..053ffda 100644 --- a/lib/pengine/native.c +++ b/lib/pengine/native.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. * @@ -16,7 +16,7 @@ #include <crm/pengine/status.h> #include <crm/pengine/complex.h> #include <crm/pengine/internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <pe_status_private.h> #ifdef PCMK__COMPAT_2_0 @@ -34,7 +34,7 @@ is_multiply_active(const pcmk_resource_t *rsc) { unsigned int count = 0; - if (rsc->variant == pcmk_rsc_variant_primitive) { + if (pcmk__is_primitive(rsc)) { pe__find_active_requires(rsc, &count); } return count > 1; @@ -59,11 +59,11 @@ native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node, } node->details->priority += priority; - pe_rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)", - pe__node_name(node), node->details->priority, - (rsc->role == pcmk_role_promoted)? "promoted " : "", - rsc->id, rsc->priority, - (rsc->role == pcmk_role_promoted)? " + 1" : ""); + pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)", + pcmk__node_name(node), node->details->priority, + (rsc->role == pcmk_role_promoted)? "promoted " : "", + rsc->id, rsc->priority, + (rsc->role == pcmk_role_promoted)? " + 1" : ""); /* Priority of a resource running on a guest node is added to the cluster * node as well. */ @@ -75,13 +75,14 @@ native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_node_t *a_node = gIter->data; a_node->details->priority += priority; - pe_rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s) " - "from guest node %s", - pe__node_name(a_node), a_node->details->priority, - (rsc->role == pcmk_role_promoted)? "promoted " : "", - rsc->id, rsc->priority, - (rsc->role == pcmk_role_promoted)? " + 1" : "", - pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s now has priority %d with %s'%s' " + "(priority: %d%s) from guest node %s", + pcmk__node_name(a_node), a_node->details->priority, + (rsc->role == pcmk_role_promoted)? "promoted " : "", + rsc->id, rsc->priority, + (rsc->role == pcmk_role_promoted)? " + 1" : "", + pcmk__node_name(node)); } } } @@ -102,28 +103,25 @@ native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, } } - pe_rsc_trace(rsc, "Adding %s to %s %s", rsc->id, pe__node_name(node), - pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : "(unmanaged)"); + pcmk__rsc_trace(rsc, "Adding %s to %s %s", rsc->id, pcmk__node_name(node), + pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : "(unmanaged)"); rsc->running_on = g_list_append(rsc->running_on, node); - if (rsc->variant == pcmk_rsc_variant_primitive) { + if (pcmk__is_primitive(rsc)) { node->details->running_rsc = g_list_append(node->details->running_rsc, rsc); - native_priority_to_node(rsc, node, failed); - } - - if ((rsc->variant == pcmk_rsc_variant_primitive) - && node->details->maintenance) { - pe__clear_resource_flags(rsc, pcmk_rsc_managed); - pe__set_resource_flags(rsc, pcmk_rsc_maintenance); + if (node->details->maintenance) { + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_maintenance); + } } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pcmk_resource_t *p = rsc->parent; - pe_rsc_info(rsc, "resource %s isn't managed", rsc->id); - resource_location(rsc, node, INFINITY, "not_managed_default", - scheduler); + pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id); + resource_location(rsc, node, PCMK_SCORE_INFINITY, + "not_managed_default", scheduler); while(p && node->details->online) { /* add without the additional location constraint */ @@ -147,28 +145,28 @@ native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, rsc->allowed_nodes = pe__node_list2table(scheduler->nodes); g_hash_table_iter_init(&gIter, rsc->allowed_nodes); while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) { - local_node->weight = -INFINITY; + local_node->weight = -PCMK_SCORE_INFINITY; } } break; case pcmk_multiply_active_block: - pe__clear_resource_flags(rsc, pcmk_rsc_managed); - pe__set_resource_flags(rsc, pcmk_rsc_blocked); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked); /* If the resource belongs to a group or bundle configured with - * multiple-active=block, block the entire entity. + * PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire + * entity. */ - if (rsc->parent - && ((rsc->parent->variant == pcmk_rsc_variant_group) - || (rsc->parent->variant == pcmk_rsc_variant_bundle)) + if ((pcmk__is_group(rsc->parent) + || pcmk__is_bundle(rsc->parent)) && (rsc->parent->recovery_type == pcmk_multiply_active_block)) { GList *gIter = rsc->parent->children; for (; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child = gIter->data; - pe__clear_resource_flags(child, pcmk_rsc_managed); - pe__set_resource_flags(child, pcmk_rsc_blocked); + pcmk__clear_rsc_flags(child, pcmk_rsc_managed); + pcmk__set_rsc_flags(child, pcmk_rsc_blocked); } } break; @@ -181,12 +179,12 @@ native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, break; } crm_debug("%s is active on multiple nodes including %s: %s", - rsc->id, pe__node_name(node), - recovery2text(rsc->recovery_type)); + rsc->id, pcmk__node_name(node), + pcmk__multiply_active_text(rsc->recovery_type)); } else { - pe_rsc_trace(rsc, "Resource %s is active on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Resource %s is active on %s", + rsc->id, pcmk__node_name(node)); } if (rsc->parent != NULL) { @@ -197,8 +195,8 @@ native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, static void recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data) { - pe__clear_resource_flags(rsc, pcmk_rsc_unique); - add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE, XML_BOOLEAN_FALSE); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique); + pcmk__insert_meta(rsc, PCMK_META_GLOBALLY_UNIQUE, PCMK_VALUE_FALSE); g_list_foreach(rsc->children, (GFunc) recursive_clear_unique, NULL); } @@ -206,15 +204,15 @@ gboolean native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) { pcmk_resource_t *parent = uber_parent(rsc); - const char *standard = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *standard = crm_element_value(rsc->xml, PCMK_XA_CLASS); uint32_t ra_caps = pcmk_get_ra_caps(standard); - pe_rsc_trace(rsc, "Processing resource %s...", rsc->id); + pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id); // Only some agent standards support unique and promotable clones if (!pcmk_is_set(ra_caps, pcmk_ra_cap_unique) && pcmk_is_set(rsc->flags, pcmk_rsc_unique) - && pe_rsc_is_clone(parent)) { + && pcmk__is_clone(parent)) { /* @COMPAT We should probably reject this situation as an error (as we * do for promotable below) rather than warn and convert, but that would @@ -223,10 +221,10 @@ native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) */ pe__force_anon(standard, parent, rsc->id, scheduler); - /* Clear globally-unique on the parent and all its descendants unpacked - * so far (clearing the parent should make any future children unpacking - * correct). We have to clear this resource explicitly because it isn't - * hooked into the parent's children yet. + /* Clear PCMK_META_GLOBALLY_UNIQUE on the parent and all its descendants + * unpacked so far (clearing the parent should make any future children + * unpacking correct). We have to clear this resource explicitly because + * it isn't hooked into the parent's children yet. */ recursive_clear_unique(parent, NULL); recursive_clear_unique(rsc, NULL); @@ -234,9 +232,9 @@ native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) if (!pcmk_is_set(ra_caps, pcmk_ra_cap_promotable) && pcmk_is_set(parent->flags, pcmk_rsc_promotable)) { - pe_err("Resource %s is of type %s and therefore " - "cannot be used as a promotable clone resource", - rsc->id, standard); + pcmk__config_err("Resource %s is of type %s and therefore " + "cannot be used as a promotable clone resource", + rsc->id, standard); return FALSE; } return TRUE; @@ -245,16 +243,14 @@ native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) static bool rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags) { - pe_rsc_trace(rsc, "Checking whether %s is on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Checking whether %s is on %s", + rsc->id, pcmk__node_name(node)); if (pcmk_is_set(flags, pcmk_rsc_match_current_node) && (rsc->running_on != NULL)) { for (GList *iter = rsc->running_on; iter; iter = iter->next) { - pcmk_node_t *loc = (pcmk_node_t *) iter->data; - - if (loc->details == node->details) { + if (pcmk__same_node((pcmk_node_t *) iter->data, node)) { return true; } } @@ -265,7 +261,7 @@ rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags) } else if (!pcmk_is_set(flags, pcmk_rsc_match_current_node) && (rsc->allocated_to != NULL) - && (rsc->allocated_to->details == node->details)) { + && pcmk__same_node(rsc->allocated_to, node)) { return true; } return false; @@ -281,9 +277,9 @@ native_find_rsc(pcmk_resource_t *rsc, const char *id, CRM_CHECK(id && rsc && rsc->id, return NULL); if (pcmk_is_set(flags, pcmk_rsc_match_clone_only)) { - const char *rid = ID(rsc->xml); + const char *rid = pcmk__xe_id(rsc->xml); - if (!pe_rsc_is_clone(pe__const_top_resource(rsc, false))) { + if (!pcmk__is_clone(pe__const_top_resource(rsc, false))) { match = false; } else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) { @@ -329,22 +325,20 @@ char * native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create, const char *name, pcmk_scheduler_t *scheduler) { - char *value_copy = NULL; const char *value = NULL; GHashTable *params = NULL; CRM_CHECK(rsc != NULL, return NULL); CRM_CHECK(name != NULL && strlen(name) != 0, return NULL); - pe_rsc_trace(rsc, "Looking up %s in %s", name, rsc->id); + pcmk__rsc_trace(rsc, "Looking up %s in %s", name, rsc->id); params = pe_rsc_params(rsc, node, scheduler); value = g_hash_table_lookup(params, name); if (value == NULL) { /* try meta attributes instead */ value = g_hash_table_lookup(rsc->meta, name); } - pcmk__str_update(&value_copy, value); - return value_copy; + return pcmk__str_copy(value); } gboolean @@ -354,16 +348,16 @@ native_active(pcmk_resource_t * rsc, gboolean all) pcmk_node_t *a_node = (pcmk_node_t *) gIter->data; if (a_node->details->unclean) { - pe_rsc_trace(rsc, "Resource %s: %s is unclean", - rsc->id, pe__node_name(a_node)); + pcmk__rsc_trace(rsc, "Resource %s: %s is unclean", + rsc->id, pcmk__node_name(a_node)); return TRUE; } else if (!a_node->details->online && pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pe_rsc_trace(rsc, "Resource %s: %s is offline", - rsc->id, pe__node_name(a_node)); + pcmk__rsc_trace(rsc, "Resource %s: %s is offline", + rsc->id, pcmk__node_name(a_node)); } else { - pe_rsc_trace(rsc, "Resource %s active on %s", - rsc->id, pe__node_name(a_node)); + pcmk__rsc_trace(rsc, "Resource %s active on %s", + rsc->id, pcmk__node_name(a_node)); return TRUE; } } @@ -453,7 +447,7 @@ native_displayable_state(const pcmk_resource_t *rsc, bool print_pending) rsc_state = native_pending_state(rsc); } if (rsc_state == NULL) { - rsc_state = role2text(native_displayable_role(rsc)); + rsc_state = pcmk_role_text(native_displayable_role(rsc)); } return rsc_state; } @@ -466,35 +460,37 @@ static void native_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); + const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); const char *rsc_state = native_displayable_state(rsc, pcmk_is_set(options, pe_print_pending)); const char *target_role = NULL; /* resource information. */ status_print("%s<resource ", pre_text); - status_print(XML_ATTR_ID "=\"%s\" ", rsc_printable_id(rsc)); + status_print(PCMK_XA_ID "=\"%s\" ", rsc_printable_id(rsc)); status_print("resource_agent=\"%s%s%s:%s\" ", class, ((prov == NULL)? "" : PROVIDER_SEP), ((prov == NULL)? "" : prov), - crm_element_value(rsc->xml, XML_ATTR_TYPE)); + crm_element_value(rsc->xml, PCMK_XA_TYPE)); status_print("role=\"%s\" ", rsc_state); if (rsc->meta) { - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); } if (target_role) { status_print("target_role=\"%s\" ", target_role); } status_print("active=\"%s\" ", pcmk__btoa(rsc->fns->active(rsc, TRUE))); - status_print("orphaned=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_removed)); + status_print("orphaned=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_removed)); status_print("blocked=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_blocked)); + pcmk__flag_text(rsc->flags, pcmk_rsc_blocked)); status_print("managed=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_managed)); - status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_failed)); + pcmk__flag_text(rsc->flags, pcmk_rsc_managed)); + status_print("failed=\"%s\" ", + pcmk__flag_text(rsc->flags, pcmk_rsc_failed)); status_print("failure_ignored=\"%s\" ", - pe__rsc_bool_str(rsc, pcmk_rsc_ignore_failure)); + pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure)); status_print("nodes_running_on=\"%d\" ", g_list_length(rsc->running_on)); if (options & pe_print_pending) { @@ -516,8 +512,8 @@ native_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options, for (; gIter != NULL; gIter = gIter->next) { pcmk_node_t *node = (pcmk_node_t *) gIter->data; - status_print("%s <node name=\"%s\" " XML_ATTR_ID "=\"%s\" " - "cached=\"%s\"/>\n", + status_print("%s <node " PCMK_XA_NAME "=\"%s\" " + PCMK_XA_ID "=\"%s\" cached=\"%s\"/>\n", pre_text, pcmk__s(node->details->uname, ""), node->details->id, pcmk__btoa(!node->details->online)); } @@ -564,13 +560,13 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts, const char *target_role, bool show_nodes) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); const char *provider = NULL; - const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE); + const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE); GString *outstr = NULL; bool have_flags = false; - if (rsc->variant != pcmk_rsc_variant_primitive) { + if (!pcmk__is_primitive(rsc)) { return NULL; } @@ -579,7 +575,7 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, CRM_CHECK(class != NULL, class = "unknown"); if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) { - provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); } if ((node == NULL) && (rsc->lock_node != NULL)) { @@ -606,7 +602,7 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, g_string_append(outstr, " FAILED"); if (role > pcmk_role_unpromoted) { - pcmk__add_word(&outstr, 0, role2text(role)); + pcmk__add_word(&outstr, 0, pcmk_role_text(role)); } } else { bool show_pending = pcmk_is_set(show_opts, pcmk_show_pending); @@ -614,7 +610,7 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending)); } if (node) { - pcmk__add_word(&outstr, 0, pe__node_name(node)); + pcmk__add_word(&outstr, 0, pcmk__node_name(node)); } // Failed probe operation @@ -623,7 +619,8 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, if (probe_op != NULL) { int rc; - pcmk__scan_min_int(crm_element_value(probe_op, XML_LRM_ATTR_RC), &rc, 0); + pcmk__scan_min_int(crm_element_value(probe_op, PCMK__XA_RC_CODE), + &rc, 0); pcmk__g_strcat(outstr, " (", services_ocf_exitcode_str(rc), ") ", NULL); } @@ -643,21 +640,33 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, have_flags = add_output_flag(outstr, pending_task, have_flags); } } - if (target_role) { - enum rsc_role_e target_role_e = text2role(target_role); + if (target_role != NULL) { + switch (pcmk_parse_role(target_role)) { + case pcmk_role_unknown: + pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE + " %s for resource %s", target_role, rsc->id); + break; - /* Only show target role if it limits our abilities (i.e. ignore - * Started, as it is the default anyways, and doesn't prevent the - * resource from becoming promoted). - */ - if (target_role_e == pcmk_role_stopped) { - have_flags = add_output_flag(outstr, "disabled", have_flags); - - } else if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags, - pcmk_rsc_promotable) - && (target_role_e == pcmk_role_unpromoted)) { - have_flags = add_output_flag(outstr, "target-role:", have_flags); - g_string_append(outstr, target_role); + case pcmk_role_stopped: + have_flags = add_output_flag(outstr, "disabled", have_flags); + break; + + case pcmk_role_unpromoted: + if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags, + pcmk_rsc_promotable)) { + have_flags = add_output_flag(outstr, + PCMK_META_TARGET_ROLE ":", + have_flags); + g_string_append(outstr, target_role); + } + break; + + default: + /* Only show target role if it limits our abilities (i.e. ignore + * Started, as it is the default anyways, and doesn't prevent + * the resource from becoming promoted). + */ + break; } } @@ -686,7 +695,7 @@ pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, // User-supplied description if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description) || pcmk__list_of_multiple(rsc->running_on)) { - const char *desc = crm_element_value(rsc->xml, XML_ATTR_DESC); + const char *desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION); if (desc) { g_string_append(outstr, " ("); @@ -718,17 +727,18 @@ pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts) { - const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE); + const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE); const char *target_role = NULL; - - xmlNodePtr list_node = NULL; const char *cl = NULL; - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); - CRM_ASSERT(kind != NULL); + xmlNode *child = NULL; + gchar *content = NULL; + + CRM_ASSERT((kind != NULL) && pcmk__is_primitive(rsc)); if (rsc->meta) { - const char *is_internal = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INTERNAL_RSC); + const char *is_internal = g_hash_table_lookup(rsc->meta, + PCMK__META_INTERNAL_RSC); if (crm_is_true(is_internal) && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) { @@ -736,37 +746,34 @@ pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc, crm_trace("skipping print of internal resource %s", rsc->id); return pcmk_rc_no_output; } - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - cl = "rsc-managed"; + cl = PCMK__VALUE_RSC_MANAGED; } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { - cl = "rsc-failed"; + cl = PCMK__VALUE_RSC_FAILED; - } else if ((rsc->variant == pcmk_rsc_variant_primitive) - && (rsc->running_on == NULL)) { - cl = "rsc-failed"; + } else if (pcmk__is_primitive(rsc) && (rsc->running_on == NULL)) { + cl = PCMK__VALUE_RSC_FAILED; } else if (pcmk__list_of_multiple(rsc->running_on)) { - cl = "rsc-multiple"; + cl = PCMK__VALUE_RSC_MULTIPLE; } else if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) { - cl = "rsc-failure-ignored"; + cl = PCMK__VALUE_RSC_FAILURE_IGNORED; } else { - cl = "rsc-ok"; + cl = PCMK__VALUE_RSC_OK; } - { - gchar *s = pcmk__native_output_string(rsc, name, node, show_opts, - target_role, true); - - list_node = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL); - pcmk_create_html_node(list_node, "span", NULL, cl, s); - g_free(s); - } + child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL); + child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl); + content = pcmk__native_output_string(rsc, name, node, show_opts, + target_role, true); + pcmk__xe_set_content(child, "%s", content); + g_free(content); return pcmk_rc_ok; } @@ -778,10 +785,11 @@ pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc, { const char *target_role = NULL; - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (rsc->meta) { - const char *is_internal = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INTERNAL_RSC); + const char *is_internal = g_hash_table_lookup(rsc->meta, + PCMK__META_INTERNAL_RSC); if (crm_is_true(is_internal) && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) { @@ -789,7 +797,7 @@ pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc, crm_trace("skipping print of internal resource %s", rsc->id); return pcmk_rc_no_output; } - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); } { @@ -813,11 +821,11 @@ common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name, { const char *target_role = NULL; - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (rsc->meta) { const char *is_internal = g_hash_table_lookup(rsc->meta, - XML_RSC_ATTR_INTERNAL_RSC); + PCMK__META_INTERNAL_RSC); if (crm_is_true(is_internal) && !pcmk_is_set(options, pe_print_implicit)) { @@ -825,7 +833,7 @@ common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name, crm_trace("skipping print of internal resource %s", rsc->id); return; } - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); } if (options & pe_print_xml) { @@ -888,17 +896,17 @@ common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name, counter++; if (options & pe_print_html) { - status_print("<li>\n%s", pe__node_name(n)); + status_print("<li>\n%s", pcmk__node_name(n)); } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) { - status_print(" %s", pe__node_name(n)); + status_print(" %s", pcmk__node_name(n)); } else if ((options & pe_print_log)) { - status_print("\t%d : %s", counter, pe__node_name(n)); + status_print("\t%d : %s", counter, pcmk__node_name(n)); } else { - status_print("%s", pe__node_name(n)); + status_print("%s", pcmk__node_name(n)); } if (options & pe_print_html) { status_print("</li>\n"); @@ -933,13 +941,14 @@ native_print(pcmk_resource_t *rsc, const char *pre_text, long options, { const pcmk_node_t *node = NULL; - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); + if (options & pe_print_xml) { native_print_xml(rsc, pre_text, options, print_data); return; } - node = pe__current_node(rsc); + node = pcmk__current_node(rsc); if (node == NULL) { // This is set only if a non-probe action is pending on this node @@ -959,57 +968,64 @@ pe__resource_xml(pcmk__output_t *out, va_list args) GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); + int rc = pcmk_rc_no_output; bool print_pending = pcmk_is_set(show_opts, pcmk_show_pending); - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); - const char *rsc_state = native_displayable_state(rsc, print_pending); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); + const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); - const char *desc = NULL; char ra_name[LINE_MAX]; - char *nodes_running_on = NULL; - const char *lock_node_name = NULL; - int rc = pcmk_rc_no_output; + const char *rsc_state = native_displayable_state(rsc, print_pending); const char *target_role = NULL; + const char *active = pcmk__btoa(rsc->fns->active(rsc, TRUE)); + const char *orphaned = pcmk__flag_text(rsc->flags, pcmk_rsc_removed); + const char *blocked = pcmk__flag_text(rsc->flags, pcmk_rsc_blocked); + const char *maintenance = pcmk__flag_text(rsc->flags, pcmk_rsc_maintenance); + const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed); + const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed); + const char *ignored = pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure); + char *nodes_running_on = NULL; + const char *pending = print_pending? native_pending_task(rsc) : NULL; + const char *locked_to = NULL; + const char *desc = pe__resource_description(rsc, show_opts); - desc = pe__resource_description(rsc, show_opts); - - if (rsc->meta != NULL) { - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); - } - - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; } - /* resource information. */ + // Resource information snprintf(ra_name, LINE_MAX, "%s%s%s:%s", class, ((prov == NULL)? "" : PROVIDER_SEP), ((prov == NULL)? "" : prov), - crm_element_value(rsc->xml, XML_ATTR_TYPE)); + crm_element_value(rsc->xml, PCMK_XA_TYPE)); + + if (rsc->meta != NULL) { + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); + } nodes_running_on = pcmk__itoa(g_list_length(rsc->running_on)); if (rsc->lock_node != NULL) { - lock_node_name = rsc->lock_node->details->uname; - } - - rc = pe__name_and_nvpairs_xml(out, true, "resource", 15, - "id", rsc_printable_id(rsc), - "resource_agent", ra_name, - "role", rsc_state, - "target_role", target_role, - "active", pcmk__btoa(rsc->fns->active(rsc, TRUE)), - "orphaned", pe__rsc_bool_str(rsc, pcmk_rsc_removed), - "blocked", pe__rsc_bool_str(rsc, pcmk_rsc_blocked), - "maintenance", pe__rsc_bool_str(rsc, pcmk_rsc_maintenance), - "managed", pe__rsc_bool_str(rsc, pcmk_rsc_managed), - "failed", pe__rsc_bool_str(rsc, pcmk_rsc_failed), - "failure_ignored", pe__rsc_bool_str(rsc, pcmk_rsc_ignore_failure), - "nodes_running_on", nodes_running_on, - "pending", (print_pending? native_pending_task(rsc) : NULL), - "locked_to", lock_node_name, - "description", desc); + locked_to = rsc->lock_node->details->uname; + } + + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE, + PCMK_XA_ID, rsc_printable_id(rsc), + PCMK_XA_RESOURCE_AGENT, ra_name, + PCMK_XA_ROLE, rsc_state, + PCMK_XA_TARGET_ROLE, target_role, + PCMK_XA_ACTIVE, active, + PCMK_XA_ORPHANED, orphaned, + PCMK_XA_BLOCKED, blocked, + PCMK_XA_MAINTENANCE, maintenance, + PCMK_XA_MANAGED, managed, + PCMK_XA_FAILED, failed, + PCMK_XA_FAILURE_IGNORED, ignored, + PCMK_XA_NODES_RUNNING_ON, nodes_running_on, + PCMK_XA_PENDING, pending, + PCMK_XA_LOCKED_TO, locked_to, + PCMK_XA_DESCRIPTION, desc, + NULL); free(nodes_running_on); CRM_ASSERT(rc == pcmk_rc_ok); @@ -1019,11 +1035,13 @@ pe__resource_xml(pcmk__output_t *out, va_list args) for (; gIter != NULL; gIter = gIter->next) { pcmk_node_t *node = (pcmk_node_t *) gIter->data; + const char *cached = pcmk__btoa(node->details->online); - rc = pe__name_and_nvpairs_xml(out, false, "node", 3, - "name", node->details->uname, - "id", node->details->id, - "cached", pcmk__btoa(node->details->online)); + rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE, + PCMK_XA_NAME, node->details->uname, + PCMK_XA_ID, node->details->id, + PCMK_XA_CACHED, cached, + NULL); CRM_ASSERT(rc == pcmk_rc_ok); } } @@ -1042,13 +1060,13 @@ pe__resource_html(pcmk__output_t *out, va_list args) GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); - const pcmk_node_t *node = pe__current_node(rsc); + const pcmk_node_t *node = pcmk__current_node(rsc); if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; } - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (node == NULL) { // This is set only if a non-probe action is pending on this node @@ -1067,9 +1085,9 @@ pe__resource_text(pcmk__output_t *out, va_list args) GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); - const pcmk_node_t *node = pe__current_node(rsc); + const pcmk_node_t *node = pcmk__current_node(rsc); - CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive); + CRM_ASSERT(pcmk__is_primitive(rsc)); if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; @@ -1085,7 +1103,7 @@ pe__resource_text(pcmk__output_t *out, va_list args) void native_free(pcmk_resource_t * rsc) { - pe_rsc_trace(rsc, "Freeing resource action list (not the data)"); + pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)"); common_free(rsc); } @@ -1097,7 +1115,7 @@ native_resource_state(const pcmk_resource_t * rsc, gboolean current) if (current) { role = rsc->role; } - pe_rsc_trace(rsc, "%s state: %s", rsc->id, role2text(role)); + pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role)); return role; } @@ -1170,8 +1188,8 @@ get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_tabl for (; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data; - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); + const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE); int offset = 0; char buffer[LINE_MAX]; @@ -1179,13 +1197,13 @@ get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_tabl int *rsc_counter = NULL; int *active_counter = NULL; - if (rsc->variant != pcmk_rsc_variant_primitive) { + if (!pcmk__is_primitive(rsc)) { continue; } offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class); if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) { - const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); if (prov != NULL) { offset += snprintf(buffer + offset, LINE_MAX - offset, @@ -1198,7 +1216,7 @@ get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_tabl if (rsc_table) { rsc_counter = g_hash_table_lookup(rsc_table, buffer); if (rsc_counter == NULL) { - rsc_counter = calloc(1, sizeof(int)); + rsc_counter = pcmk__assert_alloc(1, sizeof(int)); *rsc_counter = 0; g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter); } @@ -1225,7 +1243,7 @@ get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_tabl active_counter = g_hash_table_lookup(node_table, buffer); if (active_counter == NULL) { - active_counter = calloc(1, sizeof(int)); + active_counter = pcmk__assert_alloc(1, sizeof(int)); *active_counter = 0; g_hash_table_insert(node_table, strdup(buffer), active_counter); } @@ -1448,6 +1466,6 @@ pe__native_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, unsigned int pe__primitive_max_per_node(const pcmk_resource_t *rsc) { - CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)); + CRM_ASSERT(pcmk__is_primitive(rsc)); return 1U; } diff --git a/lib/pengine/pe_actions.c b/lib/pengine/pe_actions.c index aaa6598..b866db6 100644 --- a/lib/pengine/pe_actions.c +++ b/lib/pengine/pe_actions.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. * @@ -13,7 +13,7 @@ #include <stdbool.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/scheduler_internal.h> #include <crm/pengine/internal.h> #include <crm/common/xml_internal.h> @@ -87,26 +87,29 @@ static xmlNode * find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled) { - for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP); - operation != NULL; operation = crm_next_same_xml(operation)) { + for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, + NULL, NULL); + operation != NULL; operation = pcmk__xe_next_same(operation)) { bool enabled = false; const char *config_name = NULL; const char *interval_spec = NULL; + guint tmp_ms = 0U; - // @TODO This does not consider rules, defaults, etc. + // @TODO This does not consider meta-attributes, rules, defaults, etc. if (!include_disabled - && (pcmk__xe_get_bool_attr(operation, "enabled", + && (pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED, &enabled) == pcmk_rc_ok) && !enabled) { continue; } - interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); - if (crm_parse_interval_spec(interval_spec) != interval_ms) { + interval_spec = crm_element_value(operation, PCMK_META_INTERVAL); + pcmk_parse_interval_spec(interval_spec, &tmp_ms); + if (tmp_ms != interval_ms) { continue; } - config_name = crm_element_value(operation, "name"); + config_name = crm_element_value(operation, PCMK_XA_NAME); if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) { return operation; } @@ -166,12 +169,10 @@ static pcmk_action_t * new_action(char *key, const char *task, pcmk_resource_t *rsc, const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler) { - pcmk_action_t *action = calloc(1, sizeof(pcmk_action_t)); - - CRM_ASSERT(action != NULL); + pcmk_action_t *action = pcmk__assert_alloc(1, sizeof(pcmk_action_t)); action->rsc = rsc; - action->task = strdup(task); CRM_ASSERT(action->task != NULL); + action->task = pcmk__str_copy(task); action->uuid = key; if (node) { @@ -180,14 +181,14 @@ new_action(char *key, const char *task, pcmk_resource_t *rsc, if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) { // Resource history deletion for a node can be done on the DC - pe__set_action_flags(action, pcmk_action_on_dc); + pcmk__set_action_flags(action, pcmk_action_on_dc); } - pe__set_action_flags(action, pcmk_action_runnable); + pcmk__set_action_flags(action, pcmk_action_runnable); if (optional) { - pe__set_action_flags(action, pcmk_action_optional); + pcmk__set_action_flags(action, pcmk_action_optional); } else { - pe__clear_action_flags(action, pcmk_action_optional); + pcmk__clear_action_flags(action, pcmk_action_optional); } if (rsc == NULL) { @@ -210,11 +211,11 @@ new_action(char *key, const char *task, pcmk_resource_t *rsc, unpack_operation(action, action->op_entry, interval_ms); } - pe_rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s", - (optional? "optional" : "required"), - scheduler->action_id, key, task, - ((rsc == NULL)? "no resource" : rsc->id), - pe__node_name(node)); + pcmk__rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s", + (optional? "optional" : "required"), + scheduler->action_id, key, task, + ((rsc == NULL)? "no resource" : rsc->id), + pcmk__node_name(node)); action->id = scheduler->action_id++; scheduler->actions = g_list_prepend(scheduler->actions, action); @@ -245,14 +246,13 @@ pcmk__unpack_action_rsc_params(const xmlNode *action_xml, pe_rule_eval_data_t rule_data = { .node_hash = node_attrs, - .role = pcmk_role_unknown, .now = scheduler->now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; - pe__unpack_dataset_nvpairs(action_xml, XML_TAG_ATTR_SETS, + pe__unpack_dataset_nvpairs(action_xml, PCMK_XE_INSTANCE_ATTRIBUTES, &rule_data, params, NULL, FALSE, scheduler); return params; @@ -272,17 +272,17 @@ update_action_optional(pcmk_action_t *action, gboolean optional) if ((action->rsc != NULL) && (action->node != NULL) && !pcmk_is_set(action->flags, pcmk_action_pseudo) && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed) - && (g_hash_table_lookup(action->meta, - XML_LRM_ATTR_INTERVAL_MS) == NULL)) { - pe_rsc_debug(action->rsc, "%s on %s is optional (%s is unmanaged)", - action->uuid, pe__node_name(action->node), - action->rsc->id); - pe__set_action_flags(action, pcmk_action_optional); + && (g_hash_table_lookup(action->meta, PCMK_META_INTERVAL) == NULL)) { + pcmk__rsc_debug(action->rsc, + "%s on %s is optional (%s is unmanaged)", + action->uuid, pcmk__node_name(action->node), + action->rsc->id); + pcmk__set_action_flags(action, pcmk_action_optional); // We shouldn't clear runnable here because ... something // Otherwise require the action if requested } else if (!optional) { - pe__clear_action_flags(action, pcmk_action_optional); + pcmk__clear_action_flags(action, pcmk_action_optional); } } @@ -300,7 +300,7 @@ effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) case pcmk_role_unpromoted: if (rsc->next_role > pcmk_role_unpromoted) { pe__set_next_role(rsc, pcmk_role_unpromoted, - "no-quorum-policy=demote"); + PCMK_OPT_NO_QUORUM_POLICY "=demote"); } policy = pcmk_no_quorum_ignore; break; @@ -330,17 +330,17 @@ update_resource_action_runnable(pcmk_action_t *action, } if (action->node == NULL) { - pe_rsc_trace(action->rsc, "%s is unrunnable (unallocated)", - action->uuid); - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__rsc_trace(action->rsc, "%s is unrunnable (unallocated)", + action->uuid); + pcmk__clear_action_flags(action, pcmk_action_runnable); } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc) && !(action->node->details->online) - && (!pe__is_guest_node(action->node) + && (!pcmk__is_guest_or_bundle_node(action->node) || action->node->details->remote_requires_reset)) { - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__clear_action_flags(action, pcmk_action_runnable); do_crm_log(LOG_WARNING, "%s on %s is unrunnable (node is offline)", - action->uuid, pe__node_name(action->node)); + action->uuid, pcmk__node_name(action->node)); if (pcmk_is_set(action->rsc->flags, pcmk_rsc_managed) && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei) && !(action->node->details->unclean)) { @@ -349,54 +349,57 @@ update_resource_action_runnable(pcmk_action_t *action, } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc) && action->node->details->pending) { - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__clear_action_flags(action, pcmk_action_runnable); do_crm_log(LOG_WARNING, "Action %s on %s is unrunnable (node is pending)", - action->uuid, pe__node_name(action->node)); + action->uuid, pcmk__node_name(action->node)); } else if (action->needs == pcmk_requires_nothing) { pe_action_set_reason(action, NULL, TRUE); - if (pe__is_guest_node(action->node) + if (pcmk__is_guest_or_bundle_node(action->node) && !pe_can_fence(scheduler, action->node)) { /* An action that requires nothing usually does not require any * fencing in order to be runnable. However, there is an exception: * such an action cannot be completed if it is on a guest node whose * host is unclean and cannot be fenced. */ - pe_rsc_debug(action->rsc, "%s on %s is unrunnable " - "(node's host cannot be fenced)", - action->uuid, pe__node_name(action->node)); - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__rsc_debug(action->rsc, + "%s on %s is unrunnable " + "(node's host cannot be fenced)", + action->uuid, pcmk__node_name(action->node)); + pcmk__clear_action_flags(action, pcmk_action_runnable); } else { - pe_rsc_trace(action->rsc, - "%s on %s does not require fencing or quorum", - action->uuid, pe__node_name(action->node)); - pe__set_action_flags(action, pcmk_action_runnable); + pcmk__rsc_trace(action->rsc, + "%s on %s does not require fencing or quorum", + action->uuid, pcmk__node_name(action->node)); + pcmk__set_action_flags(action, pcmk_action_runnable); } } else { switch (effective_quorum_policy(action->rsc, scheduler)) { case pcmk_no_quorum_stop: - pe_rsc_debug(action->rsc, "%s on %s is unrunnable (no quorum)", - action->uuid, pe__node_name(action->node)); - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__rsc_debug(action->rsc, + "%s on %s is unrunnable (no quorum)", + action->uuid, pcmk__node_name(action->node)); + pcmk__clear_action_flags(action, pcmk_action_runnable); pe_action_set_reason(action, "no quorum", true); break; case pcmk_no_quorum_freeze: if (!action->rsc->fns->active(action->rsc, TRUE) || (action->rsc->next_role > action->rsc->role)) { - pe_rsc_debug(action->rsc, - "%s on %s is unrunnable (no quorum)", - action->uuid, pe__node_name(action->node)); - pe__clear_action_flags(action, pcmk_action_runnable); + pcmk__rsc_debug(action->rsc, + "%s on %s is unrunnable (no quorum)", + action->uuid, + pcmk__node_name(action->node)); + pcmk__clear_action_flags(action, pcmk_action_runnable); pe_action_set_reason(action, "quorum freeze", true); } break; default: //pe_action_set_reason(action, NULL, TRUE); - pe__set_action_flags(action, pcmk_action_runnable); + pcmk__set_action_flags(action, pcmk_action_runnable); break; } } @@ -417,13 +420,13 @@ update_resource_flags_for_action(pcmk_resource_t *rsc, * within Pacemaker, and will eventually be removed */ if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) { - pe__set_resource_flags(rsc, pcmk_rsc_stopping); + pcmk__set_rsc_flags(rsc, pcmk_rsc_stopping); } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) { if (pcmk_is_set(action->flags, pcmk_action_runnable)) { - pe__set_resource_flags(rsc, pcmk_rsc_starting); + pcmk__set_rsc_flags(rsc, pcmk_rsc_starting); } else { - pe__clear_resource_flags(rsc, pcmk_rsc_starting); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_starting); } } } @@ -431,7 +434,9 @@ update_resource_flags_for_action(pcmk_resource_t *rsc, static bool valid_stop_on_fail(const char *value) { - return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL); + return !pcmk__strcase_any_of(value, + PCMK_VALUE_STANDBY, PCMK_VALUE_DEMOTE, + PCMK_VALUE_STOP, NULL); } /*! @@ -450,18 +455,17 @@ validate_on_fail(const pcmk_resource_t *rsc, const char *action_name, const char *name = NULL; const char *role = NULL; const char *interval_spec = NULL; - const char *value = g_hash_table_lookup(meta, XML_OP_ATTR_ON_FAIL); - char *key = NULL; - char *new_value = NULL; + const char *value = g_hash_table_lookup(meta, PCMK_META_ON_FAIL); + guint interval_ms = 0U; // Stop actions can only use certain on-fail values if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none) && !valid_stop_on_fail(value)) { - pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop " + pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s stop " "action to default value because '%s' is not " "allowed for stop", rsc->id, value); - g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL); + g_hash_table_remove(meta, PCMK_META_ON_FAIL); return; } @@ -475,77 +479,79 @@ validate_on_fail(const pcmk_resource_t *rsc, const char *action_name, * block (which may have rules that need to be evaluated) rather than * XML properties. */ - for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP); - operation != NULL; operation = crm_next_same_xml(operation)) { + for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, + NULL, NULL); + operation != NULL; operation = pcmk__xe_next_same(operation)) { + bool enabled = false; const char *promote_on_fail = NULL; /* We only care about explicit on-fail (if promote uses default, so * can demote) */ - promote_on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL); + promote_on_fail = crm_element_value(operation, PCMK_META_ON_FAIL); if (promote_on_fail == NULL) { continue; } // We only care about recurring monitors for the promoted role - name = crm_element_value(operation, "name"); - role = crm_element_value(operation, "role"); + name = crm_element_value(operation, PCMK_XA_NAME); + role = crm_element_value(operation, PCMK_XA_ROLE); if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none) - || !pcmk__strcase_any_of(role, PCMK__ROLE_PROMOTED, + || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED, PCMK__ROLE_PROMOTED_LEGACY, NULL)) { continue; } - interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); - if (crm_parse_interval_spec(interval_spec) == 0) { + interval_spec = crm_element_value(operation, PCMK_META_INTERVAL); + pcmk_parse_interval_spec(interval_spec, &interval_ms); + if (interval_ms == 0U) { continue; } // We only care about enabled monitors - if ((pcmk__xe_get_bool_attr(operation, "enabled", + if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED, &enabled) == pcmk_rc_ok) && !enabled) { continue; } - // Demote actions can't default to on-fail="demote" - if (pcmk__str_eq(promote_on_fail, "demote", pcmk__str_casei)) { + /* Demote actions can't default to + * PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE + */ + if (pcmk__str_eq(promote_on_fail, PCMK_VALUE_DEMOTE, + pcmk__str_casei)) { continue; } // Use value from first applicable promote action found - key = strdup(XML_OP_ATTR_ON_FAIL); - new_value = strdup(promote_on_fail); - CRM_ASSERT((key != NULL) && (new_value != NULL)); - g_hash_table_insert(meta, key, new_value); + pcmk__insert_dup(meta, PCMK_META_ON_FAIL, promote_on_fail); } return; } if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none) - && !pcmk__str_eq(value, "ignore", pcmk__str_casei)) { - key = strdup(XML_OP_ATTR_ON_FAIL); - new_value = strdup("ignore"); - CRM_ASSERT((key != NULL) && (new_value != NULL)); - g_hash_table_insert(meta, key, new_value); + && !pcmk__str_eq(value, PCMK_VALUE_IGNORE, pcmk__str_casei)) { + + pcmk__insert_dup(meta, PCMK_META_ON_FAIL, PCMK_VALUE_IGNORE); return; } - // on-fail="demote" is allowed only for certain actions - if (pcmk__str_eq(value, "demote", pcmk__str_casei)) { - name = crm_element_value(action_config, "name"); - role = crm_element_value(action_config, "role"); - interval_spec = crm_element_value(action_config, - XML_LRM_ATTR_INTERVAL); + // PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE is allowed only for certain actions + if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) { + name = crm_element_value(action_config, PCMK_XA_NAME); + role = crm_element_value(action_config, PCMK_XA_ROLE); + interval_spec = crm_element_value(action_config, PCMK_META_INTERVAL); + pcmk_parse_interval_spec(interval_spec, &interval_ms); if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none) - && (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none) - || !pcmk__strcase_any_of(role, PCMK__ROLE_PROMOTED, - PCMK__ROLE_PROMOTED_LEGACY, NULL) - || (crm_parse_interval_spec(interval_spec) == 0))) { - pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s " + && ((interval_ms == 0U) + || !pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none) + || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED, + PCMK__ROLE_PROMOTED_LEGACY, NULL))) { + + pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s %s " "action to default value because 'demote' is not " "allowed for it", rsc->id, name); - g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL); + g_hash_table_remove(meta, PCMK_META_ON_FAIL); return; } } @@ -554,12 +560,12 @@ validate_on_fail(const pcmk_resource_t *rsc, const char *action_name, static int unpack_timeout(const char *value) { - int timeout_ms = crm_get_msec(value); + long long timeout_ms = crm_get_msec(value); - if (timeout_ms < 0) { + if (timeout_ms <= 0) { timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS; } - return timeout_ms; + return (int) QB_MIN(timeout_ms, INT_MAX); } // true if value contains valid, non-NULL interval origin for recurring op @@ -580,9 +586,9 @@ unpack_interval_origin(const char *value, const xmlNode *xml_obj, // Parse interval origin from text origin = crm_time_new(value); if (origin == NULL) { - pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation " - "'%s' because '%s' is not valid", - (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value); + pcmk__config_err("Ignoring '" PCMK_META_INTERVAL_ORIGIN "' for " + "operation '%s' because '%s' is not valid", + pcmk__s(pcmk__xe_id(xml_obj), "(missing ID)"), value); return false; } @@ -596,8 +602,7 @@ unpack_interval_origin(const char *value, const xmlNode *xml_obj, // Calculate seconds remaining until next interval result = ((result <= 0)? 0 : interval_sec) - result; crm_info("Calculated a start delay of %llds for operation '%s'", - result, - (ID(xml_obj)? ID(xml_obj) : "(unspecified)")); + result, pcmk__s(pcmk__xe_id(xml_obj), "(unspecified)")); if (start_delay != NULL) { *start_delay = result * 1000; // milliseconds @@ -608,22 +613,24 @@ unpack_interval_origin(const char *value, const xmlNode *xml_obj, static int unpack_start_delay(const char *value, GHashTable *meta) { - int start_delay = 0; + long long start_delay_ms = 0; - if (value != NULL) { - start_delay = crm_get_msec(value); + if (value == NULL) { + return 0; + } - if (start_delay < 0) { - start_delay = 0; - } + start_delay_ms = crm_get_msec(value); + start_delay_ms = QB_MIN(start_delay_ms, INT_MAX); + if (start_delay_ms < 0) { + start_delay_ms = 0; + } - if (meta) { - g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY), - pcmk__itoa(start_delay)); - } + if (meta != NULL) { + g_hash_table_replace(meta, strdup(PCMK_META_START_DELAY), + pcmk__itoa(start_delay_ms)); } - return start_delay; + return (int) start_delay_ms; } /*! @@ -641,25 +648,28 @@ most_frequent_monitor(const pcmk_resource_t *rsc) guint min_interval_ms = G_MAXUINT; xmlNode *op = NULL; - for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP); - operation != NULL; operation = crm_next_same_xml(operation)) { + for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, + NULL, NULL); + operation != NULL; operation = pcmk__xe_next_same(operation)) { + bool enabled = false; - guint interval_ms = 0; + guint interval_ms = 0U; const char *interval_spec = crm_element_value(operation, - XML_LRM_ATTR_INTERVAL); + PCMK_META_INTERVAL); // We only care about enabled recurring monitors - if (!pcmk__str_eq(crm_element_value(operation, "name"), + if (!pcmk__str_eq(crm_element_value(operation, PCMK_XA_NAME), PCMK_ACTION_MONITOR, pcmk__str_none)) { continue; } - interval_ms = crm_parse_interval_spec(interval_spec); - if (interval_ms == 0) { + + pcmk_parse_interval_spec(interval_spec, &interval_ms); + if (interval_ms == 0U) { continue; } - // @TODO This does not account for rules, defaults, etc. - if ((pcmk__xe_get_bool_attr(operation, "enabled", + // @TODO This does not consider meta-attributes, rules, defaults, etc. + if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED, &enabled) == pcmk_rc_ok) && !enabled) { continue; } @@ -694,15 +704,13 @@ pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, const xmlNode *action_config) { GHashTable *meta = NULL; - char *name = NULL; - char *value = NULL; const char *timeout_spec = NULL; const char *str = NULL; pe_rsc_eval_data_t rsc_rule_data = { - .standard = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS), - .provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER), - .agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE), + .standard = crm_element_value(rsc->xml, PCMK_XA_CLASS), + .provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER), + .agent = crm_element_value(rsc->xml, PCMK_XA_TYPE), }; pe_op_eval_data_t op_rule_data = { @@ -711,8 +719,13 @@ pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, }; pe_rule_eval_data_t rule_data = { + /* @COMPAT Support for node attribute expressions in operation + * meta-attributes (whether in the operation configuration or operation + * defaults) is deprecated. When we can break behavioral backward + * compatibility, drop this line. + */ .node_hash = (node == NULL)? NULL : node->details->attrs, - .role = pcmk_role_unknown, + .now = rsc->cluster->now, .match_data = NULL, .rsc_data = &rsc_rule_data, @@ -722,36 +735,35 @@ pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, meta = pcmk__strkey_table(free, free); // Cluster-wide <op_defaults> <meta_attributes> - pe__unpack_dataset_nvpairs(rsc->cluster->op_defaults, XML_TAG_META_SETS, - &rule_data, meta, NULL, FALSE, rsc->cluster); + pe__unpack_dataset_nvpairs(rsc->cluster->op_defaults, + PCMK_XE_META_ATTRIBUTES, &rule_data, meta, NULL, + FALSE, rsc->cluster); // Derive default timeout for probes from recurring monitor timeouts if (pcmk_is_probe(action_name, interval_ms)) { xmlNode *min_interval_mon = most_frequent_monitor(rsc); if (min_interval_mon != NULL) { - /* @TODO This does not consider timeouts set in meta_attributes - * blocks (which may also have rules that need to be evaluated). + /* @TODO This does not consider timeouts set in + * PCMK_XE_META_ATTRIBUTES blocks (which may also have rules that + * need to be evaluated). */ timeout_spec = crm_element_value(min_interval_mon, - XML_ATTR_TIMEOUT); + PCMK_META_TIMEOUT); if (timeout_spec != NULL) { - pe_rsc_trace(rsc, - "Setting default timeout for %s probe to " - "most frequent monitor's timeout '%s'", - rsc->id, timeout_spec); - name = strdup(XML_ATTR_TIMEOUT); - value = strdup(timeout_spec); - CRM_ASSERT((name != NULL) && (value != NULL)); - g_hash_table_insert(meta, name, value); + pcmk__rsc_trace(rsc, + "Setting default timeout for %s probe to " + "most frequent monitor's timeout '%s'", + rsc->id, timeout_spec); + pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec); } } } if (action_config != NULL) { // <op> <meta_attributes> take precedence over defaults - pe__unpack_dataset_nvpairs(action_config, XML_TAG_META_SETS, &rule_data, - meta, NULL, TRUE, rsc->cluster); + pe__unpack_dataset_nvpairs(action_config, PCMK_XE_META_ATTRIBUTES, + &rule_data, meta, NULL, TRUE, rsc->cluster); /* Anything set as an <op> XML property has highest precedence. * This ensures we use the name and interval from the <op> tag. @@ -759,24 +771,19 @@ pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, */ for (xmlAttrPtr attr = action_config->properties; attr != NULL; attr = attr->next) { - name = strdup((const char *) attr->name); - value = strdup(pcmk__xml_attr_value(attr)); - - CRM_ASSERT((name != NULL) && (value != NULL)); - g_hash_table_insert(meta, name, value); + pcmk__insert_dup(meta, (const char *) attr->name, + pcmk__xml_attr_value(attr)); } } - g_hash_table_remove(meta, XML_ATTR_ID); + g_hash_table_remove(meta, PCMK_XA_ID); // Normalize interval to milliseconds if (interval_ms > 0) { - name = strdup(XML_LRM_ATTR_INTERVAL); - CRM_ASSERT(name != NULL); - value = crm_strdup_printf("%u", interval_ms); - g_hash_table_insert(meta, name, value); + g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_INTERVAL), + crm_strdup_printf("%u", interval_ms)); } else { - g_hash_table_remove(meta, XML_LRM_ATTR_INTERVAL); + g_hash_table_remove(meta, PCMK_META_INTERVAL); } /* Timeout order of precedence (highest to lowest): @@ -799,39 +806,33 @@ pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout"); if (timeout_spec != NULL) { - pe_rsc_trace(rsc, - "Setting timeout for %s %s to " - "pcmk_monitor_timeout (%s)", - rsc->id, action_name, timeout_spec); - name = strdup(XML_ATTR_TIMEOUT); - value = strdup(timeout_spec); - CRM_ASSERT((name != NULL) && (value != NULL)); - g_hash_table_insert(meta, name, value); + pcmk__rsc_trace(rsc, + "Setting timeout for %s %s to " + "pcmk_monitor_timeout (%s)", + rsc->id, action_name, timeout_spec); + pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec); } } // Normalize timeout to positive milliseconds - name = strdup(XML_ATTR_TIMEOUT); - CRM_ASSERT(name != NULL); - timeout_spec = g_hash_table_lookup(meta, XML_ATTR_TIMEOUT); - g_hash_table_insert(meta, name, pcmk__itoa(unpack_timeout(timeout_spec))); + timeout_spec = g_hash_table_lookup(meta, PCMK_META_TIMEOUT); + g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_TIMEOUT), + pcmk__itoa(unpack_timeout(timeout_spec))); // Ensure on-fail has a valid value validate_on_fail(rsc, action_name, action_config, meta); - // Normalize start-delay - str = g_hash_table_lookup(meta, XML_OP_ATTR_START_DELAY); + // Normalize PCMK_META_START_DELAY + str = g_hash_table_lookup(meta, PCMK_META_START_DELAY); if (str != NULL) { unpack_start_delay(str, meta); } else { long long start_delay = 0; - str = g_hash_table_lookup(meta, XML_OP_ATTR_ORIGIN); + str = g_hash_table_lookup(meta, PCMK_META_INTERVAL_ORIGIN); if (unpack_interval_origin(str, action_config, interval_ms, rsc->cluster->now, &start_delay)) { - name = strdup(XML_OP_ATTR_START_DELAY); - CRM_ASSERT(name != NULL); - g_hash_table_insert(meta, name, + g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_START_DELAY), crm_strdup_printf("%lld", start_delay)); } } @@ -870,7 +871,7 @@ pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name) } else { value = "nothing"; } - pe_rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value); + pcmk__rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value); return requires; } @@ -893,19 +894,22 @@ pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, bool needs_remote_reset = false; enum action_fail_response on_fail = pcmk_on_fail_ignore; + // There's no enum value for unknown or invalid, so assert + CRM_ASSERT((rsc != NULL) && (action_name != NULL)); + if (value == NULL) { // Use default - } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) { on_fail = pcmk_on_fail_block; desc = "block"; - } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_FENCE, pcmk__str_casei)) { if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { on_fail = pcmk_on_fail_fence_node; desc = "node fencing"; } else { - pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for " + pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for " "%s of %s to 'stop' because 'fence' is not " "valid when fencing is disabled", action_name, rsc->id); @@ -913,11 +917,12 @@ pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, desc = "stop resource"; } - } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_STANDBY, pcmk__str_casei)) { on_fail = pcmk_on_fail_standby_node; desc = "node standby"; - } else if (pcmk__strcase_any_of(value, "ignore", PCMK__VALUE_NOTHING, + } else if (pcmk__strcase_any_of(value, + PCMK_VALUE_IGNORE, PCMK_VALUE_NOTHING, NULL)) { desc = "ignore"; @@ -925,31 +930,32 @@ pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, on_fail = pcmk_on_fail_ban; desc = "force migration"; - } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_STOP, pcmk__str_casei)) { on_fail = pcmk_on_fail_stop; desc = "stop resource"; - } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) { on_fail = pcmk_on_fail_restart; desc = "restart (and possibly migrate)"; - } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART_CONTAINER, + pcmk__str_casei)) { if (rsc->container == NULL) { - pe_rsc_debug(rsc, - "Using default " XML_OP_ATTR_ON_FAIL - " for %s of %s because it does not have a container", - action_name, rsc->id); + pcmk__rsc_debug(rsc, + "Using default " PCMK_META_ON_FAIL " for %s " + "of %s because it does not have a container", + action_name, rsc->id); } else { on_fail = pcmk_on_fail_restart_container; desc = "restart container (and possibly migrate)"; } - } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) { on_fail = pcmk_on_fail_demote; desc = "demote instance"; } else { - pcmk__config_err("Using default '" XML_OP_ATTR_ON_FAIL "' for " + pcmk__config_err("Using default '" PCMK_META_ON_FAIL "' for " "%s of %s because '%s' is not valid", action_name, rsc->id, value); } @@ -957,9 +963,10 @@ pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, /* Remote node connections are handled specially. Failures that result * in dropping an active connection must result in fencing. The only * failures that don't are probes and starts. The user can explicitly set - * on-fail="fence" to fence after start failures. + * PCMK_META_ON_FAIL=PCMK_VALUE_FENCE to fence after start failures. */ - if (pe__resource_is_remote_conn(rsc) + if (rsc->is_remote_node + && pcmk__is_remote_node(pcmk_find_node(rsc->cluster, rsc->id)) && !pcmk_is_probe(action_name, interval_ms) && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) { needs_remote_reset = true; @@ -1003,9 +1010,9 @@ pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, desc = "restart (and possibly migrate) (default)"; } - pe_rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s", - pcmk__readable_interval(interval_ms), action_name, - rsc->id, desc); + pcmk__rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s", + pcmk__readable_interval(interval_ms), action_name, + rsc->id, desc); return on_fail; } @@ -1044,13 +1051,18 @@ pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name, } // @COMPAT Check for explicitly configured role (deprecated) - value = g_hash_table_lookup(meta, "role_after_failure"); + value = g_hash_table_lookup(meta, PCMK__META_ROLE_AFTER_FAILURE); if (value != NULL) { - pe_warn_once(pcmk__wo_role_after, - "Support for role_after_failure is deprecated " - "and will be removed in a future release"); + pcmk__warn_once(pcmk__wo_role_after, + "Support for " PCMK__META_ROLE_AFTER_FAILURE " is " + "deprecated and will be removed in a future release"); if (role == pcmk_role_unknown) { - role = text2role(value); + role = pcmk_parse_role(value); + if (role == pcmk_role_unknown) { + pcmk__config_err("Ignoring invalid value %s for " + PCMK__META_ROLE_AFTER_FAILURE, + value); + } } } @@ -1062,8 +1074,8 @@ pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name, role = pcmk_role_started; } } - pe_rsc_trace(rsc, "Role after %s %s failure is: %s", - rsc->id, action_name, role2text(role)); + pcmk__rsc_trace(rsc, "Role after %s %s failure is: %s", + rsc->id, action_name, pcmk_role_text(role)); return role; } @@ -1089,7 +1101,7 @@ unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj, action->task, interval_ms, xml_obj); action->needs = pcmk__action_requires(action->rsc, action->task); - value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); + value = g_hash_table_lookup(action->meta, PCMK_META_ON_FAIL); action->on_fail = pcmk__parse_on_fail(action->rsc, action->task, interval_ms, value); @@ -1132,6 +1144,11 @@ custom_action(pcmk_resource_t *rsc, char *key, const char *task, update_action_optional(action, optional); if (rsc != NULL) { + /* An action can be initially created with a NULL node, and later have + * the node added via find_existing_action() (above) -> find_actions(). + * That is why the extra parameters are unpacked here rather than in + * new_action(). + */ if ((action->node != NULL) && (action->op_entry != NULL) && !pcmk_is_set(action->flags, pcmk_action_attrs_evaluated)) { @@ -1142,7 +1159,7 @@ custom_action(pcmk_resource_t *rsc, char *key, const char *task, } action->extra = pcmk__unpack_action_rsc_params(action->op_entry, attrs, scheduler); - pe__set_action_flags(action, pcmk_action_attrs_evaluated); + pcmk__set_action_flags(action, pcmk_action_attrs_evaluated); } update_resource_action_runnable(action, scheduler); @@ -1163,7 +1180,7 @@ get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler) if (op == NULL) { op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler); - pe__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable); + pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable); } return op; } @@ -1185,8 +1202,7 @@ find_unfencing_devices(GList *candidates, GList *matches) } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta, PCMK_STONITH_PROVIDES), - PCMK__VALUE_UNFENCING, - pcmk__str_casei)) { + PCMK_VALUE_UNFENCING, pcmk__str_casei)) { matches = g_list_prepend(matches, candidate); } } @@ -1203,7 +1219,7 @@ node_priority_fencing_delay(const pcmk_node_t *node, int lowest_priority = 0; GList *gIter = NULL; - // `priority-fencing-delay` is disabled + // PCMK_OPT_PRIORITY_FENCING_DELAY is disabled if (scheduler->priority_fencing_delay <= 0) { return 0; } @@ -1281,9 +1297,10 @@ pe_fence_op(pcmk_node_t *node, const char *op, bool optional, stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node, TRUE, scheduler); - add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname); - add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id); - add_hash_param(stonith_op->meta, "stonith_action", op); + pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE, node->details->uname); + pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE_UUID, + node->details->id); + pcmk__insert_meta(stonith_op, PCMK__META_STONITH_ACTION, op); if (pcmk_is_set(scheduler->flags, pcmk_sched_enable_unfencing)) { /* Extra work to detect device changes @@ -1293,28 +1310,25 @@ pe_fence_op(pcmk_node_t *node, const char *op, bool optional, GList *matches = find_unfencing_devices(scheduler->resources, NULL); - char *key = NULL; - char *value = NULL; - for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *match = gIter->data; const char *agent = g_hash_table_lookup(match->meta, - XML_ATTR_TYPE); - op_digest_cache_t *data = NULL; + PCMK_XA_TYPE); + pcmk__op_digest_t *data = NULL; data = pe__compare_fencing_digest(match, agent, node, scheduler); if (data->rc == pcmk__digest_mismatch) { optional = FALSE; crm_notice("Unfencing node %s because the definition of " - "%s changed", pe__node_name(node), match->id); + "%s changed", pcmk__node_name(node), match->id); if (!pcmk__is_daemon && scheduler->priv != NULL) { pcmk__output_t *out = scheduler->priv; out->info(out, "notice: Unfencing node %s because the " "definition of %s changed", - pe__node_name(node), match->id); + pcmk__node_name(node), match->id); } } @@ -1325,16 +1339,12 @@ pe_fence_op(pcmk_node_t *node, const char *op, bool optional, match->id, ":", agent, ":", data->digest_secure_calc, ",", NULL); } - key = strdup(XML_OP_ATTR_DIGESTS_ALL); - value = strdup((const char *) digests_all->str); - CRM_ASSERT((key != NULL) && (value != NULL)); - g_hash_table_insert(stonith_op->meta, key, value); + pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_ALL, + digests_all->str); g_string_free(digests_all, TRUE); - key = strdup(XML_OP_ATTR_DIGESTS_SECURE); - value = strdup((const char *) digests_secure->str); - CRM_ASSERT((key != NULL) && (value != NULL)); - g_hash_table_insert(stonith_op->meta, key, value); + pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_SECURE, + digests_secure->str); g_string_free(digests_secure, TRUE); } @@ -1344,8 +1354,10 @@ pe_fence_op(pcmk_node_t *node, const char *op, bool optional, if (scheduler->priority_fencing_delay > 0 - /* It's a suitable case where `priority-fencing-delay` applies. - * At least add `priority-fencing-delay` field as an indicator. */ + /* It's a suitable case where PCMK_OPT_PRIORITY_FENCING_DELAY + * applies. At least add PCMK_OPT_PRIORITY_FENCING_DELAY field as + * an indicator. + */ && (priority_delay /* The priority delay needs to be recalculated if this function has @@ -1353,22 +1365,22 @@ pe_fence_op(pcmk_node_t *node, const char *op, bool optional, * priority has already been calculated by native_add_running(). */ || g_hash_table_lookup(stonith_op->meta, - XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) { + PCMK_OPT_PRIORITY_FENCING_DELAY) != NULL)) { - /* Add `priority-fencing-delay` to the fencing op even if it's 0 for - * the targeting node. So that it takes precedence over any possible - * `pcmk_delay_base/max`. + /* Add PCMK_OPT_PRIORITY_FENCING_DELAY to the fencing op even if + * it's 0 for the targeting node. So that it takes precedence over + * any possible `pcmk_delay_base/max`. */ char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, scheduler)); g_hash_table_insert(stonith_op->meta, - strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY), + strdup(PCMK_OPT_PRIORITY_FENCING_DELAY), delay_s); } if(optional == FALSE && pe_can_fence(scheduler, node)) { - pe__clear_action_flags(stonith_op, pcmk_action_optional); + pcmk__clear_action_flags(stonith_op, pcmk_action_optional); pe_action_set_reason(stonith_op, reason, false); } else if(reason && stonith_op->reason == NULL) { @@ -1400,61 +1412,12 @@ pe_free_action(pcmk_action_t *action) free(action); } -int -pe_get_configured_timeout(pcmk_resource_t *rsc, const char *action, - pcmk_scheduler_t *scheduler) -{ - xmlNode *child = NULL; - GHashTable *action_meta = NULL; - const char *timeout_spec = NULL; - int timeout_ms = 0; - - pe_rule_eval_data_t rule_data = { - .node_hash = NULL, - .role = pcmk_role_unknown, - .now = scheduler->now, - .match_data = NULL, - .rsc_data = NULL, - .op_data = NULL - }; - - for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP); - child != NULL; child = crm_next_same_xml(child)) { - if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME), - pcmk__str_casei)) { - timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT); - break; - } - } - - if (timeout_spec == NULL && scheduler->op_defaults) { - action_meta = pcmk__strkey_table(free, free); - pe__unpack_dataset_nvpairs(scheduler->op_defaults, XML_TAG_META_SETS, - &rule_data, action_meta, NULL, FALSE, - scheduler); - timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT); - } - - // @TODO check meta-attributes - // @TODO maybe use min-interval monitor timeout as default for monitors - - timeout_ms = crm_get_msec(timeout_spec); - if (timeout_ms < 0) { - timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS; - } - - if (action_meta != NULL) { - g_hash_table_destroy(action_meta); - } - return timeout_ms; -} - enum action_tasks get_complex_task(const pcmk_resource_t *rsc, const char *name) { - enum action_tasks task = text2task(name); + enum action_tasks task = pcmk_parse_action(name); - if ((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)) { + if (pcmk__is_primitive(rsc)) { switch (task) { case pcmk_action_stopped: case pcmk_action_started: @@ -1503,7 +1466,7 @@ find_first_action(const GList *input, const char *uuid, const char *task, } else if (action->node == NULL) { continue; - } else if (on_node->details == action->node->details) { + } else if (pcmk__same_node(on_node, action->node)) { return action; } } @@ -1531,13 +1494,13 @@ find_actions(GList *input, const char *key, const pcmk_node_t *on_node) } else if (action->node == NULL) { crm_trace("Action %s matches (unallocated, assigning to %s)", - key, pe__node_name(on_node)); + key, pcmk__node_name(on_node)); action->node = pe__copy_node(on_node); result = g_list_prepend(result, action); - } else if (on_node->details == action->node->details) { - crm_trace("Action %s on %s matches", key, pe__node_name(on_node)); + } else if (pcmk__same_node(on_node, action->node)) { + crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node)); result = g_list_prepend(result, action); } } @@ -1564,7 +1527,7 @@ find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node) && pcmk__str_eq(on_node->details->id, action->node->details->id, pcmk__str_casei)) { - crm_trace("Action %s on %s matches", key, pe__node_name(on_node)); + crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node)); result = g_list_prepend(result, action); } } @@ -1640,11 +1603,12 @@ void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite) { if (action->reason != NULL && overwrite) { - pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'", - action->uuid, action->reason, pcmk__s(reason, "(none)")); + pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'", + action->uuid, action->reason, + pcmk__s(reason, "(none)")); } else if (action->reason == NULL) { - pe_rsc_trace(action->rsc, "Set %s reason to '%s'", - action->uuid, pcmk__s(reason, "(none)")); + pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'", + action->uuid, pcmk__s(reason, "(none)")); } else { // crm_assert(action->reason != NULL && !overwrite); return; @@ -1688,17 +1652,17 @@ pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, char *a_uuid = NULL; char *b_uuid = NULL; - const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID); - const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID); + const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID); + const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID); - const char *a_node = crm_element_value(xml_a, XML_LRM_ATTR_TARGET); - const char *b_node = crm_element_value(xml_b, XML_LRM_ATTR_TARGET); + const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE); + const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE); bool same_node = true; /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c). * - * In case that any of the lrm_rsc_op entries doesn't have on_node + * In case that any of the PCMK__XE_LRM_RSC_OP entries doesn't have on_node * attribute, we need to explicitly tell whether the two operations are on * the same node. */ @@ -1710,17 +1674,18 @@ pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, } if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) { - /* We have duplicate lrm_rsc_op entries in the status + /* We have duplicate PCMK__XE_LRM_RSC_OP entries in the status * section which is unlikely to be a good thing * - we can handle it easily enough, but we need to get * to the bottom of why it's happening. */ - pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); + pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s", + a_xml_id); sort_return(0, "duplicate"); } - crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id); - crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id); + crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id); + crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id); if (a_call_id == -1 && b_call_id == -1) { /* both are pending ops so it doesn't matter since @@ -1736,15 +1701,14 @@ pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, } else if (a_call_id >= 0 && b_call_id >= 0 && (!same_node || a_call_id == b_call_id)) { - /* - * The op and last_failed_op are the same - * Order on last-rc-change + /* The op and last_failed_op are the same. Order on + * PCMK_XA_LAST_RC_CHANGE. */ time_t last_a = -1; time_t last_b = -1; - crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a); - crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b); + crm_element_value_epoch(xml_a, PCMK_XA_LAST_RC_CHANGE, &last_a); + crm_element_value_epoch(xml_b, PCMK_XA_LAST_RC_CHANGE, &last_b); crm_trace("rc-change: %lld vs %lld", (long long) last_a, (long long) last_b); @@ -1757,15 +1721,18 @@ pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, sort_return(0, "rc-change"); } else { - /* One of the inputs is a pending operation - * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other + /* One of the inputs is a pending operation. + * Attempt to use PCMK__XA_TRANSITION_MAGIC to determine its age relative + * to the other. */ int a_id = -1; int b_id = -1; - const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC); - const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC); + const char *a_magic = crm_element_value(xml_a, + PCMK__XA_TRANSITION_MAGIC); + const char *b_magic = crm_element_value(xml_b, + PCMK__XA_TRANSITION_MAGIC); CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic")); if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL, @@ -1841,9 +1808,9 @@ pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL, optional, rsc->cluster); - pe__set_action_flags(action, pcmk_action_pseudo); + pcmk__set_action_flags(action, pcmk_action_pseudo); if (runnable) { - pe__set_action_flags(action, pcmk_action_runnable); + pcmk__set_action_flags(action, pcmk_action_runnable); } return action; } @@ -1855,17 +1822,13 @@ pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, * \param[in,out] action Action to add expected result to * \param[in] expected_result Expected result to add * - * \note This is more efficient than calling add_hash_param(). + * \note This is more efficient than calling pcmk__insert_meta(). */ void pe__add_action_expected_result(pcmk_action_t *action, int expected_result) { - char *name = NULL; - CRM_ASSERT((action != NULL) && (action->meta != NULL)); - name = strdup(XML_ATTR_TE_TARGET_RC); - CRM_ASSERT (name != NULL); - - g_hash_table_insert(action->meta, name, pcmk__itoa(expected_result)); + g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC), + pcmk__itoa(expected_result)); } diff --git a/lib/pengine/pe_digest.c b/lib/pengine/pe_digest.c index 546a2a7..6000b27 100644 --- a/lib/pengine/pe_digest.c +++ b/lib/pengine/pe_digest.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. * @@ -13,7 +13,6 @@ #include <stdbool.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> #include <crm/pengine/internal.h> @@ -33,7 +32,7 @@ extern bool pcmk__is_daemon; void pe__free_digests(gpointer ptr) { - op_digest_cache_t *data = ptr; + pcmk__op_digest_t *data = ptr; if (data != NULL) { free_xml(data->params_all); @@ -96,7 +95,7 @@ attr_in_string(xmlAttrPtr a, void *user_data) * \param[in,out] scheduler Scheduler data */ static void -calculate_main_digest(op_digest_cache_t *data, pcmk_resource_t *rsc, +calculate_main_digest(pcmk__op_digest_t *data, pcmk_resource_t *rsc, const pcmk_node_t *node, GHashTable *params, const char *task, guint *interval_ms, const xmlNode *xml_op, const char *op_version, @@ -104,18 +103,18 @@ calculate_main_digest(op_digest_cache_t *data, pcmk_resource_t *rsc, { xmlNode *action_config = NULL; - data->params_all = create_xml_node(NULL, XML_TAG_PARAMS); + data->params_all = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS); /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers * that themselves are Pacemaker Remote nodes */ - (void) pe__add_bundle_remote_name(rsc, scheduler, data->params_all, - XML_RSC_ATTR_REMOTE_RA_ADDR); + (void) pe__add_bundle_remote_name(rsc, data->params_all, + PCMK_REMOTE_RA_ADDR); if (overrides != NULL) { // If interval was overridden, reset it - const char *interval_s = g_hash_table_lookup(overrides, CRM_META "_" - XML_LRM_ATTR_INTERVAL); + const char *meta_name = CRM_META "_" PCMK_META_INTERVAL; + const char *interval_s = g_hash_table_lookup(overrides, meta_name); if (interval_s != NULL) { long long value_ll; @@ -185,22 +184,22 @@ is_fence_param(xmlAttrPtr attr, void *user_data) * \param[in] overrides Key/value hash table to override resource parameters */ static void -calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc, +calculate_secure_digest(pcmk__op_digest_t *data, const pcmk_resource_t *rsc, GHashTable *params, const xmlNode *xml_op, const char *op_version, GHashTable *overrides) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); const char *secure_list = NULL; bool old_version = (compare_version(op_version, "3.16.0") < 0); if (xml_op == NULL) { secure_list = " passwd password user "; } else { - secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE); + secure_list = crm_element_value(xml_op, PCMK__XA_OP_SECURE_PARAMS); } if (old_version) { - data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); + data->params_secure = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS); if (overrides != NULL) { g_hash_table_foreach(overrides, hash2field, data->params_secure); } @@ -209,7 +208,7 @@ calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc, } else { // Start with a copy of all parameters - data->params_secure = copy_xml(data->params_all); + data->params_secure = pcmk__xml_copy(NULL, data->params_all); } if (secure_list != NULL) { @@ -236,7 +235,8 @@ calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc, * Remove any timeout that made it this far, to match. */ if (old_version) { - xml_remove_prop(data->params_secure, CRM_META "_" XML_ATTR_TIMEOUT); + pcmk__xe_remove_attr(data->params_secure, + CRM_META "_" PCMK_META_TIMEOUT); } data->digest_secure_calc = calculate_operation_digest(data->params_secure, @@ -255,7 +255,7 @@ calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc, * data->params_all, which already has overrides applied. */ static void -calculate_restart_digest(op_digest_cache_t *data, const xmlNode *xml_op, +calculate_restart_digest(pcmk__op_digest_t *data, const xmlNode *xml_op, const char *op_version) { const char *value = NULL; @@ -266,21 +266,21 @@ calculate_restart_digest(op_digest_cache_t *data, const xmlNode *xml_op, } // And the history must have a restart digest to compare against - if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) { + if (crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST) == NULL) { return; } // Start with a copy of all parameters - data->params_restart = copy_xml(data->params_all); + data->params_restart = pcmk__xml_copy(NULL, data->params_all); // Then filter out reloadable parameters, if any - value = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); + value = crm_element_value(xml_op, PCMK__XA_OP_FORCE_RESTART); if (value != NULL) { pcmk__xe_remove_matching_attrs(data->params_restart, attr_not_in_string, (void *) value); } - value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + value = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET); data->digest_restart_calc = calculate_operation_digest(data->params_restart, value); } @@ -302,28 +302,33 @@ calculate_restart_digest(op_digest_cache_t *data, const xmlNode *xml_op, * \note It is the caller's responsibility to free the result using * pe__free_digests(). */ -op_digest_cache_t * +pcmk__op_digest_t * pe__calculate_digests(pcmk_resource_t *rsc, const char *task, guint *interval_ms, const pcmk_node_t *node, const xmlNode *xml_op, GHashTable *overrides, bool calc_secure, pcmk_scheduler_t *scheduler) { - op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t)); + pcmk__op_digest_t *data = NULL; const char *op_version = NULL; GHashTable *params = NULL; + CRM_CHECK(scheduler != NULL, return NULL); + + data = calloc(1, sizeof(pcmk__op_digest_t)); if (data == NULL) { + pcmk__sched_err("Could not allocate memory for operation digest"); return NULL; } data->rc = pcmk__digest_match; if (xml_op != NULL) { - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET); } - if (op_version == NULL && scheduler != NULL && scheduler->input != NULL) { - op_version = crm_element_value(scheduler->input, XML_ATTR_CRM_VERSION); + if ((op_version == NULL) && (scheduler->input != NULL)) { + op_version = crm_element_value(scheduler->input, + PCMK_XA_CRM_FEATURE_SET); } if (op_version == NULL) { @@ -355,12 +360,12 @@ pe__calculate_digests(pcmk_resource_t *rsc, const char *task, * * \return Pointer to node's digest cache entry */ -static op_digest_cache_t * +static pcmk__op_digest_t * rsc_action_digest(pcmk_resource_t *rsc, const char *task, guint interval_ms, pcmk_node_t *node, const xmlNode *xml_op, bool calc_secure, pcmk_scheduler_t *scheduler) { - op_digest_cache_t *data = NULL; + pcmk__op_digest_t *data = NULL; char *key = pcmk__op_key(rsc->id, task, interval_ms); data = g_hash_table_lookup(node->details->digest_cache, key); @@ -385,38 +390,38 @@ rsc_action_digest(pcmk_resource_t *rsc, const char *task, guint interval_ms, * * \return Pointer to node's digest cache entry, with comparison result set */ -op_digest_cache_t * +pcmk__op_digest_t * rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, pcmk_node_t *node, pcmk_scheduler_t *scheduler) { - op_digest_cache_t *data = NULL; + pcmk__op_digest_t *data = NULL; guint interval_ms = 0; const char *op_version; - const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION); const char *digest_all; const char *digest_restart; CRM_ASSERT(node != NULL); - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); - digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); - digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); + op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET); + digest_all = crm_element_value(xml_op, PCMK__XA_OP_DIGEST); + digest_restart = crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST); - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms); data = rsc_action_digest(rsc, task, interval_ms, node, xml_op, pcmk_is_set(scheduler->flags, pcmk_sched_sanitized), scheduler); if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) { - pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s " - "changed: hash was %s vs. now %s (restart:%s) %s", - interval_ms, task, rsc->id, pe__node_name(node), - pcmk__s(digest_restart, "missing"), - data->digest_restart_calc, - op_version, - crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + pcmk__rsc_info(rsc, + "Parameters to %ums-interval %s action for %s on %s " + "changed: hash was %s vs. now %s (restart:%s) %s", + interval_ms, task, rsc->id, pcmk__node_name(node), + pcmk__s(digest_restart, "missing"), + data->digest_restart_calc, op_version, + crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC)); data->rc = pcmk__digest_restart; } else if (digest_all == NULL) { @@ -429,27 +434,32 @@ rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, * digest matches, enforce a restart rather than a reload-agent anyway. * So that it ensures any changes of the extra parameters get applied * for this specific operation, and the digests calculated for the - * resulting lrm_rsc_op will be correct. + * resulting PCMK__XE_LRM_RSC_OP will be correct. * Preserve the implied rc pcmk__digest_restart for the case that the * main digest doesn't match. */ if ((interval_ms == 0) && (data->rc == pcmk__digest_restart)) { - pe_rsc_info(rsc, "Parameters containing extra ones to %ums-interval" - " %s action for %s on %s " - "changed: hash was %s vs. now %s (restart:%s) %s", - interval_ms, task, rsc->id, pe__node_name(node), - pcmk__s(digest_all, "missing"), data->digest_all_calc, - op_version, - crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + pcmk__rsc_info(rsc, + "Parameters containing extra ones to %ums-interval" + " %s action for %s on %s " + "changed: hash was %s vs. now %s (restart:%s) %s", + interval_ms, task, rsc->id, pcmk__node_name(node), + pcmk__s(digest_all, "missing"), + data->digest_all_calc, op_version, + crm_element_value(xml_op, + PCMK__XA_TRANSITION_MAGIC)); } else { - pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s " - "changed: hash was %s vs. now %s (%s:%s) %s", - interval_ms, task, rsc->id, pe__node_name(node), - pcmk__s(digest_all, "missing"), data->digest_all_calc, - (interval_ms > 0)? "reschedule" : "reload", - op_version, - crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + pcmk__rsc_info(rsc, + "Parameters to %ums-interval %s action for %s on %s " + "changed: hash was %s vs. now %s (%s:%s) %s", + interval_ms, task, rsc->id, pcmk__node_name(node), + pcmk__s(digest_all, "missing"), + data->digest_all_calc, + (interval_ms > 0)? "reschedule" : "reload", + op_version, + crm_element_value(xml_op, + PCMK__XA_TRANSITION_MAGIC)); data->rc = pcmk__digest_mismatch; } @@ -537,18 +547,19 @@ unfencing_digest_matches(const char *rsc_id, const char *agent, * * \return Node's digest cache entry */ -op_digest_cache_t * +pcmk__op_digest_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler) { const char *node_summary = NULL; // Calculate device's current parameter digests - op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U, + pcmk__op_digest_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U, node, NULL, TRUE, scheduler); // Check whether node has special unfencing summary node attribute - node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL); + node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_ALL, NULL, + pcmk__rsc_node_current); if (node_summary == NULL) { data->rc = pcmk__digest_unknown; return data; @@ -562,7 +573,8 @@ pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, } // Check whether secure parameter digest matches - node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE); + node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_SECURE, NULL, + pcmk__rsc_node_current); if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc, node_summary)) { data->rc = pcmk__digest_match; @@ -570,7 +582,7 @@ pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk__output_t *out = scheduler->priv; out->info(out, "Only 'private' parameters to %s " "for unfencing %s changed", rsc->id, - pe__node_name(node)); + pcmk__node_name(node)); } return data; } @@ -587,14 +599,14 @@ pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, out->info(out, "Parameters to %s for unfencing " "%s changed, try '%s'", rsc->id, - pe__node_name(node), digest); + pcmk__node_name(node), digest); free(digest); } else if (!pcmk__is_daemon) { char *digest = create_unfencing_summary(rsc->id, agent, data->digest_secure_calc); printf("Parameters to %s for unfencing %s changed, try '%s'\n", - rsc->id, pe__node_name(node), digest); + rsc->id, pcmk__node_name(node), digest); free(digest); } } diff --git a/lib/pengine/pe_health.c b/lib/pengine/pe_health.c index 93028ae..4f7eb10 100644 --- a/lib/pengine/pe_health.c +++ b/lib/pengine/pe_health.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. * @@ -15,7 +15,8 @@ /*! * \internal - * \brief Set the node health values to use for "red", "yellow", and "green" + * \brief Set the node health values to use for \c PCMK_VALUE_RED, + * \c PCMK_VALUE_YELLOW, and \c PCMK_VALUE_GREEN * * \param[in,out] scheduler Scheduler data */ @@ -30,23 +31,23 @@ pe__unpack_node_health_scores(pcmk_scheduler_t *scheduler) break; case pcmk__health_strategy_no_red: - pcmk__score_red = -INFINITY; + pcmk__score_red = -PCMK_SCORE_INFINITY; pcmk__score_yellow = 0; pcmk__score_green = 0; break; case pcmk__health_strategy_only_green: - pcmk__score_red = -INFINITY; - pcmk__score_yellow = -INFINITY; + pcmk__score_red = -PCMK_SCORE_INFINITY; + pcmk__score_yellow = -PCMK_SCORE_INFINITY; pcmk__score_green = 0; break; default: // progressive or custom - pcmk__score_red = pe__health_score(PCMK__OPT_NODE_HEALTH_RED, + pcmk__score_red = pe__health_score(PCMK_OPT_NODE_HEALTH_RED, scheduler); - pcmk__score_green = pe__health_score(PCMK__OPT_NODE_HEALTH_GREEN, + pcmk__score_green = pe__health_score(PCMK_OPT_NODE_HEALTH_GREEN, scheduler); - pcmk__score_yellow = pe__health_score(PCMK__OPT_NODE_HEALTH_YELLOW, + pcmk__score_yellow = pe__health_score(PCMK_OPT_NODE_HEALTH_YELLOW, scheduler); break; } @@ -54,9 +55,9 @@ pe__unpack_node_health_scores(pcmk_scheduler_t *scheduler) if ((pcmk__score_red != 0) || (pcmk__score_yellow != 0) || (pcmk__score_green != 0)) { crm_debug("Values of node health scores: " - PCMK__VALUE_RED "=%d " - PCMK__VALUE_YELLOW "=%d " - PCMK__VALUE_GREEN "=%d", + PCMK_VALUE_RED "=%d " + PCMK_VALUE_YELLOW "=%d " + PCMK_VALUE_GREEN "=%d", pcmk__score_red, pcmk__score_yellow, pcmk__score_green); } } @@ -135,9 +136,9 @@ pe__node_health(pcmk_node_t *node) * or pcmk__score_yellow equals pcmk__score_green, so check the * textual value first to be able to distinguish those. */ - if (pcmk__str_eq(value, PCMK__VALUE_RED, pcmk__str_casei)) { + if (pcmk__str_eq(value, PCMK_VALUE_RED, pcmk__str_casei)) { return -1; - } else if (pcmk__str_eq(value, PCMK__VALUE_YELLOW, + } else if (pcmk__str_eq(value, PCMK_VALUE_YELLOW, pcmk__str_casei)) { rc = 0; continue; diff --git a/lib/pengine/pe_notif.c b/lib/pengine/pe_notif.c index 0e1e239..c13637b 100644 --- a/lib/pengine/pe_notif.c +++ b/lib/pengine/pe_notif.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. * @@ -8,7 +8,7 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/internal.h> #include <pacemaker-internal.h> @@ -95,9 +95,8 @@ compare_notify_entries(gconstpointer a, gconstpointer b) static notify_entry_t * dup_notify_entry(const notify_entry_t *entry) { - notify_entry_t *dup = calloc(1, sizeof(notify_entry_t)); + notify_entry_t *dup = pcmk__assert_alloc(1, sizeof(notify_entry_t)); - CRM_ASSERT(dup != NULL); dup->rsc = entry->rsc; dup->node = entry->node; return dup; @@ -141,9 +140,9 @@ get_node_names(const GList *list, GString **all_node_names, // Add to host node name list if appropriate if (host_node_names != NULL) { - if (pe__is_guest_node(node) + if (pcmk__is_guest_or_bundle_node(node) && (node->details->remote_rsc->container->running_on != NULL)) { - node = pe__current_node(node->details->remote_rsc->container); + node = pcmk__current_node(node->details->remote_rsc->container); if (node->details->uname == NULL) { continue; } @@ -253,8 +252,7 @@ copy_meta_to_notify(gpointer key, gpointer value, gpointer user_data) return; } - g_hash_table_insert(notify->meta, strdup((const char *) key), - strdup((const char *) value)); + pcmk__insert_dup(notify->meta, (const char *) key, (const char *) value); } static void @@ -264,7 +262,7 @@ add_notify_data_to_action_meta(const notify_data_t *n_data, for (const GSList *item = n_data->keys; item; item = item->next) { const pcmk_nvpair_t *nvpair = (const pcmk_nvpair_t *) item->data; - add_hash_param(action->meta, nvpair->name, nvpair->value); + pcmk__insert_meta(action, nvpair->name, nvpair->value); } } @@ -290,9 +288,9 @@ new_notify_pseudo_action(pcmk_resource_t *rsc, const pcmk_action_t *action, notif_action, NULL, pcmk_is_set(action->flags, pcmk_action_optional), rsc->cluster); - pe__set_action_flags(notify, pcmk_action_pseudo); - add_hash_param(notify->meta, "notify_key_type", notif_type); - add_hash_param(notify->meta, "notify_key_operation", action->task); + pcmk__set_action_flags(notify, pcmk_action_pseudo); + pcmk__insert_meta(notify, "notify_key_type", notif_type); + pcmk__insert_meta(notify, "notify_key_operation", action->task); return notify; } @@ -332,16 +330,16 @@ new_notify_action(pcmk_resource_t *rsc, const pcmk_node_t *node, skip_reason = "original action not runnable"; } if (skip_reason != NULL) { - pe_rsc_trace(rsc, "Skipping notify action for %s on %s: %s", - rsc->id, pe__node_name(node), skip_reason); + pcmk__rsc_trace(rsc, "Skipping notify action for %s on %s: %s", + rsc->id, pcmk__node_name(node), skip_reason); return NULL; } value = g_hash_table_lookup(op->meta, "notify_type"); // "pre" or "post" task = g_hash_table_lookup(op->meta, "notify_operation"); // original action - pe_rsc_trace(rsc, "Creating notify action for %s on %s (%s-%s)", - rsc->id, pe__node_name(node), value, task); + pcmk__rsc_trace(rsc, "Creating notify action for %s on %s (%s-%s)", + rsc->id, pcmk__node_name(node), value, task); // Create the notify action key = pcmk__notify_key(rsc->id, value, task); @@ -379,7 +377,7 @@ new_post_notify_action(pcmk_resource_t *rsc, const pcmk_node_t *node, notify = new_notify_action(rsc, node, n_data->post, n_data->post_done, n_data); if (notify != NULL) { - notify->priority = INFINITY; + notify->priority = PCMK_SCORE_INFINITY; } // Order recurring monitors after all "post-" notifications complete @@ -390,8 +388,7 @@ new_post_notify_action(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_action_t *mon = (pcmk_action_t *) iter->data; const char *interval_ms_s = NULL; - interval_ms_s = g_hash_table_lookup(mon->meta, - XML_LRM_ATTR_INTERVAL_MS); + interval_ms_s = g_hash_table_lookup(mon->meta, PCMK_META_INTERVAL); if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches) || pcmk__str_eq(mon->task, PCMK_ACTION_CANCEL, pcmk__str_none)) { continue; // Not a recurring monitor @@ -441,8 +438,7 @@ pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task, return NULL; } - n_data = calloc(1, sizeof(notify_data_t)); - CRM_ASSERT(n_data != NULL); + n_data = pcmk__assert_alloc(1, sizeof(notify_data_t)); n_data->action = task; @@ -451,18 +447,17 @@ pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task, // Create "pre-" notify pseudo-action for clone n_data->pre = new_notify_pseudo_action(rsc, action, PCMK_ACTION_NOTIFY, "pre"); - pe__set_action_flags(n_data->pre, pcmk_action_runnable); - add_hash_param(n_data->pre->meta, "notify_type", "pre"); - add_hash_param(n_data->pre->meta, "notify_operation", n_data->action); + pcmk__set_action_flags(n_data->pre, pcmk_action_runnable); + pcmk__insert_meta(n_data->pre, "notify_type", "pre"); + pcmk__insert_meta(n_data->pre, "notify_operation", n_data->action); // Create "pre-" notifications complete pseudo-action for clone n_data->pre_done = new_notify_pseudo_action(rsc, action, PCMK_ACTION_NOTIFIED, "confirmed-pre"); - pe__set_action_flags(n_data->pre_done, pcmk_action_runnable); - add_hash_param(n_data->pre_done->meta, "notify_type", "pre"); - add_hash_param(n_data->pre_done->meta, - "notify_operation", n_data->action); + pcmk__set_action_flags(n_data->pre_done, pcmk_action_runnable); + pcmk__insert_meta(n_data->pre_done, "notify_type", "pre"); + pcmk__insert_meta(n_data->pre_done, "notify_operation", n_data->action); // Order "pre-" -> "pre-" complete -> original action order_actions(n_data->pre, n_data->pre_done, pcmk__ar_ordered); @@ -474,28 +469,28 @@ pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task, // Create "post-" notify pseudo-action for clone n_data->post = new_notify_pseudo_action(rsc, complete, PCMK_ACTION_NOTIFY, "post"); - n_data->post->priority = INFINITY; + n_data->post->priority = PCMK_SCORE_INFINITY; if (pcmk_is_set(complete->flags, pcmk_action_runnable)) { - pe__set_action_flags(n_data->post, pcmk_action_runnable); + pcmk__set_action_flags(n_data->post, pcmk_action_runnable); } else { - pe__clear_action_flags(n_data->post, pcmk_action_runnable); + pcmk__clear_action_flags(n_data->post, pcmk_action_runnable); } - add_hash_param(n_data->post->meta, "notify_type", "post"); - add_hash_param(n_data->post->meta, "notify_operation", n_data->action); + pcmk__insert_meta(n_data->post, "notify_type", "post"); + pcmk__insert_meta(n_data->post, "notify_operation", n_data->action); // Create "post-" notifications complete pseudo-action for clone n_data->post_done = new_notify_pseudo_action(rsc, complete, PCMK_ACTION_NOTIFIED, "confirmed-post"); - n_data->post_done->priority = INFINITY; + n_data->post_done->priority = PCMK_SCORE_INFINITY; if (pcmk_is_set(complete->flags, pcmk_action_runnable)) { - pe__set_action_flags(n_data->post_done, pcmk_action_runnable); + pcmk__set_action_flags(n_data->post_done, pcmk_action_runnable); } else { - pe__clear_action_flags(n_data->post_done, pcmk_action_runnable); + pcmk__clear_action_flags(n_data->post_done, pcmk_action_runnable); } - add_hash_param(n_data->post_done->meta, "notify_type", "post"); - add_hash_param(n_data->post_done->meta, - "notify_operation", n_data->action); + pcmk__insert_meta(n_data->post_done, "notify_type", "post"); + pcmk__insert_meta(n_data->post_done, + "notify_operation", n_data->action); // Order original action complete -> "post-" -> "post-" complete order_actions(complete, n_data->post, pcmk__ar_first_implies_then); @@ -523,9 +518,8 @@ pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task, static notify_entry_t * new_notify_entry(const pcmk_resource_t *rsc, const pcmk_node_t *node) { - notify_entry_t *entry = calloc(1, sizeof(notify_entry_t)); + notify_entry_t *entry = pcmk__assert_alloc(1, sizeof(notify_entry_t)); - CRM_ASSERT(entry != NULL); entry->rsc = rsc; entry->node = node; return entry; @@ -595,9 +589,10 @@ collect_resource_data(const pcmk_resource_t *rsc, bool activity, break; default: - crm_err("Resource %s role on %s (%s) is not supported for " - "notifications (bug?)", - rsc->id, pe__node_name(node), role2text(rsc->role)); + pcmk__sched_err("Resource %s role on %s (%s) is not supported for " + "notifications (bug?)", + rsc->id, pcmk__node_name(node), + pcmk_role_text(rsc->role)); free(entry); break; } @@ -612,7 +607,7 @@ collect_resource_data(const pcmk_resource_t *rsc, bool activity, if (!pcmk_is_set(op->flags, pcmk_action_optional) && (op->node != NULL)) { - enum action_tasks task = text2task(op->task); + enum action_tasks task = pcmk_parse_action(op->task); if ((task == pcmk_action_stop) && op->node->details->unclean) { // Create anyway (additional noise if node can't be fenced) @@ -753,8 +748,9 @@ add_notif_keys(const pcmk_resource_t *rsc, notify_data_t *n_data) add_notify_env_free_gs(n_data, "notify_available_uname", node_list); g_list_free(nodes); - source = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET); - if (pcmk__str_eq("host", source, pcmk__str_none)) { + source = g_hash_table_lookup(rsc->meta, + PCMK_META_CONTAINER_ATTRIBUTE_TARGET); + if (pcmk__str_eq(PCMK_VALUE_HOST, source, pcmk__str_none)) { get_node_names(rsc->cluster->nodes, &node_list, &metal_list); add_notify_env_free_gs(n_data, "notify_all_hosts", metal_list); } else { @@ -763,13 +759,13 @@ add_notif_keys(const pcmk_resource_t *rsc, notify_data_t *n_data) add_notify_env_free_gs(n_data, "notify_all_uname", node_list); if (required && (n_data->pre != NULL)) { - pe__clear_action_flags(n_data->pre, pcmk_action_optional); - pe__clear_action_flags(n_data->pre_done, pcmk_action_optional); + pcmk__clear_action_flags(n_data->pre, pcmk_action_optional); + pcmk__clear_action_flags(n_data->pre_done, pcmk_action_optional); } if (required && (n_data->post != NULL)) { - pe__clear_action_flags(n_data->post, pcmk_action_optional); - pe__clear_action_flags(n_data->post_done, pcmk_action_optional); + pcmk__clear_action_flags(n_data->post, pcmk_action_optional); + pcmk__clear_action_flags(n_data->post_done, pcmk_action_optional); } } @@ -809,7 +805,7 @@ create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data) GList *iter = NULL; pcmk_action_t *stop = NULL; pcmk_action_t *start = NULL; - enum action_tasks task = text2task(n_data->action); + enum action_tasks task = pcmk_parse_action(n_data->action); // If this is a clone, call recursively for each instance if (rsc->children != NULL) { @@ -823,7 +819,7 @@ create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data) if (!pcmk_is_set(op->flags, pcmk_action_optional) && (op->node != NULL)) { - switch (text2task(op->task)) { + switch (pcmk_parse_action(op->task)) { case pcmk_action_start: case pcmk_action_stop: case pcmk_action_promote: @@ -840,24 +836,24 @@ create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data) switch (task) { case pcmk_action_start: if (n_data->start == NULL) { - pe_rsc_trace(rsc, "No notify action needed for %s %s", - rsc->id, n_data->action); + pcmk__rsc_trace(rsc, "No notify action needed for %s %s", + rsc->id, n_data->action); return; } break; case pcmk_action_promote: if (n_data->promote == NULL) { - pe_rsc_trace(rsc, "No notify action needed for %s %s", - rsc->id, n_data->action); + pcmk__rsc_trace(rsc, "No notify action needed for %s %s", + rsc->id, n_data->action); return; } break; case pcmk_action_demote: if (n_data->demote == NULL) { - pe_rsc_trace(rsc, "No notify action needed for %s %s", - rsc->id, n_data->action); + pcmk__rsc_trace(rsc, "No notify action needed for %s %s", + rsc->id, n_data->action); return; } break; @@ -867,8 +863,8 @@ create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data) break; } - pe_rsc_trace(rsc, "Creating notify actions for %s %s", - rsc->id, n_data->action); + pcmk__rsc_trace(rsc, "Creating notify actions for %s %s", + rsc->id, n_data->action); // Create notify actions for stop or demote if ((rsc->role != pcmk_role_stopped) @@ -918,8 +914,8 @@ create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data) } } if (rsc->allocated_to == NULL) { - pe_proc_err("Next role '%s' but %s is not allocated", - role2text(rsc->next_role), rsc->id); + pcmk__sched_err("Next role '%s' but %s is not allocated", + pcmk_role_text(rsc->next_role), rsc->id); return; } if ((task != pcmk_action_start) || (start == NULL) diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c index 65f3c18..b1cd8cc 100644 --- a/lib/pengine/pe_output.c +++ b/lib/pengine/pe_output.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/scheduler_internal.h> #include <crm/cib/util.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/internal.h> const char * @@ -24,15 +24,15 @@ pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts) const char * desc = NULL; // User-supplied description if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)) { - desc = crm_element_value(rsc->xml, XML_ATTR_DESC); + desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION); } return desc; } /* Never display node attributes whose name starts with one of these prefixes */ #define FILTER_STR { PCMK__FAIL_COUNT_PREFIX, PCMK__LAST_FAILURE_PREFIX, \ - "shutdown", PCMK_NODE_ATTR_TERMINATE, "standby", "#", \ - NULL } + PCMK__NODE_ATTR_SHUTDOWN, PCMK_NODE_ATTR_TERMINATE, \ + PCMK_NODE_ATTR_STANDBY, "#", NULL } static int compare_attribute(gconstpointer a, gconstpointer b) @@ -68,7 +68,7 @@ add_extra_info(const pcmk_node_t *node, GList *rsc_list, for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data; - const char *type = g_hash_table_lookup(rsc->meta, "type"); + const char *type = g_hash_table_lookup(rsc->meta, PCMK_XA_TYPE); const char *name = NULL; GHashTable *params = NULL; @@ -84,7 +84,7 @@ add_extra_info(const pcmk_node_t *node, GList *rsc_list, } params = pe_rsc_params(rsc, node, scheduler); - name = g_hash_table_lookup(params, "name"); + name = g_hash_table_lookup(params, PCMK_XA_NAME); if (name == NULL) { name = "pingd"; @@ -143,12 +143,13 @@ get_operation_list(xmlNode *rsc_entry) { GList *op_list = NULL; xmlNode *rsc_op = NULL; - for (rsc_op = pcmk__xe_first_child(rsc_entry); rsc_op != NULL; - rsc_op = pcmk__xe_next(rsc_op)) { - const char *task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); + for (rsc_op = pcmk__xe_first_child(rsc_entry, NULL, NULL, NULL); + rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op)) { + + const char *task = crm_element_value(rsc_op, PCMK_XA_OPERATION); const char *interval_ms_s = crm_element_value(rsc_op, - XML_LRM_ATTR_INTERVAL_MS); - const char *op_rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC); + PCMK_META_INTERVAL); + const char *op_rc = crm_element_value(rsc_op, PCMK__XA_RC_CODE); int op_rc_i; pcmk__scan_min_int(op_rc, &op_rc_i, 0); @@ -166,7 +167,7 @@ get_operation_list(xmlNode *rsc_entry) { continue; } - if (pcmk__str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, pcmk__str_none)) { + if (pcmk__xe_is(rsc_op, PCMK__XE_LRM_RSC_OP)) { op_list = g_list_append(op_list, rsc_op); } } @@ -179,7 +180,9 @@ static void add_dump_node(gpointer key, gpointer value, gpointer user_data) { xmlNodePtr node = user_data; - pcmk_create_xml_text_node(node, (const char *) key, (const char *) value); + + node = pcmk__xe_create(node, (const char *) key); + pcmk__xe_set_content(node, "%s", (const char *) value); } static void @@ -193,12 +196,19 @@ append_dump_text(gpointer key, gpointer value, gpointer user_data) *dump_text = new_text; } +#define XPATH_STACK "//" PCMK_XE_NVPAIR \ + "[@" PCMK_XA_NAME "='" \ + PCMK_OPT_CLUSTER_INFRASTRUCTURE "']" + static const char * get_cluster_stack(pcmk_scheduler_t *scheduler) { - xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']", - scheduler->input, LOG_DEBUG); - return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown"; + xmlNode *stack = get_xpath_object(XPATH_STACK, scheduler->input, LOG_DEBUG); + + if (stack != NULL) { + return crm_element_value(stack, PCMK_XA_VALUE); + } + return PCMK_VALUE_UNKNOWN; } static char * @@ -221,12 +231,12 @@ last_changed_string(const char *last_written, const char *user, static char * op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s, int rc, bool print_timing) { - const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); + const char *call = crm_element_value(xml_op, PCMK__XA_CALL_ID); char *interval_str = NULL; char *buf = NULL; if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) { - char *pair = pcmk__format_nvpair("interval", interval_ms_s, "ms"); + char *pair = pcmk__format_nvpair(PCMK_XA_INTERVAL, interval_ms_s, "ms"); interval_str = crm_strdup_printf(" %s", pair); free(pair); } @@ -240,26 +250,27 @@ op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s, time_t epoch = 0; - if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &epoch) == pcmk_ok) + if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, + &epoch) == pcmk_ok) && (epoch > 0)) { char *epoch_str = pcmk__epoch2str(&epoch, 0); last_change_str = crm_strdup_printf(" %s=\"%s\"", - XML_RSC_OP_LAST_CHANGE, + PCMK_XA_LAST_RC_CHANGE, pcmk__s(epoch_str, "")); free(epoch_str); } - value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC); + value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); if (value) { - char *pair = pcmk__format_nvpair(XML_RSC_OP_T_EXEC, value, "ms"); + char *pair = pcmk__format_nvpair(PCMK_XA_EXEC_TIME, value, "ms"); exec_str = crm_strdup_printf(" %s", pair); free(pair); } - value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE); + value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME); if (value) { - char *pair = pcmk__format_nvpair(XML_RSC_OP_T_QUEUE, value, "ms"); + char *pair = pcmk__format_nvpair(PCMK_XA_QUEUE_TIME, value, "ms"); queue_str = crm_strdup_printf(" %s", pair); free(pair); } @@ -307,19 +318,19 @@ resource_history_string(pcmk_resource_t *rsc, const char *rsc_id, bool all, char *lastfail_s = NULL; if (failcount > 0) { - failcount_s = crm_strdup_printf(" %s=%d", PCMK__FAIL_COUNT_PREFIX, - failcount); + failcount_s = crm_strdup_printf(" %s=%d", + PCMK_XA_FAIL_COUNT, failcount); } else { failcount_s = strdup(""); } if (last_failure > 0) { buf = pcmk__epoch2str(&last_failure, 0); lastfail_s = crm_strdup_printf(" %s='%s'", - PCMK__LAST_FAILURE_PREFIX, buf); + PCMK_XA_LAST_FAILURE, buf); free(buf); } - buf = crm_strdup_printf("%s: migration-threshold=%d%s%s", + buf = crm_strdup_printf("%s: " PCMK_META_MIGRATION_THRESHOLD "=%d%s%s", rsc_id, rsc->migration_threshold, failcount_s, lastfail_s? lastfail_s : ""); free(failcount_s); @@ -345,7 +356,7 @@ static const char * get_node_feature_set(const pcmk_node_t *node) { if (node->details->online && node->details->expected_up - && !pe__is_guest_or_remote_node(node)) { + && !pcmk__is_pacemaker_remote_node(node)) { const char *feature_set = g_hash_table_lookup(node->details->attrs, CRM_ATTR_FEATURE_SET); @@ -376,16 +387,19 @@ is_mixed_version(pcmk_scheduler_t *scheduler) return false; } -static char * -formatted_xml_buf(const pcmk_resource_t *rsc, bool raw) +static void +formatted_xml_buf(const pcmk_resource_t *rsc, GString *xml_buf, bool raw) { - if (raw) { - return dump_xml_formatted(rsc->orig_xml ? rsc->orig_xml : rsc->xml); + if (raw && (rsc->orig_xml != NULL)) { + pcmk__xml_string(rsc->orig_xml, pcmk__xml_fmt_pretty, xml_buf, 0); } else { - return dump_xml_formatted(rsc->xml); + pcmk__xml_string(rsc->xml, pcmk__xml_fmt_pretty, xml_buf, 0); } } +#define XPATH_DC_VERSION "//" PCMK_XE_NVPAIR \ + "[@" PCMK_XA_NAME "='" PCMK_OPT_DC_VERSION "']" + PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *", "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t") static int @@ -405,13 +419,13 @@ cluster_summary(pcmk__output_t *out, va_list args) { } if (pcmk_is_set(section_opts, pcmk_section_dc)) { - xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']", + xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION, scheduler->input, LOG_DEBUG); const char *dc_version_s = dc_version? - crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE) + crm_element_value(dc_version, PCMK_XA_VALUE) : NULL; const char *quorum = crm_element_value(scheduler->input, - XML_ATTR_HAVE_QUORUM); + PCMK_XA_HAVE_QUORUM); char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL; bool mixed_version = is_mixed_version(scheduler); @@ -423,13 +437,13 @@ cluster_summary(pcmk__output_t *out, va_list args) { if (pcmk_is_set(section_opts, pcmk_section_times)) { const char *last_written = crm_element_value(scheduler->input, - XML_CIB_ATTR_WRITTEN); + PCMK_XA_CIB_LAST_WRITTEN); const char *user = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_USER); + PCMK_XA_UPDATE_USER); const char *client = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_CLIENT); + PCMK_XA_UPDATE_CLIENT); const char *origin = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_ORIG); + PCMK_XA_UPDATE_ORIGIN); PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary"); out->message(out, "cluster-times", @@ -480,13 +494,13 @@ cluster_summary_html(pcmk__output_t *out, va_list args) { /* Always print DC if none, even if not requested */ if ((scheduler->dc_node == NULL) || pcmk_is_set(section_opts, pcmk_section_dc)) { - xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']", + xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION, scheduler->input, LOG_DEBUG); const char *dc_version_s = dc_version? - crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE) + crm_element_value(dc_version, PCMK_XA_VALUE) : NULL; const char *quorum = crm_element_value(scheduler->input, - XML_ATTR_HAVE_QUORUM); + PCMK_XA_HAVE_QUORUM); char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL; bool mixed_version = is_mixed_version(scheduler); @@ -498,13 +512,13 @@ cluster_summary_html(pcmk__output_t *out, va_list args) { if (pcmk_is_set(section_opts, pcmk_section_times)) { const char *last_written = crm_element_value(scheduler->input, - XML_CIB_ATTR_WRITTEN); + PCMK_XA_CIB_LAST_WRITTEN); const char *user = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_USER); + PCMK_XA_UPDATE_USER); const char *client = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_CLIENT); + PCMK_XA_UPDATE_CLIENT); const char *origin = crm_element_value(scheduler->input, - XML_ATTR_UPDATE_ORIG); + PCMK_XA_UPDATE_ORIGIN); PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary"); out->message(out, "cluster-times", @@ -551,9 +565,9 @@ pe__node_display_name(pcmk_node_t *node, bool print_detail) CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL)); /* Host is displayed only if this is a guest node and detail is requested */ - if (print_detail && pe__is_guest_node(node)) { + if (print_detail && pcmk__is_guest_or_bundle_node(node)) { const pcmk_resource_t *container = node->details->remote_rsc->container; - const pcmk_node_t *host_node = pe__current_node(container); + const pcmk_node_t *host_node = pcmk__current_node(container); if (host_node && host_node->details) { node_host = host_node->details->uname; @@ -578,8 +592,7 @@ pe__node_display_name(pcmk_node_t *node, bool print_detail) } /* Allocate and populate display name */ - node_name = malloc(name_len); - CRM_ASSERT(node_name != NULL); + node_name = pcmk__assert_alloc(name_len, sizeof(char)); strcpy(node_name, node->details->uname); if (node_host) { strcat(node_name, "@"); @@ -594,27 +607,21 @@ pe__node_display_name(pcmk_node_t *node, bool print_detail) } int -pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name - , size_t pairs_count, ...) +pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name, + ...) { xmlNodePtr xml_node = NULL; - va_list args; + va_list pairs; CRM_ASSERT(tag_name != NULL); xml_node = pcmk__output_xml_peek_parent(out); CRM_ASSERT(xml_node != NULL); - xml_node = create_xml_node(xml_node, tag_name); + xml_node = pcmk__xe_create(xml_node, tag_name); - va_start(args, pairs_count); - while(pairs_count--) { - const char *param_name = va_arg(args, const char *); - const char *param_value = va_arg(args, const char *); - if (param_name && param_value) { - crm_xml_add(xml_node, param_name, param_value); - } - }; - va_end(args); + va_start(pairs, tag_name); + pcmk__xe_set_propv(xml_node, pairs); + va_end(pairs); if (is_list) { pcmk__output_xml_push_parent(out, xml_node); @@ -629,23 +636,23 @@ role_desc(enum rsc_role_e role) #ifdef PCMK__COMPAT_2_0 return "as " PCMK__ROLE_PROMOTED_LEGACY " "; #else - return "in " PCMK__ROLE_PROMOTED " role "; + return "in " PCMK_ROLE_PROMOTED " role "; #endif } return ""; } -PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t") +PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t") static int ban_html(pcmk__output_t *out, va_list args) { pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *); - pe__location_t *location = va_arg(args, pe__location_t *); + pcmk__location_t *location = va_arg(args, pcmk__location_t *); uint32_t show_opts = va_arg(args, uint32_t); char *node_name = pe__node_display_name(pe_node, pcmk_is_set(show_opts, pcmk_show_node_id)); char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s", - location->id, location->rsc_lh->id, + location->id, location->rsc->id, role_desc(location->role_filter), node_name); pcmk__output_create_html_node(out, "li", NULL, NULL, buf); @@ -655,46 +662,46 @@ ban_html(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t") +PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t") static int ban_text(pcmk__output_t *out, va_list args) { pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *); - pe__location_t *location = va_arg(args, pe__location_t *); + pcmk__location_t *location = va_arg(args, pcmk__location_t *); uint32_t show_opts = va_arg(args, uint32_t); char *node_name = pe__node_display_name(pe_node, pcmk_is_set(show_opts, pcmk_show_node_id)); out->list_item(out, NULL, "%s\tprevents %s from running %son %s", - location->id, location->rsc_lh->id, + location->id, location->rsc->id, role_desc(location->role_filter), node_name); free(node_name); return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t") +PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t") static int ban_xml(pcmk__output_t *out, va_list args) { pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *); - pe__location_t *location = va_arg(args, pe__location_t *); + pcmk__location_t *location = va_arg(args, pcmk__location_t *); uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t); const char *promoted_only = pcmk__btoa(location->role_filter == pcmk_role_promoted); char *weight_s = pcmk__itoa(pe_node->weight); - pcmk__output_create_xml_node(out, "ban", - "id", location->id, - "resource", location->rsc_lh->id, - "node", pe_node->details->uname, - "weight", weight_s, - "promoted-only", promoted_only, + pcmk__output_create_xml_node(out, PCMK_XE_BAN, + PCMK_XA_ID, location->id, + PCMK_XA_RESOURCE, location->rsc->id, + PCMK_XA_NODE, pe_node->details->uname, + PCMK_XA_WEIGHT, weight_s, + PCMK_XA_PROMOTED_ONLY, promoted_only, /* This is a deprecated alias for * promoted_only. Removing it will break * backward compatibility of the API schema, * which will require an API schema major * version bump. */ - "master_only", promoted_only, + PCMK__XA_PROMOTED_ONLY_LEGACY, promoted_only, NULL); free(weight_s); @@ -717,8 +724,8 @@ ban_list(pcmk__output_t *out, va_list args) { /* Print each ban */ for (gIter = scheduler->placement_constraints; gIter != NULL; gIter = gIter->next) { - pe__location_t *location = gIter->data; - const pcmk_resource_t *rsc = location->rsc_lh; + pcmk__location_t *location = gIter->data; + const pcmk_resource_t *rsc = location->rsc; if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) { continue; @@ -731,7 +738,7 @@ ban_list(pcmk__output_t *out, va_list args) { continue; } - for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) { + for (gIter2 = location->nodes; gIter2 != NULL; gIter2 = gIter2->next) { pcmk_node_t *node = (pcmk_node_t *) gIter2->data; if (node->weight < 0) { @@ -755,53 +762,61 @@ cluster_counts_html(pcmk__output_t *out, va_list args) { xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL); xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; - char *nnodes_str = crm_strdup_printf("%d node%s configured", - nnodes, pcmk__plural_s(nnodes)); - - pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str); - free(nnodes_str); + child = pcmk__html_create(nodes_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%d node%s configured", + nnodes, pcmk__plural_s(nnodes)); if (ndisabled && nblocked) { - char *s = crm_strdup_printf("%d resource instance%s configured (%d ", - nresources, pcmk__plural_s(nresources), - ndisabled); - pcmk_create_html_node(resources_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%d resource instance%s configured (%d ", + nresources, pcmk__plural_s(nresources), ndisabled); - pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "DISABLED"); - s = crm_strdup_printf(", %d ", nblocked); - pcmk_create_html_node(resources_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, ", %d ", nblocked); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "BLOCKED"); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " from further action due to failure)"); - pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); - pcmk_create_html_node(resources_node, "span", NULL, NULL, - " from further action due to failure)"); } else if (ndisabled && !nblocked) { - char *s = crm_strdup_printf("%d resource instance%s configured (%d ", - nresources, pcmk__plural_s(nresources), - ndisabled); - pcmk_create_html_node(resources_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%d resource instance%s configured (%d ", + nresources, pcmk__plural_s(nresources), + ndisabled); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "DISABLED"); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, ")"); - pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); - pcmk_create_html_node(resources_node, "span", NULL, NULL, ")"); } else if (!ndisabled && nblocked) { - char *s = crm_strdup_printf("%d resource instance%s configured (%d ", - nresources, pcmk__plural_s(nresources), - nblocked); - pcmk_create_html_node(resources_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%d resource instance%s configured (%d ", + nresources, pcmk__plural_s(nresources), + nblocked); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "BLOCKED"); + + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " from further action due to failure)"); - pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); - pcmk_create_html_node(resources_node, "span", NULL, NULL, - " from further action due to failure)"); } else { - char *s = crm_strdup_printf("%d resource instance%s configured", - nresources, pcmk__plural_s(nresources)); - pcmk_create_html_node(resources_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%d resource instance%s configured", + nresources, pcmk__plural_s(nresources)); } return pcmk_rc_ok; @@ -849,23 +864,30 @@ cluster_counts_xml(pcmk__output_t *out, va_list args) { int ndisabled = va_arg(args, int); int nblocked = va_arg(args, int); - xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured", NULL); - xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured", NULL); + xmlNodePtr nodes_node = NULL; + xmlNodePtr resources_node = NULL; + char *s = NULL; - char *s = pcmk__itoa(nnodes); - crm_xml_add(nodes_node, "number", s); + nodes_node = pcmk__output_create_xml_node(out, PCMK_XE_NODES_CONFIGURED, + NULL); + resources_node = pcmk__output_create_xml_node(out, + PCMK_XE_RESOURCES_CONFIGURED, + NULL); + + s = pcmk__itoa(nnodes); + crm_xml_add(nodes_node, PCMK_XA_NUMBER, s); free(s); s = pcmk__itoa(nresources); - crm_xml_add(resources_node, "number", s); + crm_xml_add(resources_node, PCMK_XA_NUMBER, s); free(s); s = pcmk__itoa(ndisabled); - crm_xml_add(resources_node, "disabled", s); + crm_xml_add(resources_node, PCMK_XA_DISABLED, s); free(s); s = pcmk__itoa(nblocked); - crm_xml_add(resources_node, "blocked", s); + crm_xml_add(resources_node, PCMK_XA_BLOCKED, s); free(s); return pcmk_rc_ok; @@ -882,28 +904,42 @@ cluster_dc_html(pcmk__output_t *out, va_list args) { bool mixed_version = va_arg(args, int); xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; - pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: "); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "Current DC: "); if (dc) { - char *buf = crm_strdup_printf("%s (version %s) -", dc_name, - dc_version_s ? dc_version_s : "unknown"); - pcmk_create_html_node(node, "span", NULL, NULL, buf); - free(buf); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%s (version %s) -", + dc_name, pcmk__s(dc_version_s, "unknown")); if (mixed_version) { - pcmk_create_html_node(node, "span", NULL, "warning", - " MIXED-VERSION"); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_WARNING); + pcmk__xe_set_content(child, " MIXED-VERSION"); } - pcmk_create_html_node(node, "span", NULL, NULL, " partition"); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " partition"); + if (crm_is_true(quorum)) { - pcmk_create_html_node(node, "span", NULL, NULL, " with"); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " with"); + } else { - pcmk_create_html_node(node, "span", NULL, "warning", " WITHOUT"); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_WARNING); + pcmk__xe_set_content(child, " WITHOUT"); } - pcmk_create_html_node(node, "span", NULL, NULL, " quorum"); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " quorum"); + } else { - pcmk_create_html_node(node, "span", NULL, "warning", "NONE"); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_WARNING); + pcmk__xe_set_content(child, "NONE"); } return pcmk_rc_ok; @@ -943,17 +979,20 @@ cluster_dc_xml(pcmk__output_t *out, va_list args) { bool mixed_version = va_arg(args, int); if (dc) { - pcmk__output_create_xml_node(out, "current_dc", - "present", "true", - "version", dc_version_s ? dc_version_s : "", - "name", dc->details->uname, - "id", dc->details->id, - "with_quorum", pcmk__btoa(crm_is_true(quorum)), - "mixed_version", pcmk__btoa(mixed_version), + const char *with_quorum = pcmk__btoa(crm_is_true(quorum)); + const char *mixed_version_s = pcmk__btoa(mixed_version); + + pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC, + PCMK_XA_PRESENT, PCMK_VALUE_TRUE, + PCMK_XA_VERSION, pcmk__s(dc_version_s, ""), + PCMK_XA_NAME, dc->details->uname, + PCMK_XA_ID, dc->details->id, + PCMK_XA_WITH_QUORUM, with_quorum, + PCMK_XA_MIXED_VERSION, mixed_version_s, NULL); } else { - pcmk__output_create_xml_node(out, "current_dc", - "present", "false", + pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC, + PCMK_XA_PRESENT, PCMK_VALUE_FALSE, NULL); } @@ -1020,18 +1059,33 @@ cluster_options_html(pcmk__output_t *out, va_list args) { if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "Resource management: "); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "DISABLED"); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, + " (the cluster will not attempt to start, stop," + " or recover services)"); - pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: "); - pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED"); - pcmk_create_html_node(node, "span", NULL, NULL, - " (the cluster will not attempt to start, stop, or recover services)"); } else if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_all)) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "Resource management: "); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "STOPPED"); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, + " (the cluster will keep all resources stopped)"); - pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: "); - pcmk_create_html_node(node, "span", NULL, "bold", "STOPPED"); - pcmk_create_html_node(node, "span", NULL, NULL, - " (the cluster will keep all resources stopped)"); } else { out->list_item(out, NULL, "Resource management: enabled"); } @@ -1096,53 +1150,70 @@ cluster_options_text(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } -#define bv(flag) pcmk__btoa(pcmk_is_set(scheduler->flags, (flag))) - -PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *") -static int -cluster_options_xml(pcmk__output_t *out, va_list args) { - pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); - - const char *no_quorum_policy = NULL; - char *stonith_timeout_str = pcmk__itoa(scheduler->stonith_timeout); - char *priority_fencing_delay_str = pcmk__itoa(scheduler->priority_fencing_delay * 1000); - - switch (scheduler->no_quorum_policy) { +/*! + * \internal + * \brief Get readable string representation of a no-quorum policy + * + * \param[in] policy No-quorum policy + * + * \return String representation of \p policy + */ +static const char * +no_quorum_policy_text(enum pe_quorum_policy policy) +{ + switch (policy) { case pcmk_no_quorum_freeze: - no_quorum_policy = "freeze"; - break; + return PCMK_VALUE_FREEZE; case pcmk_no_quorum_stop: - no_quorum_policy = "stop"; - break; + return PCMK_VALUE_STOP; case pcmk_no_quorum_demote: - no_quorum_policy = "demote"; - break; + return PCMK_VALUE_DEMOTE; case pcmk_no_quorum_ignore: - no_quorum_policy = "ignore"; - break; + return PCMK_VALUE_IGNORE; case pcmk_no_quorum_fence: - no_quorum_policy = "suicide"; - break; + return PCMK_VALUE_FENCE_LEGACY; + + default: + return PCMK_VALUE_UNKNOWN; } +} - pcmk__output_create_xml_node(out, "cluster_options", - "stonith-enabled", - bv(pcmk_sched_fencing_enabled), - "symmetric-cluster", - bv(pcmk_sched_symmetric_cluster), - "no-quorum-policy", no_quorum_policy, - "maintenance-mode", - bv(pcmk_sched_in_maintenance), - "stop-all-resources", bv(pcmk_sched_stop_all), - "stonith-timeout-ms", stonith_timeout_str, - "priority-fencing-delay-ms", priority_fencing_delay_str, +PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *") +static int +cluster_options_xml(pcmk__output_t *out, va_list args) { + pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); + + const char *stonith_enabled = pcmk__flag_text(scheduler->flags, + pcmk_sched_fencing_enabled); + const char *symmetric_cluster = + pcmk__flag_text(scheduler->flags, pcmk_sched_symmetric_cluster); + const char *no_quorum_policy = + no_quorum_policy_text(scheduler->no_quorum_policy); + const char *maintenance_mode = pcmk__flag_text(scheduler->flags, + pcmk_sched_in_maintenance); + const char *stop_all_resources = pcmk__flag_text(scheduler->flags, + pcmk_sched_stop_all); + char *stonith_timeout_ms_s = pcmk__itoa(scheduler->stonith_timeout); + char *priority_fencing_delay_ms_s = + pcmk__itoa(scheduler->priority_fencing_delay * 1000); + + pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_OPTIONS, + PCMK_XA_STONITH_ENABLED, stonith_enabled, + PCMK_XA_SYMMETRIC_CLUSTER, symmetric_cluster, + PCMK_XA_NO_QUORUM_POLICY, no_quorum_policy, + PCMK_XA_MAINTENANCE_MODE, maintenance_mode, + PCMK_XA_STOP_ALL_RESOURCES, stop_all_resources, + PCMK_XA_STONITH_TIMEOUT_MS, + stonith_timeout_ms_s, + PCMK_XA_PRIORITY_FENCING_DELAY_MS, + priority_fencing_delay_ms_s, NULL); - free(stonith_timeout_str); - free(priority_fencing_delay_str); + free(stonith_timeout_ms_s); + free(priority_fencing_delay_ms_s); return pcmk_rc_ok; } @@ -1155,15 +1226,24 @@ cluster_stack_html(pcmk__output_t *out, va_list args) { (enum pcmk_pacemakerd_state) va_arg(args, int); xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "Stack: "); - pcmk_create_html_node(node, "span", NULL, "bold", "Stack: "); - pcmk_create_html_node(node, "span", NULL, NULL, stack_s); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%s", stack_s); if (pcmkd_state != pcmk_pacemakerd_state_invalid) { - pcmk_create_html_node(node, "span", NULL, NULL, " ("); - pcmk_create_html_node(node, "span", NULL, NULL, - pcmk__pcmkd_state_enum2friendly(pcmkd_state)); - pcmk_create_html_node(node, "span", NULL, NULL, ")"); + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " ("); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%s", + pcmk__pcmkd_state_enum2friendly(pcmkd_state)); + + child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, ")"); } return pcmk_rc_ok; } @@ -1198,9 +1278,9 @@ cluster_stack_xml(pcmk__output_t *out, va_list args) { state_s = pcmk_pacemakerd_api_daemon_state_enum2text(pcmkd_state); } - pcmk__output_create_xml_node(out, "stack", - "type", stack_s, - "pacemakerd-state", state_s, + pcmk__output_create_xml_node(out, PCMK_XE_STACK, + PCMK_XA_TYPE, stack_s, + PCMK_XA_PACEMAKERD_STATE, state_s, NULL); return pcmk_rc_ok; @@ -1218,24 +1298,36 @@ cluster_times_html(pcmk__output_t *out, va_list args) { xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL); xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; - char *time_s = pcmk__epoch2str(NULL, 0); + char *time_s = NULL; - pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: "); - pcmk_create_html_node(updated_node, "span", NULL, NULL, time_s); + child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "Last updated: "); + + child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL); + time_s = pcmk__epoch2str(NULL, 0); + pcmk__xe_set_content(child, "%s", time_s); + free(time_s); if (our_nodename != NULL) { - pcmk_create_html_node(updated_node, "span", NULL, NULL, " on "); - pcmk_create_html_node(updated_node, "span", NULL, NULL, our_nodename); - } + child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, " on "); - free(time_s); - time_s = last_changed_string(last_written, user, client, origin); + child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%s", our_nodename); + } - pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: "); - pcmk_create_html_node(changed_node, "span", NULL, NULL, time_s); + child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "Last change: "); + child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL, NULL); + time_s = last_changed_string(last_written, user, client, origin); + pcmk__xe_set_content(child, "%s", time_s); free(time_s); + return pcmk_rc_ok; } @@ -1251,16 +1343,16 @@ cluster_times_xml(pcmk__output_t *out, va_list args) { char *time_s = pcmk__epoch2str(NULL, 0); - pcmk__output_create_xml_node(out, "last_update", - "time", time_s, - "origin", our_nodename, + pcmk__output_create_xml_node(out, PCMK_XE_LAST_UPDATE, + PCMK_XA_TIME, time_s, + PCMK_XA_ORIGIN, our_nodename, NULL); - pcmk__output_create_xml_node(out, "last_change", - "time", last_written ? last_written : "", - "user", user ? user : "", - "client", client ? client : "", - "origin", origin ? origin : "", + pcmk__output_create_xml_node(out, PCMK_XE_LAST_CHANGE, + PCMK_XA_TIME, pcmk__s(last_written, ""), + PCMK_XA_USER, pcmk__s(user, ""), + PCMK_XA_CLIENT, pcmk__s(client, ""), + PCMK_XA_ORIGIN, pcmk__s(origin, ""), NULL); free(time_s); @@ -1319,8 +1411,9 @@ failed_action_friendly(pcmk__output_t *out, const xmlNode *xml_op, if (pcmk__str_empty(op_key) || !parse_op_key(op_key, &rsc_id, &task, &interval_ms)) { - rsc_id = strdup("unknown resource"); - task = strdup("unknown action"); + + pcmk__str_update(&rsc_id, "unknown resource"); + pcmk__str_update(&task, "unknown action"); interval_ms = 0; } CRM_ASSERT((rsc_id != NULL) && (task != NULL)); @@ -1353,7 +1446,7 @@ failed_action_friendly(pcmk__output_t *out, const xmlNode *xml_op, } - if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &last_change_epoch) == pcmk_ok) { char *s = pcmk__epoch2str(&last_change_epoch, 0); @@ -1396,8 +1489,8 @@ failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op, int status, const char *exit_reason, const char *exec_time) { - const char *call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); - const char *queue_time = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE); + const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID); + const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME); const char *exit_status = services_ocf_exitcode_str(rc); const char *lrm_status = pcmk_exec_status_str(status); time_t last_change_epoch = 0; @@ -1423,12 +1516,12 @@ failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op, pcmk__g_strcat(str, ", exitreason='", exit_reason, "'", NULL); } - if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &last_change_epoch) == pcmk_ok) { char *last_change_str = pcmk__epoch2str(&last_change_epoch, 0); pcmk__g_strcat(str, - ", " XML_RSC_OP_LAST_CHANGE "=" + ", " PCMK_XA_LAST_RC_CHANGE "=" "'", last_change_str, "'", NULL); free(last_change_str); } @@ -1443,26 +1536,25 @@ failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op, g_string_free(str, TRUE); } -PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t") +PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t") static int failed_action_default(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); uint32_t show_opts = va_arg(args, uint32_t); - const char *op_key = pe__xe_history_key(xml_op); - const char *node_name = crm_element_value(xml_op, XML_ATTR_UNAME); - const char *exit_reason = crm_element_value(xml_op, - XML_LRM_ATTR_EXIT_REASON); - const char *exec_time = crm_element_value(xml_op, XML_RSC_OP_T_EXEC); + const char *op_key = pcmk__xe_history_key(xml_op); + const char *node_name = crm_element_value(xml_op, PCMK_XA_UNAME); + const char *exit_reason = crm_element_value(xml_op, PCMK_XA_EXIT_REASON); + const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); int rc; int status; - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0); + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0); - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), - &status, 0); + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status, + 0); if (pcmk__str_empty(node_name)) { node_name = "unknown node"; @@ -1478,44 +1570,60 @@ failed_action_default(pcmk__output_t *out, va_list args) return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t") +PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t") static int failed_action_xml(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t); - const char *op_key = pe__xe_history_key(xml_op); - const char *op_key_name = "op_key"; + const char *op_key = pcmk__xe_history_key(xml_op); + const char *op_key_name = PCMK_XA_OP_KEY; int rc; int status; - const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON); + const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME); + const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID); + const char *exitstatus = NULL; + const char *exit_reason = pcmk__s(crm_element_value(xml_op, + PCMK_XA_EXIT_REASON), + "none"); + const char *status_s = NULL; time_t epoch = 0; + gchar *exit_reason_esc = NULL; char *rc_s = NULL; - char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none"); xmlNodePtr node = NULL; - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0); - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), - &status, 0); + if (pcmk__xml_needs_escape(exit_reason, pcmk__xml_escape_attr)) { + exit_reason_esc = pcmk__xml_escape(exit_reason, pcmk__xml_escape_attr); + exit_reason = exit_reason_esc; + } + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0); + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status, + 0); - rc_s = pcmk__itoa(rc); - if (crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY) == NULL) { - op_key_name = "id"; + if (crm_element_value(xml_op, PCMK__XA_OPERATION_KEY) == NULL) { + op_key_name = PCMK_XA_ID; } - node = pcmk__output_create_xml_node(out, "failure", + exitstatus = services_ocf_exitcode_str(rc); + rc_s = pcmk__itoa(rc); + status_s = pcmk_exec_status_str(status); + node = pcmk__output_create_xml_node(out, PCMK_XE_FAILURE, op_key_name, op_key, - "node", crm_element_value(xml_op, XML_ATTR_UNAME), - "exitstatus", services_ocf_exitcode_str(rc), - "exitreason", pcmk__s(reason_s, ""), - "exitcode", rc_s, - "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID), - "status", pcmk_exec_status_str(status), + PCMK_XA_NODE, uname, + PCMK_XA_EXITSTATUS, exitstatus, + PCMK_XA_EXITREASON, exit_reason, + PCMK_XA_EXITCODE, rc_s, + PCMK_XA_CALL, call_id, + PCMK_XA_STATUS, status_s, NULL); free(rc_s); - if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &epoch) == pcmk_ok) && (epoch > 0)) { + + const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME); + const char *exec = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); + const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION); guint interval_ms = 0; char *interval_ms_s = NULL; char *rc_change = pcmk__epoch2str(&epoch, @@ -1523,21 +1631,22 @@ failed_action_xml(pcmk__output_t *out, va_list args) { |crm_time_log_timeofday |crm_time_log_with_timezone); - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms); interval_ms_s = crm_strdup_printf("%u", interval_ms); - pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, rc_change, - "queued", crm_element_value(xml_op, XML_RSC_OP_T_QUEUE), - "exec", crm_element_value(xml_op, XML_RSC_OP_T_EXEC), - "interval", interval_ms_s, - "task", crm_element_value(xml_op, XML_LRM_ATTR_TASK), + pcmk__xe_set_props(node, + PCMK_XA_LAST_RC_CHANGE, rc_change, + PCMK_XA_QUEUED, queue_time, + PCMK_XA_EXEC, exec, + PCMK_XA_INTERVAL, interval_ms_s, + PCMK_XA_TASK, task, NULL); free(interval_ms_s); free(rc_change); } - free(reason_s); + g_free(exit_reason_esc); return pcmk_rc_ok; } @@ -1558,11 +1667,13 @@ failed_action_list(pcmk__output_t *out, va_list args) { return rc; } - for (xml_op = pcmk__xml_first_child(scheduler->failed); xml_op != NULL; - xml_op = pcmk__xml_next(xml_op)) { + for (xml_op = pcmk__xe_first_child(scheduler->failed, NULL, NULL, NULL); + xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) { + char *rsc = NULL; - if (!pcmk__str_in_list(crm_element_value(xml_op, XML_ATTR_UNAME), only_node, + if (!pcmk__str_in_list(crm_element_value(xml_op, PCMK_XA_UNAME), + only_node, pcmk__str_star_matches|pcmk__str_casei)) { continue; } @@ -1571,7 +1682,7 @@ failed_action_list(pcmk__output_t *out, va_list args) { continue; } - if (!parse_op_key(pe__xe_history_key(xml_op), &rsc, NULL, NULL)) { + if (!parse_op_key(pcmk__xe_history_key(xml_op), &rsc, NULL, NULL)) { continue; } @@ -1594,51 +1705,71 @@ static void status_node(pcmk_node_t *node, xmlNodePtr parent, uint32_t show_opts) { int health = pe__node_health(node); + xmlNode *child = NULL; // Cluster membership if (node->details->online) { - pcmk_create_html_node(parent, "span", NULL, "online", " online"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_ONLINE); + pcmk__xe_set_content(child, " online"); + } else { - pcmk_create_html_node(parent, "span", NULL, "offline", " OFFLINE"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_OFFLINE); + pcmk__xe_set_content(child, " OFFLINE"); } // Standby mode if (node->details->standby_onfail && (node->details->running_rsc != NULL)) { - pcmk_create_html_node(parent, "span", NULL, "standby", - " (in standby due to on-fail," - " with active resources)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_STANDBY); + pcmk__xe_set_content(child, + " (in standby due to " PCMK_META_ON_FAIL "," + " with active resources)"); + } else if (node->details->standby_onfail) { - pcmk_create_html_node(parent, "span", NULL, "standby", - " (in standby due to on-fail)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_STANDBY); + pcmk__xe_set_content(child, + " (in standby due to " PCMK_META_ON_FAIL ")"); + } else if (node->details->standby && (node->details->running_rsc != NULL)) { - pcmk_create_html_node(parent, "span", NULL, "standby", - " (in standby, with active resources)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_STANDBY); + pcmk__xe_set_content(child, + " (in standby, with active resources)"); + } else if (node->details->standby) { - pcmk_create_html_node(parent, "span", NULL, "standby", " (in standby)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK_VALUE_STANDBY); + pcmk__xe_set_content(child, " (in standby)"); } // Maintenance mode if (node->details->maintenance) { - pcmk_create_html_node(parent, "span", NULL, "maint", - " (in maintenance mode)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK__VALUE_MAINT); + pcmk__xe_set_content(child, " (in maintenance mode)"); } // Node health if (health < 0) { - pcmk_create_html_node(parent, "span", NULL, "health_red", - " (health is RED)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK__VALUE_HEALTH_RED); + pcmk__xe_set_content(child, " (health is RED)"); + } else if (health == 0) { - pcmk_create_html_node(parent, "span", NULL, "health_yellow", - " (health is YELLOW)"); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, + PCMK__VALUE_HEALTH_YELLOW); + pcmk__xe_set_content(child, " (health is YELLOW)"); } // Feature set if (pcmk_is_set(show_opts, pcmk_show_feature_set)) { const char *feature_set = get_node_feature_set(node); if (feature_set != NULL) { - char *buf = crm_strdup_printf(", feature set %s", feature_set); - pcmk_create_html_node(parent, "span", NULL, NULL, buf); - free(buf); + child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, ", feature set %s", feature_set); } } } @@ -1656,14 +1787,16 @@ node_html(pcmk__output_t *out, va_list args) { char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id)); if (full) { - xmlNodePtr item_node; + xmlNode *item_node = NULL; + xmlNode *child = NULL; if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) { GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc); out->begin_list(out, NULL, NULL, "%s:", node_name); item_node = pcmk__output_xml_create_parent(out, "li", NULL); - pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:"); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "Status:"); status_node(node, item_node, show_opts); if (rscs != NULL) { @@ -1682,7 +1815,8 @@ node_html(pcmk__output_t *out, va_list args) { out->begin_list(out, NULL, NULL, "%s:", node_name); item_node = pcmk__output_xml_create_parent(out, "li", NULL); - pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:"); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "Status:"); status_node(node, item_node, show_opts); for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { @@ -1690,7 +1824,7 @@ node_html(pcmk__output_t *out, va_list args) { PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources"); show_opts |= pcmk_show_rsc_only; - out->message(out, crm_map_element_name(rsc->xml), show_opts, + out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc, only_node, only_rsc); } @@ -1699,13 +1833,11 @@ node_html(pcmk__output_t *out, va_list args) { out->end_list(out); } else { - char *buf = crm_strdup_printf("%s:", node_name); - item_node = pcmk__output_create_xml_node(out, "li", NULL); - pcmk_create_html_node(item_node, "span", NULL, "bold", buf); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "%s:", node_name); status_node(node, item_node, show_opts); - - free(buf); } } else { out->begin_list(out, NULL, NULL, "%s:", node_name); @@ -1741,7 +1873,7 @@ node_text_status(const pcmk_node_t *node) return "pending"; } else if (node->details->standby_onfail && node->details->online) { - return "standby (on-fail)"; + return "standby (" PCMK_META_ON_FAIL ")"; } else if (node->details->standby) { if (node->details->online) { @@ -1784,9 +1916,9 @@ node_text(pcmk__output_t *out, va_list args) { int health = pe__node_health(node); // Create a summary line with node type, name, and status - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { g_string_append(str, "GuestNode"); - } else if (pe__is_remote_node(node)) { + } else if (pcmk__is_remote_node(node)) { g_string_append(str, "RemoteNode"); } else { g_string_append(str, "Node"); @@ -1833,8 +1965,8 @@ node_text(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = (pcmk_resource_t *) gIter2->data; show_opts |= pcmk_show_rsc_only; - out->message(out, crm_map_element_name(rsc->xml), show_opts, - rsc, only_node, only_rsc); + out->message(out, pcmk__map_element_name(rsc->xml), + show_opts, rsc, only_node, only_rsc); } out->end_list(out); @@ -1855,6 +1987,54 @@ node_text(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } +/*! + * \internal + * \brief Convert an integer health value to a string representation + * + * \param[in] health Integer health value + * + * \retval \c PCMK_VALUE_RED if \p health is less than 0 + * \retval \c PCMK_VALUE_YELLOW if \p health is equal to 0 + * \retval \c PCMK_VALUE_GREEN if \p health is greater than 0 + */ +static const char * +health_text(int health) +{ + if (health < 0) { + return PCMK_VALUE_RED; + } else if (health == 0) { + return PCMK_VALUE_YELLOW; + } else { + return PCMK_VALUE_GREEN; + } +} + +/*! + * \internal + * \brief Convert a node type to a string representation + * + * \param[in] type Node type + * + * \retval \c PCMK_VALUE_MEMBER if \p node_type is \c pcmk_node_variant_cluster + * \retval \c PCMK_VALUE_REMOTE if \p node_type is \c pcmk_node_variant_remote + * \retval \c PCMK__VALUE_PING if \p node_type is \c node_ping + * \retval \c PCMK_VALUE_UNKNOWN otherwise + */ +static const char * +node_type_str(enum node_type type) +{ + switch (type) { + case pcmk_node_variant_cluster: + return PCMK_VALUE_MEMBER; + case pcmk_node_variant_remote: + return PCMK_VALUE_REMOTE; + case node_ping: + return PCMK__VALUE_PING; + default: + return PCMK_VALUE_UNKNOWN; + } +} + PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *", "GList *") static int @@ -1866,54 +2046,48 @@ node_xml(pcmk__output_t *out, va_list args) { GList *only_rsc = va_arg(args, GList *); if (full) { - const char *node_type = "unknown"; - char *length_s = pcmk__itoa(g_list_length(node->details->running_rsc)); - int health = pe__node_health(node); - const char *health_s = NULL; - const char *feature_set; - - switch (node->details->type) { - case pcmk_node_variant_cluster: - node_type = "member"; - break; - case pcmk_node_variant_remote: - node_type = "remote"; - break; - case node_ping: - node_type = "ping"; - break; - } - - if (health < 0) { - health_s = "red"; - } else if (health == 0) { - health_s = "yellow"; - } else { - health_s = "green"; - } - - feature_set = get_node_feature_set(node); - - pe__name_and_nvpairs_xml(out, true, "node", 15, - "name", node->details->uname, - "id", node->details->id, - "online", pcmk__btoa(node->details->online), - "standby", pcmk__btoa(node->details->standby), - "standby_onfail", pcmk__btoa(node->details->standby_onfail), - "maintenance", pcmk__btoa(node->details->maintenance), - "pending", pcmk__btoa(node->details->pending), - "unclean", pcmk__btoa(node->details->unclean), - "health", health_s, - "feature_set", feature_set, - "shutdown", pcmk__btoa(node->details->shutdown), - "expected_up", pcmk__btoa(node->details->expected_up), - "is_dc", pcmk__btoa(node->details->is_dc), - "resources_running", length_s, - "type", node_type); - - if (pe__is_guest_node(node)) { + const char *online = pcmk__btoa(node->details->online); + const char *standby = pcmk__btoa(node->details->standby); + const char *standby_onfail = pcmk__btoa(node->details->standby_onfail); + const char *maintenance = pcmk__btoa(node->details->maintenance); + const char *pending = pcmk__btoa(node->details->pending); + const char *unclean = pcmk__btoa(node->details->unclean); + const char *health = health_text(pe__node_health(node)); + const char *feature_set = get_node_feature_set(node); + const char *shutdown = pcmk__btoa(node->details->shutdown); + const char *expected_up = pcmk__btoa(node->details->expected_up); + const char *is_dc = pcmk__btoa(node->details->is_dc); + int length = g_list_length(node->details->running_rsc); + char *resources_running = pcmk__itoa(length); + const char *node_type = node_type_str(node->details->type); + + int rc = pcmk_rc_ok; + + rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_NODE, + PCMK_XA_NAME, node->details->uname, + PCMK_XA_ID, node->details->id, + PCMK_XA_ONLINE, online, + PCMK_XA_STANDBY, standby, + PCMK_XA_STANDBY_ONFAIL, standby_onfail, + PCMK_XA_MAINTENANCE, maintenance, + PCMK_XA_PENDING, pending, + PCMK_XA_UNCLEAN, unclean, + PCMK_XA_HEALTH, health, + PCMK_XA_FEATURE_SET, feature_set, + PCMK_XA_SHUTDOWN, shutdown, + PCMK_XA_EXPECTED_UP, expected_up, + PCMK_XA_IS_DC, is_dc, + PCMK_XA_RESOURCES_RUNNING, resources_running, + PCMK_XA_TYPE, node_type, + NULL); + + free(resources_running); + CRM_ASSERT(rc == pcmk_rc_ok); + + if (pcmk__is_guest_or_bundle_node(node)) { xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out); - crm_xml_add(xml_node, "id_as_resource", node->details->remote_rsc->container->id); + crm_xml_add(xml_node, PCMK_XA_ID_AS_RESOURCE, + node->details->remote_rsc->container->id); } if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) { @@ -1923,17 +2097,15 @@ node_xml(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; show_opts |= pcmk_show_rsc_only; - out->message(out, crm_map_element_name(rsc->xml), show_opts, + out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc, only_node, only_rsc); } } - free(length_s); - out->end_list(out); } else { - pcmk__output_xml_create_parent(out, "node", - "name", node->details->uname, + pcmk__output_xml_create_parent(out, PCMK_XE_NODE, + PCMK_XA_NAME, node->details->uname, NULL); } @@ -1979,25 +2151,28 @@ node_attribute_html(pcmk__output_t *out, va_list args) { int expected_score = va_arg(args, int); if (add_extra) { - int v; - char *s = crm_strdup_printf("%s: %s", name, value); + int v = 0; xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL); + xmlNode *child = NULL; - if (value == NULL) { - v = 0; - } else { + if (value != NULL) { pcmk__scan_min_int(value, &v, INT_MIN); } - pcmk_create_html_node(item_node, "span", NULL, NULL, s); - free(s); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL); + pcmk__xe_set_content(child, "%s: %s", name, value); if (v <= 0) { - pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)"); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, "(connectivity is lost)"); + } else if (v < expected_score) { - char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score); - pcmk_create_html_node(item_node, "span", NULL, "bold", buf); - free(buf); + child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, + PCMK__VALUE_BOLD); + pcmk__xe_set_content(child, + "(connectivity is degraded -- expected %d)", + expected_score); } } else { out->list_item(out, NULL, "%s: %s", name, value); @@ -2006,7 +2181,7 @@ node_attribute_html(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNodePtr") +PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *") static int node_and_op(pcmk__output_t *out, va_list args) { pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); @@ -2016,18 +2191,19 @@ node_and_op(pcmk__output_t *out, va_list args) { gchar *node_str = NULL; char *last_change_str = NULL; - const char *op_rsc = crm_element_value(xml_op, "resource"); + const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE); int status; time_t last_change = 0; - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), - &status, PCMK_EXEC_UNKNOWN); + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status, + PCMK_EXEC_UNKNOWN); rsc = pe_find_resource(scheduler->resources, op_rsc); if (rsc) { - const pcmk_node_t *node = pe__current_node(rsc); - const char *target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + const pcmk_node_t *node = pcmk__current_node(rsc); + const char *target_role = g_hash_table_lookup(rsc->meta, + PCMK_META_TARGET_ROLE); uint32_t show_opts = pcmk_show_rsc_only | pcmk_show_pending; if (node == NULL) { @@ -2040,19 +2216,21 @@ node_and_op(pcmk__output_t *out, va_list args) { node_str = crm_strdup_printf("Unknown resource %s", op_rsc); } - if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &last_change) == pcmk_ok) { + const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); + last_change_str = crm_strdup_printf(", %s='%s', exec=%sms", - XML_RSC_OP_LAST_CHANGE, + PCMK_XA_LAST_RC_CHANGE, pcmk__trim(ctime(&last_change)), - crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); + exec_time); } out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s", - node_str, pe__xe_history_key(xml_op), - crm_element_value(xml_op, XML_ATTR_UNAME), - crm_element_value(xml_op, XML_LRM_ATTR_CALLID), - crm_element_value(xml_op, XML_LRM_ATTR_RC), + node_str, pcmk__xe_history_key(xml_op), + crm_element_value(xml_op, PCMK_XA_UNAME), + crm_element_value(xml_op, PCMK__XA_CALL_ID), + crm_element_value(xml_op, PCMK__XA_RC_CODE), last_change_str ? last_change_str : "", pcmk_exec_status_str(status)); @@ -2061,50 +2239,63 @@ node_and_op(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNodePtr") +PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *") static int node_and_op_xml(pcmk__output_t *out, va_list args) { pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); xmlNodePtr xml_op = va_arg(args, xmlNodePtr); pcmk_resource_t *rsc = NULL; - const char *op_rsc = crm_element_value(xml_op, "resource"); + const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME); + const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID); + const char *rc_s = crm_element_value(xml_op, PCMK__XA_RC_CODE); + const char *status_s = NULL; + const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE); int status; time_t last_change = 0; xmlNode *node = NULL; - pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), + pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status, PCMK_EXEC_UNKNOWN); - node = pcmk__output_create_xml_node(out, "operation", - "op", pe__xe_history_key(xml_op), - "node", crm_element_value(xml_op, XML_ATTR_UNAME), - "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID), - "rc", crm_element_value(xml_op, XML_LRM_ATTR_RC), - "status", pcmk_exec_status_str(status), + status_s = pcmk_exec_status_str(status); + + node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION, + PCMK_XA_OP, pcmk__xe_history_key(xml_op), + PCMK_XA_NODE, uname, + PCMK_XA_CALL, call_id, + PCMK_XA_RC, rc_s, + PCMK_XA_STATUS, status_s, NULL); rsc = pe_find_resource(scheduler->resources, op_rsc); if (rsc) { - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE); - char *agent_tuple = NULL; - - agent_tuple = crm_strdup_printf("%s:%s:%s", class, - pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider) ? crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER) : "", - kind); - - pcmk__xe_set_props(node, "rsc", rsc_printable_id(rsc), - "agent", agent_tuple, + const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS); + const char *provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER); + const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE); + bool has_provider = pcmk_is_set(pcmk_get_ra_caps(class), + pcmk_ra_cap_provider); + + char *agent_tuple = crm_strdup_printf("%s:%s:%s", + class, + (has_provider? provider : ""), + kind); + + pcmk__xe_set_props(node, + PCMK_XA_RSC, rsc_printable_id(rsc), + PCMK_XA_AGENT, agent_tuple, NULL); free(agent_tuple); } - if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &last_change) == pcmk_ok) { - pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, - pcmk__trim(ctime(&last_change)), - XML_RSC_OP_T_EXEC, crm_element_value(xml_op, XML_RSC_OP_T_EXEC), + const char *last_rc_change = pcmk__trim(ctime(&last_change)); + const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); + + pcmk__xe_set_props(node, + PCMK_XA_LAST_RC_CHANGE, last_rc_change, + PCMK_XA_EXEC_TIME, exec_time, NULL); } @@ -2119,14 +2310,14 @@ node_attribute_xml(pcmk__output_t *out, va_list args) { bool add_extra = va_arg(args, int); int expected_score = va_arg(args, int); - xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute", - "name", name, - "value", value, + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE, + PCMK_XA_NAME, name, + PCMK_XA_VALUE, value, NULL); if (add_extra) { char *buf = pcmk__itoa(expected_score); - crm_xml_add(node, "expected", buf); + crm_xml_add(node, PCMK_XA_EXPECTED, buf); free(buf); } @@ -2181,7 +2372,7 @@ node_attribute_list(pcmk__output_t *out, va_list args) { int expected_score = 0; bool add_extra = false; - value = pe_node_attribute_raw(node, name); + value = pcmk__node_attr(node, name, NULL, pcmk__rsc_node_current); add_extra = add_extra_info(node, node->details->running_rsc, scheduler, name, &expected_score); @@ -2207,7 +2398,7 @@ node_capacity(pcmk__output_t *out, va_list args) const char *comment = va_arg(args, const char *); char *dump_text = crm_strdup_printf("%s: %s capacity:", - comment, pe__node_name(node)); + comment, pcmk__node_name(node)); g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text); out->list_item(out, NULL, "%s", dump_text); @@ -2221,11 +2412,12 @@ static int node_capacity_xml(pcmk__output_t *out, va_list args) { const pcmk_node_t *node = va_arg(args, pcmk_node_t *); + const char *uname = node->details->uname; const char *comment = va_arg(args, const char *); - xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "capacity", - "node", node->details->uname, - "comment", comment, + xmlNodePtr xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CAPACITY, + PCMK_XA_NODE, uname, + PCMK_XA_COMMENT, comment, NULL); g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node); @@ -2233,7 +2425,7 @@ node_capacity_xml(pcmk__output_t *out, va_list args) } PCMK__OUTPUT_ARGS("node-history-list", "pcmk_scheduler_t *", "pcmk_node_t *", - "xmlNodePtr", "GList *", "GList *", "uint32_t", "uint32_t") + "xmlNode *", "GList *", "GList *", "uint32_t", "uint32_t") static int node_history_list(pcmk__output_t *out, va_list args) { pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); @@ -2248,13 +2440,15 @@ node_history_list(pcmk__output_t *out, va_list args) { xmlNode *rsc_entry = NULL; int rc = pcmk_rc_no_output; - lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); + lrm_rsc = pcmk__xe_first_child(node_state, PCMK__XE_LRM, NULL, NULL); + lrm_rsc = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCES, NULL, NULL); /* Print history of each of the node's resources */ - for (rsc_entry = first_named_child(lrm_rsc, XML_LRM_TAG_RESOURCE); - rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) { - const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + for (rsc_entry = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCE, NULL, + NULL); + rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) { + + const char *rsc_id = crm_element_value(rsc_entry, PCMK_XA_ID); pcmk_resource_t *rsc = pe_find_resource(scheduler->resources, rsc_id); const pcmk_resource_t *parent = pe__const_top_resource(rsc, false); @@ -2266,7 +2460,7 @@ node_history_list(pcmk__output_t *out, va_list args) { * * For other resource types, is_filtered is okay. */ - if (parent->variant == pcmk_rsc_variant_group) { + if (pcmk__is_group(parent)) { if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) && !pcmk__str_in_list(rsc_printable_id(parent), only_rsc, @@ -2298,13 +2492,15 @@ node_history_list(pcmk__output_t *out, va_list args) { failcount, last_failure, false); } else { GList *op_list = get_operation_list(rsc_entry); - pcmk_resource_t *rsc = pe_find_resource(scheduler->resources, - crm_element_value(rsc_entry, XML_ATTR_ID)); + pcmk_resource_t *rsc = NULL; if (op_list == NULL) { continue; } + rsc = pe_find_resource(scheduler->resources, + crm_element_value(rsc_entry, PCMK_XA_ID)); + if (rc == pcmk_rc_no_output) { rc = pcmk_rc_ok; out->message(out, "node", node, show_opts, false, only_node, @@ -2389,10 +2585,10 @@ node_list_text(pcmk__output_t *out, va_list args) { } else if (node->details->online) { // Display online node in a list - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { pcmk__add_word(&online_guest_nodes, 1024, node_name); - } else if (pe__is_remote_node(node)) { + } else if (pcmk__is_remote_node(node)) { pcmk__add_word(&online_remote_nodes, 1024, node_name); } else { @@ -2403,10 +2599,10 @@ node_list_text(pcmk__output_t *out, va_list args) { } else { // Display offline node in a list - if (pe__is_remote_node(node)) { + if (pcmk__is_remote_node(node)) { pcmk__add_word(&offline_remote_nodes, 1024, node_name); - } else if (pe__is_guest_node(node)) { + } else if (pcmk__is_guest_or_bundle_node(node)) { /* ignore offline guest nodes */ } else { @@ -2461,7 +2657,11 @@ node_list_xml(pcmk__output_t *out, va_list args) { uint32_t show_opts = va_arg(args, uint32_t); bool print_spacer G_GNUC_UNUSED = va_arg(args, int); - out->begin_list(out, NULL, NULL, "nodes"); + /* PCMK_XE_NODES acts as the list's element name for CLI tools that use + * pcmk__output_enable_list_element. Otherwise PCMK_XE_NODES is the + * value of the list's PCMK_XA_NAME attribute. + */ + out->begin_list(out, NULL, NULL, PCMK_XE_NODES); for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *node = (pcmk_node_t *) gIter->data; @@ -2490,16 +2690,19 @@ node_summary(pcmk__output_t *out, va_list args) { xmlNode *node_state = NULL; xmlNode *cib_status = pcmk_find_cib_element(scheduler->input, - XML_CIB_TAG_STATUS); + PCMK_XE_STATUS); int rc = pcmk_rc_no_output; if (xmlChildElementCount(cib_status) == 0) { return rc; } - for (node_state = first_named_child(cib_status, XML_CIB_TAG_STATE); - node_state != NULL; node_state = crm_next_same_xml(node_state)) { - pcmk_node_t *node = pe_find_node_id(scheduler->nodes, ID(node_state)); + for (node_state = pcmk__xe_first_child(cib_status, PCMK__XE_NODE_STATE, + NULL, NULL); + node_state != NULL; node_state = pcmk__xe_next_same(node_state)) { + + pcmk_node_t *node = pe_find_node_id(scheduler->nodes, + pcmk__xe_id(node_state)); if (!node || !node->details || !node->details->online) { continue; @@ -2551,20 +2754,20 @@ node_weight_xml(pcmk__output_t *out, va_list args) const char *uname = va_arg(args, const char *); const char *score = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_create_xml_node(out, "node_weight", - "function", prefix, - "node", uname, - "score", score, + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_NODE_WEIGHT, + PCMK_XA_FUNCTION, prefix, + PCMK_XA_NODE, uname, + PCMK_XA_SCORE, score, NULL); if (rsc) { - crm_xml_add(node, "id", rsc->id); + crm_xml_add(node, PCMK_XA_ID, rsc->id); } return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t") +PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t") static int op_history_text(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); @@ -2582,7 +2785,7 @@ op_history_text(pcmk__output_t *out, va_list args) { return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t") +PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t") static int op_history_xml(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); @@ -2591,18 +2794,22 @@ op_history_xml(pcmk__output_t *out, va_list args) { int rc = va_arg(args, int); uint32_t show_opts = va_arg(args, uint32_t); + const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID); char *rc_s = pcmk__itoa(rc); - xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history", - "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID), - "task", task, - "rc", rc_s, - "rc_text", services_ocf_exitcode_str(rc), - NULL); + const char *rc_text = services_ocf_exitcode_str(rc); + xmlNodePtr node = NULL; + + node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION_HISTORY, + PCMK_XA_CALL, call_id, + PCMK_XA_TASK, task, + PCMK_XA_RC, rc_s, + PCMK_XA_RC_TEXT, rc_text, + NULL); free(rc_s); if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) { char *s = crm_strdup_printf("%sms", interval_ms_s); - crm_xml_add(node, "interval", s); + crm_xml_add(node, PCMK_XA_INTERVAL, s); free(s); } @@ -2610,23 +2817,23 @@ op_history_xml(pcmk__output_t *out, va_list args) { const char *value = NULL; time_t epoch = 0; - if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &epoch) == pcmk_ok) && (epoch > 0)) { char *s = pcmk__epoch2str(&epoch, 0); - crm_xml_add(node, XML_RSC_OP_LAST_CHANGE, s); + crm_xml_add(node, PCMK_XA_LAST_RC_CHANGE, s); free(s); } - value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC); + value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME); if (value) { char *s = crm_strdup_printf("%sms", value); - crm_xml_add(node, XML_RSC_OP_T_EXEC, s); + crm_xml_add(node, PCMK_XA_EXEC_TIME, s); free(s); } - value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE); + value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME); if (value) { char *s = crm_strdup_printf("%sms", value); - crm_xml_add(node, XML_RSC_OP_T_QUEUE, s); + crm_xml_add(node, PCMK_XA_QUEUE_TIME, s); free(s); } } @@ -2659,13 +2866,13 @@ promotion_score_xml(pcmk__output_t *out, va_list args) pcmk_node_t *chosen = va_arg(args, pcmk_node_t *); const char *score = va_arg(args, const char *); - xmlNodePtr node = pcmk__output_create_xml_node(out, "promotion_score", - "id", child_rsc->id, - "score", score, + xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_PROMOTION_SCORE, + PCMK_XA_ID, child_rsc->id, + PCMK_XA_SCORE, score, NULL); if (chosen) { - crm_xml_add(node, "node", chosen->details->uname); + crm_xml_add(node, PCMK_XA_NODE, chosen->details->uname); } return pcmk_rc_ok; @@ -2675,29 +2882,22 @@ PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool") static int resource_config(pcmk__output_t *out, va_list args) { const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *); + GString *xml_buf = g_string_sized_new(1024); bool raw = va_arg(args, int); - char *rsc_xml = formatted_xml_buf(rsc, raw); + formatted_xml_buf(rsc, xml_buf, raw); - out->output_xml(out, "xml", rsc_xml); + out->output_xml(out, PCMK_XE_XML, xml_buf->str); - free(rsc_xml); + g_string_free(xml_buf, TRUE); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool") static int resource_config_text(pcmk__output_t *out, va_list args) { - const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *); - bool raw = va_arg(args, int); - - char *rsc_xml = formatted_xml_buf(rsc, raw); - pcmk__formatted_printf(out, "Resource XML:\n"); - out->output_xml(out, "xml", rsc_xml); - - free(rsc_xml); - return pcmk_rc_ok; + return resource_config(out, args); } PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *", @@ -2734,31 +2934,33 @@ resource_history_xml(pcmk__output_t *out, va_list args) { time_t last_failure = va_arg(args, time_t); bool as_header = va_arg(args, int); - xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history", - "id", rsc_id, + xmlNodePtr node = pcmk__output_xml_create_parent(out, + PCMK_XE_RESOURCE_HISTORY, + PCMK_XA_ID, rsc_id, NULL); if (rsc == NULL) { - pcmk__xe_set_bool_attr(node, "orphan", true); + pcmk__xe_set_bool_attr(node, PCMK_XA_ORPHAN, true); } else if (all || failcount || last_failure > 0) { char *migration_s = pcmk__itoa(rsc->migration_threshold); - pcmk__xe_set_props(node, "orphan", "false", - "migration-threshold", migration_s, + pcmk__xe_set_props(node, + PCMK_XA_ORPHAN, PCMK_VALUE_FALSE, + PCMK_META_MIGRATION_THRESHOLD, migration_s, NULL); free(migration_s); if (failcount > 0) { char *s = pcmk__itoa(failcount); - crm_xml_add(node, PCMK__FAIL_COUNT_PREFIX, s); + crm_xml_add(node, PCMK_XA_FAIL_COUNT, s); free(s); } if (last_failure > 0) { char *s = pcmk__epoch2str(&last_failure, 0); - crm_xml_add(node, PCMK__LAST_FAILURE_PREFIX, s); + crm_xml_add(node, PCMK_XA_LAST_FAILURE, s); free(s); } } @@ -2843,7 +3045,7 @@ resource_list(pcmk__output_t *out, va_list args) /* Skip primitives already counted in a brief summary */ } else if (pcmk_is_set(show_opts, pcmk_show_brief) - && (rsc->variant == pcmk_rsc_variant_primitive)) { + && pcmk__is_primitive(rsc)) { continue; /* Skip resources that aren't at least partially active, @@ -2863,7 +3065,7 @@ resource_list(pcmk__output_t *out, va_list args) } /* Print this resource */ - x = out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc, + x = out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc, only_node, only_rsc); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; @@ -2911,10 +3113,10 @@ resource_operation_list(pcmk__output_t *out, va_list args) /* Print each operation */ for (gIter = op_list; gIter != NULL; gIter = gIter->next) { xmlNode *xml_op = (xmlNode *) gIter->data; - const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION); const char *interval_ms_s = crm_element_value(xml_op, - XML_LRM_ATTR_INTERVAL_MS); - const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); + PCMK_META_INTERVAL); + const char *op_rc = crm_element_value(xml_op, PCMK__XA_RC_CODE); int op_rc_i; pcmk__scan_min_int(op_rc, &op_rc_i, 0); @@ -2958,7 +3160,7 @@ resource_util(pcmk__output_t *out, va_list args) const char *fn = va_arg(args, const char *); char *dump_text = crm_strdup_printf("%s: %s utilization on %s:", - fn, rsc->id, pe__node_name(node)); + fn, rsc->id, pcmk__node_name(node)); g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text); out->list_item(out, NULL, "%s", dump_text); @@ -2974,95 +3176,169 @@ resource_util_xml(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); pcmk_node_t *node = va_arg(args, pcmk_node_t *); + const char *uname = node->details->uname; const char *fn = va_arg(args, const char *); - xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "utilization", - "resource", rsc->id, - "node", node->details->uname, - "function", fn, - NULL); + xmlNodePtr xml_node = NULL; + + xml_node = pcmk__output_create_xml_node(out, PCMK_XE_UTILIZATION, + PCMK_XA_RESOURCE, rsc->id, + PCMK_XA_NODE, uname, + PCMK_XA_FUNCTION, fn, + NULL); g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node); return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *") +PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool") static int -ticket_html(pcmk__output_t *out, va_list args) { +ticket_default(pcmk__output_t *out, va_list args) { pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *); + bool raw = va_arg(args, int); + bool details = va_arg(args, int); - if (ticket->last_granted > -1) { - char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0); + GString *detail_str = NULL; - out->list_item(out, NULL, "%s:\t%s%s %s=\"%s\"", ticket->id, - ticket->granted ? "granted" : "revoked", - ticket->standby ? " [standby]" : "", - "last-granted", pcmk__s(epoch_str, "")); - free(epoch_str); - } else { - out->list_item(out, NULL, "%s:\t%s%s", ticket->id, - ticket->granted ? "granted" : "revoked", - ticket->standby ? " [standby]" : ""); + if (raw) { + out->list_item(out, ticket->id, "%s", ticket->id); + return pcmk_rc_ok; } - return pcmk_rc_ok; -} + if (details && g_hash_table_size(ticket->state) > 0) { + GHashTableIter iter; + const char *name = NULL; + const char *value = NULL; + bool already_added = false; -PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *") -static int -ticket_text(pcmk__output_t *out, va_list args) { - pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *); + detail_str = g_string_sized_new(100); + pcmk__g_strcat(detail_str, "\t(", NULL); - if (ticket->last_granted > -1) { - char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0); + g_hash_table_iter_init(&iter, ticket->state); + while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) { + if (already_added) { + g_string_append_printf(detail_str, ", %s=", name); + } else { + g_string_append_printf(detail_str, "%s=", name); + already_added = true; + } - out->list_item(out, ticket->id, "%s%s %s=\"%s\"", - ticket->granted ? "granted" : "revoked", - ticket->standby ? " [standby]" : "", - "last-granted", pcmk__s(epoch_str, "")); - free(epoch_str); + if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, "expires", NULL)) { + char *epoch_str = NULL; + long long time_ll; + + pcmk__scan_ll(value, &time_ll, 0); + epoch_str = pcmk__epoch2str((const time_t *) &time_ll, 0); + pcmk__g_strcat(detail_str, epoch_str, NULL); + free(epoch_str); + } else { + pcmk__g_strcat(detail_str, value, NULL); + } + } + + pcmk__g_strcat(detail_str, ")", NULL); + } + + if (ticket->last_granted > -1) { + /* Prior to the introduction of the details & raw arguments to this + * function, last-granted would always be added in this block. We need + * to preserve that behavior. At the same time, we also need to preserve + * the existing behavior from crm_ticket, which would include last-granted + * as part of the (...) detail string. + * + * Luckily we can check detail_str - if it's NULL, either there were no + * details, or we are preserving the previous behavior of this function. + * If it's not NULL, we are either preserving the previous behavior of + * crm_ticket or we were given details=true as an argument. + */ + if (detail_str == NULL) { + char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0); + + out->list_item(out, NULL, "%s\t%s%s last-granted=\"%s\"", + ticket->id, + (ticket->granted? "granted" : "revoked"), + (ticket->standby? " [standby]" : ""), + pcmk__s(epoch_str, "")); + free(epoch_str); + } else { + out->list_item(out, NULL, "%s\t%s%s %s", + ticket->id, + (ticket->granted? "granted" : "revoked"), + (ticket->standby? " [standby]" : ""), + detail_str->str); + } } else { - out->list_item(out, ticket->id, "%s%s", + out->list_item(out, NULL, "%s\t%s%s%s", ticket->id, ticket->granted ? "granted" : "revoked", - ticket->standby ? " [standby]" : ""); + ticket->standby ? " [standby]" : "", + detail_str != NULL ? detail_str->str : ""); + } + + if (detail_str != NULL) { + g_string_free(detail_str, TRUE); } return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *") +PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool") static int ticket_xml(pcmk__output_t *out, va_list args) { pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *); + bool raw G_GNUC_UNUSED = va_arg(args, int); + bool details G_GNUC_UNUSED = va_arg(args, int); + + const char *status = NULL; + const char *standby = pcmk__btoa(ticket->standby); xmlNodePtr node = NULL; + GHashTableIter iter; + const char *name = NULL; + const char *value = NULL; - node = pcmk__output_create_xml_node(out, "ticket", - "id", ticket->id, - "status", ticket->granted ? "granted" : "revoked", - "standby", pcmk__btoa(ticket->standby), + status = ticket->granted? PCMK_VALUE_GRANTED : PCMK_VALUE_REVOKED; + + node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET, + PCMK_XA_ID, ticket->id, + PCMK_XA_STATUS, status, + PCMK_XA_STANDBY, standby, NULL); if (ticket->last_granted > -1) { char *buf = pcmk__epoch2str(&ticket->last_granted, 0); - crm_xml_add(node, "last-granted", buf); + crm_xml_add(node, PCMK_XA_LAST_GRANTED, buf); free(buf); } + g_hash_table_iter_init(&iter, ticket->state); + while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) { + /* PCMK_XA_LAST_GRANTED and "expires" are already added by the check + * for ticket->last_granted above. + */ + if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, PCMK_XA_EXPIRES, + NULL)) { + continue; + } + + crm_xml_add(node, name, value); + } + return pcmk_rc_ok; } -PCMK__OUTPUT_ARGS("ticket-list", "pcmk_scheduler_t *", "bool") +PCMK__OUTPUT_ARGS("ticket-list", "GHashTable *", "bool", "bool", "bool") static int ticket_list(pcmk__output_t *out, va_list args) { - pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *); + GHashTable *tickets = va_arg(args, GHashTable *); bool print_spacer = va_arg(args, int); + bool raw = va_arg(args, int); + bool details = va_arg(args, int); GHashTableIter iter; - gpointer key, value; + gpointer value; - if (g_hash_table_size(scheduler->tickets) == 0) { + if (g_hash_table_size(tickets) == 0) { return pcmk_rc_no_output; } @@ -3072,10 +3348,10 @@ ticket_list(pcmk__output_t *out, va_list args) { out->begin_list(out, NULL, NULL, "Tickets"); /* Print each ticket */ - g_hash_table_iter_init(&iter, scheduler->tickets); - while (g_hash_table_iter_next(&iter, &key, &value)) { + g_hash_table_iter_init(&iter, tickets); + while (g_hash_table_iter_next(&iter, NULL, &value)) { pcmk_ticket_t *ticket = (pcmk_ticket_t *) value; - out->message(out, "ticket", ticket); + out->message(out, "ticket", ticket, raw, details); } /* Close section */ @@ -3150,8 +3426,7 @@ static pcmk__message_entry_t fmt_functions[] = { { "resource-operation-list", "default", resource_operation_list }, { "resource-util", "default", resource_util }, { "resource-util", "xml", resource_util_xml }, - { "ticket", "default", ticket_text }, - { "ticket", "html", ticket_html }, + { "ticket", "default", ticket_default }, { "ticket", "xml", ticket_xml }, { "ticket-list", "default", ticket_list }, diff --git a/lib/pengine/pe_status_private.h b/lib/pengine/pe_status_private.h index bb0ee4e..309f0b7 100644 --- a/lib/pengine/pe_status_private.h +++ b/lib/pengine/pe_status_private.h @@ -114,7 +114,7 @@ G_GNUC_INTERNAL gboolean unpack_status(xmlNode *status, pcmk_scheduler_t *scheduler); G_GNUC_INTERNAL -op_digest_cache_t *pe__compare_fencing_digest(pcmk_resource_t *rsc, +pcmk__op_digest_t *pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler); diff --git a/lib/pengine/remote.c b/lib/pengine/remote.c index 6b5058c..983ce80 100644 --- a/lib/pengine/remote.c +++ b/lib/pengine/remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the Pacemaker project contributors + * Copyright 2013-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,48 +8,11 @@ */ #include <crm_internal.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/scheduler_internal.h> #include <crm/pengine/internal.h> #include <glib.h> -bool -pe__resource_is_remote_conn(const pcmk_resource_t *rsc) -{ - return (rsc != NULL) && rsc->is_remote_node - && pe__is_remote_node(pe_find_node(rsc->cluster->nodes, rsc->id)); -} - -bool -pe__is_remote_node(const pcmk_node_t *node) -{ - return (node != NULL) && (node->details->type == pcmk_node_variant_remote) - && ((node->details->remote_rsc == NULL) - || (node->details->remote_rsc->container == NULL)); -} - -bool -pe__is_guest_node(const pcmk_node_t *node) -{ - return (node != NULL) && (node->details->type == pcmk_node_variant_remote) - && (node->details->remote_rsc != NULL) - && (node->details->remote_rsc->container != NULL); -} - -bool -pe__is_guest_or_remote_node(const pcmk_node_t *node) -{ - return (node != NULL) && (node->details->type == pcmk_node_variant_remote); -} - -bool -pe__is_bundle_node(const pcmk_node_t *node) -{ - return pe__is_guest_node(node) - && pe_rsc_is_bundled(node->details->remote_rsc); -} - /*! * \internal * \brief Check whether a resource creates a guest node @@ -89,17 +52,17 @@ xml_contains_remote_node(xmlNode *xml) return false; } - value = crm_element_value(xml, XML_ATTR_TYPE); + value = crm_element_value(xml, PCMK_XA_TYPE); if (!pcmk__str_eq(value, "remote", pcmk__str_casei)) { return false; } - value = crm_element_value(xml, XML_AGENT_ATTR_CLASS); + value = crm_element_value(xml, PCMK_XA_CLASS); if (!pcmk__str_eq(value, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) { return false; } - value = crm_element_value(xml, XML_AGENT_ATTR_PROVIDER); + value = crm_element_value(xml, PCMK_XA_PROVIDER); if (!pcmk__str_eq(value, "pacemaker", pcmk__str_casei)) { return false; } @@ -132,7 +95,7 @@ pe_foreach_guest_node(const pcmk_scheduler_t *scheduler, pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; if (rsc->is_remote_node && (rsc->container != NULL)) { - pcmk_node_t *guest_node = pe_find_node(scheduler->nodes, rsc->id); + pcmk_node_t *guest_node = pcmk_find_node(scheduler, rsc->id); if (guest_node) { (*helper)(guest_node, user_data); @@ -145,14 +108,16 @@ pe_foreach_guest_node(const pcmk_scheduler_t *scheduler, * \internal * \brief Create CIB XML for an implicit remote connection * - * \param[in,out] parent If not NULL, use as parent XML element - * \param[in] uname Name of Pacemaker Remote node - * \param[in] container If not NULL, use this as connection container - * \param[in] migrateable If not NULL, use as allow-migrate value - * \param[in] is_managed If not NULL, use as is-managed value - * \param[in] start_timeout If not NULL, use as remote connect timeout - * \param[in] server If not NULL, use as remote server value - * \param[in] port If not NULL, use as remote port value + * \param[in,out] parent If not \c NULL, use as parent XML element + * \param[in] uname Name of Pacemaker Remote node + * \param[in] container_id If not \c NULL, use this as connection container + * \param[in] migrateable If not \c NULL, use as remote + * \c PCMK_META_ALLOW_MIGRATE value + * \param[in] is_managed If not \c NULL, use as remote + * \c PCMK_META_IS_MANAGED value + * \param[in] start_timeout If not \c NULL, use as remote connect timeout + * \param[in] server If not \c NULL, use as \c PCMK_REMOTE_RA_ADDR + * \param[in] port If not \c NULL, use as \c PCMK_REMOTE_RA_PORT * * \return Newly created XML */ @@ -165,46 +130,45 @@ pe_create_remote_xml(xmlNode *parent, const char *uname, xmlNode *remote; xmlNode *xml_sub; - remote = create_xml_node(parent, XML_CIB_TAG_RESOURCE); + remote = pcmk__xe_create(parent, PCMK_XE_PRIMITIVE); // Add identity - crm_xml_add(remote, XML_ATTR_ID, uname); - crm_xml_add(remote, XML_AGENT_ATTR_CLASS, PCMK_RESOURCE_CLASS_OCF); - crm_xml_add(remote, XML_AGENT_ATTR_PROVIDER, "pacemaker"); - crm_xml_add(remote, XML_ATTR_TYPE, "remote"); + crm_xml_add(remote, PCMK_XA_ID, uname); + crm_xml_add(remote, PCMK_XA_CLASS, PCMK_RESOURCE_CLASS_OCF); + crm_xml_add(remote, PCMK_XA_PROVIDER, "pacemaker"); + crm_xml_add(remote, PCMK_XA_TYPE, "remote"); // Add meta-attributes - xml_sub = create_xml_node(remote, XML_TAG_META_SETS); - crm_xml_set_id(xml_sub, "%s-%s", uname, XML_TAG_META_SETS); + xml_sub = pcmk__xe_create(remote, PCMK_XE_META_ATTRIBUTES); + crm_xml_set_id(xml_sub, "%s-%s", uname, PCMK_XE_META_ATTRIBUTES); crm_create_nvpair_xml(xml_sub, NULL, - XML_RSC_ATTR_INTERNAL_RSC, XML_BOOLEAN_TRUE); + PCMK__META_INTERNAL_RSC, PCMK_VALUE_TRUE); if (container_id) { crm_create_nvpair_xml(xml_sub, NULL, - XML_RSC_ATTR_CONTAINER, container_id); + PCMK__META_CONTAINER, container_id); } if (migrateable) { crm_create_nvpair_xml(xml_sub, NULL, - XML_OP_ATTR_ALLOW_MIGRATE, migrateable); + PCMK_META_ALLOW_MIGRATE, migrateable); } if (is_managed) { - crm_create_nvpair_xml(xml_sub, NULL, XML_RSC_ATTR_MANAGED, is_managed); + crm_create_nvpair_xml(xml_sub, NULL, PCMK_META_IS_MANAGED, is_managed); } // Add instance attributes if (port || server) { - xml_sub = create_xml_node(remote, XML_TAG_ATTR_SETS); - crm_xml_set_id(xml_sub, "%s-%s", uname, XML_TAG_ATTR_SETS); + xml_sub = pcmk__xe_create(remote, PCMK_XE_INSTANCE_ATTRIBUTES); + crm_xml_set_id(xml_sub, "%s-%s", uname, PCMK_XE_INSTANCE_ATTRIBUTES); if (server) { - crm_create_nvpair_xml(xml_sub, NULL, XML_RSC_ATTR_REMOTE_RA_ADDR, - server); + crm_create_nvpair_xml(xml_sub, NULL, PCMK_REMOTE_RA_ADDR, server); } if (port) { - crm_create_nvpair_xml(xml_sub, NULL, "port", port); + crm_create_nvpair_xml(xml_sub, NULL, PCMK_REMOTE_RA_PORT, port); } } // Add operations - xml_sub = create_xml_node(remote, "operations"); + xml_sub = pcmk__xe_create(remote, PCMK_XE_OPERATIONS); crm_create_op_xml(xml_sub, uname, PCMK_ACTION_MONITOR, "30s", "30s"); if (start_timeout) { crm_create_op_xml(xml_sub, uname, PCMK_ACTION_START, "0", @@ -230,10 +194,10 @@ pe__add_param_check(const xmlNode *rsc_op, pcmk_resource_t *rsc, CRM_CHECK(scheduler && rsc_op && rsc && node, return); - check_op = calloc(1, sizeof(struct check_op)); - CRM_ASSERT(check_op != NULL); + check_op = pcmk__assert_alloc(1, sizeof(struct check_op)); - crm_trace("Deferring checks of %s until after allocation", ID(rsc_op)); + crm_trace("Deferring checks of %s until after allocation", + pcmk__xe_id(rsc_op)); check_op->rsc_op = rsc_op; check_op->rsc = rsc; check_op->node = node; diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c index 50f9f64..540bc0d 100644 --- a/lib/pengine/rules.c +++ b/lib/pengine/rules.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. * @@ -8,324 +8,70 @@ */ #include <crm_internal.h> -#include <crm/crm.h> -#include <crm/msg_xml.h> -#include <crm/common/xml.h> -#include <crm/common/xml_internal.h> #include <glib.h> +#include <crm/crm.h> +#include <crm/common/xml.h> #include <crm/pengine/rules.h> -#include <crm/pengine/rules_internal.h> + +#include <crm/common/iso8601_internal.h> +#include <crm/common/nvpair_internal.h> +#include <crm/common/rules_internal.h> +#include <crm/common/xml_internal.h> #include <crm/pengine/internal.h> +#include <crm/pengine/rules_internal.h> #include <sys/types.h> #include <regex.h> -#include <ctype.h> CRM_TRACE_INIT_DATA(pe_rules); /*! - * \brief Evaluate any rules contained by given XML element - * - * \param[in,out] xml XML element to check for rules - * \param[in] node_hash Node attributes to use to evaluate expressions - * \param[in] now Time to use when evaluating expressions - * \param[out] next_change If not NULL, set to when evaluation will change - * - * \return TRUE if no rules, or any of rules present is in effect, else FALSE - */ -gboolean -pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, - crm_time_t *next_change) -{ - pe_rule_eval_data_t rule_data = { - .node_hash = node_hash, - .role = pcmk_role_unknown, - .now = now, - .match_data = NULL, - .rsc_data = NULL, - .op_data = NULL - }; - - return pe_eval_rules(ruleset, &rule_data, next_change); -} - -gboolean -pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, - crm_time_t *now, crm_time_t *next_change, - pe_match_data_t *match_data) -{ - pe_rule_eval_data_t rule_data = { - .node_hash = node_hash, - .role = role, - .now = now, - .match_data = match_data, - .rsc_data = NULL, - .op_data = NULL - }; - - return pe_eval_expr(rule, &rule_data, next_change); -} - -/*! - * \brief Evaluate one rule subelement (pass/fail) - * - * A rule element may contain another rule, a node attribute expression, or a - * date expression. Given any one of those, evaluate it and return whether it - * passed. - * - * \param[in,out] expr Rule subelement XML - * \param[in] node_hash Node attributes to use when evaluating expression - * \param[in] role Resource role to use when evaluating expression - * \param[in] now Time to use when evaluating expression - * \param[out] next_change If not NULL, set to when evaluation will change - * \param[in] match_data If not NULL, resource back-references and params - * - * \return TRUE if expression is in effect under given conditions, else FALSE - */ -gboolean -pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, - crm_time_t *now, crm_time_t *next_change, - pe_match_data_t *match_data) -{ - pe_rule_eval_data_t rule_data = { - .node_hash = node_hash, - .role = role, - .now = now, - .match_data = match_data, - .rsc_data = NULL, - .op_data = NULL - }; - - return pe_eval_subexpr(expr, &rule_data, next_change); -} - -enum expression_type -find_expression_type(xmlNode * expr) -{ - const char *attr = NULL; - - attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); - - if (pcmk__xe_is(expr, PCMK_XE_DATE_EXPRESSION)) { - return time_expr; - - } else if (pcmk__xe_is(expr, PCMK_XE_RSC_EXPRESSION)) { - return rsc_expr; - - } else if (pcmk__xe_is(expr, PCMK_XE_OP_EXPRESSION)) { - return op_expr; - - } else if (pcmk__xe_is(expr, XML_TAG_RULE)) { - return nested_rule; - - } else if (!pcmk__xe_is(expr, XML_TAG_EXPRESSION)) { - return not_expr; - - } else if (pcmk__str_any_of(attr, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, NULL)) { - return loc_expr; - - } else if (pcmk__str_eq(attr, CRM_ATTR_ROLE, pcmk__str_none)) { - return role_expr; - } - - return attr_expr; -} - -/* As per the nethack rules: - * - * moon period = 29.53058 days ~= 30, year = 365.2422 days - * days moon phase advances on first day of year compared to preceding year - * = 365.2422 - 12*29.53058 ~= 11 - * years in Metonic cycle (time until same phases fall on the same days of - * the month) = 18.6 ~= 19 - * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 - * (29 as initial condition) - * current phase in days = first day phase + days elapsed in year - * 6 moons ~= 177 days - * 177 ~= 8 reported phases * 22 - * + 11/22 for rounding + * \internal + * \brief Map pe_rule_eval_data_t to pcmk_rule_input_t * - * 0-7, with 0: new, 4: full + * \param[out] new New data struct + * \param[in] old Old data struct */ - -static int -phase_of_the_moon(const crm_time_t *now) -{ - uint32_t epact, diy, goldn; - uint32_t y; - - crm_time_get_ordinal(now, &y, &diy); - - goldn = (y % 19) + 1; - epact = (11 * goldn + 18) % 30; - if ((epact == 25 && goldn > 11) || epact == 24) - epact++; - - return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); -} - -static int -check_one(const xmlNode *cron_spec, const char *xml_field, uint32_t time_field) -{ - int rc = pcmk_rc_undetermined; - const char *value = crm_element_value(cron_spec, xml_field); - long long low, high; - - if (value == NULL) { - /* Return pe_date_result_undetermined if the field is missing. */ - goto bail; - } - - if (pcmk__parse_ll_range(value, &low, &high) != pcmk_rc_ok) { - goto bail; - } else if (low == high) { - /* A single number was given, not a range. */ - if (time_field < low) { - rc = pcmk_rc_before_range; - } else if (time_field > high) { - rc = pcmk_rc_after_range; - } else { - rc = pcmk_rc_within_range; - } - } else if (low != -1 && high != -1) { - /* This is a range with both bounds. */ - if (time_field < low) { - rc = pcmk_rc_before_range; - } else if (time_field > high) { - rc = pcmk_rc_after_range; - } else { - rc = pcmk_rc_within_range; - } - } else if (low == -1) { - /* This is a range with no starting value. */ - rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range; - } else if (high == -1) { - /* This is a range with no ending value. */ - rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range; - } - -bail: - if (rc == pcmk_rc_within_range) { - crm_debug("Condition '%s' in %s: passed", value, xml_field); - } else { - crm_debug("Condition '%s' in %s: failed", value, xml_field); - } - - return rc; -} - -static gboolean -check_passes(int rc) { - /* _within_range is obvious. _undetermined is a pass because - * this is the return value if a field is not given. In this - * case, we just want to ignore it and check other fields to - * see if they place some restriction on what can pass. - */ - return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined; -} - -#define CHECK_ONE(spec, name, var) do { \ - int subpart_rc = check_one(spec, name, var); \ - if (check_passes(subpart_rc) == FALSE) { \ - return subpart_rc; \ - } \ -} while (0) - -int -pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec) -{ - uint32_t h, m, s, y, d, w; - - CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied); - - crm_time_get_gregorian(now, &y, &m, &d); - CHECK_ONE(cron_spec, "years", y); - CHECK_ONE(cron_spec, "months", m); - CHECK_ONE(cron_spec, "monthdays", d); - - crm_time_get_timeofday(now, &h, &m, &s); - CHECK_ONE(cron_spec, "hours", h); - CHECK_ONE(cron_spec, "minutes", m); - CHECK_ONE(cron_spec, "seconds", s); - - crm_time_get_ordinal(now, &y, &d); - CHECK_ONE(cron_spec, "yeardays", d); - - crm_time_get_isoweek(now, &y, &w, &d); - CHECK_ONE(cron_spec, "weekyears", y); - CHECK_ONE(cron_spec, "weeks", w); - CHECK_ONE(cron_spec, "weekdays", d); - - CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now)); - if (crm_element_value(cron_spec, "moon") != NULL) { - pcmk__config_warn("Support for 'moon' in date_spec elements " - "(such as %s) is deprecated and will be removed " - "in a future release of Pacemaker", ID(cron_spec)); - } - - /* If we get here, either no fields were specified (which is success), or all - * the fields that were specified had their conditions met (which is also a - * success). Thus, the result is success. - */ - return pcmk_rc_ok; -} - static void -update_field(crm_time_t *t, const xmlNode *xml, const char *attr, - void (*time_fn)(crm_time_t *, int)) +map_rule_input(pcmk_rule_input_t *new, const pe_rule_eval_data_t *old) { - long long value; - - if ((pcmk__scan_ll(crm_element_value(xml, attr), &value, 0LL) == pcmk_rc_ok) - && (value != 0LL) && (value >= INT_MIN) && (value <= INT_MAX)) { - time_fn(t, (int) value); + if (old == NULL) { + return; } -} - -static crm_time_t * -parse_xml_duration(const crm_time_t *start, const xmlNode *duration_spec) -{ - crm_time_t *end = pcmk_copy_time(start); - - update_field(end, duration_spec, "years", crm_time_add_years); - update_field(end, duration_spec, "months", crm_time_add_months); - update_field(end, duration_spec, "weeks", crm_time_add_weeks); - update_field(end, duration_spec, "days", crm_time_add_days); - update_field(end, duration_spec, "hours", crm_time_add_hours); - update_field(end, duration_spec, "minutes", crm_time_add_minutes); - update_field(end, duration_spec, "seconds", crm_time_add_seconds); - - return end; -} - -// Set next_change to t if t is earlier -static void -crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t) -{ - if ((next_change != NULL) && (t != NULL)) { - if (!crm_time_is_defined(next_change) - || (crm_time_compare(t, next_change) < 0)) { - crm_time_set(next_change, t); + new->now = old->now; + new->node_attrs = old->node_hash; + if (old->rsc_data != NULL) { + new->rsc_standard = old->rsc_data->standard; + new->rsc_provider = old->rsc_data->provider; + new->rsc_agent = old->rsc_data->agent; + } + if (old->match_data != NULL) { + new->rsc_params = old->match_data->params; + new->rsc_meta = old->match_data->meta; + if (old->match_data->re != NULL) { + new->rsc_id = old->match_data->re->string; + new->rsc_id_submatches = old->match_data->re->pmatch; + new->rsc_id_nmatches = old->match_data->re->nregs; } } + if (old->op_data != NULL) { + new->op_name = old->op_data->op_name; + new->op_interval_ms = old->op_data->interval; + } } -// Information about a block of nvpair elements -typedef struct sorted_set_s { - int score; // This block's score for sorting - const char *name; // This block's ID - const char *special_name; // ID that should sort first - xmlNode *attr_set; // This block - gboolean overwrite; // Whether existing values will be overwritten -} sorted_set_t; - static gint -sort_pairs(gconstpointer a, gconstpointer b) +sort_pairs(gconstpointer a, gconstpointer b, gpointer user_data) { - const sorted_set_t *pair_a = a; - const sorted_set_t *pair_b = b; + const xmlNode *pair_a = a; + const xmlNode *pair_b = b; + pcmk__nvpair_unpack_t *unpack_data = user_data; + + const char *score = NULL; + int score_a = 0; + int score_b = 0; if (a == NULL && b == NULL) { return 0; @@ -335,27 +81,35 @@ sort_pairs(gconstpointer a, gconstpointer b) return -1; } - if (pcmk__str_eq(pair_a->name, pair_a->special_name, pcmk__str_casei)) { + if (pcmk__str_eq(pcmk__xe_id(pair_a), unpack_data->first_id, + pcmk__str_none)) { return -1; - } else if (pcmk__str_eq(pair_b->name, pair_a->special_name, pcmk__str_casei)) { + } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id, + pcmk__str_none)) { return 1; } + score = crm_element_value(pair_a, PCMK_XA_SCORE); + score_a = char2score(score); + + score = crm_element_value(pair_b, PCMK_XA_SCORE); + score_b = char2score(score); + /* If we're overwriting values, we want lowest score first, so the highest * score is processed last; if we're not overwriting values, we want highest * score first, so nothing else overwrites it. */ - if (pair_a->score < pair_b->score) { - return pair_a->overwrite? -1 : 1; - } else if (pair_a->score > pair_b->score) { - return pair_a->overwrite? 1 : -1; + if (score_a < score_b) { + return unpack_data->overwrite? -1 : 1; + } else if (score_a > score_b) { + return unpack_data->overwrite? 1 : -1; } return 0; } static void -populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top) +populate_hash(xmlNode *nvpair_list, GHashTable *hash, bool overwrite) { const char *name = NULL; const char *value = NULL; @@ -363,24 +117,24 @@ populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlN xmlNode *list = nvpair_list; xmlNode *an_attr = NULL; - if (pcmk__xe_is(list->children, XML_TAG_ATTRS)) { + if (pcmk__xe_is(list->children, PCMK__XE_ATTRIBUTES)) { list = list->children; } - for (an_attr = pcmk__xe_first_child(list); an_attr != NULL; - an_attr = pcmk__xe_next(an_attr)) { + for (an_attr = pcmk__xe_first_child(list, NULL, NULL, NULL); + an_attr != NULL; an_attr = pcmk__xe_next(an_attr)) { - if (pcmk__str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, pcmk__str_none)) { - xmlNode *ref_nvpair = expand_idref(an_attr, top); + if (pcmk__xe_is(an_attr, PCMK_XE_NVPAIR)) { + xmlNode *ref_nvpair = expand_idref(an_attr, NULL); - name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME); - if (name == NULL) { - name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME); + name = crm_element_value(an_attr, PCMK_XA_NAME); + if ((name == NULL) && (ref_nvpair != NULL)) { + name = crm_element_value(ref_nvpair, PCMK_XA_NAME); } - value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE); - if (value == NULL) { - value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE); + value = crm_element_value(an_attr, PCMK_XA_VALUE); + if ((value == NULL) && (ref_nvpair != NULL)) { + value = crm_element_value(ref_nvpair, PCMK_XA_VALUE); } if (name == NULL || value == NULL) { @@ -390,6 +144,11 @@ populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlN old_value = g_hash_table_lookup(hash, name); if (pcmk__str_eq(value, "#default", pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting meta-attributes (such " + "as %s) to the explicit value '#default' is " + "deprecated and will be removed in a future " + "release", name); if (old_value) { crm_trace("Letting %s default (removing explicit value \"%s\")", name, value); @@ -399,95 +158,69 @@ populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlN } else if (old_value == NULL) { crm_trace("Setting %s=\"%s\"", name, value); - g_hash_table_insert(hash, strdup(name), strdup(value)); + pcmk__insert_dup(hash, name, value); } else if (overwrite) { crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")", name, value, old_value); - g_hash_table_replace(hash, strdup(name), strdup(value)); + pcmk__insert_dup(hash, name, value); } } } } -typedef struct unpack_data_s { - gboolean overwrite; - void *hash; - crm_time_t *next_change; - const pe_rule_eval_data_t *rule_data; - xmlNode *top; -} unpack_data_t; - static void unpack_attr_set(gpointer data, gpointer user_data) { - sorted_set_t *pair = data; - unpack_data_t *unpack_data = user_data; + xmlNode *pair = data; + pcmk__nvpair_unpack_t *unpack_data = user_data; - if (!pe_eval_rules(pair->attr_set, unpack_data->rule_data, - unpack_data->next_change)) { + if (pcmk__evaluate_rules(pair, &(unpack_data->rule_input), + unpack_data->next_change) != pcmk_rc_ok) { return; } - crm_trace("Adding attributes from %s (score %d) %s overwrite", - pair->name, pair->score, - (unpack_data->overwrite? "with" : "without")); - populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top); + crm_trace("Adding name/value pairs from %s %s overwrite", + pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without")); + populate_hash(pair, unpack_data->values, unpack_data->overwrite); } /*! * \internal * \brief Create a sorted list of nvpair blocks * - * \param[in,out] top XML document root (used to expand id-ref's) * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name If not NULL, only get blocks of this element - * \param[in] always_first If not NULL, sort block with this ID as first * - * \return List of sorted_set_t entries for nvpair blocks + * \return List of XML blocks of name/value pairs */ static GList * -make_pairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, - const char *always_first, gboolean overwrite) +make_pairs(const xmlNode *xml_obj, const char *set_name) { GList *unsorted = NULL; if (xml_obj == NULL) { return NULL; } - for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL; - attr_set = pcmk__xe_next(attr_set)) { + for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) { - if (pcmk__str_eq(set_name, (const char *) attr_set->name, - pcmk__str_null_matches)) { - const char *score = NULL; - sorted_set_t *pair = NULL; - xmlNode *expanded_attr_set = expand_idref(attr_set, top); + if ((set_name == NULL) || pcmk__xe_is(attr_set, set_name)) { + xmlNode *expanded_attr_set = expand_idref(attr_set, NULL); if (expanded_attr_set == NULL) { - // Schema (if not "none") prevents this - continue; + continue; // Not possible with schema validation enabled } - - pair = calloc(1, sizeof(sorted_set_t)); - pair->name = ID(expanded_attr_set); - pair->special_name = always_first; - pair->attr_set = expanded_attr_set; - pair->overwrite = overwrite; - - score = crm_element_value(expanded_attr_set, XML_RULE_ATTR_SCORE); - pair->score = char2score(score); - - unsorted = g_list_prepend(unsorted, pair); + unsorted = g_list_prepend(unsorted, expanded_attr_set); } } - return g_list_sort(unsorted, sort_pairs); + return unsorted; } /*! * \brief Extract nvpair blocks contained by an XML element into a hash table * - * \param[in,out] top XML document root (used to expand id-ref's) + * \param[in,out] top Ignored * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name If not NULL, only use blocks of this element * \param[in] rule_data Matching parameters to use when unpacking @@ -502,26 +235,28 @@ pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, const char *always_first, gboolean overwrite, crm_time_t *next_change) { - GList *pairs = make_pairs(top, xml_obj, set_name, always_first, overwrite); + GList *pairs = make_pairs(xml_obj, set_name); if (pairs) { - unpack_data_t data = { - .hash = hash, + pcmk__nvpair_unpack_t data = { + .values = hash, + .first_id = always_first, .overwrite = overwrite, .next_change = next_change, - .top = top, - .rule_data = rule_data }; + map_rule_input(&(data.rule_input), rule_data); + + pairs = g_list_sort_with_data(pairs, sort_pairs, &data); g_list_foreach(pairs, unpack_attr_set, &data); - g_list_free_full(pairs, free); + g_list_free(pairs); } } /*! * \brief Extract nvpair blocks contained by an XML element into a hash table * - * \param[in,out] top XML document root (used to expand id-ref's) + * \param[in,out] top Ignored * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name Element name to identify nvpair blocks * \param[in] node_hash Node attributes to use when evaluating rules @@ -539,709 +274,67 @@ pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, { pe_rule_eval_data_t rule_data = { .node_hash = node_hash, - .role = pcmk_role_unknown, .now = now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; - pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash, + pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash, always_first, overwrite, next_change); } -/*! - * \brief Expand any regular expression submatches (%0-%9) in a string - * - * \param[in] string String possibly containing submatch variables - * \param[in] match_data If not NULL, regular expression matches - * - * \return Newly allocated string identical to \p string with submatches - * expanded, or NULL if there were no matches - */ -char * -pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data) -{ - size_t len = 0; - int i; - const char *p, *last_match_index; - char *p_dst, *result = NULL; - - if (pcmk__str_empty(string) || !match_data) { - return NULL; - } - - p = last_match_index = string; - - while (*p) { - if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) { - i = *(p + 1) - '0'; - if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 && - match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) { - len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so); - last_match_index = p + 2; - } - p++; - } - p++; - } - len += p - last_match_index + 1; - - /* FIXME: Excessive? */ - if (len - 1 <= 0) { - return NULL; - } - - p_dst = result = calloc(1, len); - p = string; - - while (*p) { - if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) { - i = *(p + 1) - '0'; - if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 && - match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) { - /* rm_eo can be equal to rm_so, but then there is nothing to do */ - int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so; - memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len); - p_dst += match_len; - } - p++; - } else { - *(p_dst) = *(p); - p_dst++; - } - p++; - } +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START - return result; -} +#include <crm/pengine/rules_compat.h> -/*! - * \brief Evaluate rules - * - * \param[in,out] ruleset XML possibly containing rule sub-elements - * \param[in] rule_data - * \param[out] next_change If not NULL, set to when evaluation will change - * - * \return TRUE if there are no rules or - */ gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change) { - // If there are no rules, pass by default - gboolean ruleset_default = TRUE; - - for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE); - rule != NULL; rule = crm_next_same_xml(rule)) { - - ruleset_default = FALSE; - if (pe_eval_expr(rule, rule_data, next_change)) { - /* Only the deprecated "lifetime" element of location constraints - * may contain more than one rule at the top level -- the schema - * limits a block of nvpairs to a single top-level rule. So, this - * effectively means that a lifetime is active if any rule it - * contains is active. - */ - return TRUE; - } - } + pcmk_rule_input_t rule_input = { NULL, }; - return ruleset_default; + map_rule_input(&rule_input, rule_data); + return pcmk__evaluate_rules(ruleset, &rule_input, + next_change) == pcmk_rc_ok; } -/*! - * \brief Evaluate all of a rule's expressions - * - * \param[in,out] rule XML containing a rule definition or its id-ref - * \param[in] rule_data Matching parameters to check against rule - * \param[out] next_change If not NULL, set to when evaluation will change - * - * \return TRUE if \p rule_data passes \p rule, otherwise FALSE - */ gboolean -pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, - crm_time_t *next_change) -{ - xmlNode *expr = NULL; - gboolean test = TRUE; - gboolean empty = TRUE; - gboolean passed = TRUE; - gboolean do_and = TRUE; - const char *value = NULL; - - rule = expand_idref(rule, NULL); - value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP); - if (pcmk__str_eq(value, "or", pcmk__str_casei)) { - do_and = FALSE; - passed = FALSE; - } - - crm_trace("Testing rule %s", ID(rule)); - for (expr = pcmk__xe_first_child(rule); expr != NULL; - expr = pcmk__xe_next(expr)) { - - test = pe_eval_subexpr(expr, rule_data, next_change); - empty = FALSE; - - if (test && do_and == FALSE) { - crm_trace("Expression %s/%s passed", ID(rule), ID(expr)); - return TRUE; - - } else if (test == FALSE && do_and) { - crm_trace("Expression %s/%s failed", ID(rule), ID(expr)); - return FALSE; - } - } - - if (empty) { - crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule)); - } - - crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed"); - return passed; -} - -/*! - * \brief Evaluate a single rule expression, including any subexpressions - * - * \param[in,out] expr XML containing a rule expression - * \param[in] rule_data Matching parameters to check against expression - * \param[out] next_change If not NULL, set to when evaluation will change - * - * \return TRUE if \p rule_data passes \p expr, otherwise FALSE - */ -gboolean -pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, - crm_time_t *next_change) -{ - gboolean accept = FALSE; - const char *uname = NULL; - - switch (find_expression_type(expr)) { - case nested_rule: - accept = pe_eval_expr(expr, rule_data, next_change); - break; - case attr_expr: - case loc_expr: - /* these expressions can never succeed if there is - * no node to compare with - */ - if (rule_data->node_hash != NULL) { - accept = pe__eval_attr_expr(expr, rule_data); - } - break; - - case time_expr: - switch (pe__eval_date_expr(expr, rule_data, next_change)) { - case pcmk_rc_within_range: - case pcmk_rc_ok: - accept = TRUE; - break; - - default: - accept = FALSE; - break; - } - break; - - case role_expr: - accept = pe__eval_role_expr(expr, rule_data); - break; - - case rsc_expr: - accept = pe__eval_rsc_expr(expr, rule_data); - break; - - case op_expr: - accept = pe__eval_op_expr(expr, rule_data); - break; - - default: - CRM_CHECK(FALSE /* bad type */ , return FALSE); - accept = FALSE; - } - if (rule_data->node_hash) { - uname = g_hash_table_lookup(rule_data->node_hash, CRM_ATTR_UNAME); - } - - crm_trace("Expression %s %s on %s", - ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes"); - return accept; -} - -/*! - * \internal - * \brief Compare two values in a rule's node attribute expression - * - * \param[in] l_val Value on left-hand side of comparison - * \param[in] r_val Value on right-hand side of comparison - * \param[in] type How to interpret the values (allowed values: - * \c "string", \c "integer", \c "number", - * \c "version", \c NULL) - * \param[in] op Type of comparison - * - * \return -1 if <tt>(l_val < r_val)</tt>, - * 0 if <tt>(l_val == r_val)</tt>, - * 1 if <tt>(l_val > r_val)</tt> - */ -static int -compare_attr_expr_vals(const char *l_val, const char *r_val, const char *type, - const char *op) -{ - int cmp = 0; - - if (l_val != NULL && r_val != NULL) { - if (type == NULL) { - if (pcmk__strcase_any_of(op, "lt", "lte", "gt", "gte", NULL)) { - if (pcmk__char_in_any_str('.', l_val, r_val, NULL)) { - type = "number"; - } else { - type = "integer"; - } - - } else { - type = "string"; - } - crm_trace("Defaulting to %s based comparison for '%s' op", type, op); - } - - if (pcmk__str_eq(type, "string", pcmk__str_casei)) { - cmp = strcasecmp(l_val, r_val); - - } else if (pcmk__str_eq(type, "integer", pcmk__str_casei)) { - long long l_val_num; - int rc1 = pcmk__scan_ll(l_val, &l_val_num, 0LL); - - long long r_val_num; - int rc2 = pcmk__scan_ll(r_val, &r_val_num, 0LL); - - if ((rc1 == pcmk_rc_ok) && (rc2 == pcmk_rc_ok)) { - if (l_val_num < r_val_num) { - cmp = -1; - } else if (l_val_num > r_val_num) { - cmp = 1; - } else { - cmp = 0; - } - - } else { - crm_debug("Integer parse error. Comparing %s and %s as strings", - l_val, r_val); - cmp = compare_attr_expr_vals(l_val, r_val, "string", op); - } - - } else if (pcmk__str_eq(type, "number", pcmk__str_casei)) { - double l_val_num; - double r_val_num; - - int rc1 = pcmk__scan_double(l_val, &l_val_num, NULL, NULL); - int rc2 = pcmk__scan_double(r_val, &r_val_num, NULL, NULL); - - if (rc1 == pcmk_rc_ok && rc2 == pcmk_rc_ok) { - if (l_val_num < r_val_num) { - cmp = -1; - } else if (l_val_num > r_val_num) { - cmp = 1; - } else { - cmp = 0; - } - - } else { - crm_debug("Floating-point parse error. Comparing %s and %s as " - "strings", l_val, r_val); - cmp = compare_attr_expr_vals(l_val, r_val, "string", op); - } - - } else if (pcmk__str_eq(type, "version", pcmk__str_casei)) { - cmp = compare_version(l_val, r_val); - - } - - } else if (l_val == NULL && r_val == NULL) { - cmp = 0; - } else if (r_val == NULL) { - cmp = 1; - } else { // l_val == NULL && r_val != NULL - cmp = -1; - } - - return cmp; -} - -/*! - * \internal - * \brief Check whether an attribute expression evaluates to \c true - * - * \param[in] l_val Value on left-hand side of comparison - * \param[in] r_val Value on right-hand side of comparison - * \param[in] type How to interpret the values (allowed values: - * \c "string", \c "integer", \c "number", - * \c "version", \c NULL) - * \param[in] op Type of comparison. - * - * \return \c true if expression evaluates to \c true, \c false - * otherwise - */ -static bool -accept_attr_expr(const char *l_val, const char *r_val, const char *type, - const char *op) -{ - int cmp; - - if (pcmk__str_eq(op, "defined", pcmk__str_casei)) { - return (l_val != NULL); - - } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) { - return (l_val == NULL); - - } - - cmp = compare_attr_expr_vals(l_val, r_val, type, op); - - if (pcmk__str_eq(op, "eq", pcmk__str_casei)) { - return (cmp == 0); - - } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) { - return (cmp != 0); - - } else if (l_val == NULL || r_val == NULL) { - // The comparison is meaningless from this point on - return false; - - } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) { - return (cmp < 0); - - } else if (pcmk__str_eq(op, "lte", pcmk__str_casei)) { - return (cmp <= 0); - - } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) { - return (cmp > 0); - - } else if (pcmk__str_eq(op, "gte", pcmk__str_casei)) { - return (cmp >= 0); - } - - return false; // Should never reach this point -} - -/*! - * \internal - * \brief Get correct value according to value-source - * - * \param[in] value value given in rule expression - * \param[in] value_source value-source given in rule expressions - * \param[in] match_data If not NULL, resource back-references and params - */ -static const char * -expand_value_source(const char *value, const char *value_source, - const pe_match_data_t *match_data) -{ - GHashTable *table = NULL; - - if (pcmk__str_empty(value)) { - return NULL; // value_source is irrelevant - - } else if (pcmk__str_eq(value_source, "param", pcmk__str_casei)) { - table = match_data->params; - - } else if (pcmk__str_eq(value_source, "meta", pcmk__str_casei)) { - table = match_data->meta; - - } else { // literal - return value; - } - - if (table == NULL) { - return NULL; - } - return (const char *) g_hash_table_lookup(table, value); -} - -/*! - * \internal - * \brief Evaluate a node attribute expression based on #uname, #id, #kind, - * or a generic node attribute - * - * \param[in] expr XML of rule expression - * \param[in] rule_data The match_data and node_hash members are used - * - * \return TRUE if rule_data satisfies the expression, FALSE otherwise - */ -gboolean -pe__eval_attr_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data) -{ - gboolean attr_allocated = FALSE; - const char *h_val = NULL; - - const char *op = NULL; - const char *type = NULL; - const char *attr = NULL; - const char *value = NULL; - const char *value_source = NULL; - - attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); - op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); - value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); - type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); - value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE); - - if (attr == NULL) { - pe_err("Expression %s invalid: " XML_EXPR_ATTR_ATTRIBUTE - " not specified", pcmk__s(ID(expr), "without ID")); - return FALSE; - } else if (op == NULL) { - pe_err("Expression %s invalid: " XML_EXPR_ATTR_OPERATION - " not specified", pcmk__s(ID(expr), "without ID")); - } - - if (rule_data->match_data != NULL) { - // Expand any regular expression submatches (%0-%9) in attribute name - if (rule_data->match_data->re != NULL) { - char *resolved_attr = pe_expand_re_matches(attr, rule_data->match_data->re); - - if (resolved_attr != NULL) { - attr = (const char *) resolved_attr; - attr_allocated = TRUE; - } - } - - // Get value appropriate to value-source - value = expand_value_source(value, value_source, rule_data->match_data); - } - - if (rule_data->node_hash != NULL) { - h_val = (const char *)g_hash_table_lookup(rule_data->node_hash, attr); - } - - if (attr_allocated) { - free((char *)attr); - attr = NULL; - } - - return accept_attr_expr(h_val, value, type, op); -} - -/*! - * \internal - * \brief Evaluate a date_expression - * - * \param[in] expr XML of rule expression - * \param[in] rule_data Only the now member is used - * \param[out] next_change If not NULL, set to when evaluation will change - * - * \return Standard Pacemaker return code - */ -int -pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data, - crm_time_t *next_change) -{ - crm_time_t *start = NULL; - crm_time_t *end = NULL; - const char *value = NULL; - const char *op = crm_element_value(expr, "operation"); - - xmlNode *duration_spec = NULL; - xmlNode *date_spec = NULL; - - // "undetermined" will also be returned for parsing errors - int rc = pcmk_rc_undetermined; - - crm_trace("Testing expression: %s", ID(expr)); - - duration_spec = first_named_child(expr, "duration"); - date_spec = first_named_child(expr, "date_spec"); - - value = crm_element_value(expr, "start"); - if (value != NULL) { - start = crm_time_new(value); - } - value = crm_element_value(expr, "end"); - if (value != NULL) { - end = crm_time_new(value); - } - - if (start != NULL && end == NULL && duration_spec != NULL) { - end = parse_xml_duration(start, duration_spec); - } - - if (pcmk__str_eq(op, "in_range", pcmk__str_null_matches | pcmk__str_casei)) { - if ((start == NULL) && (end == NULL)) { - // in_range requires at least one of start or end - } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) { - rc = pcmk_rc_before_range; - crm_time_set_if_earlier(next_change, start); - } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) { - rc = pcmk_rc_after_range; - } else { - rc = pcmk_rc_within_range; - if (end && next_change) { - // Evaluation doesn't change until second after end - crm_time_add_seconds(end, 1); - crm_time_set_if_earlier(next_change, end); - } - } - - } else if (pcmk__str_eq(op, "date_spec", pcmk__str_casei)) { - rc = pe_cron_range_satisfied(rule_data->now, date_spec); - // @TODO set next_change appropriately - - } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) { - if (start == NULL) { - // gt requires start - } else if (crm_time_compare(rule_data->now, start) > 0) { - rc = pcmk_rc_within_range; - } else { - rc = pcmk_rc_before_range; - - // Evaluation doesn't change until second after start - crm_time_add_seconds(start, 1); - crm_time_set_if_earlier(next_change, start); - } - - } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) { - if (end == NULL) { - // lt requires end - } else if (crm_time_compare(rule_data->now, end) < 0) { - rc = pcmk_rc_within_range; - crm_time_set_if_earlier(next_change, end); - } else { - rc = pcmk_rc_after_range; - } - } - - crm_time_free(start); - crm_time_free(end); - return rc; -} - -gboolean -pe__eval_op_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data) +pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, + crm_time_t *next_change) { - const char *name = crm_element_value(expr, XML_NVPAIR_ATTR_NAME); - const char *interval_s = crm_element_value(expr, XML_LRM_ATTR_INTERVAL); - guint interval; - - crm_trace("Testing op_defaults expression: %s", ID(expr)); - - if (rule_data->op_data == NULL) { - crm_trace("No operations data provided"); - return FALSE; - } - - interval = crm_parse_interval_spec(interval_s); - if (interval == 0 && errno != 0) { - crm_trace("Could not parse interval: %s", interval_s); - return FALSE; - } - - if (interval_s != NULL && interval != rule_data->op_data->interval) { - crm_trace("Interval doesn't match: %d != %d", interval, rule_data->op_data->interval); - return FALSE; - } - - if (!pcmk__str_eq(name, rule_data->op_data->op_name, pcmk__str_none)) { - crm_trace("Name doesn't match: %s != %s", name, rule_data->op_data->op_name); - return FALSE; - } + pcmk_rule_input_t rule_input = { + .node_attrs = node_hash, + .now = now, + }; - return TRUE; + return pcmk__evaluate_rules(ruleset, &rule_input, next_change); } -/*! - * \internal - * \brief Evaluate a node attribute expression based on #role - * - * \param[in] expr XML of rule expression - * \param[in] rule_data Only the role member is used - * - * \return TRUE if rule_data->role satisfies the expression, FALSE otherwise - */ gboolean -pe__eval_role_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data) +pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, + crm_time_t *now, crm_time_t *next_change, + pe_match_data_t *match_data) { - gboolean accept = FALSE; - const char *op = NULL; - const char *value = NULL; - - if (rule_data->role == pcmk_role_unknown) { - return accept; - } - - value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); - op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); - - if (pcmk__str_eq(op, "defined", pcmk__str_casei)) { - if (rule_data->role > pcmk_role_started) { - accept = TRUE; - } - - } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) { - if ((rule_data->role > pcmk_role_unknown) - && (rule_data->role < pcmk_role_unpromoted)) { - accept = TRUE; - } - - } else if (pcmk__str_eq(op, "eq", pcmk__str_casei)) { - if (text2role(value) == rule_data->role) { - accept = TRUE; - } - - } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) { - // Test "ne" only with promotable clone roles - if ((rule_data->role > pcmk_role_unknown) - && (rule_data->role < pcmk_role_unpromoted)) { - accept = FALSE; + pcmk_rule_input_t rule_input = { + .node_attrs = node_hash, + .now = now, + }; - } else if (text2role(value) != rule_data->role) { - accept = TRUE; + if (match_data != NULL) { + rule_input.rsc_params = match_data->params; + rule_input.rsc_meta = match_data->meta; + if (match_data->re != NULL) { + rule_input.rsc_id = match_data->re->string; + rule_input.rsc_id_submatches = match_data->re->pmatch; + rule_input.rsc_id_nmatches = match_data->re->nregs; } } - return accept; + return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok; } gboolean -pe__eval_rsc_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data) -{ - const char *class = crm_element_value(expr, XML_AGENT_ATTR_CLASS); - const char *provider = crm_element_value(expr, XML_AGENT_ATTR_PROVIDER); - const char *type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); - - crm_trace("Testing rsc_defaults expression: %s", ID(expr)); - - if (rule_data->rsc_data == NULL) { - crm_trace("No resource data provided"); - return FALSE; - } - - if (class != NULL && - !pcmk__str_eq(class, rule_data->rsc_data->standard, pcmk__str_none)) { - crm_trace("Class doesn't match: %s != %s", class, rule_data->rsc_data->standard); - return FALSE; - } - - if ((provider == NULL && rule_data->rsc_data->provider != NULL) || - (provider != NULL && rule_data->rsc_data->provider == NULL) || - !pcmk__str_eq(provider, rule_data->rsc_data->provider, pcmk__str_none)) { - crm_trace("Provider doesn't match: %s != %s", provider, rule_data->rsc_data->provider); - return FALSE; - } - - if (type != NULL && - !pcmk__str_eq(type, rule_data->rsc_data->agent, pcmk__str_none)) { - crm_trace("Agent doesn't match: %s != %s", type, rule_data->rsc_data->agent); - return FALSE; - } - - return TRUE; -} - -// Deprecated functions kept only for backward API compatibility -// LCOV_EXCL_START - -#include <crm/pengine/rules_compat.h> - -gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now) { return pe_evaluate_rules(ruleset, node_hash, now, NULL); @@ -1272,6 +365,29 @@ pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, } gboolean +pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, + crm_time_t *now, crm_time_t *next_change, + pe_match_data_t *match_data) +{ + pcmk_rule_input_t rule_input = { + .now = now, + .node_attrs = node_hash, + }; + + if (match_data != NULL) { + rule_input.rsc_params = match_data->params; + rule_input.rsc_meta = match_data->meta; + if (match_data->re != NULL) { + rule_input.rsc_id = match_data->re->string; + rule_input.rsc_id_submatches = match_data->re->pmatch; + rule_input.rsc_id_nmatches = match_data->re->nregs; + } + } + return pcmk__evaluate_condition(expr, &rule_input, + next_change) == pcmk_rc_ok; +} + +gboolean test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now) { return pe_test_expression(expr, node_hash, role, now, NULL, NULL); @@ -1296,6 +412,27 @@ pe_test_expression_full(xmlNode *expr, GHashTable *node_hash, return pe_test_expression(expr, node_hash, role, now, NULL, match_data); } +gboolean +pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change) +{ + pcmk_rule_input_t rule_input = { NULL, }; + + map_rule_input(&rule_input, rule_data); + return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok; +} + +gboolean +pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change) +{ + pcmk_rule_input_t rule_input = { NULL, }; + + map_rule_input(&rule_input, rule_data); + return pcmk__evaluate_condition(expr, &rule_input, + next_change) == pcmk_rc_ok; +} + void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, @@ -1304,16 +441,31 @@ unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name, { pe_rule_eval_data_t rule_data = { .node_hash = node_hash, - .role = pcmk_role_unknown, .now = now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; - pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash, always_first, + pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash, always_first, overwrite, NULL); } +enum expression_type +find_expression_type(xmlNode *expr) +{ + return pcmk__condition_type(expr); +} + +char * +pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data) +{ + if (match_data == NULL) { + return NULL; + } + return pcmk__replace_submatches(string, match_data->string, + match_data->pmatch, match_data->nregs); +} + // LCOV_EXCL_STOP // End deprecated API diff --git a/lib/pengine/rules_alerts.c b/lib/pengine/rules_alerts.c index 9eed7ff..73351c7 100644 --- a/lib/pengine/rules_alerts.c +++ b/lib/pengine/rules_alerts.c @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 the Pacemaker project contributors + * Copyright 2015-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,7 +9,7 @@ #include <crm_internal.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/rules.h> #include <crm/common/alerts_internal.h> #include <crm/common/xml_internal.h> @@ -34,8 +34,8 @@ get_meta_attrs_from_cib(xmlNode *basenode, pcmk__alert_t *entry, const char *value = NULL; int rc = pcmk_rc_ok; - pe_unpack_nvpairs(basenode, basenode, XML_TAG_META_SETS, NULL, config_hash, - NULL, FALSE, now, NULL); + pe_unpack_nvpairs(basenode, basenode, PCMK_XE_META_ATTRIBUTES, NULL, + config_hash, NULL, FALSE, now, NULL); crm_time_free(now); value = g_hash_table_lookup(config_hash, PCMK_META_ENABLED); @@ -45,16 +45,20 @@ get_meta_attrs_from_cib(xmlNode *basenode, pcmk__alert_t *entry, goto done; } - value = g_hash_table_lookup(config_hash, XML_ALERT_ATTR_TIMEOUT); + value = g_hash_table_lookup(config_hash, PCMK_META_TIMEOUT); if (value) { - entry->timeout = crm_get_msec(value); + long long timeout_ms = crm_get_msec(value); + + entry->timeout = (int) QB_MIN(timeout_ms, INT_MAX); if (entry->timeout <= 0) { if (entry->timeout == 0) { crm_trace("Alert %s uses default timeout of %dmsec", entry->id, PCMK__ALERT_DEFAULT_TIMEOUT_MS); } else { - crm_warn("Alert %s has invalid timeout value '%s', using default %dmsec", - entry->id, (char*)value, PCMK__ALERT_DEFAULT_TIMEOUT_MS); + pcmk__config_warn("Alert %s has invalid timeout value '%s', " + "using default (%d ms)", + entry->id, value, + PCMK__ALERT_DEFAULT_TIMEOUT_MS); } entry->timeout = PCMK__ALERT_DEFAULT_TIMEOUT_MS; } else { @@ -65,7 +69,7 @@ get_meta_attrs_from_cib(xmlNode *basenode, pcmk__alert_t *entry, *max_timeout = entry->timeout; } } - value = g_hash_table_lookup(config_hash, XML_ALERT_ATTR_TSTAMP_FORMAT); + value = g_hash_table_lookup(config_hash, PCMK_META_TIMESTAMP_FORMAT); if (value) { /* hard to do any checks here as merely anything can * can be a valid time-format-string @@ -89,7 +93,8 @@ get_envvars_from_cib(xmlNode *basenode, pcmk__alert_t *entry) return; } - child = first_named_child(basenode, XML_TAG_ATTR_SETS); + child = pcmk__xe_first_child(basenode, PCMK_XE_INSTANCE_ATTRIBUTES, NULL, + NULL); if (child == NULL) { return; } @@ -98,16 +103,16 @@ get_envvars_from_cib(xmlNode *basenode, pcmk__alert_t *entry) entry->envvars = pcmk__strkey_table(free, free); } - for (child = first_named_child(child, XML_CIB_TAG_NVPAIR); child != NULL; - child = crm_next_same_xml(child)) { + for (child = pcmk__xe_first_child(child, PCMK_XE_NVPAIR, NULL, NULL); + child != NULL; child = pcmk__xe_next_same(child)) { - const char *name = crm_element_value(child, XML_NVPAIR_ATTR_NAME); - const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE); + const char *name = crm_element_value(child, PCMK_XA_NAME); + const char *value = crm_element_value(child, PCMK_XA_VALUE); if (value == NULL) { value = ""; } - g_hash_table_insert(entry->envvars, strdup(name), strdup(value)); + pcmk__insert_dup(entry->envvars, name, value); crm_trace("Alert %s: added environment variable %s='%s'", entry->id, name, value); } @@ -116,33 +121,34 @@ get_envvars_from_cib(xmlNode *basenode, pcmk__alert_t *entry) static void unpack_alert_filter(xmlNode *basenode, pcmk__alert_t *entry) { - xmlNode *select = first_named_child(basenode, XML_CIB_TAG_ALERT_SELECT); + xmlNode *select = pcmk__xe_first_child(basenode, PCMK_XE_SELECT, NULL, + NULL); xmlNode *event_type = NULL; uint32_t flags = pcmk__alert_none; - for (event_type = pcmk__xe_first_child(select); event_type != NULL; - event_type = pcmk__xe_next(event_type)) { + for (event_type = pcmk__xe_first_child(select, NULL, NULL, NULL); + event_type != NULL; event_type = pcmk__xe_next(event_type)) { - if (pcmk__xe_is(event_type, XML_CIB_TAG_ALERT_FENCING)) { + if (pcmk__xe_is(event_type, PCMK_XE_SELECT_FENCING)) { flags |= pcmk__alert_fencing; - } else if (pcmk__xe_is(event_type, XML_CIB_TAG_ALERT_NODES)) { + } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_NODES)) { flags |= pcmk__alert_node; - } else if (pcmk__xe_is(event_type, XML_CIB_TAG_ALERT_RESOURCES)) { + } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_RESOURCES)) { flags |= pcmk__alert_resource; - } else if (pcmk__xe_is(event_type, XML_CIB_TAG_ALERT_ATTRIBUTES)) { + } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_ATTRIBUTES)) { xmlNode *attr; const char *attr_name; int nattrs = 0; flags |= pcmk__alert_attribute; - for (attr = first_named_child(event_type, XML_CIB_TAG_ALERT_ATTR); - attr != NULL; - attr = crm_next_same_xml(attr)) { + for (attr = pcmk__xe_first_child(event_type, PCMK_XE_ATTRIBUTE, + NULL, NULL); + attr != NULL; attr = pcmk__xe_next_same(attr)) { - attr_name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME); + attr_name = crm_element_value(attr, PCMK_XA_NAME); if (attr_name) { if (nattrs == 0) { g_strfreev(entry->select_attribute_name); @@ -216,17 +222,22 @@ pe_unpack_alerts(const xmlNode *alerts) return alert_list; } - for (alert = first_named_child(alerts, XML_CIB_TAG_ALERT); - alert != NULL; alert = crm_next_same_xml(alert)) { + for (alert = pcmk__xe_first_child(alerts, PCMK_XE_ALERT, NULL, NULL); + alert != NULL; alert = pcmk__xe_next_same(alert)) { xmlNode *recipient; int recipients = 0; - const char *alert_id = ID(alert); - const char *alert_path = crm_element_value(alert, XML_ALERT_ATTR_PATH); + const char *alert_id = pcmk__xe_id(alert); + const char *alert_path = crm_element_value(alert, PCMK_XA_PATH); /* The schema should enforce this, but to be safe ... */ - if ((alert_id == NULL) || (alert_path == NULL)) { - crm_warn("Ignoring invalid alert without id and path"); + if (alert_id == NULL) { + pcmk__config_warn("Ignoring invalid alert without " PCMK_XA_ID); + crm_log_xml_info(alert, "missing-id"); + continue; + } + if (alert_path == NULL) { + pcmk__config_warn("Ignoring alert %s: No " PCMK_XA_PATH, alert_id); continue; } @@ -247,14 +258,15 @@ pe_unpack_alerts(const xmlNode *alerts) entry->id, entry->path, entry->timeout, entry->tstamp_format, (entry->envvars? g_hash_table_size(entry->envvars) : 0)); - for (recipient = first_named_child(alert, XML_CIB_TAG_ALERT_RECIPIENT); - recipient != NULL; recipient = crm_next_same_xml(recipient)) { + for (recipient = pcmk__xe_first_child(alert, PCMK_XE_RECIPIENT, NULL, + NULL); + recipient != NULL; recipient = pcmk__xe_next_same(recipient)) { pcmk__alert_t *recipient_entry = pcmk__dup_alert(entry); recipients++; - recipient_entry->recipient = strdup(crm_element_value(recipient, - XML_ALERT_ATTR_REC_VALUE)); + recipient_entry->recipient = crm_element_value_copy(recipient, + PCMK_XA_VALUE); if (unpack_alert(recipient, recipient_entry, &max_timeout) != pcmk_rc_ok) { @@ -265,7 +277,8 @@ pe_unpack_alerts(const xmlNode *alerts) } alert_list = g_list_prepend(alert_list, recipient_entry); crm_debug("Alert %s has recipient %s with value %s and %d envvars", - entry->id, ID(recipient), recipient_entry->recipient, + entry->id, pcmk__xe_id(recipient), + recipient_entry->recipient, (recipient_entry->envvars? g_hash_table_size(recipient_entry->envvars) : 0)); } diff --git a/lib/pengine/status.c b/lib/pengine/status.c index e6ec237..36209da 100644 --- a/lib/pengine/status.c +++ b/lib/pengine/status.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. * @@ -12,8 +12,8 @@ #include <sys/param.h> #include <crm/crm.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> +#include <crm/common/cib_internal.h> #include <glib.h> @@ -56,6 +56,31 @@ pe_free_working_set(pcmk_scheduler_t *scheduler) } } +#define XPATH_DEPRECATED_RULES \ + "//" PCMK_XE_OP_DEFAULTS "//" PCMK_XE_EXPRESSION \ + "|//" PCMK_XE_OP "//" PCMK_XE_EXPRESSION + +/*! + * \internal + * \brief Log a warning for deprecated rule syntax in operations + * + * \param[in] scheduler Scheduler data + */ +static void +check_for_deprecated_rules(pcmk_scheduler_t *scheduler) +{ + // @COMPAT Drop this function when support for the syntax is dropped + xmlNode *deprecated = get_xpath_object(XPATH_DEPRECATED_RULES, + scheduler->input, LOG_NEVER); + + if (deprecated != NULL) { + pcmk__warn_once(pcmk__wo_op_attr_expr, + "Support for rules with node attribute expressions in " + PCMK_XE_OP " or " PCMK_XE_OP_DEFAULTS " is deprecated " + "and will be dropped in a future release"); + } +} + /* * Unpack everything * At the end you'll have: @@ -70,18 +95,27 @@ pe_free_working_set(pcmk_scheduler_t *scheduler) gboolean cluster_status(pcmk_scheduler_t * scheduler) { + const char *new_version = NULL; xmlNode *section = NULL; if ((scheduler == NULL) || (scheduler->input == NULL)) { return FALSE; } + new_version = crm_element_value(scheduler->input, PCMK_XA_CRM_FEATURE_SET); + + if (pcmk__check_feature_set(new_version) != pcmk_rc_ok) { + pcmk__config_err("Can't process CIB with feature set '%s' greater than our own '%s'", + new_version, CRM_FEATURE_SET); + return FALSE; + } + crm_trace("Beginning unpack"); if (scheduler->failed != NULL) { free_xml(scheduler->failed); } - scheduler->failed = create_xml_node(NULL, "failed-ops"); + scheduler->failed = pcmk__xe_create(NULL, "failed-ops"); if (scheduler->now == NULL) { scheduler->now = crm_time_new(NULL); @@ -89,47 +123,48 @@ cluster_status(pcmk_scheduler_t * scheduler) if (scheduler->dc_uuid == NULL) { scheduler->dc_uuid = crm_element_value_copy(scheduler->input, - XML_ATTR_DC_UUID); + PCMK_XA_DC_UUID); } - if (pcmk__xe_attr_is_true(scheduler->input, XML_ATTR_HAVE_QUORUM)) { - pe__set_working_set_flags(scheduler, pcmk_sched_quorate); + if (pcmk__xe_attr_is_true(scheduler->input, PCMK_XA_HAVE_QUORUM)) { + pcmk__set_scheduler_flags(scheduler, pcmk_sched_quorate); } else { - pe__clear_working_set_flags(scheduler, pcmk_sched_quorate); + pcmk__clear_scheduler_flags(scheduler, pcmk_sched_quorate); } - scheduler->op_defaults = get_xpath_object("//" XML_CIB_TAG_OPCONFIG, + scheduler->op_defaults = get_xpath_object("//" PCMK_XE_OP_DEFAULTS, scheduler->input, LOG_NEVER); - scheduler->rsc_defaults = get_xpath_object("//" XML_CIB_TAG_RSCCONFIG, + check_for_deprecated_rules(scheduler); + + scheduler->rsc_defaults = get_xpath_object("//" PCMK_XE_RSC_DEFAULTS, scheduler->input, LOG_NEVER); - section = get_xpath_object("//" XML_CIB_TAG_CRMCONFIG, scheduler->input, + section = get_xpath_object("//" PCMK_XE_CRM_CONFIG, scheduler->input, LOG_TRACE); unpack_config(section, scheduler); if (!pcmk_any_flags_set(scheduler->flags, pcmk_sched_location_only|pcmk_sched_quorate) && (scheduler->no_quorum_policy != pcmk_no_quorum_ignore)) { - crm_warn("Fencing and resource management disabled due to lack of quorum"); + pcmk__sched_warn("Fencing and resource management disabled " + "due to lack of quorum"); } - section = get_xpath_object("//" XML_CIB_TAG_NODES, scheduler->input, - LOG_TRACE); + section = get_xpath_object("//" PCMK_XE_NODES, scheduler->input, LOG_TRACE); unpack_nodes(section, scheduler); - section = get_xpath_object("//" XML_CIB_TAG_RESOURCES, scheduler->input, + section = get_xpath_object("//" PCMK_XE_RESOURCES, scheduler->input, LOG_TRACE); if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) { unpack_remote_nodes(section, scheduler); } unpack_resources(section, scheduler); - section = get_xpath_object("//" XML_CIB_TAG_TAGS, scheduler->input, - LOG_NEVER); + section = get_xpath_object("//" PCMK_XE_TAGS, scheduler->input, LOG_NEVER); unpack_tags(section, scheduler); if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) { - section = get_xpath_object("//"XML_CIB_TAG_STATUS, scheduler->input, + section = get_xpath_object("//" PCMK_XE_STATUS, scheduler->input, LOG_TRACE); unpack_status(section, scheduler); } @@ -144,7 +179,7 @@ cluster_status(pcmk_scheduler_t * scheduler) scheduler->blocked_resources); } - pe__set_working_set_flags(scheduler, pcmk_sched_have_status); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_status); return TRUE; } @@ -207,8 +242,8 @@ pe_free_nodes(GList *nodes) /* This is called after pe_free_resources(), which means that we can't * use node->details->uname for Pacemaker Remote nodes. */ - crm_trace("Freeing node %s", (pe__is_guest_or_remote_node(node)? - "(guest or remote)" : pe__node_name(node))); + crm_trace("Freeing node %s", (pcmk__is_pacemaker_remote_node(node)? + "(guest or remote)" : pcmk__node_name(node))); if (node->details->attrs != NULL) { g_hash_table_destroy(node->details->attrs); @@ -235,12 +270,12 @@ pe__free_ordering(GList *constraints) GList *iterator = constraints; while (iterator != NULL) { - pe__ordering_t *order = iterator->data; + pcmk__action_relation_t *order = iterator->data; iterator = iterator->next; - free(order->lh_action_task); - free(order->rh_action_task); + free(order->task1); + free(order->task2); free(order); } if (constraints != NULL) { @@ -254,11 +289,11 @@ pe__free_location(GList *constraints) GList *iterator = constraints; while (iterator != NULL) { - pe__location_t *cons = iterator->data; + pcmk__location_t *cons = iterator->data; iterator = iterator->next; - g_list_free_full(cons->node_list_rh, free); + g_list_free_full(cons->nodes, free); free(cons->id); free(cons); } @@ -282,7 +317,7 @@ cleanup_calculations(pcmk_scheduler_t *scheduler) return; } - pe__clear_working_set_flags(scheduler, pcmk_sched_have_status); + pcmk__clear_scheduler_flags(scheduler, pcmk_sched_have_status); if (scheduler->config_hash != NULL) { g_hash_table_destroy(scheduler->config_hash); } @@ -378,12 +413,12 @@ set_working_set_defaults(pcmk_scheduler_t *scheduler) scheduler->flags = 0x0ULL; - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_symmetric_cluster |pcmk_sched_stop_removed_resources |pcmk_sched_cancel_removed_actions); - if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, "true")) { - pe__set_working_set_flags(scheduler, pcmk_sched_concurrent_fencing); + if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, PCMK_VALUE_TRUE)) { + pcmk__set_scheduler_flags(scheduler, pcmk_sched_concurrent_fencing); } } @@ -431,7 +466,7 @@ pe_find_node_any(const GList *nodes, const char *id, const char *uname) match = pe_find_node_id(nodes, id); } if ((match == NULL) && (uname != NULL)) { - match = pe_find_node(nodes, uname); + match = pcmk__find_node_in_list(nodes, uname); } return match; } @@ -461,6 +496,11 @@ pe_find_node_id(const GList *nodes, const char *id) return NULL; } +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/pengine/status_compat.h> + /*! * \brief Find a node by name in a list of nodes * @@ -472,12 +512,8 @@ pe_find_node_id(const GList *nodes, const char *id) pcmk_node_t * pe_find_node(const GList *nodes, const char *node_name) { - for (const GList *iter = nodes; iter != NULL; iter = iter->next) { - pcmk_node_t *node = (pcmk_node_t *) iter->data; - - if (pcmk__str_eq(node->details->uname, node_name, pcmk__str_casei)) { - return node; - } - } - return NULL; + return pcmk__find_node_in_list(nodes, node_name); } + +// LCOV_EXCL_STOP +// End deprecated API diff --git a/lib/pengine/tests/Makefile.am b/lib/pengine/tests/Makefile.am index 48ec5b4..52a0ff6 100644 --- a/lib/pengine/tests/Makefile.am +++ b/lib/pengine/tests/Makefile.am @@ -1,14 +1,14 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # +MAINTAINERCLEANFILES = Makefile.in -SUBDIRS = rules \ - native \ +SUBDIRS = native \ status \ unpack \ utils diff --git a/lib/pengine/tests/native/native_find_rsc_test.c b/lib/pengine/tests/native/native_find_rsc_test.c index b85ca24..ac1337d 100644 --- a/lib/pengine/tests/native/native_find_rsc_test.c +++ b/lib/pengine/tests/native/native_find_rsc_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -30,7 +30,7 @@ setup(void **state) { crm_xml_init(); path = crm_strdup_printf("%s/crm_mon.xml", getenv("PCMK_CTS_CLI_DIR")); - input = filename2xml(path); + input = pcmk__xml_read(path); free(path); if (input == NULL) { @@ -43,16 +43,16 @@ setup(void **state) { return 1; } - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_counts|pcmk_sched_no_compat); scheduler->input = input; cluster_status(scheduler); /* Get references to the cluster nodes so we don't have to find them repeatedly. */ - cluster01 = pe_find_node(scheduler->nodes, "cluster01"); - cluster02 = pe_find_node(scheduler->nodes, "cluster02"); - httpd_bundle_0 = pe_find_node(scheduler->nodes, "httpd-bundle-0"); + cluster01 = pcmk_find_node(scheduler, "cluster01"); + cluster02 = pcmk_find_node(scheduler, "cluster02"); + httpd_bundle_0 = pcmk_find_node(scheduler, "httpd-bundle-0"); /* Get references to several resources we use frequently. */ for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) { @@ -539,7 +539,7 @@ bundle_rsc(void **state) { } static bool -bundle_first_replica(pe__bundle_replica_t *replica, void *user_data) +bundle_first_replica(pcmk__bundle_replica_t *replica, void *user_data) { pcmk_resource_t *ip_0 = replica->ip; pcmk_resource_t *child_0 = replica->child; diff --git a/lib/pengine/tests/native/pe_base_name_eq_test.c b/lib/pengine/tests/native/pe_base_name_eq_test.c index cb3c908..68112bb 100644 --- a/lib/pengine/tests/native/pe_base_name_eq_test.c +++ b/lib/pengine/tests/native/pe_base_name_eq_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -29,7 +29,7 @@ setup(void **state) { crm_xml_init(); path = crm_strdup_printf("%s/crm_mon.xml", getenv("PCMK_CTS_CLI_DIR")); - input = filename2xml(path); + input = pcmk__xml_read(path); free(path); if (input == NULL) { @@ -42,7 +42,7 @@ setup(void **state) { return 1; } - pe__set_working_set_flags(scheduler, + pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_counts|pcmk_sched_no_compat); scheduler->input = input; diff --git a/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c b/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c deleted file mode 100644 index a8ba6cf..0000000 --- a/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2020-2022 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <glib.h> - -#include <crm/msg_xml.h> -#include <crm/common/unittest_internal.h> -#include <crm/common/xml.h> -#include <crm/pengine/rules_internal.h> - -static void -run_one_test(const char *t, const char *x, int expected) { - crm_time_t *tm = crm_time_new(t); - xmlNodePtr xml = string2xml(x); - - assert_int_equal(pe_cron_range_satisfied(tm, xml), expected); - - crm_time_free(tm); - free_xml(xml); -} - -static void -no_time_given(void **state) { - assert_int_equal(pe_cron_range_satisfied(NULL, NULL), pcmk_rc_op_unsatisfied); -} - -static void -any_time_satisfies_empty_spec(void **state) { - crm_time_t *tm = crm_time_new(NULL); - - assert_int_equal(pe_cron_range_satisfied(tm, NULL), pcmk_rc_ok); - - crm_time_free(tm); -} - -static void -time_satisfies_year_spec(void **state) { - run_one_test("2020-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2020'/>", - pcmk_rc_ok); -} - -static void -time_after_year_spec(void **state) { - run_one_test("2020-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2019'/>", - pcmk_rc_after_range); -} - -static void -time_satisfies_year_range(void **state) { - run_one_test("2020-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2010-2030'/>", - pcmk_rc_ok); -} - -static void -time_before_year_range(void **state) { - run_one_test("2000-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2010-2030'/>", - pcmk_rc_before_range); -} - -static void -time_after_year_range(void **state) { - run_one_test("2020-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2010-2015'/>", - pcmk_rc_after_range); -} - -static void -range_without_start_year_passes(void **state) { - run_one_test("2010-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='-2020'/>", - pcmk_rc_ok); -} - -static void -range_without_end_year_passes(void **state) { - run_one_test("2010-01-01", - "<date_spec " XML_ATTR_ID "='spec' years='2000-'/>", - pcmk_rc_ok); - run_one_test("2000-10-01", - "<date_spec " XML_ATTR_ID "='spec' years='2000-'/>", - pcmk_rc_ok); -} - -static void -yeardays_satisfies(void **state) { - run_one_test("2020-01-30", - "<date_spec " XML_ATTR_ID "='spec' yeardays='30'/>", - pcmk_rc_ok); -} - -static void -time_after_yeardays_spec(void **state) { - run_one_test("2020-02-15", - "<date_spec " XML_ATTR_ID "='spec' yeardays='40'/>", - pcmk_rc_after_range); -} - -static void -yeardays_feb_29_satisfies(void **state) { - run_one_test("2016-02-29", - "<date_spec " XML_ATTR_ID "='spec' yeardays='60'/>", - pcmk_rc_ok); -} - -static void -exact_ymd_satisfies(void **state) { - run_one_test("2001-12-31", - "<date_spec " XML_ATTR_ID "='spec' years='2001' months='12' " - "monthdays='31'/>", - pcmk_rc_ok); -} - -static void -range_in_month_satisfies(void **state) { - run_one_test("2001-06-10", - "<date_spec " XML_ATTR_ID "='spec' years='2001' months='6' " - "monthdays='1-10'/>", - pcmk_rc_ok); -} - -static void -exact_ymd_after_range(void **state) { - run_one_test("2001-12-31", - "<date_spec " XML_ATTR_ID "='spec' years='2001' months='12' " - "monthdays='30'/>", - pcmk_rc_after_range); -} - -static void -time_after_monthdays_range(void **state) { - run_one_test("2001-06-10", - "<date_spec " XML_ATTR_ID "='spec' years='2001' months='6' " - "monthdays='11-15'/>", - pcmk_rc_before_range); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(no_time_given), - cmocka_unit_test(any_time_satisfies_empty_spec), - cmocka_unit_test(time_satisfies_year_spec), - cmocka_unit_test(time_after_year_spec), - cmocka_unit_test(time_satisfies_year_range), - cmocka_unit_test(time_before_year_range), - cmocka_unit_test(time_after_year_range), - cmocka_unit_test(range_without_start_year_passes), - cmocka_unit_test(range_without_end_year_passes), - cmocka_unit_test(yeardays_satisfies), - cmocka_unit_test(time_after_yeardays_spec), - cmocka_unit_test(yeardays_feb_29_satisfies), - cmocka_unit_test(exact_ymd_satisfies), - cmocka_unit_test(range_in_month_satisfies), - cmocka_unit_test(exact_ymd_after_range), - cmocka_unit_test(time_after_monthdays_range)) diff --git a/lib/pengine/tests/status/Makefile.am b/lib/pengine/tests/status/Makefile.am index c7ddb70..f91d150 100644 --- a/lib/pengine/tests/status/Makefile.am +++ b/lib/pengine/tests/status/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2022-2023 the Pacemaker project contributors +# Copyright 2022-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -15,7 +15,6 @@ LDADD += $(top_builddir)/lib/pengine/libpe_status_test.la # Add "_test" to the end of all test program names to simplify .gitignore. check_PROGRAMS = pe_find_node_any_test \ pe_find_node_id_test \ - pe_find_node_test \ pe_new_working_set_test \ set_working_set_defaults_test diff --git a/lib/pengine/tests/status/pe_find_node_any_test.c b/lib/pengine/tests/status/pe_find_node_any_test.c index 5f5a27e..bbe046b 100644 --- a/lib/pengine/tests/status/pe_find_node_any_test.c +++ b/lib/pengine/tests/status/pe_find_node_any_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -24,13 +24,13 @@ static void non_null_list(void **state) { GList *nodes = NULL; - pcmk_node_t *a = calloc(1, sizeof(pcmk_node_t)); - pcmk_node_t *b = calloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *a = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *b = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); - a->details = calloc(1, sizeof(struct pe_node_shared_s)); + a->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); a->details->uname = "cluster1"; a->details->id = "id1"; - b->details = calloc(1, sizeof(struct pe_node_shared_s)); + b->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); b->details->uname = "cluster2"; b->details->id = "id2"; diff --git a/lib/pengine/tests/status/pe_find_node_id_test.c b/lib/pengine/tests/status/pe_find_node_id_test.c index c6b8773..694d760 100644 --- a/lib/pengine/tests/status/pe_find_node_id_test.c +++ b/lib/pengine/tests/status/pe_find_node_id_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,12 +22,12 @@ static void non_null_list(void **state) { GList *nodes = NULL; - pcmk_node_t *a = calloc(1, sizeof(pcmk_node_t)); - pcmk_node_t *b = calloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *a = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *b = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); - a->details = calloc(1, sizeof(struct pe_node_shared_s)); + a->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); a->details->id = "id1"; - b->details = calloc(1, sizeof(struct pe_node_shared_s)); + b->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); b->details->id = "id2"; nodes = g_list_append(nodes, a); diff --git a/lib/pengine/tests/status/pe_find_node_test.c b/lib/pengine/tests/status/pe_find_node_test.c deleted file mode 100644 index 305ddc9..0000000 --- a/lib/pengine/tests/status/pe_find_node_test.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2022-2023 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <crm/common/unittest_internal.h> -#include <crm/pengine/internal.h> - -static void -empty_list(void **state) { - assert_null(pe_find_node(NULL, NULL)); - assert_null(pe_find_node(NULL, "cluster1")); -} - -static void -non_null_list(void **state) { - GList *nodes = NULL; - - pcmk_node_t *a = calloc(1, sizeof(pcmk_node_t)); - pcmk_node_t *b = calloc(1, sizeof(pcmk_node_t)); - - a->details = calloc(1, sizeof(struct pe_node_shared_s)); - a->details->uname = "cluster1"; - b->details = calloc(1, sizeof(struct pe_node_shared_s)); - b->details->uname = "cluster2"; - - nodes = g_list_append(nodes, a); - nodes = g_list_append(nodes, b); - - assert_ptr_equal(a, pe_find_node(nodes, "cluster1")); - assert_null(pe_find_node(nodes, "cluster10")); - assert_null(pe_find_node(nodes, "nodecluster1")); - assert_ptr_equal(b, pe_find_node(nodes, "CLUSTER2")); - assert_null(pe_find_node(nodes, "xyz")); - - free(a->details); - free(a); - free(b->details); - free(b); - g_list_free(nodes); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(empty_list), - cmocka_unit_test(non_null_list)) diff --git a/lib/pengine/tests/status/set_working_set_defaults_test.c b/lib/pengine/tests/status/set_working_set_defaults_test.c index 7045a33..546cd8a 100644 --- a/lib/pengine/tests/status/set_working_set_defaults_test.c +++ b/lib/pengine/tests/status/set_working_set_defaults_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2023 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,7 +20,8 @@ static void check_defaults(void **state) { uint32_t flags; - pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t)); + pcmk_scheduler_t *scheduler = pcmk__assert_alloc(1, + sizeof(pcmk_scheduler_t)); set_working_set_defaults(scheduler); @@ -28,7 +29,7 @@ check_defaults(void **state) { |pcmk_sched_stop_removed_resources |pcmk_sched_cancel_removed_actions; - if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, "true")) { + if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, PCMK_VALUE_TRUE)) { flags |= pcmk_sched_concurrent_fencing; } diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c index 3429d56..de623d4 100644 --- a/lib/pengine/unpack.c +++ b/lib/pengine/unpack.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. * @@ -16,7 +16,6 @@ #include <crm/crm.h> #include <crm/services.h> -#include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/xml_internal.h> @@ -46,11 +45,13 @@ struct action_history { }; /* This uses pcmk__set_flags_as()/pcmk__clear_flags_as() directly rather than - * use pe__set_working_set_flags()/pe__clear_working_set_flags() so that the + * use pcmk__set_scheduler_flags()/pcmk__clear_scheduler_flags() so that the * flag is stringified more readably in log messages. */ #define set_config_flag(scheduler, option, flag) do { \ - const char *scf_value = pe_pref((scheduler)->config_hash, (option)); \ + GHashTable *config_hash = (scheduler)->config_hash; \ + const char *scf_value = pcmk__cluster_option(config_hash, (option)); \ + \ if (scf_value != NULL) { \ if (crm_is_true(scf_value)) { \ (scheduler)->flags = pcmk__set_flags_as(__func__, __LINE__, \ @@ -87,11 +88,11 @@ is_dangling_guest_node(pcmk_node_t *node) /* we are looking for a remote-node that was supposed to be mapped to a * container resource, but all traces of that container have disappeared * from both the config and the status section. */ - if (pe__is_guest_or_remote_node(node) && - node->details->remote_rsc && - node->details->remote_rsc->container == NULL && - pcmk_is_set(node->details->remote_rsc->flags, - pcmk_rsc_removed_filler)) { + if (pcmk__is_pacemaker_remote_node(node) + && (node->details->remote_rsc != NULL) + && (node->details->remote_rsc->container == NULL) + && pcmk_is_set(node->details->remote_rsc->flags, + pcmk_rsc_removed_filler)) { return TRUE; } @@ -104,7 +105,8 @@ is_dangling_guest_node(pcmk_node_t *node) * \param[in,out] scheduler Scheduler data * \param[in,out] node Node to fence * \param[in] reason Text description of why fencing is needed - * \param[in] priority_delay Whether to consider `priority-fencing-delay` + * \param[in] priority_delay Whether to consider + * \c PCMK_OPT_PRIORITY_FENCING_DELAY */ void pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, @@ -113,7 +115,7 @@ pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, CRM_CHECK(node, return); /* A guest node is fenced by marking its container as failed */ - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { pcmk_resource_t *rsc = node->details->remote_rsc->container; if (!pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { @@ -121,19 +123,19 @@ pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, crm_notice("Not fencing guest node %s " "(otherwise would because %s): " "its guest resource %s is unmanaged", - pe__node_name(node), reason, rsc->id); + pcmk__node_name(node), reason, rsc->id); } else { - crm_warn("Guest node %s will be fenced " - "(by recovering its guest resource %s): %s", - pe__node_name(node), rsc->id, reason); + pcmk__sched_warn("Guest node %s will be fenced " + "(by recovering its guest resource %s): %s", + pcmk__node_name(node), rsc->id, reason); /* We don't mark the node as unclean because that would prevent the * node from running resources. We want to allow it to run resources * in this transition if the recovery succeeds. */ node->details->remote_requires_reset = TRUE; - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__set_rsc_flags(rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); } } @@ -141,39 +143,39 @@ pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, crm_info("Cleaning up dangling connection for guest node %s: " "fencing was already done because %s, " "and guest resource no longer exists", - pe__node_name(node), reason); - pe__set_resource_flags(node->details->remote_rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__node_name(node), reason); + pcmk__set_rsc_flags(node->details->remote_rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); - } else if (pe__is_remote_node(node)) { + } else if (pcmk__is_remote_node(node)) { pcmk_resource_t *rsc = node->details->remote_rsc; if ((rsc != NULL) && !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { crm_notice("Not fencing remote node %s " "(otherwise would because %s): connection is unmanaged", - pe__node_name(node), reason); + pcmk__node_name(node), reason); } else if(node->details->remote_requires_reset == FALSE) { node->details->remote_requires_reset = TRUE; - crm_warn("Remote node %s %s: %s", - pe__node_name(node), - pe_can_fence(scheduler, node)? "will be fenced" : "is unclean", - reason); + pcmk__sched_warn("Remote node %s %s: %s", + pcmk__node_name(node), + pe_can_fence(scheduler, node)? "will be fenced" : "is unclean", + reason); } node->details->unclean = TRUE; - // No need to apply `priority-fencing-delay` for remote nodes + // No need to apply PCMK_OPT_PRIORITY_FENCING_DELAY for remote nodes pe_fence_op(node, NULL, TRUE, reason, FALSE, scheduler); } else if (node->details->unclean) { crm_trace("Cluster node %s %s because %s", - pe__node_name(node), + pcmk__node_name(node), pe_can_fence(scheduler, node)? "would also be fenced" : "also is unclean", reason); } else { - crm_warn("Cluster node %s %s: %s", - pe__node_name(node), - pe_can_fence(scheduler, node)? "will be fenced" : "is unclean", - reason); + pcmk__sched_warn("Cluster node %s %s: %s", + pcmk__node_name(node), + pe_can_fence(scheduler, node)? "will be fenced" : "is unclean", + reason); node->details->unclean = TRUE; pe_fence_op(node, NULL, TRUE, reason, priority_delay, scheduler); } @@ -182,17 +184,17 @@ pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, // @TODO xpaths can't handle templates, rules, or id-refs // nvpair with provides or requires set to unfencing -#define XPATH_UNFENCING_NVPAIR XML_CIB_TAG_NVPAIR \ - "[(@" XML_NVPAIR_ATTR_NAME "='" PCMK_STONITH_PROVIDES "'" \ - "or @" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_REQUIRES "') " \ - "and @" XML_NVPAIR_ATTR_VALUE "='" PCMK__VALUE_UNFENCING "']" +#define XPATH_UNFENCING_NVPAIR PCMK_XE_NVPAIR \ + "[(@" PCMK_XA_NAME "='" PCMK_STONITH_PROVIDES "'" \ + "or @" PCMK_XA_NAME "='" PCMK_META_REQUIRES "') " \ + "and @" PCMK_XA_VALUE "='" PCMK_VALUE_UNFENCING "']" // unfencing in rsc_defaults or any resource #define XPATH_ENABLE_UNFENCING \ - "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES \ - "//" XML_TAG_META_SETS "/" XPATH_UNFENCING_NVPAIR \ - "|/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RSCCONFIG \ - "/" XML_TAG_META_SETS "/" XPATH_UNFENCING_NVPAIR + "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RESOURCES \ + "//" PCMK_XE_META_ATTRIBUTES "/" XPATH_UNFENCING_NVPAIR \ + "|/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_RSC_DEFAULTS \ + "/" PCMK_XE_META_ATTRIBUTES "/" XPATH_UNFENCING_NVPAIR static void set_if_xpath(uint64_t flag, const char *xpath, pcmk_scheduler_t *scheduler) @@ -202,7 +204,7 @@ set_if_xpath(uint64_t flag, const char *xpath, pcmk_scheduler_t *scheduler) if (!pcmk_is_set(scheduler->flags, flag)) { result = xpath_search(scheduler->input, xpath); if (result && (numXpathResults(result) > 0)) { - pe__set_working_set_flags(scheduler, flag); + pcmk__set_scheduler_flags(scheduler, flag); } freeXpathObject(result); } @@ -212,11 +214,11 @@ gboolean unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) { const char *value = NULL; + guint interval_ms = 0U; GHashTable *config_hash = pcmk__strkey_table(free, free); pe_rule_eval_data_t rule_data = { .node_hash = NULL, - .role = pcmk_role_unknown, .now = scheduler->now, .match_data = NULL, .rsc_data = NULL, @@ -225,22 +227,24 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) scheduler->config_hash = config_hash; - pe__unpack_dataset_nvpairs(config, XML_CIB_TAG_PROPSET, &rule_data, config_hash, - CIB_OPTIONS_FIRST, FALSE, scheduler); + pe__unpack_dataset_nvpairs(config, PCMK_XE_CLUSTER_PROPERTY_SET, &rule_data, + config_hash, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, + FALSE, scheduler); - verify_pe_options(scheduler->config_hash); + pcmk__validate_cluster_options(config_hash); - set_config_flag(scheduler, "enable-startup-probes", + set_config_flag(scheduler, PCMK_OPT_ENABLE_STARTUP_PROBES, pcmk_sched_probe_resources); if (!pcmk_is_set(scheduler->flags, pcmk_sched_probe_resources)) { crm_info("Startup probes: disabled (dangerous)"); } - value = pe_pref(scheduler->config_hash, XML_ATTR_HAVE_WATCHDOG); + value = pcmk__cluster_option(config_hash, PCMK_OPT_HAVE_WATCHDOG); if (value && crm_is_true(value)) { crm_info("Watchdog-based self-fencing will be performed via SBD if " - "fencing is required and stonith-watchdog-timeout is nonzero"); - pe__set_working_set_flags(scheduler, pcmk_sched_have_fencing); + "fencing is required and " PCMK_OPT_STONITH_WATCHDOG_TIMEOUT + " is nonzero"); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_fencing); } /* Set certain flags via xpath here, so they can be used before the relevant @@ -249,28 +253,37 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) set_if_xpath(pcmk_sched_enable_unfencing, XPATH_ENABLE_UNFENCING, scheduler); - value = pe_pref(scheduler->config_hash, "stonith-timeout"); - scheduler->stonith_timeout = (int) crm_parse_interval_spec(value); + value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT); + pcmk_parse_interval_spec(value, &interval_ms); + + if (interval_ms >= INT_MAX) { + scheduler->stonith_timeout = INT_MAX; + } else { + scheduler->stonith_timeout = (int) interval_ms; + } crm_debug("STONITH timeout: %d", scheduler->stonith_timeout); - set_config_flag(scheduler, "stonith-enabled", pcmk_sched_fencing_enabled); + set_config_flag(scheduler, PCMK_OPT_STONITH_ENABLED, + pcmk_sched_fencing_enabled); if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) { crm_debug("STONITH of failed nodes is enabled"); } else { crm_debug("STONITH of failed nodes is disabled"); } - scheduler->stonith_action = pe_pref(scheduler->config_hash, - "stonith-action"); - if (!strcmp(scheduler->stonith_action, "poweroff")) { - pe_warn_once(pcmk__wo_poweroff, - "Support for stonith-action of 'poweroff' is deprecated " - "and will be removed in a future release (use 'off' instead)"); + scheduler->stonith_action = pcmk__cluster_option(config_hash, + PCMK_OPT_STONITH_ACTION); + if (!strcmp(scheduler->stonith_action, PCMK__ACTION_POWEROFF)) { + pcmk__warn_once(pcmk__wo_poweroff, + "Support for " PCMK_OPT_STONITH_ACTION " of " + "'" PCMK__ACTION_POWEROFF "' is deprecated and will be " + "removed in a future release " + "(use '" PCMK_ACTION_OFF "' instead)"); scheduler->stonith_action = PCMK_ACTION_OFF; } crm_trace("STONITH will %s nodes", scheduler->stonith_action); - set_config_flag(scheduler, "concurrent-fencing", + set_config_flag(scheduler, PCMK_OPT_CONCURRENT_FENCING, pcmk_sched_concurrent_fencing); if (pcmk_is_set(scheduler->flags, pcmk_sched_concurrent_fencing)) { crm_debug("Concurrent fencing is enabled"); @@ -278,51 +291,52 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) crm_debug("Concurrent fencing is disabled"); } - value = pe_pref(scheduler->config_hash, - XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY); + value = pcmk__cluster_option(config_hash, PCMK_OPT_PRIORITY_FENCING_DELAY); if (value) { - scheduler->priority_fencing_delay = crm_parse_interval_spec(value) - / 1000; + pcmk_parse_interval_spec(value, &interval_ms); + scheduler->priority_fencing_delay = (int) (interval_ms / 1000); crm_trace("Priority fencing delay is %ds", scheduler->priority_fencing_delay); } - set_config_flag(scheduler, "stop-all-resources", pcmk_sched_stop_all); + set_config_flag(scheduler, PCMK_OPT_STOP_ALL_RESOURCES, + pcmk_sched_stop_all); crm_debug("Stop all active resources: %s", - pcmk__btoa(pcmk_is_set(scheduler->flags, pcmk_sched_stop_all))); + pcmk__flag_text(scheduler->flags, pcmk_sched_stop_all)); - set_config_flag(scheduler, "symmetric-cluster", + set_config_flag(scheduler, PCMK_OPT_SYMMETRIC_CLUSTER, pcmk_sched_symmetric_cluster); if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) { crm_debug("Cluster is symmetric" " - resources can run anywhere by default"); } - value = pe_pref(scheduler->config_hash, "no-quorum-policy"); + value = pcmk__cluster_option(config_hash, PCMK_OPT_NO_QUORUM_POLICY); - if (pcmk__str_eq(value, "ignore", pcmk__str_casei)) { + if (pcmk__str_eq(value, PCMK_VALUE_IGNORE, pcmk__str_casei)) { scheduler->no_quorum_policy = pcmk_no_quorum_ignore; - } else if (pcmk__str_eq(value, "freeze", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_FREEZE, pcmk__str_casei)) { scheduler->no_quorum_policy = pcmk_no_quorum_freeze; - } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) { scheduler->no_quorum_policy = pcmk_no_quorum_demote; - } else if (pcmk__str_eq(value, "suicide", pcmk__str_casei)) { + } else if (pcmk__str_eq(value, PCMK_VALUE_FENCE_LEGACY, pcmk__str_casei)) { if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) { int do_panic = 0; - crm_element_value_int(scheduler->input, XML_ATTR_QUORUM_PANIC, + crm_element_value_int(scheduler->input, PCMK_XA_NO_QUORUM_PANIC, &do_panic); if (do_panic || pcmk_is_set(scheduler->flags, pcmk_sched_quorate)) { scheduler->no_quorum_policy = pcmk_no_quorum_fence; } else { - crm_notice("Resetting no-quorum-policy to 'stop': cluster has never had quorum"); + crm_notice("Resetting " PCMK_OPT_NO_QUORUM_POLICY + " to 'stop': cluster has never had quorum"); scheduler->no_quorum_policy = pcmk_no_quorum_stop; } } else { - pcmk__config_err("Resetting no-quorum-policy to 'stop' because " - "fencing is disabled"); + pcmk__config_err("Resetting " PCMK_OPT_NO_QUORUM_POLICY + " to 'stop' because fencing is disabled"); scheduler->no_quorum_policy = pcmk_no_quorum_stop; } @@ -349,7 +363,7 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) break; } - set_config_flag(scheduler, "stop-orphan-resources", + set_config_flag(scheduler, PCMK_OPT_STOP_ORPHAN_RESOURCES, pcmk_sched_stop_removed_resources); if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_removed_resources)) { crm_trace("Orphan resources are stopped"); @@ -357,7 +371,7 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) crm_trace("Orphan resources are ignored"); } - set_config_flag(scheduler, "stop-orphan-actions", + set_config_flag(scheduler, PCMK_OPT_STOP_ORPHAN_ACTIONS, pcmk_sched_cancel_removed_actions); if (pcmk_is_set(scheduler->flags, pcmk_sched_cancel_removed_actions)) { crm_trace("Orphan resource actions are stopped"); @@ -365,27 +379,26 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) crm_trace("Orphan resource actions are ignored"); } - value = pe_pref(scheduler->config_hash, "remove-after-stop"); + value = pcmk__cluster_option(config_hash, PCMK__OPT_REMOVE_AFTER_STOP); if (value != NULL) { if (crm_is_true(value)) { - pe__set_working_set_flags(scheduler, pcmk_sched_remove_after_stop); -#ifndef PCMK__COMPAT_2_0 - pe_warn_once(pcmk__wo_remove_after, - "Support for the remove-after-stop cluster property is" - " deprecated and will be removed in a future release"); -#endif + pcmk__set_scheduler_flags(scheduler, pcmk_sched_remove_after_stop); + pcmk__warn_once(pcmk__wo_remove_after, + "Support for the " PCMK__OPT_REMOVE_AFTER_STOP + " cluster property is deprecated and will be " + "removed in a future release"); } else { - pe__clear_working_set_flags(scheduler, + pcmk__clear_scheduler_flags(scheduler, pcmk_sched_remove_after_stop); } } - set_config_flag(scheduler, "maintenance-mode", pcmk_sched_in_maintenance); + set_config_flag(scheduler, PCMK_OPT_MAINTENANCE_MODE, + pcmk_sched_in_maintenance); crm_trace("Maintenance mode: %s", - pcmk__btoa(pcmk_is_set(scheduler->flags, - pcmk_sched_in_maintenance))); + pcmk__flag_text(scheduler->flags, pcmk_sched_in_maintenance)); - set_config_flag(scheduler, "start-failure-is-fatal", + set_config_flag(scheduler, PCMK_OPT_START_FAILURE_IS_FATAL, pcmk_sched_start_failure_fatal); if (pcmk_is_set(scheduler->flags, pcmk_sched_start_failure_fatal)) { crm_trace("Start failures are always fatal"); @@ -394,26 +407,28 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) } if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) { - set_config_flag(scheduler, "startup-fencing", + set_config_flag(scheduler, PCMK_OPT_STARTUP_FENCING, pcmk_sched_startup_fencing); } if (pcmk_is_set(scheduler->flags, pcmk_sched_startup_fencing)) { crm_trace("Unseen nodes will be fenced"); } else { - pe_warn_once(pcmk__wo_blind, "Blind faith: not fencing unseen nodes"); + pcmk__warn_once(pcmk__wo_blind, + "Blind faith: not fencing unseen nodes"); } pe__unpack_node_health_scores(scheduler); - scheduler->placement_strategy = pe_pref(scheduler->config_hash, - "placement-strategy"); + scheduler->placement_strategy = + pcmk__cluster_option(config_hash, PCMK_OPT_PLACEMENT_STRATEGY); crm_trace("Placement strategy: %s", scheduler->placement_strategy); - set_config_flag(scheduler, "shutdown-lock", pcmk_sched_shutdown_lock); + set_config_flag(scheduler, PCMK_OPT_SHUTDOWN_LOCK, + pcmk_sched_shutdown_lock); if (pcmk_is_set(scheduler->flags, pcmk_sched_shutdown_lock)) { - value = pe_pref(scheduler->config_hash, - XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT); - scheduler->shutdown_lock = crm_parse_interval_spec(value) / 1000; + value = pcmk__cluster_option(config_hash, PCMK_OPT_SHUTDOWN_LOCK_LIMIT); + pcmk_parse_interval_spec(value, &(scheduler->shutdown_lock)); + scheduler->shutdown_lock /= 1000; crm_trace("Resources will be locked to nodes that were cleanly " "shut down (locks expire after %s)", pcmk__readable_interval(scheduler->shutdown_lock)); @@ -422,9 +437,9 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler) "shut down"); } - value = pe_pref(scheduler->config_hash, - XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT); - scheduler->node_pending_timeout = crm_parse_interval_spec(value) / 1000; + value = pcmk__cluster_option(config_hash, PCMK_OPT_NODE_PENDING_TIMEOUT); + pcmk_parse_interval_spec(value, &(scheduler->node_pending_timeout)); + scheduler->node_pending_timeout /= 1000; if (scheduler->node_pending_timeout == 0) { crm_trace("Do not fence pending nodes"); } else { @@ -442,12 +457,13 @@ pe_create_node(const char *id, const char *uname, const char *type, { pcmk_node_t *new_node = NULL; - if (pe_find_node(scheduler->nodes, uname) != NULL) { + if (pcmk_find_node(scheduler, uname) != NULL) { pcmk__config_warn("More than one node entry has name '%s'", uname); } new_node = calloc(1, sizeof(pcmk_node_t)); if (new_node == NULL) { + pcmk__sched_err("Could not allocate memory for node %s", uname); return NULL; } @@ -456,6 +472,7 @@ pe_create_node(const char *id, const char *uname, const char *type, if (new_node->details == NULL) { free(new_node); + pcmk__sched_err("Could not allocate memory for node %s", uname); return NULL; } @@ -468,37 +485,37 @@ pe_create_node(const char *id, const char *uname, const char *type, new_node->details->running_rsc = NULL; new_node->details->data_set = scheduler; - if (pcmk__str_eq(type, "member", pcmk__str_null_matches | pcmk__str_casei)) { + if (pcmk__str_eq(type, PCMK_VALUE_MEMBER, + pcmk__str_null_matches|pcmk__str_casei)) { new_node->details->type = pcmk_node_variant_cluster; - } else if (pcmk__str_eq(type, "remote", pcmk__str_casei)) { + } else if (pcmk__str_eq(type, PCMK_VALUE_REMOTE, pcmk__str_casei)) { new_node->details->type = pcmk_node_variant_remote; - pe__set_working_set_flags(scheduler, pcmk_sched_have_remote_nodes); + pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_remote_nodes); } else { /* @COMPAT 'ping' is the default for backward compatibility, but it * should be changed to 'member' at a compatibility break */ - if (!pcmk__str_eq(type, "ping", pcmk__str_casei)) { + if (!pcmk__str_eq(type, PCMK__VALUE_PING, pcmk__str_casei)) { pcmk__config_warn("Node %s has unrecognized type '%s', " - "assuming 'ping'", pcmk__s(uname, "without name"), - type); + "assuming '" PCMK__VALUE_PING "'", + pcmk__s(uname, "without name"), type); } - pe_warn_once(pcmk__wo_ping_node, - "Support for nodes of type 'ping' (such as %s) is " - "deprecated and will be removed in a future release", - pcmk__s(uname, "unnamed node")); + pcmk__warn_once(pcmk__wo_ping_node, + "Support for nodes of type '" PCMK__VALUE_PING "' " + "(such as %s) is deprecated and will be removed in a " + "future release", + pcmk__s(uname, "unnamed node")); new_node->details->type = node_ping; } new_node->details->attrs = pcmk__strkey_table(free, free); - if (pe__is_guest_or_remote_node(new_node)) { - g_hash_table_insert(new_node->details->attrs, strdup(CRM_ATTR_KIND), - strdup("remote")); + if (pcmk__is_pacemaker_remote_node(new_node)) { + pcmk__insert_dup(new_node->details->attrs, CRM_ATTR_KIND, "remote"); } else { - g_hash_table_insert(new_node->details->attrs, strdup(CRM_ATTR_KIND), - strdup("cluster")); + pcmk__insert_dup(new_node->details->attrs, CRM_ATTR_KIND, "cluster"); } new_node->details->utilization = pcmk__strkey_table(free, free); @@ -516,7 +533,7 @@ expand_remote_rsc_meta(xmlNode *xml_obj, xmlNode *parent, pcmk_scheduler_t *data xmlNode *attr_set = NULL; xmlNode *attr = NULL; - const char *container_id = ID(xml_obj); + const char *container_id = pcmk__xe_id(xml_obj); const char *remote_name = NULL; const char *remote_server = NULL; const char *remote_port = NULL; @@ -524,30 +541,39 @@ expand_remote_rsc_meta(xmlNode *xml_obj, xmlNode *parent, pcmk_scheduler_t *data const char *remote_allow_migrate=NULL; const char *is_managed = NULL; - for (attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL; - attr_set = pcmk__xe_next(attr_set)) { + for (attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) { - if (!pcmk__str_eq((const char *)attr_set->name, XML_TAG_META_SETS, - pcmk__str_casei)) { + if (!pcmk__xe_is(attr_set, PCMK_XE_META_ATTRIBUTES)) { continue; } - for (attr = pcmk__xe_first_child(attr_set); attr != NULL; - attr = pcmk__xe_next(attr)) { - const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE); - const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME); + for (attr = pcmk__xe_first_child(attr_set, NULL, NULL, NULL); + attr != NULL; attr = pcmk__xe_next(attr)) { + + const char *value = crm_element_value(attr, PCMK_XA_VALUE); + const char *name = crm_element_value(attr, PCMK_XA_NAME); + + if (name == NULL) { // Sanity + continue; + } - if (pcmk__str_eq(name, XML_RSC_ATTR_REMOTE_NODE, pcmk__str_casei)) { + if (strcmp(name, PCMK_META_REMOTE_NODE) == 0) { remote_name = value; - } else if (pcmk__str_eq(name, "remote-addr", pcmk__str_casei)) { + + } else if (strcmp(name, PCMK_META_REMOTE_ADDR) == 0) { remote_server = value; - } else if (pcmk__str_eq(name, "remote-port", pcmk__str_casei)) { + + } else if (strcmp(name, PCMK_META_REMOTE_PORT) == 0) { remote_port = value; - } else if (pcmk__str_eq(name, "remote-connect-timeout", pcmk__str_casei)) { + + } else if (strcmp(name, PCMK_META_REMOTE_CONNECT_TIMEOUT) == 0) { connect_timeout = value; - } else if (pcmk__str_eq(name, "remote-allow-migrate", pcmk__str_casei)) { - remote_allow_migrate=value; - } else if (pcmk__str_eq(name, XML_RSC_ATTR_MANAGED, pcmk__str_casei)) { + + } else if (strcmp(name, PCMK_META_REMOTE_ALLOW_MIGRATE) == 0) { + remote_allow_migrate = value; + + } else if (strcmp(name, PCMK_META_IS_MANAGED) == 0) { is_managed = value; } } @@ -603,20 +629,20 @@ unpack_nodes(xmlNode *xml_nodes, pcmk_scheduler_t *scheduler) const char *type = NULL; const char *score = NULL; - for (xml_obj = pcmk__xe_first_child(xml_nodes); xml_obj != NULL; - xml_obj = pcmk__xe_next(xml_obj)) { + for (xml_obj = pcmk__xe_first_child(xml_nodes, NULL, NULL, NULL); + xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { - if (pcmk__str_eq((const char *)xml_obj->name, XML_CIB_TAG_NODE, pcmk__str_none)) { + if (pcmk__xe_is(xml_obj, PCMK_XE_NODE)) { new_node = NULL; - id = crm_element_value(xml_obj, XML_ATTR_ID); - uname = crm_element_value(xml_obj, XML_ATTR_UNAME); - type = crm_element_value(xml_obj, XML_ATTR_TYPE); - score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + id = crm_element_value(xml_obj, PCMK_XA_ID); + uname = crm_element_value(xml_obj, PCMK_XA_UNAME); + type = crm_element_value(xml_obj, PCMK_XA_TYPE); + score = crm_element_value(xml_obj, PCMK_XA_SCORE); crm_trace("Processing node %s/%s", uname, id); if (id == NULL) { - pcmk__config_err("Ignoring <" XML_CIB_TAG_NODE + pcmk__config_err("Ignoring <" PCMK_XE_NODE "> entry in configuration without id"); continue; } @@ -630,12 +656,13 @@ unpack_nodes(xmlNode *xml_nodes, pcmk_scheduler_t *scheduler) add_node_attrs(xml_obj, new_node, FALSE, scheduler); - crm_trace("Done with node %s", crm_element_value(xml_obj, XML_ATTR_UNAME)); + crm_trace("Done with node %s", + crm_element_value(xml_obj, PCMK_XA_UNAME)); } } if (scheduler->localhost - && (pe_find_node(scheduler->nodes, scheduler->localhost) == NULL)) { + && (pcmk_find_node(scheduler, scheduler->localhost) == NULL)) { crm_info("Creating a fake local node"); pe_create_node(scheduler->localhost, scheduler->localhost, NULL, 0, scheduler); @@ -654,18 +681,20 @@ setup_container(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler) return; } - container_id = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_CONTAINER); + container_id = g_hash_table_lookup(rsc->meta, PCMK__META_CONTAINER); if (container_id && !pcmk__str_eq(container_id, rsc->id, pcmk__str_casei)) { pcmk_resource_t *container = pe_find_resource(scheduler->resources, container_id); if (container) { rsc->container = container; - pe__set_resource_flags(container, pcmk_rsc_has_filler); + pcmk__set_rsc_flags(container, pcmk_rsc_has_filler); container->fillers = g_list_append(container->fillers, rsc); - pe_rsc_trace(rsc, "Resource %s's container is %s", rsc->id, container_id); + pcmk__rsc_trace(rsc, "Resource %s's container is %s", + rsc->id, container_id); } else { - pe_err("Resource %s: Unknown resource container (%s)", rsc->id, container_id); + pcmk__config_err("Resource %s: Unknown resource container (%s)", + rsc->id, container_id); } } } @@ -678,8 +707,8 @@ unpack_remote_nodes(xmlNode *xml_resources, pcmk_scheduler_t *scheduler) /* Create remote nodes and guest nodes from the resource configuration * before unpacking resources. */ - for (xml_obj = pcmk__xe_first_child(xml_resources); xml_obj != NULL; - xml_obj = pcmk__xe_next(xml_obj)) { + for (xml_obj = pcmk__xe_first_child(xml_resources, NULL, NULL, NULL); + xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { const char *new_node_id = NULL; @@ -687,15 +716,16 @@ unpack_remote_nodes(xmlNode *xml_resources, pcmk_scheduler_t *scheduler) * primitives. */ if (xml_contains_remote_node(xml_obj)) { - new_node_id = ID(xml_obj); - /* The "pe_find_node" check is here to make sure we don't iterate over - * an expanded node that has already been added to the node list. */ + new_node_id = pcmk__xe_id(xml_obj); + /* The pcmk_find_node() check ensures we don't iterate over an + * expanded node that has already been added to the node list + */ if (new_node_id - && (pe_find_node(scheduler->nodes, new_node_id) == NULL)) { + && (pcmk_find_node(scheduler, new_node_id) == NULL)) { crm_trace("Found remote node %s defined by resource %s", - new_node_id, ID(xml_obj)); - pe_create_node(new_node_id, new_node_id, "remote", NULL, - scheduler); + new_node_id, pcmk__xe_id(xml_obj)); + pe_create_node(new_node_id, new_node_id, PCMK_VALUE_REMOTE, + NULL, scheduler); } continue; } @@ -703,7 +733,7 @@ unpack_remote_nodes(xmlNode *xml_resources, pcmk_scheduler_t *scheduler) /* Check for guest nodes, which are defined by special meta-attributes * of a primitive of any type (for example, VirtualDomain or Xen). */ - if (pcmk__str_eq((const char *)xml_obj->name, XML_CIB_TAG_RESOURCE, pcmk__str_none)) { + if (pcmk__xe_is(xml_obj, PCMK_XE_PRIMITIVE)) { /* This will add an ocf:pacemaker:remote primitive to the * configuration for the guest node's connection, to be unpacked * later. @@ -711,11 +741,11 @@ unpack_remote_nodes(xmlNode *xml_resources, pcmk_scheduler_t *scheduler) new_node_id = expand_remote_rsc_meta(xml_obj, xml_resources, scheduler); if (new_node_id - && (pe_find_node(scheduler->nodes, new_node_id) == NULL)) { + && (pcmk_find_node(scheduler, new_node_id) == NULL)) { crm_trace("Found guest node %s in resource %s", - new_node_id, ID(xml_obj)); - pe_create_node(new_node_id, new_node_id, "remote", NULL, - scheduler); + new_node_id, pcmk__xe_id(xml_obj)); + pe_create_node(new_node_id, new_node_id, PCMK_VALUE_REMOTE, + NULL, scheduler); } continue; } @@ -723,20 +753,21 @@ unpack_remote_nodes(xmlNode *xml_resources, pcmk_scheduler_t *scheduler) /* Check for guest nodes inside a group. Clones are currently not * supported as guest nodes. */ - if (pcmk__str_eq((const char *)xml_obj->name, XML_CIB_TAG_GROUP, pcmk__str_none)) { + if (pcmk__xe_is(xml_obj, PCMK_XE_GROUP)) { xmlNode *xml_obj2 = NULL; - for (xml_obj2 = pcmk__xe_first_child(xml_obj); xml_obj2 != NULL; - xml_obj2 = pcmk__xe_next(xml_obj2)) { + for (xml_obj2 = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); + xml_obj2 != NULL; xml_obj2 = pcmk__xe_next(xml_obj2)) { new_node_id = expand_remote_rsc_meta(xml_obj2, xml_resources, scheduler); if (new_node_id - && (pe_find_node(scheduler->nodes, new_node_id) == NULL)) { + && (pcmk_find_node(scheduler, new_node_id) == NULL)) { crm_trace("Found guest node %s in resource %s inside group %s", - new_node_id, ID(xml_obj2), ID(xml_obj)); - pe_create_node(new_node_id, new_node_id, "remote", NULL, - scheduler); + new_node_id, pcmk__xe_id(xml_obj2), + pcmk__xe_id(xml_obj)); + pe_create_node(new_node_id, new_node_id, PCMK_VALUE_REMOTE, + NULL, scheduler); } } } @@ -766,11 +797,11 @@ link_rsc2remotenode(pcmk_scheduler_t *scheduler, pcmk_resource_t *new_rsc) return; } - remote_node = pe_find_node(scheduler->nodes, new_rsc->id); + remote_node = pcmk_find_node(scheduler, new_rsc->id); CRM_CHECK(remote_node != NULL, return); - pe_rsc_trace(new_rsc, "Linking remote connection resource %s to %s", - new_rsc->id, pe__node_name(remote_node)); + pcmk__rsc_trace(new_rsc, "Linking remote connection resource %s to %s", + new_rsc->id, pcmk__node_name(remote_node)); remote_node->details->remote_rsc = new_rsc; if (new_rsc->container == NULL) { @@ -783,8 +814,8 @@ link_rsc2remotenode(pcmk_scheduler_t *scheduler, pcmk_resource_t *new_rsc) /* pe_create_node() marks the new node as "remote" or "cluster"; now * that we know the node is a guest node, update it correctly. */ - g_hash_table_replace(remote_node->details->attrs, strdup(CRM_ATTR_KIND), - strdup("container")); + pcmk__insert_dup(remote_node->details->attrs, + CRM_ATTR_KIND, "container"); } } @@ -820,11 +851,11 @@ unpack_resources(const xmlNode *xml_resources, pcmk_scheduler_t *scheduler) scheduler->template_rsc_sets = pcmk__strkey_table(free, destroy_tag); - for (xml_obj = pcmk__xe_first_child(xml_resources); xml_obj != NULL; - xml_obj = pcmk__xe_next(xml_obj)) { + for (xml_obj = pcmk__xe_first_child(xml_resources, NULL, NULL, NULL); + xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { pcmk_resource_t *new_rsc = NULL; - const char *id = ID(xml_obj); + const char *id = pcmk__xe_id(xml_obj); if (pcmk__str_empty(id)) { pcmk__config_err("Ignoring <%s> resource without ID", @@ -832,23 +863,20 @@ unpack_resources(const xmlNode *xml_resources, pcmk_scheduler_t *scheduler) continue; } - if (pcmk__str_eq((const char *) xml_obj->name, XML_CIB_TAG_RSC_TEMPLATE, - pcmk__str_none)) { + if (pcmk__xe_is(xml_obj, PCMK_XE_TEMPLATE)) { if (g_hash_table_lookup_extended(scheduler->template_rsc_sets, id, NULL, NULL) == FALSE) { /* Record the template's ID for the knowledge of its existence anyway. */ - g_hash_table_insert(scheduler->template_rsc_sets, strdup(id), - NULL); + pcmk__insert_dup(scheduler->template_rsc_sets, id, NULL); } continue; } - crm_trace("Unpacking <%s " XML_ATTR_ID "='%s'>", - xml_obj->name, id); + crm_trace("Unpacking <%s " PCMK_XA_ID "='%s'>", xml_obj->name, id); if (pe__unpack_resource(xml_obj, &new_rsc, NULL, scheduler) == pcmk_rc_ok) { scheduler->resources = g_list_append(scheduler->resources, new_rsc); - pe_rsc_trace(new_rsc, "Added resource %s", new_rsc->id); + pcmk__rsc_trace(new_rsc, "Added resource %s", new_rsc->id); } else { pcmk__config_err("Ignoring <%s> resource '%s' " @@ -873,7 +901,8 @@ unpack_resources(const xmlNode *xml_resources, pcmk_scheduler_t *scheduler) && !pcmk_is_set(scheduler->flags, pcmk_sched_have_fencing)) { pcmk__config_err("Resource start-up disabled since no STONITH resources have been defined"); - pcmk__config_err("Either configure some or disable STONITH with the stonith-enabled option"); + pcmk__config_err("Either configure some or disable STONITH with the " + PCMK_OPT_STONITH_ENABLED " option"); pcmk__config_err("NOTE: Clusters with shared data need STONITH to ensure data integrity"); } @@ -887,33 +916,33 @@ unpack_tags(xmlNode *xml_tags, pcmk_scheduler_t *scheduler) scheduler->tags = pcmk__strkey_table(free, destroy_tag); - for (xml_tag = pcmk__xe_first_child(xml_tags); xml_tag != NULL; - xml_tag = pcmk__xe_next(xml_tag)) { + for (xml_tag = pcmk__xe_first_child(xml_tags, NULL, NULL, NULL); + xml_tag != NULL; xml_tag = pcmk__xe_next(xml_tag)) { xmlNode *xml_obj_ref = NULL; - const char *tag_id = ID(xml_tag); + const char *tag_id = pcmk__xe_id(xml_tag); - if (!pcmk__str_eq((const char *)xml_tag->name, XML_CIB_TAG_TAG, pcmk__str_none)) { + if (!pcmk__xe_is(xml_tag, PCMK_XE_TAG)) { continue; } if (tag_id == NULL) { - pcmk__config_err("Ignoring <%s> without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> without " PCMK_XA_ID, (const char *) xml_tag->name); continue; } - for (xml_obj_ref = pcmk__xe_first_child(xml_tag); xml_obj_ref != NULL; - xml_obj_ref = pcmk__xe_next(xml_obj_ref)) { + for (xml_obj_ref = pcmk__xe_first_child(xml_tag, NULL, NULL, NULL); + xml_obj_ref != NULL; xml_obj_ref = pcmk__xe_next(xml_obj_ref)) { - const char *obj_ref = ID(xml_obj_ref); + const char *obj_ref = pcmk__xe_id(xml_obj_ref); - if (!pcmk__str_eq((const char *)xml_obj_ref->name, XML_CIB_TAG_OBJ_REF, pcmk__str_none)) { + if (!pcmk__xe_is(xml_obj_ref, PCMK_XE_OBJ_REF)) { continue; } if (obj_ref == NULL) { - pcmk__config_err("Ignoring <%s> for tag '%s' without " XML_ATTR_ID, + pcmk__config_err("Ignoring <%s> for tag '%s' without " PCMK_XA_ID, xml_obj_ref->name, tag_id); continue; } @@ -940,7 +969,7 @@ unpack_ticket_state(xmlNode *xml_ticket, pcmk_scheduler_t *scheduler) pcmk_ticket_t *ticket = NULL; - ticket_id = ID(xml_ticket); + ticket_id = pcmk__xe_id(xml_ticket); if (pcmk__str_empty(ticket_id)) { return FALSE; } @@ -959,13 +988,13 @@ unpack_ticket_state(xmlNode *xml_ticket, pcmk_scheduler_t *scheduler) const char *prop_name = (const char *)xIter->name; const char *prop_value = pcmk__xml_attr_value(xIter); - if (pcmk__str_eq(prop_name, XML_ATTR_ID, pcmk__str_none)) { + if (pcmk__str_eq(prop_name, PCMK_XA_ID, pcmk__str_none)) { continue; } - g_hash_table_replace(ticket->state, strdup(prop_name), strdup(prop_value)); + pcmk__insert_dup(ticket->state, prop_name, prop_value); } - granted = g_hash_table_lookup(ticket->state, "granted"); + granted = g_hash_table_lookup(ticket->state, PCMK__XA_GRANTED); if (granted && crm_is_true(granted)) { ticket->granted = TRUE; crm_info("We have ticket '%s'", ticket->id); @@ -974,7 +1003,7 @@ unpack_ticket_state(xmlNode *xml_ticket, pcmk_scheduler_t *scheduler) crm_info("We do not have ticket '%s'", ticket->id); } - last_granted = g_hash_table_lookup(ticket->state, "last-granted"); + last_granted = g_hash_table_lookup(ticket->state, PCMK_XA_LAST_GRANTED); if (last_granted) { long long last_granted_ll; @@ -982,7 +1011,7 @@ unpack_ticket_state(xmlNode *xml_ticket, pcmk_scheduler_t *scheduler) ticket->last_granted = (time_t) last_granted_ll; } - standby = g_hash_table_lookup(ticket->state, "standby"); + standby = g_hash_table_lookup(ticket->state, PCMK_XA_STANDBY); if (standby && crm_is_true(standby)) { ticket->standby = TRUE; if (ticket->granted) { @@ -1002,10 +1031,10 @@ unpack_tickets_state(xmlNode *xml_tickets, pcmk_scheduler_t *scheduler) { xmlNode *xml_obj = NULL; - for (xml_obj = pcmk__xe_first_child(xml_tickets); xml_obj != NULL; - xml_obj = pcmk__xe_next(xml_obj)) { + for (xml_obj = pcmk__xe_first_child(xml_tickets, NULL, NULL, NULL); + xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { - if (!pcmk__str_eq((const char *)xml_obj->name, XML_CIB_TAG_TICKET_STATE, pcmk__str_none)) { + if (!pcmk__xe_is(xml_obj, PCMK__XE_TICKET_STATE)) { continue; } unpack_ticket_state(xml_obj, scheduler); @@ -1018,20 +1047,21 @@ static void unpack_handle_remote_attrs(pcmk_node_t *this_node, const xmlNode *state, pcmk_scheduler_t *scheduler) { - const char *resource_discovery_enabled = NULL; + const char *discovery = NULL; const xmlNode *attrs = NULL; pcmk_resource_t *rsc = NULL; - if (!pcmk__str_eq((const char *)state->name, XML_CIB_TAG_STATE, pcmk__str_none)) { + if (!pcmk__xe_is(state, PCMK__XE_NODE_STATE)) { return; } - if ((this_node == NULL) || !pe__is_guest_or_remote_node(this_node)) { + if ((this_node == NULL) || !pcmk__is_pacemaker_remote_node(this_node)) { return; } - crm_trace("Processing Pacemaker Remote node %s", pe__node_name(this_node)); + crm_trace("Processing Pacemaker Remote node %s", + pcmk__node_name(this_node)); - pcmk__scan_min_int(crm_element_value(state, XML_NODE_IS_MAINTENANCE), + pcmk__scan_min_int(crm_element_value(state, PCMK__XA_NODE_IN_MAINTENANCE), &(this_node->details->remote_maintenance), 0); rsc = this_node->details->remote_rsc; @@ -1039,33 +1069,45 @@ unpack_handle_remote_attrs(pcmk_node_t *this_node, const xmlNode *state, this_node->details->unclean = FALSE; this_node->details->unseen = FALSE; } - attrs = find_xml_node(state, XML_TAG_TRANSIENT_NODEATTRS, FALSE); + attrs = pcmk__xe_first_child(state, PCMK__XE_TRANSIENT_ATTRIBUTES, NULL, + NULL); add_node_attrs(attrs, this_node, TRUE, scheduler); if (pe__shutdown_requested(this_node)) { - crm_info("%s is shutting down", pe__node_name(this_node)); + crm_info("%s is shutting down", pcmk__node_name(this_node)); this_node->details->shutdown = TRUE; } - - if (crm_is_true(pe_node_attribute_raw(this_node, "standby"))) { - crm_info("%s is in standby mode", pe__node_name(this_node)); + + if (crm_is_true(pcmk__node_attr(this_node, PCMK_NODE_ATTR_STANDBY, NULL, + pcmk__rsc_node_current))) { + crm_info("%s is in standby mode", pcmk__node_name(this_node)); this_node->details->standby = TRUE; } - if (crm_is_true(pe_node_attribute_raw(this_node, "maintenance")) || - ((rsc != NULL) && !pcmk_is_set(rsc->flags, pcmk_rsc_managed))) { - crm_info("%s is in maintenance mode", pe__node_name(this_node)); + if (crm_is_true(pcmk__node_attr(this_node, PCMK_NODE_ATTR_MAINTENANCE, NULL, + pcmk__rsc_node_current)) + || ((rsc != NULL) && !pcmk_is_set(rsc->flags, pcmk_rsc_managed))) { + crm_info("%s is in maintenance mode", pcmk__node_name(this_node)); this_node->details->maintenance = TRUE; } - resource_discovery_enabled = pe_node_attribute_raw(this_node, XML_NODE_ATTR_RSC_DISCOVERY); - if (resource_discovery_enabled && !crm_is_true(resource_discovery_enabled)) { - if (pe__is_remote_node(this_node) + discovery = pcmk__node_attr(this_node, + PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED, + NULL, pcmk__rsc_node_current); + if ((discovery != NULL) && !crm_is_true(discovery)) { + pcmk__warn_once(pcmk__wo_rdisc_enabled, + "Support for the " + PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED + " node attribute is deprecated and will be removed" + " (and behave as 'true') in a future release."); + + if (pcmk__is_remote_node(this_node) && !pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) { - crm_warn("Ignoring " XML_NODE_ATTR_RSC_DISCOVERY - " attribute on Pacemaker Remote node %s" - " because fencing is disabled", - pe__node_name(this_node)); + pcmk__config_warn("Ignoring " + PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED + " attribute on Pacemaker Remote node %s" + " because fencing is disabled", + pcmk__node_name(this_node)); } else { /* This is either a remote node with fencing enabled, or a guest * node. We don't care whether fencing is enabled when fencing guest @@ -1073,7 +1115,7 @@ unpack_handle_remote_attrs(pcmk_node_t *this_node, const xmlNode *state, * resource. */ crm_info("%s has resource discovery disabled", - pe__node_name(this_node)); + pcmk__node_name(this_node)); this_node->details->rsc_discovery_enabled = FALSE; } } @@ -1092,26 +1134,33 @@ unpack_transient_attributes(const xmlNode *state, pcmk_node_t *node, pcmk_scheduler_t *scheduler) { const char *discovery = NULL; - const xmlNode *attrs = find_xml_node(state, XML_TAG_TRANSIENT_NODEATTRS, - FALSE); + const xmlNode *attrs = pcmk__xe_first_child(state, + PCMK__XE_TRANSIENT_ATTRIBUTES, + NULL, NULL); add_node_attrs(attrs, node, TRUE, scheduler); - if (crm_is_true(pe_node_attribute_raw(node, "standby"))) { - crm_info("%s is in standby mode", pe__node_name(node)); + if (crm_is_true(pcmk__node_attr(node, PCMK_NODE_ATTR_STANDBY, NULL, + pcmk__rsc_node_current))) { + crm_info("%s is in standby mode", pcmk__node_name(node)); node->details->standby = TRUE; } - if (crm_is_true(pe_node_attribute_raw(node, "maintenance"))) { - crm_info("%s is in maintenance mode", pe__node_name(node)); + if (crm_is_true(pcmk__node_attr(node, PCMK_NODE_ATTR_MAINTENANCE, NULL, + pcmk__rsc_node_current))) { + crm_info("%s is in maintenance mode", pcmk__node_name(node)); node->details->maintenance = TRUE; } - discovery = pe_node_attribute_raw(node, XML_NODE_ATTR_RSC_DISCOVERY); + discovery = pcmk__node_attr(node, + PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED, + NULL, pcmk__rsc_node_current); if ((discovery != NULL) && !crm_is_true(discovery)) { - crm_warn("Ignoring " XML_NODE_ATTR_RSC_DISCOVERY - " attribute for %s because disabling resource discovery " - "is not allowed for cluster nodes", pe__node_name(node)); + pcmk__config_warn("Ignoring " + PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED + " attribute for %s because disabling resource" + " discovery is not allowed for cluster nodes", + pcmk__node_name(node)); } } @@ -1120,9 +1169,9 @@ unpack_transient_attributes(const xmlNode *state, pcmk_node_t *node, * \brief Unpack a node state entry (first pass) * * Unpack one node state entry from status. This unpacks information from the - * node_state element itself and node attributes inside it, but not the - * resource history inside it. Multiple passes through the status are needed to - * fully unpack everything. + * \C PCMK__XE_NODE_STATE element itself and node attributes inside it, but not + * the resource history inside it. Multiple passes through the status are needed + * to fully unpack everything. * * \param[in] state CIB node state XML * \param[in,out] scheduler Scheduler data @@ -1134,40 +1183,41 @@ unpack_node_state(const xmlNode *state, pcmk_scheduler_t *scheduler) const char *uname = NULL; pcmk_node_t *this_node = NULL; - id = crm_element_value(state, XML_ATTR_ID); + id = crm_element_value(state, PCMK_XA_ID); if (id == NULL) { - crm_warn("Ignoring malformed " XML_CIB_TAG_STATE " entry without " - XML_ATTR_ID); + pcmk__config_err("Ignoring invalid " PCMK__XE_NODE_STATE " entry without " + PCMK_XA_ID); + crm_log_xml_info(state, "missing-id"); return; } - uname = crm_element_value(state, XML_ATTR_UNAME); + uname = crm_element_value(state, PCMK_XA_UNAME); if (uname == NULL) { /* If a joining peer makes the cluster acquire the quorum from corosync * meanwhile it has not joined CPG membership of pacemaker-controld yet, - * it's possible that the created node_state entry doesn't have an uname - * yet. We should recognize the node as `pending` and wait for it to - * join CPG. + * it's possible that the created PCMK__XE_NODE_STATE entry doesn't have + * a PCMK_XA_UNAME yet. We should recognize the node as `pending` and + * wait for it to join CPG. */ - crm_trace("Handling " XML_CIB_TAG_STATE " entry with id=\"%s\" without " - XML_ATTR_UNAME, id); + crm_trace("Handling " PCMK__XE_NODE_STATE " entry with id=\"%s\" " + "without " PCMK_XA_UNAME, + id); } this_node = pe_find_node_any(scheduler->nodes, id, uname); if (this_node == NULL) { - pcmk__config_warn("Ignoring recorded node state for id=\"%s\" (%s) " - "because it is no longer in the configuration", - id, pcmk__s(uname, "uname unknown")); + crm_notice("Ignoring recorded state for removed node with name %s and " + PCMK_XA_ID " %s", pcmk__s(uname, "unknown"), id); return; } - if (pe__is_guest_or_remote_node(this_node)) { + if (pcmk__is_pacemaker_remote_node(this_node)) { /* We can't determine the online status of Pacemaker Remote nodes until * after all resource history has been unpacked. In this first pass, we * do need to mark whether the node has been fenced, as this plays a * role during unpacking cluster node resource state. */ - pcmk__scan_min_int(crm_element_value(state, XML_NODE_IS_FENCED), + pcmk__scan_min_int(crm_element_value(state, PCMK__XA_NODE_FENCED), &(this_node->details->remote_was_fenced), 0); return; } @@ -1181,7 +1231,7 @@ unpack_node_state(const xmlNode *state, pcmk_scheduler_t *scheduler) this_node->details->unseen = FALSE; crm_trace("Determining online status of cluster node %s (id %s)", - pe__node_name(this_node), id); + pcmk__node_name(this_node), id); determine_online_status(state, this_node, scheduler); if (!pcmk_is_set(scheduler->flags, pcmk_sched_quorate) @@ -1219,18 +1269,20 @@ unpack_node_history(const xmlNode *status, bool fence, { int rc = pcmk_rc_ok; - // Loop through all node_state entries in CIB status - for (const xmlNode *state = first_named_child(status, XML_CIB_TAG_STATE); - state != NULL; state = crm_next_same_xml(state)) { + // Loop through all PCMK__XE_NODE_STATE entries in CIB status + for (const xmlNode *state = pcmk__xe_first_child(status, + PCMK__XE_NODE_STATE, NULL, + NULL); + state != NULL; state = pcmk__xe_next_same(state)) { - const char *id = ID(state); - const char *uname = crm_element_value(state, XML_ATTR_UNAME); + const char *id = pcmk__xe_id(state); + const char *uname = crm_element_value(state, PCMK_XA_UNAME); pcmk_node_t *this_node = NULL; if ((id == NULL) || (uname == NULL)) { // Warning already logged in first pass through status section crm_trace("Not unpacking resource history from malformed " - XML_CIB_TAG_STATE " without id and/or uname"); + PCMK__XE_NODE_STATE " without id and/or uname"); continue; } @@ -1251,7 +1303,7 @@ unpack_node_history(const xmlNode *status, bool fence, if (fence) { // We're processing all remaining nodes - } else if (pe__is_guest_node(this_node)) { + } else if (pcmk__is_guest_or_bundle_node(this_node)) { /* We can unpack a guest node's history only after we've unpacked * other resource history to the point that we know that the node's * connection and containing resource are both up. @@ -1266,7 +1318,7 @@ unpack_node_history(const xmlNode *status, bool fence, continue; } - } else if (pe__is_remote_node(this_node)) { + } else if (pcmk__is_remote_node(this_node)) { /* We can unpack a remote node's history only after we've unpacked * other resource history to the point that we know that the node's * connection is up, with the exception of when shutdown locks are @@ -1296,7 +1348,7 @@ unpack_node_history(const xmlNode *status, bool fence, continue; } - if (pe__is_guest_or_remote_node(this_node)) { + if (pcmk__is_pacemaker_remote_node(this_node)) { determine_remote_online_status(scheduler, this_node); unpack_handle_remote_attrs(this_node, state, scheduler); } @@ -1326,13 +1378,13 @@ unpack_status(xmlNode *status, pcmk_scheduler_t *scheduler) scheduler->tickets = pcmk__strkey_table(free, destroy_ticket); } - for (state = pcmk__xe_first_child(status); state != NULL; + for (state = pcmk__xe_first_child(status, NULL, NULL, NULL); state != NULL; state = pcmk__xe_next(state)) { - if (pcmk__str_eq((const char *)state->name, XML_CIB_TAG_TICKETS, pcmk__str_none)) { + if (pcmk__xe_is(state, PCMK_XE_TICKETS)) { unpack_tickets_state((xmlNode *) state, scheduler); - } else if (pcmk__str_eq((const char *)state->name, XML_CIB_TAG_STATE, pcmk__str_none)) { + } else if (pcmk__xe_is(state, PCMK__XE_NODE_STATE)) { unpack_node_state(state, scheduler); } } @@ -1353,7 +1405,7 @@ unpack_status(xmlNode *status, pcmk_scheduler_t *scheduler) if (scheduler->stop_needed != NULL) { for (GList *item = scheduler->stop_needed; item; item = item->next) { pcmk_resource_t *container = item->data; - pcmk_node_t *node = pe__current_node(container); + pcmk_node_t *node = pcmk__current_node(container); if (node) { stop_action(container, node, FALSE); @@ -1370,7 +1422,7 @@ unpack_status(xmlNode *status, pcmk_scheduler_t *scheduler) for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *this_node = gIter->data; - if (!pe__is_guest_or_remote_node(this_node)) { + if (!pcmk__is_pacemaker_remote_node(this_node)) { continue; } if (this_node->details->shutdown @@ -1390,7 +1442,7 @@ unpack_status(xmlNode *status, pcmk_scheduler_t *scheduler) * \internal * \brief Unpack node's time when it became a member at the cluster layer * - * \param[in] node_state Node's node_state entry + * \param[in] node_state Node's \c PCMK__XE_NODE_STATE entry * \param[in,out] scheduler Scheduler data * * \return Epoch time when node became a cluster member @@ -1410,7 +1462,7 @@ unpack_node_member(const xmlNode *node_state, pcmk_scheduler_t *scheduler) /* If in_ccm=0, we'll return 0 here. If in_ccm=1, either the entry was * recorded as a boolean for a DC < 2.1.7, or the node is pending * shutdown and has left the CPG, in which case it was set to 1 to avoid - * fencing for node-pending-timeout. + * fencing for PCMK_OPT_NODE_PENDING_TIMEOUT. * * We return the effective time for in_ccm=1 because what's important to * avoid fencing is that effective time minus this value is less than @@ -1424,7 +1476,7 @@ unpack_node_member(const xmlNode *node_state, pcmk_scheduler_t *scheduler) if ((pcmk__scan_ll(member_time, &when_member, 0LL) != pcmk_rc_ok) || (when_member < 0LL)) { crm_warn("Unrecognized value '%s' for " PCMK__XA_IN_CCM - " in " XML_CIB_TAG_STATE " entry", member_time); + " in " PCMK__XE_NODE_STATE " entry", member_time); return -1LL; } return when_member; @@ -1435,7 +1487,7 @@ unpack_node_member(const xmlNode *node_state, pcmk_scheduler_t *scheduler) * \internal * \brief Unpack node's time when it became online in process group * - * \param[in] node_state Node's node_state entry + * \param[in] node_state Node's \c PCMK__XE_NODE_STATE entry * * \return Epoch time when node became online in process group (or 0 if not * online, or 1 for legacy online entries) @@ -1443,14 +1495,14 @@ unpack_node_member(const xmlNode *node_state, pcmk_scheduler_t *scheduler) static long long unpack_node_online(const xmlNode *node_state) { - const char *peer_time = crm_element_value(node_state, PCMK__XA_CRMD); + const char *peer_time = crm_element_value(node_state, PCMK_XA_CRMD); // @COMPAT Entries recorded for DCs < 2.1.7 have "online" or "offline" - if (pcmk__str_eq(peer_time, OFFLINESTATUS, + if (pcmk__str_eq(peer_time, PCMK_VALUE_OFFLINE, pcmk__str_casei|pcmk__str_null_matches)) { return 0LL; - } else if (pcmk__str_eq(peer_time, ONLINESTATUS, pcmk__str_casei)) { + } else if (pcmk__str_eq(peer_time, PCMK_VALUE_ONLINE, pcmk__str_casei)) { return 1LL; } else { @@ -1458,8 +1510,8 @@ unpack_node_online(const xmlNode *node_state) if ((pcmk__scan_ll(peer_time, &when_online, 0LL) != pcmk_rc_ok) || (when_online < 0)) { - crm_warn("Unrecognized value '%s' for " PCMK__XA_CRMD " in " - XML_CIB_TAG_STATE " entry, assuming offline", peer_time); + crm_warn("Unrecognized value '%s' for " PCMK_XA_CRMD " in " + PCMK__XE_NODE_STATE " entry, assuming offline", peer_time); return 0LL; } return when_online; @@ -1471,7 +1523,7 @@ unpack_node_online(const xmlNode *node_state) * \brief Unpack node attribute for user-requested fencing * * \param[in] node Node to check - * \param[in] node_state Node's node_state entry in CIB status + * \param[in] node_state Node's \c PCMK__XE_NODE_STATE entry in CIB status * * \return \c true if fencing has been requested for \p node, otherwise \c false */ @@ -1480,7 +1532,8 @@ unpack_node_terminate(const pcmk_node_t *node, const xmlNode *node_state) { long long value = 0LL; int value_i = 0; - const char *value_s = pe_node_attribute_raw(node, PCMK_NODE_ATTR_TERMINATE); + const char *value_s = pcmk__node_attr(node, PCMK_NODE_ATTR_TERMINATE, + NULL, pcmk__rsc_node_current); // Value may be boolean or an epoch time if (crm_str_to_boolean(value_s, &value_i) == 1) { @@ -1490,7 +1543,7 @@ unpack_node_terminate(const pcmk_node_t *node, const xmlNode *node_state) return (value > 0); } crm_warn("Ignoring unrecognized value '%s' for " PCMK_NODE_ATTR_TERMINATE - "node attribute for %s", value_s, pe__node_name(node)); + "node attribute for %s", value_s, pcmk__node_name(node)); return false; } @@ -1501,12 +1554,12 @@ determine_online_status_no_fencing(pcmk_scheduler_t *scheduler, { gboolean online = FALSE; const char *join = crm_element_value(node_state, PCMK__XA_JOIN); - const char *exp_state = crm_element_value(node_state, PCMK__XA_EXPECTED); + const char *exp_state = crm_element_value(node_state, PCMK_XA_EXPECTED); long long when_member = unpack_node_member(node_state, scheduler); long long when_online = unpack_node_online(node_state); if (when_member <= 0) { - crm_trace("Node %s is %sdown", pe__node_name(this_node), + crm_trace("Node %s is %sdown", pcmk__node_name(this_node), ((when_member < 0)? "presumed " : "")); } else if (when_online > 0) { @@ -1514,20 +1567,20 @@ determine_online_status_no_fencing(pcmk_scheduler_t *scheduler, online = TRUE; } else { crm_debug("Node %s is not ready to run resources: %s", - pe__node_name(this_node), join); + pcmk__node_name(this_node), join); } } else if (this_node->details->expected_up == FALSE) { crm_trace("Node %s controller is down: " "member@%lld online@%lld join=%s expected=%s", - pe__node_name(this_node), when_member, when_online, + pcmk__node_name(this_node), when_member, when_online, pcmk__s(join, "<null>"), pcmk__s(exp_state, "<null>")); } else { /* mark it unclean */ pe_fence_node(scheduler, this_node, "peer is unexpectedly down", FALSE); crm_info("Node %s member@%lld online@%lld join=%s expected=%s", - pe__node_name(this_node), when_member, when_online, + pcmk__node_name(this_node), when_member, when_online, pcmk__s(join, "<null>"), pcmk__s(exp_state, "<null>")); } return online; @@ -1543,7 +1596,7 @@ determine_online_status_no_fencing(pcmk_scheduler_t *scheduler, * \param[in] when_online Epoch time when node joined controller group * * \return true if node has been pending (on the way up) longer than - * node-pending-timeout, otherwise false + * \c PCMK_OPT_NODE_PENDING_TIMEOUT, otherwise false * \note This will also update the cluster's recheck time if appropriate. */ static inline bool @@ -1573,35 +1626,35 @@ determine_online_status_fencing(pcmk_scheduler_t *scheduler, { bool termination_requested = unpack_node_terminate(this_node, node_state); const char *join = crm_element_value(node_state, PCMK__XA_JOIN); - const char *exp_state = crm_element_value(node_state, PCMK__XA_EXPECTED); + const char *exp_state = crm_element_value(node_state, PCMK_XA_EXPECTED); long long when_member = unpack_node_member(node_state, scheduler); long long when_online = unpack_node_online(node_state); /* - PCMK__XA_JOIN ::= member|down|pending|banned - - PCMK__XA_EXPECTED ::= member|down + - PCMK_XA_EXPECTED ::= member|down @COMPAT with entries recorded for DCs < 2.1.7 - PCMK__XA_IN_CCM ::= true|false - - PCMK__XA_CRMD ::= online|offline + - PCMK_XA_CRMD ::= online|offline Since crm_feature_set 3.18.0 (pacemaker-2.1.7): - PCMK__XA_IN_CCM ::= <timestamp>|0 Since when node has been a cluster member. A value 0 of means the node is not a cluster member. - - PCMK__XA_CRMD ::= <timestamp>|0 + - PCMK_XA_CRMD ::= <timestamp>|0 Since when peer has been online in CPG. A value 0 means the peer is offline in CPG. */ crm_trace("Node %s member@%lld online@%lld join=%s expected=%s%s", - pe__node_name(this_node), when_member, when_online, + pcmk__node_name(this_node), when_member, when_online, pcmk__s(join, "<null>"), pcmk__s(exp_state, "<null>"), (termination_requested? " (termination requested)" : "")); if (this_node->details->shutdown) { - crm_debug("%s is shutting down", pe__node_name(this_node)); + crm_debug("%s is shutting down", pcmk__node_name(this_node)); /* Slightly different criteria since we can't shut down a dead peer */ return (when_online > 0); @@ -1620,7 +1673,7 @@ determine_online_status_fencing(pcmk_scheduler_t *scheduler, } else if (termination_requested) { if ((when_member <= 0) && (when_online <= 0) && pcmk__str_eq(join, CRMD_JOINSTATE_DOWN, pcmk__str_none)) { - crm_info("%s was fenced as requested", pe__node_name(this_node)); + crm_info("%s was fenced as requested", pcmk__node_name(this_node)); return false; } pe_fence_node(scheduler, this_node, "fencing was requested", false); @@ -1635,17 +1688,17 @@ determine_online_status_fencing(pcmk_scheduler_t *scheduler, } else if ((when_member > 0) || (when_online > 0)) { crm_info("- %s is not ready to run resources", - pe__node_name(this_node)); + pcmk__node_name(this_node)); this_node->details->standby = TRUE; this_node->details->pending = TRUE; } else { crm_trace("%s is down or still coming up", - pe__node_name(this_node)); + pcmk__node_name(this_node)); } } else if (when_member <= 0) { - // Consider `priority-fencing-delay` for lost nodes + // Consider PCMK_OPT_PRIORITY_FENCING_DELAY for lost nodes pe_fence_node(scheduler, this_node, "peer is no longer part of the cluster", TRUE); @@ -1656,11 +1709,12 @@ determine_online_status_fencing(pcmk_scheduler_t *scheduler, /* Everything is running at this point, now check join state */ } else if (pcmk__str_eq(join, CRMD_JOINSTATE_MEMBER, pcmk__str_none)) { - crm_info("%s is active", pe__node_name(this_node)); + crm_info("%s is active", pcmk__node_name(this_node)); } else if (pcmk__str_any_of(join, CRMD_JOINSTATE_PENDING, CRMD_JOINSTATE_DOWN, NULL)) { - crm_info("%s is not ready to run resources", pe__node_name(this_node)); + crm_info("%s is not ready to run resources", + pcmk__node_name(this_node)); this_node->details->standby = TRUE; this_node->details->pending = TRUE; @@ -1750,7 +1804,7 @@ determine_online_status(const xmlNode *node_state, pcmk_node_t *this_node, pcmk_scheduler_t *scheduler) { gboolean online = FALSE; - const char *exp_state = crm_element_value(node_state, PCMK__XA_EXPECTED); + const char *exp_state = crm_element_value(node_state, PCMK_XA_EXPECTED); CRM_CHECK(this_node != NULL, return); @@ -1786,30 +1840,30 @@ determine_online_status(const xmlNode *node_state, pcmk_node_t *this_node, } else { /* remove node from contention */ this_node->fixed = TRUE; // @COMPAT deprecated and unused - this_node->weight = -INFINITY; + this_node->weight = -PCMK_SCORE_INFINITY; } if (online && this_node->details->shutdown) { /* don't run resources here */ this_node->fixed = TRUE; // @COMPAT deprecated and unused - this_node->weight = -INFINITY; + this_node->weight = -PCMK_SCORE_INFINITY; } if (this_node->details->type == node_ping) { - crm_info("%s is not a Pacemaker node", pe__node_name(this_node)); + crm_info("%s is not a Pacemaker node", pcmk__node_name(this_node)); } else if (this_node->details->unclean) { - pe_proc_warn("%s is unclean", pe__node_name(this_node)); + pcmk__sched_warn("%s is unclean", pcmk__node_name(this_node)); } else if (this_node->details->online) { - crm_info("%s is %s", pe__node_name(this_node), + crm_info("%s is %s", pcmk__node_name(this_node), this_node->details->shutdown ? "shutting down" : this_node->details->pending ? "pending" : this_node->details->standby ? "standby" : this_node->details->maintenance ? "maintenance" : "online"); } else { - crm_trace("%s is offline", pe__node_name(this_node)); + crm_trace("%s is offline", pcmk__node_name(this_node)); } } @@ -1891,8 +1945,7 @@ clone_zero(const char *last_rsc_id) char *zero = NULL; CRM_ASSERT(end); - zero = calloc(base_name_len + 3, sizeof(char)); - CRM_ASSERT(zero); + zero = pcmk__assert_alloc(base_name_len + 3, sizeof(char)); memcpy(zero, last_rsc_id, base_name_len); zero[base_name_len] = ':'; zero[base_name_len + 1] = '0'; @@ -1904,10 +1957,10 @@ create_fake_resource(const char *rsc_id, const xmlNode *rsc_entry, pcmk_scheduler_t *scheduler) { pcmk_resource_t *rsc = NULL; - xmlNode *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); + xmlNode *xml_rsc = pcmk__xe_create(NULL, PCMK_XE_PRIMITIVE); - copy_in_properties(xml_rsc, rsc_entry); - crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id); + pcmk__xe_copy_attrs(xml_rsc, rsc_entry, pcmk__xaf_none); + crm_xml_add(xml_rsc, PCMK_XA_ID, rsc_id); crm_log_xml_debug(xml_rsc, "Orphan resource"); if (pe__unpack_resource(xml_rsc, &rsc, NULL, scheduler) != pcmk_rc_ok) { @@ -1918,9 +1971,10 @@ create_fake_resource(const char *rsc_id, const xmlNode *rsc_entry, pcmk_node_t *node; crm_debug("Detected orphaned remote node %s", rsc_id); - node = pe_find_node(scheduler->nodes, rsc_id); + node = pcmk_find_node(scheduler, rsc_id); if (node == NULL) { - node = pe_create_node(rsc_id, rsc_id, "remote", NULL, scheduler); + node = pe_create_node(rsc_id, rsc_id, PCMK_VALUE_REMOTE, NULL, + scheduler); } link_rsc2remotenode(scheduler, rsc); @@ -1930,12 +1984,12 @@ create_fake_resource(const char *rsc_id, const xmlNode *rsc_entry, } } - if (crm_element_value(rsc_entry, XML_RSC_ATTR_CONTAINER)) { + if (crm_element_value(rsc_entry, PCMK__META_CONTAINER)) { /* This orphaned rsc needs to be mapped to a container. */ crm_trace("Detected orphaned container filler %s", rsc_id); - pe__set_resource_flags(rsc, pcmk_rsc_removed_filler); + pcmk__set_rsc_flags(rsc, pcmk_rsc_removed_filler); } - pe__set_resource_flags(rsc, pcmk_rsc_removed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_removed); scheduler->resources = g_list_append(scheduler->resources, rsc); return rsc; } @@ -1961,8 +2015,8 @@ create_anonymous_orphan(pcmk_resource_t *parent, const char *rsc_id, pcmk_resource_t *orphan = top->fns->find_rsc(top, rsc_id, NULL, pcmk_rsc_match_clone_only); - pe_rsc_debug(parent, "Created orphan %s for %s: %s on %s", - top->id, parent->id, rsc_id, pe__node_name(node)); + pcmk__rsc_debug(parent, "Created orphan %s for %s: %s on %s", + top->id, parent->id, rsc_id, pcmk__node_name(node)); return orphan; } @@ -1972,8 +2026,9 @@ create_anonymous_orphan(pcmk_resource_t *parent, const char *rsc_id, * * Return a child instance of the specified anonymous clone, in order of * preference: (1) the instance running on the specified node, if any; - * (2) an inactive instance (i.e. within the total of clone-max instances); - * (3) a newly created orphan (i.e. clone-max instances are already active). + * (2) an inactive instance (i.e. within the total of \c PCMK_META_CLONE_MAX + * instances); (3) a newly created orphan (that is, \c PCMK_META_CLONE_MAX + * instances are already active). * * \param[in,out] scheduler Scheduler data * \param[in] node Node on which to check for instance @@ -1989,13 +2044,11 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, pcmk_resource_t *inactive_instance = NULL; gboolean skip_inactive = FALSE; - CRM_ASSERT(parent != NULL); - CRM_ASSERT(pe_rsc_is_clone(parent)); - CRM_ASSERT(!pcmk_is_set(parent->flags, pcmk_rsc_unique)); + CRM_ASSERT(pcmk__is_anonymous_clone(parent)); // Check for active (or partially active, for cloned groups) instance - pe_rsc_trace(parent, "Looking for %s on %s in %s", - rsc_id, pe__node_name(node), parent->id); + pcmk__rsc_trace(parent, "Looking for %s on %s in %s", + rsc_id, pcmk__node_name(node), parent->id); for (rIter = parent->children; rsc == NULL && rIter; rIter = rIter->next) { GList *locations = NULL; pcmk_resource_t *child = rIter->data; @@ -2010,8 +2063,8 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, * (1) when child is a cloned group and we have already unpacked the * history of another member of the group on the same node; * (2) when we've already unpacked the history of another numbered - * instance on the same node (which can happen if globally-unique - * was flipped from true to false); and + * instance on the same node (which can happen if + * PCMK_META_GLOBALLY_UNIQUE was flipped from true to false); and * (3) when we re-run calculations on the same scheduler data as part of * a simulation. */ @@ -2023,7 +2076,7 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, */ CRM_LOG_ASSERT(locations->next == NULL); - if (((pcmk_node_t *) locations->data)->details == node->details) { + if (pcmk__same_node((pcmk_node_t *) locations->data, node)) { /* This child instance is active on the requested node, so check * for a corresponding configured resource. We use find_rsc() * instead of child because child may be a cloned group, and we @@ -2036,26 +2089,26 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, if (rsc) { /* If there are multiple instance history entries for an * anonymous clone in a single node's history (which can - * happen if globally-unique is switched from true to - * false), we want to consider the instances beyond the + * happen if PCMK_META_GLOBALLY_UNIQUE is switched from true + * to false), we want to consider the instances beyond the * first as orphans, even if there are inactive instance * numbers available. */ if (rsc->running_on) { crm_notice("Active (now-)anonymous clone %s has " "multiple (orphan) instance histories on %s", - parent->id, pe__node_name(node)); + parent->id, pcmk__node_name(node)); skip_inactive = TRUE; rsc = NULL; } else { - pe_rsc_trace(parent, "Resource %s, active", rsc->id); + pcmk__rsc_trace(parent, "Resource %s, active", rsc->id); } } } g_list_free(locations); } else { - pe_rsc_trace(parent, "Resource %s, skip inactive", child->id); + pcmk__rsc_trace(parent, "Resource %s, skip inactive", child->id); if (!skip_inactive && !inactive_instance && !pcmk_is_set(child->flags, pcmk_rsc_blocked)) { // Remember one inactive instance in case we don't find active @@ -2065,8 +2118,9 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, /* ... but don't use it if it was already associated with a * pending action on another node */ - if (inactive_instance && inactive_instance->pending_node - && (inactive_instance->pending_node->details != node->details)) { + if ((inactive_instance != NULL) && + (inactive_instance->pending_node != NULL) && + !pcmk__same_node(inactive_instance->pending_node, node)) { inactive_instance = NULL; } } @@ -2074,15 +2128,17 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, } if ((rsc == NULL) && !skip_inactive && (inactive_instance != NULL)) { - pe_rsc_trace(parent, "Resource %s, empty slot", inactive_instance->id); + pcmk__rsc_trace(parent, "Resource %s, empty slot", + inactive_instance->id); rsc = inactive_instance; } - /* If the resource has "requires" set to "quorum" or "nothing", and we don't - * have a clone instance for every node, we don't want to consume a valid - * instance number for unclean nodes. Such instances may appear to be active - * according to the history, but should be considered inactive, so we can - * start an instance elsewhere. Treat such instances as orphans. + /* If the resource has PCMK_META_REQUIRES set to PCMK_VALUE_QUORUM or + * PCMK_VALUE_NOTHING, and we don't have a clone instance for every node, we + * don't want to consume a valid instance number for unclean nodes. Such + * instances may appear to be active according to the history, but should be + * considered inactive, so we can start an instance elsewhere. Treat such + * instances as orphans. * * An exception is instances running on guest nodes -- since guest node * "fencing" is actually just a resource stop, requires shouldn't apply. @@ -2092,7 +2148,7 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, */ if ((rsc != NULL) && !pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing) && (!node->details->online || node->details->unclean) - && !pe__is_guest_node(node) + && !pcmk__is_guest_or_bundle_node(node) && !pe__is_universal_clone(parent, scheduler)) { rsc = NULL; @@ -2100,7 +2156,7 @@ find_anonymous_clone(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, if (rsc == NULL) { rsc = create_anonymous_orphan(parent, rsc_id, node, scheduler); - pe_rsc_trace(parent, "Resource %s, orphan", rsc->id); + pcmk__rsc_trace(parent, "Resource %s, orphan", rsc->id); } return rsc; } @@ -2117,8 +2173,8 @@ unpack_find_resource(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, if (rsc == NULL) { /* If we didn't find the resource by its name in the operation history, - * check it again as a clone instance. Even when clone-max=0, we create - * a single :0 orphan to match against here. + * check it again as a clone instance. Even when PCMK_META_CLONE_MAX=0, + * we create a single :0 orphan to match against here. */ char *clone0_id = clone_zero(rsc_id); pcmk_resource_t *clone0 = pe_find_resource(scheduler->resources, @@ -2143,9 +2199,9 @@ unpack_find_resource(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, parent = uber_parent(rsc); } - if (pe_rsc_is_anon_clone(parent)) { + if (pcmk__is_anonymous_clone(parent)) { - if (pe_rsc_is_bundled(parent)) { + if (pcmk__is_bundled(parent)) { rsc = pe__find_bundle_replica(parent->parent, node); } else { char *base = clone_strip(rsc_id); @@ -2156,13 +2212,13 @@ unpack_find_resource(pcmk_scheduler_t *scheduler, const pcmk_node_t *node, } } - if (rsc && !pcmk__str_eq(rsc_id, rsc->id, pcmk__str_casei) - && !pcmk__str_eq(rsc_id, rsc->clone_name, pcmk__str_casei)) { + if (rsc && !pcmk__str_eq(rsc_id, rsc->id, pcmk__str_none) + && !pcmk__str_eq(rsc_id, rsc->clone_name, pcmk__str_none)) { pcmk__str_update(&rsc->clone_name, rsc_id); - pe_rsc_debug(rsc, "Internally renamed %s on %s to %s%s", - rsc_id, pe__node_name(node), rsc->id, - (pcmk_is_set(rsc->flags, pcmk_rsc_removed)? " (ORPHAN)" : "")); + pcmk__rsc_debug(rsc, "Internally renamed %s on %s to %s%s", + rsc_id, pcmk__node_name(node), rsc->id, + pcmk_is_set(rsc->flags, pcmk_rsc_removed)? " (ORPHAN)" : ""); } return rsc; } @@ -2172,22 +2228,23 @@ process_orphan_resource(const xmlNode *rsc_entry, const pcmk_node_t *node, pcmk_scheduler_t *scheduler) { pcmk_resource_t *rsc = NULL; - const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + const char *rsc_id = crm_element_value(rsc_entry, PCMK_XA_ID); - crm_debug("Detected orphan resource %s on %s", rsc_id, pe__node_name(node)); + crm_debug("Detected orphan resource %s on %s", + rsc_id, pcmk__node_name(node)); rsc = create_fake_resource(rsc_id, rsc_entry, scheduler); if (rsc == NULL) { return NULL; } if (!pcmk_is_set(scheduler->flags, pcmk_sched_stop_removed_resources)) { - pe__clear_resource_flags(rsc, pcmk_rsc_managed); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); } else { CRM_CHECK(rsc != NULL, return NULL); - pe_rsc_trace(rsc, "Added orphan %s", rsc->id); - resource_location(rsc, NULL, -INFINITY, "__orphan_do_not_run__", - scheduler); + pcmk__rsc_trace(rsc, "Added orphan %s", rsc->id); + resource_location(rsc, NULL, -PCMK_SCORE_INFINITY, + "__orphan_do_not_run__", scheduler); } return rsc; } @@ -2201,9 +2258,9 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, enum action_fail_response save_on_fail = pcmk_on_fail_ignore; CRM_ASSERT(rsc); - pe_rsc_trace(rsc, "Resource %s is %s on %s: on_fail=%s", - rsc->id, role2text(rsc->role), pe__node_name(node), - fail2text(on_fail)); + pcmk__rsc_trace(rsc, "Resource %s is %s on %s: on_fail=%s", + rsc->id, pcmk_role_text(rsc->role), pcmk__node_name(node), + pcmk_on_fail_text(on_fail)); /* process current state */ if (rsc->role != pcmk_role_unknown) { @@ -2213,11 +2270,11 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, if (g_hash_table_lookup(iter->known_on, node->details->id) == NULL) { pcmk_node_t *n = pe__copy_node(node); - pe_rsc_trace(rsc, "%s%s%s known on %s", - rsc->id, - ((rsc->clone_name == NULL)? "" : " also known as "), - ((rsc->clone_name == NULL)? "" : rsc->clone_name), - pe__node_name(n)); + pcmk__rsc_trace(rsc, "%s%s%s known on %s", + rsc->id, + ((rsc->clone_name == NULL)? "" : " also known as "), + ((rsc->clone_name == NULL)? "" : rsc->clone_name), + pcmk__node_name(n)); g_hash_table_insert(iter->known_on, (gpointer) n->details->id, n); } if (pcmk_is_set(iter->flags, pcmk_rsc_unique)) { @@ -2242,14 +2299,14 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, * operation history in the CIB will be cleared, freeing the affected * resource to run again once we are sure we know its state. */ - if (pe__is_guest_node(node)) { - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + if (pcmk__is_guest_or_bundle_node(node)) { + pcmk__set_rsc_flags(rsc, pcmk_rsc_failed|pcmk_rsc_stop_if_failed); should_fence = TRUE; } else if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { - if (pe__is_remote_node(node) && node->details->remote_rsc + if (pcmk__is_remote_node(node) + && (node->details->remote_rsc != NULL) && !pcmk_is_set(node->details->remote_rsc->flags, pcmk_rsc_failed)) { @@ -2293,7 +2350,7 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, break; case pcmk_on_fail_demote: - pe__set_resource_flags(rsc, pcmk_rsc_failed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_failed); demote_action(rsc, node, FALSE); break; @@ -2315,35 +2372,35 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, /* is_managed == FALSE will prevent any * actions being sent for the resource */ - pe__clear_resource_flags(rsc, pcmk_rsc_managed); - pe__set_resource_flags(rsc, pcmk_rsc_blocked); + pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked); break; case pcmk_on_fail_ban: /* make sure it comes up somewhere else * or not at all */ - resource_location(rsc, node, -INFINITY, "__action_migration_auto__", - rsc->cluster); + resource_location(rsc, node, -PCMK_SCORE_INFINITY, + "__action_migration_auto__", rsc->cluster); break; case pcmk_on_fail_stop: - pe__set_next_role(rsc, pcmk_role_stopped, "on-fail=stop"); + pe__set_next_role(rsc, pcmk_role_stopped, + PCMK_META_ON_FAIL "=" PCMK_VALUE_STOP); break; case pcmk_on_fail_restart: if ((rsc->role != pcmk_role_stopped) && (rsc->role != pcmk_role_unknown)) { - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__set_rsc_flags(rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); stop_action(rsc, node, FALSE); } break; case pcmk_on_fail_restart_container: - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); - if (rsc->container && pe_rsc_is_bundled(rsc)) { + pcmk__set_rsc_flags(rsc, pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + if ((rsc->container != NULL) && pcmk__is_bundled(rsc)) { /* A bundle's remote connection can run on a different node than * the bundle's container. We don't necessarily know where the * container is running yet, so remember it and add a stop @@ -2360,17 +2417,14 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, break; case pcmk_on_fail_reset_remote: - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_failed|pcmk_rsc_stop_if_failed); if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) { tmpnode = NULL; if (rsc->is_remote_node) { - tmpnode = pe_find_node(rsc->cluster->nodes, rsc->id); + tmpnode = pcmk_find_node(rsc->cluster, rsc->id); } - if (tmpnode && - pe__is_remote_node(tmpnode) && - tmpnode->details->remote_was_fenced == 0) { - + if (pcmk__is_remote_node(tmpnode) + && !(tmpnode->details->remote_was_fenced)) { /* The remote connection resource failed in a way that * should result in fencing the remote node. */ @@ -2397,7 +2451,7 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, * result in a fencing operation regardless if we're going to attempt to * reconnect to the remote-node in this transition or not. */ if (pcmk_is_set(rsc->flags, pcmk_rsc_failed) && rsc->is_remote_node) { - tmpnode = pe_find_node(rsc->cluster->nodes, rsc->id); + tmpnode = pcmk_find_node(rsc->cluster, rsc->id); if (tmpnode && tmpnode->details->unclean) { tmpnode->details->unseen = FALSE; } @@ -2407,13 +2461,13 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, && (rsc->role != pcmk_role_unknown)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) { if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { - pcmk__config_warn("Detected active orphan %s running on %s", - rsc->id, pe__node_name(node)); + crm_notice("Removed resource %s is active on %s and will be " + "stopped when possible", + rsc->id, pcmk__node_name(node)); } else { - pcmk__config_warn("Resource '%s' must be stopped manually on " - "%s because cluster is configured not to " - "stop active orphans", - rsc->id, pe__node_name(node)); + crm_notice("Removed resource %s must be stopped manually on %s " + "because " PCMK_OPT_STOP_ORPHAN_RESOURCES + " is set to false", rsc->id, pcmk__node_name(node)); } } @@ -2424,11 +2478,11 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, break; case pcmk_on_fail_demote: case pcmk_on_fail_block: - pe__set_resource_flags(rsc, pcmk_rsc_failed); + pcmk__set_rsc_flags(rsc, pcmk_rsc_failed); break; default: - pe__set_resource_flags(rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__set_rsc_flags(rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); break; } @@ -2436,7 +2490,8 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, /* Only do this for older status sections that included instance numbers * Otherwise stopped instances will appear as orphans */ - pe_rsc_trace(rsc, "Resetting clone_name %s for %s (stopped)", rsc->clone_name, rsc->id); + pcmk__rsc_trace(rsc, "Resetting clone_name %s for %s (stopped)", + rsc->clone_name, rsc->id); free(rsc->clone_name); rsc->clone_name = NULL; @@ -2448,7 +2503,7 @@ process_rsc_state(pcmk_resource_t *rsc, pcmk_node_t *node, for (; gIter != NULL; gIter = gIter->next) { pcmk_action_t *stop = (pcmk_action_t *) gIter->data; - pe__set_action_flags(stop, pcmk_action_optional); + pcmk__set_action_flags(stop, pcmk_action_optional); } g_list_free(possible_matches); @@ -2479,51 +2534,52 @@ process_recurring(pcmk_node_t *node, pcmk_resource_t *rsc, GList *gIter = sorted_op_list; CRM_ASSERT(rsc); - pe_rsc_trace(rsc, "%s: Start index %d, stop index = %d", rsc->id, start_index, stop_index); + pcmk__rsc_trace(rsc, "%s: Start index %d, stop index = %d", + rsc->id, start_index, stop_index); for (; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode *) gIter->data; guint interval_ms = 0; char *key = NULL; - const char *id = ID(rsc_op); + const char *id = pcmk__xe_id(rsc_op); counter++; if (node->details->online == FALSE) { - pe_rsc_trace(rsc, "Skipping %s on %s: node is offline", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping %s on %s: node is offline", + rsc->id, pcmk__node_name(node)); break; /* Need to check if there's a monitor for role="Stopped" */ } else if (start_index < stop_index && counter <= stop_index) { - pe_rsc_trace(rsc, "Skipping %s on %s: resource is not active", - id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping %s on %s: resource is not active", + id, pcmk__node_name(node)); continue; } else if (counter < start_index) { - pe_rsc_trace(rsc, "Skipping %s on %s: old %d", - id, pe__node_name(node), counter); + pcmk__rsc_trace(rsc, "Skipping %s on %s: old %d", + id, pcmk__node_name(node), counter); continue; } - crm_element_value_ms(rsc_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms); if (interval_ms == 0) { - pe_rsc_trace(rsc, "Skipping %s on %s: non-recurring", - id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping %s on %s: non-recurring", + id, pcmk__node_name(node)); continue; } - status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); + status = crm_element_value(rsc_op, PCMK__XA_OP_STATUS); if (pcmk__str_eq(status, "-1", pcmk__str_casei)) { - pe_rsc_trace(rsc, "Skipping %s on %s: status", - id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Skipping %s on %s: status", + id, pcmk__node_name(node)); continue; } - task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); + task = crm_element_value(rsc_op, PCMK_XA_OPERATION); /* create the action */ key = pcmk__op_key(rsc->id, task, interval_ms); - pe_rsc_trace(rsc, "Creating %s on %s", key, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Creating %s on %s", key, pcmk__node_name(node)); custom_action(rsc, key, task, node, TRUE, scheduler); } } @@ -2546,8 +2602,8 @@ calculate_active_ops(const GList *sorted_op_list, int *start_index, counter++; - task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); - status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); + task = crm_element_value(rsc_op, PCMK_XA_OPERATION); + status = crm_element_value(rsc_op, PCMK__XA_OP_STATUS); if (pcmk__str_eq(task, PCMK_ACTION_STOP, pcmk__str_casei) && pcmk__str_eq(status, "0", pcmk__str_casei)) { @@ -2560,7 +2616,7 @@ calculate_active_ops(const GList *sorted_op_list, int *start_index, } else if ((implied_monitor_start <= *stop_index) && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)) { - const char *rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC); + const char *rc = crm_element_value(rsc_op, PCMK__XA_RC_CODE); if (pcmk__strcase_any_of(rc, "0", "8", NULL)) { implied_monitor_start = counter; @@ -2587,14 +2643,14 @@ unpack_shutdown_lock(const xmlNode *rsc_entry, pcmk_resource_t *rsc, { time_t lock_time = 0; // When lock started (i.e. node shutdown time) - if ((crm_element_value_epoch(rsc_entry, XML_CONFIG_ATTR_SHUTDOWN_LOCK, + if ((crm_element_value_epoch(rsc_entry, PCMK_OPT_SHUTDOWN_LOCK, &lock_time) == pcmk_ok) && (lock_time != 0)) { if ((scheduler->shutdown_lock > 0) && (get_effective_time(scheduler) > (lock_time + scheduler->shutdown_lock))) { - pe_rsc_info(rsc, "Shutdown lock for %s on %s expired", - rsc->id, pe__node_name(node)); + pcmk__rsc_info(rsc, "Shutdown lock for %s on %s expired", + rsc->id, pcmk__node_name(node)); pe__clear_resource_history(rsc, node); } else { /* @COMPAT I don't like breaking const signatures, but @@ -2609,10 +2665,10 @@ unpack_shutdown_lock(const xmlNode *rsc_entry, pcmk_resource_t *rsc, /*! * \internal - * \brief Unpack one lrm_resource entry from a node's CIB status + * \brief Unpack one \c PCMK__XE_LRM_RESOURCE entry from a node's CIB status * * \param[in,out] node Node whose status is being unpacked - * \param[in] rsc_entry lrm_resource XML being unpacked + * \param[in] rsc_entry \c PCMK__XE_LRM_RESOURCE XML being unpacked * \param[in,out] scheduler Scheduler data * * \return Resource corresponding to the entry, or NULL if no operation history @@ -2626,7 +2682,7 @@ unpack_lrm_resource(pcmk_node_t *node, const xmlNode *lrm_resource, int start_index = -1; enum rsc_role_e req_role = pcmk_role_unknown; - const char *rsc_id = ID(lrm_resource); + const char *rsc_id = pcmk__xe_id(lrm_resource); pcmk_resource_t *rsc = NULL; GList *op_list = NULL; @@ -2639,16 +2695,20 @@ unpack_lrm_resource(pcmk_node_t *node, const xmlNode *lrm_resource, enum rsc_role_e saved_role = pcmk_role_unknown; if (rsc_id == NULL) { - crm_warn("Ignoring malformed " XML_LRM_TAG_RESOURCE - " entry without id"); + pcmk__config_err("Ignoring invalid " PCMK__XE_LRM_RESOURCE + " entry: No " PCMK_XA_ID); + crm_log_xml_info(lrm_resource, "missing-id"); return NULL; } - crm_trace("Unpacking " XML_LRM_TAG_RESOURCE " for %s on %s", - rsc_id, pe__node_name(node)); + crm_trace("Unpacking " PCMK__XE_LRM_RESOURCE " for %s on %s", + rsc_id, pcmk__node_name(node)); - // Build a list of individual lrm_rsc_op entries, so we can sort them - for (rsc_op = first_named_child(lrm_resource, XML_LRM_TAG_RSC_OP); - rsc_op != NULL; rsc_op = crm_next_same_xml(rsc_op)) { + /* Build a list of individual PCMK__XE_LRM_RSC_OP entries, so we can sort + * them + */ + for (rsc_op = pcmk__xe_first_child(lrm_resource, PCMK__XE_LRM_RSC_OP, NULL, + NULL); + rsc_op != NULL; rsc_op = pcmk__xe_next_same(rsc_op)) { op_list = g_list_prepend(op_list, rsc_op); } @@ -2702,12 +2762,14 @@ unpack_lrm_resource(pcmk_node_t *node, const xmlNode *lrm_resource, if ((rsc->next_role == pcmk_role_unknown) || (req_role < rsc->next_role)) { - pe__set_next_role(rsc, req_role, XML_RSC_ATTR_TARGET_ROLE); + pe__set_next_role(rsc, req_role, PCMK_META_TARGET_ROLE); } else if (req_role > rsc->next_role) { - pe_rsc_info(rsc, "%s: Not overwriting calculated next role %s" - " with requested next role %s", - rsc->id, role2text(rsc->next_role), role2text(req_role)); + pcmk__rsc_info(rsc, + "%s: Not overwriting calculated next role %s" + " with requested next role %s", + rsc->id, pcmk_role_text(rsc->next_role), + pcmk_role_text(req_role)); } } @@ -2722,7 +2784,8 @@ static void handle_orphaned_container_fillers(const xmlNode *lrm_rsc_list, pcmk_scheduler_t *scheduler) { - for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rsc_list); + for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rsc_list, NULL, + NULL, NULL); rsc_entry != NULL; rsc_entry = pcmk__xe_next(rsc_entry)) { pcmk_resource_t *rsc; @@ -2730,12 +2793,12 @@ handle_orphaned_container_fillers(const xmlNode *lrm_rsc_list, const char *rsc_id; const char *container_id; - if (!pcmk__str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, pcmk__str_casei)) { + if (!pcmk__xe_is(rsc_entry, PCMK__XE_LRM_RESOURCE)) { continue; } - container_id = crm_element_value(rsc_entry, XML_RSC_ATTR_CONTAINER); - rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + container_id = crm_element_value(rsc_entry, PCMK__META_CONTAINER); + rsc_id = crm_element_value(rsc_entry, PCMK_XA_ID); if (container_id == NULL || rsc_id == NULL) { continue; } @@ -2751,8 +2814,8 @@ handle_orphaned_container_fillers(const xmlNode *lrm_rsc_list, continue; } - pe_rsc_trace(rsc, "Mapped container of orphaned resource %s to %s", - rsc->id, container_id); + pcmk__rsc_trace(rsc, "Mapped container of orphaned resource %s to %s", + rsc->id, container_id); rsc->container = container; container->fillers = g_list_append(container->fillers, rsc); } @@ -2772,19 +2835,21 @@ unpack_node_lrm(pcmk_node_t *node, const xmlNode *xml, { bool found_orphaned_container_filler = false; - // Drill down to lrm_resources section - xml = find_xml_node(xml, XML_CIB_TAG_LRM, FALSE); + // Drill down to PCMK__XE_LRM_RESOURCES section + xml = pcmk__xe_first_child(xml, PCMK__XE_LRM, NULL, NULL); if (xml == NULL) { return; } - xml = find_xml_node(xml, XML_LRM_TAG_RESOURCES, FALSE); + xml = pcmk__xe_first_child(xml, PCMK__XE_LRM_RESOURCES, NULL, NULL); if (xml == NULL) { return; } - // Unpack each lrm_resource entry - for (const xmlNode *rsc_entry = first_named_child(xml, XML_LRM_TAG_RESOURCE); - rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) { + // Unpack each PCMK__XE_LRM_RESOURCE entry + for (const xmlNode *rsc_entry = pcmk__xe_first_child(xml, + PCMK__XE_LRM_RESOURCE, + NULL, NULL); + rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) { pcmk_resource_t *rsc = unpack_lrm_resource(node, rsc_entry, scheduler); @@ -2823,12 +2888,12 @@ set_node_score(gpointer key, gpointer value, gpointer user_data) node->weight = *score; } -#define XPATH_NODE_STATE "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS \ - "/" XML_CIB_TAG_STATE -#define SUB_XPATH_LRM_RESOURCE "/" XML_CIB_TAG_LRM \ - "/" XML_LRM_TAG_RESOURCES \ - "/" XML_LRM_TAG_RESOURCE -#define SUB_XPATH_LRM_RSC_OP "/" XML_LRM_TAG_RSC_OP +#define XPATH_NODE_STATE "/" PCMK_XE_CIB "/" PCMK_XE_STATUS \ + "/" PCMK__XE_NODE_STATE +#define SUB_XPATH_LRM_RESOURCE "/" PCMK__XE_LRM \ + "/" PCMK__XE_LRM_RESOURCES \ + "/" PCMK__XE_LRM_RESOURCE +#define SUB_XPATH_LRM_RSC_OP "/" PCMK__XE_LRM_RSC_OP static xmlNode * find_lrm_op(const char *resource, const char *op, const char *node, const char *source, @@ -2842,21 +2907,21 @@ find_lrm_op(const char *resource, const char *op, const char *node, const char * xpath = g_string_sized_new(256); pcmk__g_strcat(xpath, - XPATH_NODE_STATE "[@" XML_ATTR_UNAME "='", node, "']" - SUB_XPATH_LRM_RESOURCE "[@" XML_ATTR_ID "='", resource, "']" - SUB_XPATH_LRM_RSC_OP "[@" XML_LRM_ATTR_TASK "='", op, "'", + XPATH_NODE_STATE "[@" PCMK_XA_UNAME "='", node, "']" + SUB_XPATH_LRM_RESOURCE "[@" PCMK_XA_ID "='", resource, "']" + SUB_XPATH_LRM_RSC_OP "[@" PCMK_XA_OPERATION "='", op, "'", NULL); /* Need to check against transition_magic too? */ if ((source != NULL) && (strcmp(op, PCMK_ACTION_MIGRATE_TO) == 0)) { pcmk__g_strcat(xpath, - " and @" XML_LRM_ATTR_MIGRATE_TARGET "='", source, "']", + " and @" PCMK__META_MIGRATE_TARGET "='", source, "']", NULL); } else if ((source != NULL) && (strcmp(op, PCMK_ACTION_MIGRATE_FROM) == 0)) { pcmk__g_strcat(xpath, - " and @" XML_LRM_ATTR_MIGRATE_SOURCE "='", source, "']", + " and @" PCMK__META_MIGRATE_SOURCE "='", source, "']", NULL); } else { g_string_append_c(xpath, ']'); @@ -2870,8 +2935,8 @@ find_lrm_op(const char *resource, const char *op, const char *node, const char * int rc = PCMK_OCF_UNKNOWN_ERROR; int status = PCMK_EXEC_ERROR; - crm_element_value_int(xml, XML_LRM_ATTR_RC, &rc); - crm_element_value_int(xml, XML_LRM_ATTR_OPSTATUS, &status); + crm_element_value_int(xml, PCMK__XA_RC_CODE, &rc); + crm_element_value_int(xml, PCMK__XA_OP_STATUS, &status); if ((rc != target_rc) || (status != PCMK_EXEC_DONE)) { return NULL; } @@ -2890,8 +2955,8 @@ find_lrm_resource(const char *rsc_id, const char *node_name, xpath = g_string_sized_new(256); pcmk__g_strcat(xpath, - XPATH_NODE_STATE "[@" XML_ATTR_UNAME "='", node_name, "']" - SUB_XPATH_LRM_RESOURCE "[@" XML_ATTR_ID "='", rsc_id, "']", + XPATH_NODE_STATE "[@" PCMK_XA_UNAME "='", node_name, "']" + SUB_XPATH_LRM_RESOURCE "[@" PCMK_XA_ID "='", rsc_id, "']", NULL); xml = get_xpath_object((const char *) xpath->str, scheduler->input, @@ -2915,17 +2980,18 @@ unknown_on_node(pcmk_resource_t *rsc, const char *node_name) { bool result = false; xmlXPathObjectPtr search; - GString *xpath = g_string_sized_new(256); + char *xpath = NULL; - pcmk__g_strcat(xpath, - XPATH_NODE_STATE "[@" XML_ATTR_UNAME "='", node_name, "']" - SUB_XPATH_LRM_RESOURCE "[@" XML_ATTR_ID "='", rsc->id, "']" - SUB_XPATH_LRM_RSC_OP "[@" XML_LRM_ATTR_RC "!='193']", - NULL); - search = xpath_search(rsc->cluster->input, (const char *) xpath->str); + xpath = crm_strdup_printf(XPATH_NODE_STATE "[@" PCMK_XA_UNAME "='%s']" + SUB_XPATH_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']" + SUB_XPATH_LRM_RSC_OP + "[@" PCMK__XA_RC_CODE "!='%d']", + node_name, rsc->id, PCMK_OCF_UNKNOWN); + + search = xpath_search(rsc->cluster->input, xpath); result = (numXpathResults(search) == 0); freeXpathObject(search); - g_string_free(xpath, TRUE); + free(xpath); return result; } @@ -2979,15 +3045,17 @@ non_monitor_after(const char *rsc_id, const char *node_name, return false; } - for (xmlNode *op = first_named_child(lrm_resource, XML_LRM_TAG_RSC_OP); - op != NULL; op = crm_next_same_xml(op)) { + for (xmlNode *op = pcmk__xe_first_child(lrm_resource, PCMK__XE_LRM_RSC_OP, + NULL, NULL); + op != NULL; op = pcmk__xe_next_same(op)) { + const char * task = NULL; if (op == xml_op) { continue; } - task = crm_element_value(op, XML_LRM_ATTR_TASK); + task = crm_element_value(op, PCMK_XA_OPERATION); if (pcmk__str_any_of(task, PCMK_ACTION_START, PCMK_ACTION_STOP, PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM, @@ -3027,8 +3095,8 @@ newer_state_after_migrate(const char *rsc_id, const char *node_name, xml_op = migrate_from; } - source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); - target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); + source = crm_element_value(xml_op, PCMK__META_MIGRATE_SOURCE); + target = crm_element_value(xml_op, PCMK__META_MIGRATE_TARGET); /* It's preferred to compare to the migrate event on the same node if * existing, since call ids are more reliable. @@ -3078,30 +3146,32 @@ get_migration_node_names(const xmlNode *entry, const pcmk_node_t *source_node, const pcmk_node_t *target_node, const char **source_name, const char **target_name) { - *source_name = crm_element_value(entry, XML_LRM_ATTR_MIGRATE_SOURCE); - *target_name = crm_element_value(entry, XML_LRM_ATTR_MIGRATE_TARGET); + *source_name = crm_element_value(entry, PCMK__META_MIGRATE_SOURCE); + *target_name = crm_element_value(entry, PCMK__META_MIGRATE_TARGET); if ((*source_name == NULL) || (*target_name == NULL)) { - crm_err("Ignoring resource history entry %s without " - XML_LRM_ATTR_MIGRATE_SOURCE " and " XML_LRM_ATTR_MIGRATE_TARGET, - ID(entry)); + pcmk__config_err("Ignoring resource history entry %s without " + PCMK__META_MIGRATE_SOURCE " and " + PCMK__META_MIGRATE_TARGET, pcmk__xe_id(entry)); return pcmk_rc_unpack_error; } if ((source_node != NULL) && !pcmk__str_eq(*source_name, source_node->details->uname, pcmk__str_casei|pcmk__str_null_matches)) { - crm_err("Ignoring resource history entry %s because " - XML_LRM_ATTR_MIGRATE_SOURCE "='%s' does not match %s", - ID(entry), *source_name, pe__node_name(source_node)); + pcmk__config_err("Ignoring resource history entry %s because " + PCMK__META_MIGRATE_SOURCE "='%s' does not match %s", + pcmk__xe_id(entry), *source_name, + pcmk__node_name(source_node)); return pcmk_rc_unpack_error; } if ((target_node != NULL) && !pcmk__str_eq(*target_name, target_node->details->uname, pcmk__str_casei|pcmk__str_null_matches)) { - crm_err("Ignoring resource history entry %s because " - XML_LRM_ATTR_MIGRATE_TARGET "='%s' does not match %s", - ID(entry), *target_name, pe__node_name(target_node)); + pcmk__config_err("Ignoring resource history entry %s because " + PCMK__META_MIGRATE_TARGET "='%s' does not match %s", + pcmk__xe_id(entry), *target_name, + pcmk__node_name(target_node)); return pcmk_rc_unpack_error; } @@ -3123,8 +3193,8 @@ get_migration_node_names(const xmlNode *entry, const pcmk_node_t *source_node, static void add_dangling_migration(pcmk_resource_t *rsc, const pcmk_node_t *node) { - pe_rsc_trace(rsc, "Dangling migration of %s requires stop on %s", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, "Dangling migration of %s requires stop on %s", + rsc->id, pcmk__node_name(node)); rsc->role = pcmk_role_stopped; rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, (gpointer) node); @@ -3201,9 +3271,8 @@ unpack_migrate_to_success(struct action_history *history) */ return; } - crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc); - crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, - &from_status); + crm_element_value_int(migrate_from, PCMK__XA_RC_CODE, &from_rc); + crm_element_value_int(migrate_from, PCMK__XA_OP_STATUS, &from_status); } /* If the resource has newer state on both the source and target after the @@ -3230,7 +3299,7 @@ unpack_migrate_to_success(struct action_history *history) */ history->rsc->role = pcmk_role_started; - target_node = pe_find_node(history->rsc->cluster->nodes, target); + target_node = pcmk_find_node(history->rsc->cluster, target); active_on_target = !target_newer_state && (target_node != NULL) && target_node->details->online; @@ -3240,9 +3309,9 @@ unpack_migrate_to_success(struct action_history *history) TRUE); } else { // Mark resource as failed, require recovery, and prevent migration - pe__set_resource_flags(history->rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); - pe__clear_resource_flags(history->rsc, pcmk_rsc_migratable); + pcmk__set_rsc_flags(history->rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__clear_rsc_flags(history->rsc, pcmk_rsc_migratable); } return; } @@ -3259,8 +3328,8 @@ unpack_migrate_to_success(struct action_history *history) } if (active_on_target) { - pcmk_node_t *source_node = pe_find_node(history->rsc->cluster->nodes, - source); + pcmk_node_t *source_node = pcmk_find_node(history->rsc->cluster, + source); native_add_running(history->rsc, target_node, history->rsc->cluster, FALSE); @@ -3277,9 +3346,9 @@ unpack_migrate_to_success(struct action_history *history) } else if (!source_newer_op) { // Mark resource as failed, require recovery, and prevent migration - pe__set_resource_flags(history->rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); - pe__clear_resource_flags(history->rsc, pcmk_rsc_migratable); + pcmk__set_rsc_flags(history->rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__clear_rsc_flags(history->rsc, pcmk_rsc_migratable); } } @@ -3328,8 +3397,8 @@ unpack_migrate_to_failure(struct action_history *history) * active there. * (if it is up). */ - pcmk_node_t *target_node = pe_find_node(history->rsc->cluster->nodes, - target); + pcmk_node_t *target_node = pcmk_find_node(history->rsc->cluster, + target); if (target_node && target_node->details->online) { native_add_running(history->rsc, target_node, history->rsc->cluster, @@ -3394,8 +3463,8 @@ unpack_migrate_from_failure(struct action_history *history) /* The resource has no newer state on the source, so assume it's still * active there (if it is up). */ - pcmk_node_t *source_node = pe_find_node(history->rsc->cluster->nodes, - source); + pcmk_node_t *source_node = pcmk_find_node(history->rsc->cluster, + source); if (source_node && source_node->details->online) { native_add_running(history->rsc, source_node, history->rsc->cluster, @@ -3420,23 +3489,23 @@ record_failed_op(struct action_history *history) for (const xmlNode *xIter = history->rsc->cluster->failed->children; xIter != NULL; xIter = xIter->next) { - const char *key = pe__xe_history_key(xIter); - const char *uname = crm_element_value(xIter, XML_ATTR_UNAME); + const char *key = pcmk__xe_history_key(xIter); + const char *uname = crm_element_value(xIter, PCMK_XA_UNAME); if (pcmk__str_eq(history->key, key, pcmk__str_none) && pcmk__str_eq(uname, history->node->details->uname, pcmk__str_casei)) { crm_trace("Skipping duplicate entry %s on %s", - history->key, pe__node_name(history->node)); + history->key, pcmk__node_name(history->node)); return; } } crm_trace("Adding entry for %s on %s to failed action list", - history->key, pe__node_name(history->node)); - crm_xml_add(history->xml, XML_ATTR_UNAME, history->node->details->uname); - crm_xml_add(history->xml, XML_LRM_ATTR_RSCID, history->rsc->id); - add_node_copy(history->rsc->cluster->failed, history->xml); + history->key, pcmk__node_name(history->node)); + crm_xml_add(history->xml, PCMK_XA_UNAME, history->node->details->uname); + crm_xml_add(history->xml, PCMK__XA_RSC_ID, history->rsc->id); + pcmk__xml_copy(history->rsc->cluster->failed, history->xml); } static char * @@ -3445,22 +3514,20 @@ last_change_str(const xmlNode *xml_op) time_t when; char *result = NULL; - if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, + if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE, &when) == pcmk_ok) { char *when_s = pcmk__epoch2str(&when, 0); const char *p = strchr(when_s, ' '); // Skip day of week to make message shorter if ((p != NULL) && (*(++p) != '\0')) { - result = strdup(p); - CRM_ASSERT(result != NULL); + result = pcmk__str_copy(p); } free(when_s); } if (result == NULL) { - result = strdup("unknown time"); - CRM_ASSERT(result != NULL); + result = pcmk__str_copy("unknown_time"); } return result; @@ -3565,15 +3632,16 @@ cmp_on_fail(enum action_fail_response first, enum action_fail_response second) static void ban_from_all_nodes(pcmk_resource_t *rsc) { - int score = -INFINITY; + int score = -PCMK_SCORE_INFINITY; pcmk_resource_t *fail_rsc = rsc; if (fail_rsc->parent != NULL) { pcmk_resource_t *parent = uber_parent(fail_rsc); - if (pe_rsc_is_anon_clone(parent)) { - /* For anonymous clones, if an operation with on-fail=stop fails for - * any instance, the entire clone must stop. + if (pcmk__is_anonymous_clone(parent)) { + /* For anonymous clones, if an operation with + * PCMK_META_ON_FAIL=PCMK_VALUE_STOP fails for any instance, the + * entire clone must stop. */ fail_rsc = parent; } @@ -3608,7 +3676,7 @@ unpack_failure_handling(struct action_history *history, history->task, history->interval_ms, config); - const char *on_fail_str = g_hash_table_lookup(meta, XML_OP_ATTR_ON_FAIL); + const char *on_fail_str = g_hash_table_lookup(meta, PCMK_META_ON_FAIL); *on_fail = pcmk__parse_on_fail(history->rsc, history->task, history->interval_ms, on_fail_str); @@ -3649,17 +3717,17 @@ unpack_rsc_op_failure(struct action_history *history, (pcmk__str_empty(history->exit_reason)? "" : ": "), pcmk__s(history->exit_reason, ""), (is_probe? "probe" : history->task), history->rsc->id, - pe__node_name(history->node), last_change_s, + pcmk__node_name(history->node), last_change_s, history->exit_status, history->id); } else { - crm_warn("Unexpected result (%s%s%s) was recorded for " - "%s of %s on %s at %s " CRM_XS " exit-status=%d id=%s", - services_ocf_exitcode_str(history->exit_status), - (pcmk__str_empty(history->exit_reason)? "" : ": "), - pcmk__s(history->exit_reason, ""), - (is_probe? "probe" : history->task), history->rsc->id, - pe__node_name(history->node), last_change_s, - history->exit_status, history->id); + pcmk__sched_warn("Unexpected result (%s%s%s) was recorded for %s of " + "%s on %s at %s " CRM_XS " exit-status=%d id=%s", + services_ocf_exitcode_str(history->exit_status), + (pcmk__str_empty(history->exit_reason)? "" : ": "), + pcmk__s(history->exit_reason, ""), + (is_probe? "probe" : history->task), history->rsc->id, + pcmk__node_name(history->node), last_change_s, + history->exit_status, history->id); if (is_probe && (history->exit_status != PCMK_OCF_OK) && (history->exit_status != PCMK_OCF_NOT_RUNNING) @@ -3669,8 +3737,9 @@ unpack_rsc_op_failure(struct action_history *history, * didn't know resources will be probed even where they can't run. */ crm_notice("If it is not possible for %s to run on %s, see " - "the resource-discovery option for location constraints", - history->rsc->id, pe__node_name(history->node)); + "the " PCMK_XA_RESOURCE_DISCOVERY " option for location " + "constraints", + history->rsc->id, pcmk__node_name(history->node)); } record_failed_op(history); @@ -3679,14 +3748,14 @@ unpack_rsc_op_failure(struct action_history *history, free(last_change_s); if (cmp_on_fail(*on_fail, config_on_fail) < 0) { - pe_rsc_trace(history->rsc, "on-fail %s -> %s for %s", - fail2text(*on_fail), fail2text(config_on_fail), - history->key); + pcmk__rsc_trace(history->rsc, "on-fail %s -> %s for %s", + pcmk_on_fail_text(*on_fail), + pcmk_on_fail_text(config_on_fail), history->key); *on_fail = config_on_fail; } if (strcmp(history->task, PCMK_ACTION_STOP) == 0) { - resource_location(history->rsc, history->node, -INFINITY, + resource_location(history->rsc, history->node, -PCMK_SCORE_INFINITY, "__stop_fail__", history->rsc->cluster); } else if (strcmp(history->task, PCMK_ACTION_MIGRATE_TO) == 0) { @@ -3702,7 +3771,7 @@ unpack_rsc_op_failure(struct action_history *history, if (config_on_fail == pcmk_on_fail_block) { history->rsc->role = pcmk_role_promoted; pe__set_next_role(history->rsc, pcmk_role_stopped, - "demote with on-fail=block"); + "demote with " PCMK_META_ON_FAIL "=block"); } else if (history->exit_status == PCMK_OCF_NOT_RUNNING) { history->rsc->role = pcmk_role_stopped; @@ -3719,19 +3788,20 @@ unpack_rsc_op_failure(struct action_history *history, if (is_probe && (history->exit_status == PCMK_OCF_NOT_INSTALLED)) { /* leave stopped */ - pe_rsc_trace(history->rsc, "Leaving %s stopped", history->rsc->id); + pcmk__rsc_trace(history->rsc, "Leaving %s stopped", history->rsc->id); history->rsc->role = pcmk_role_stopped; } else if (history->rsc->role < pcmk_role_started) { - pe_rsc_trace(history->rsc, "Setting %s active", history->rsc->id); + pcmk__rsc_trace(history->rsc, "Setting %s active", history->rsc->id); set_active(history->rsc); } - pe_rsc_trace(history->rsc, - "Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s", - history->rsc->id, role2text(history->rsc->role), - pcmk__btoa(history->node->details->unclean), - fail2text(config_on_fail), role2text(fail_role)); + pcmk__rsc_trace(history->rsc, + "Resource %s: role=%s unclean=%s on_fail=%s fail_role=%s", + history->rsc->id, pcmk_role_text(history->rsc->role), + pcmk__btoa(history->node->details->unclean), + pcmk_on_fail_text(config_on_fail), + pcmk_role_text(fail_role)); if ((fail_role != pcmk_role_started) && (history->rsc->next_role < fail_role)) { @@ -3765,19 +3835,20 @@ block_if_unrecoverable(struct action_history *history) } last_change_s = last_change_str(history->xml); - pe_proc_err("No further recovery can be attempted for %s " - "because %s on %s failed (%s%s%s) at %s " - CRM_XS " rc=%d id=%s", - history->rsc->id, history->task, pe__node_name(history->node), - services_ocf_exitcode_str(history->exit_status), - (pcmk__str_empty(history->exit_reason)? "" : ": "), - pcmk__s(history->exit_reason, ""), - last_change_s, history->exit_status, history->id); + pcmk__sched_err("No further recovery can be attempted for %s " + "because %s on %s failed (%s%s%s) at %s " + CRM_XS " rc=%d id=%s", + history->rsc->id, history->task, + pcmk__node_name(history->node), + services_ocf_exitcode_str(history->exit_status), + (pcmk__str_empty(history->exit_reason)? "" : ": "), + pcmk__s(history->exit_reason, ""), + last_change_s, history->exit_status, history->id); free(last_change_s); - pe__clear_resource_flags(history->rsc, pcmk_rsc_managed); - pe__set_resource_flags(history->rsc, pcmk_rsc_blocked); + pcmk__clear_rsc_flags(history->rsc, pcmk_rsc_managed); + pcmk__set_rsc_flags(history->rsc, pcmk_rsc_blocked); } /*! @@ -3841,7 +3912,7 @@ remap_operation(struct action_history *history, } } - if (!pe_rsc_is_bundled(history->rsc) + if (!pcmk__is_bundled(history->rsc) && pcmk_xe_mask_probe_failure(history->xml) && ((history->execution_status != PCMK_EXEC_DONE) || (history->exit_status != PCMK_OCF_NOT_RUNNING))) { @@ -3887,24 +3958,24 @@ remap_operation(struct action_history *history, */ remap_because(history, &why, PCMK_EXEC_ERROR, "obsolete history format"); - crm_warn("Expected result not found for %s on %s " - "(corrupt or obsolete CIB?)", - history->key, pe__node_name(history->node)); + pcmk__config_warn("Expected result not found for %s on %s " + "(corrupt or obsolete CIB?)", + history->key, pcmk__node_name(history->node)); } else if (history->exit_status == history->expected_exit_status) { remap_because(history, &why, PCMK_EXEC_DONE, "expected result"); } else { remap_because(history, &why, PCMK_EXEC_ERROR, "unexpected result"); - pe_rsc_debug(history->rsc, - "%s on %s: expected %d (%s), got %d (%s%s%s)", - history->key, pe__node_name(history->node), - history->expected_exit_status, - services_ocf_exitcode_str(history->expected_exit_status), - history->exit_status, - services_ocf_exitcode_str(history->exit_status), - (pcmk__str_empty(history->exit_reason)? "" : ": "), - pcmk__s(history->exit_reason, "")); + pcmk__rsc_debug(history->rsc, + "%s on %s: expected %d (%s), got %d (%s%s%s)", + history->key, pcmk__node_name(history->node), + history->expected_exit_status, + services_ocf_exitcode_str(history->expected_exit_status), + history->exit_status, + services_ocf_exitcode_str(history->exit_status), + (pcmk__str_empty(history->exit_reason)? "" : ": "), + pcmk__s(history->exit_reason, "")); } switch (history->exit_status) { @@ -3914,9 +3985,10 @@ remap_operation(struct action_history *history, char *last_change_s = last_change_str(history->xml); remap_because(history, &why, PCMK_EXEC_DONE, "probe"); - pe_rsc_info(history->rsc, "Probe found %s active on %s at %s", - history->rsc->id, pe__node_name(history->node), - last_change_s); + pcmk__rsc_info(history->rsc, + "Probe found %s active on %s at %s", + history->rsc->id, pcmk__node_name(history->node), + last_change_s); free(last_change_s); } break; @@ -3944,10 +4016,10 @@ remap_operation(struct action_history *history, char *last_change_s = last_change_str(history->xml); remap_because(history, &why, PCMK_EXEC_DONE, "probe"); - pe_rsc_info(history->rsc, - "Probe found %s active and promoted on %s at %s", - history->rsc->id, pe__node_name(history->node), - last_change_s); + pcmk__rsc_info(history->rsc, + "Probe found %s active and promoted on %s at %s", + history->rsc->id, + pcmk__node_name(history->node), last_change_s); free(last_change_s); } if (!expired @@ -3970,7 +4042,7 @@ remap_operation(struct action_history *history, case PCMK_OCF_UNIMPLEMENT_FEATURE: { guint interval_ms = 0; - crm_element_value_ms(history->xml, XML_LRM_ATTR_INTERVAL_MS, + crm_element_value_ms(history->xml, PCMK_META_INTERVAL, &interval_ms); if (interval_ms == 0) { @@ -4002,7 +4074,7 @@ remap_operation(struct action_history *history, crm_info("Treating unknown exit status %d from %s of %s " "on %s at %s as failure", history->exit_status, task, history->rsc->id, - pe__node_name(history->node), last_change_s); + pcmk__node_name(history->node), last_change_s); remap_because(history, &why, PCMK_EXEC_ERROR, "unknown exit status"); free(last_change_s); @@ -4012,13 +4084,13 @@ remap_operation(struct action_history *history, remap_done: if (why != NULL) { - pe_rsc_trace(history->rsc, - "Remapped %s result from [%s: %s] to [%s: %s] " - "because of %s", - history->key, pcmk_exec_status_str(orig_exec_status), - crm_exit_str(orig_exit_status), - pcmk_exec_status_str(history->execution_status), - crm_exit_str(history->exit_status), why); + pcmk__rsc_trace(history->rsc, + "Remapped %s result from [%s: %s] to [%s: %s] " + "because of %s", + history->key, pcmk_exec_status_str(orig_exec_status), + crm_exit_str(orig_exit_status), + pcmk_exec_status_str(history->execution_status), + crm_exit_str(history->exit_status), why); } } @@ -4037,7 +4109,7 @@ should_clear_for_param_change(const xmlNode *xml_op, const char *task, rsc->cluster); } else { - op_digest_cache_t *digest_data = NULL; + pcmk__op_digest_t *digest_data = NULL; digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster); @@ -4045,7 +4117,7 @@ should_clear_for_param_change(const xmlNode *xml_op, const char *task, case pcmk__digest_unknown: crm_trace("Resource %s history entry %s on %s" " has no digest to compare", - rsc->id, pe__xe_history_key(xml_op), + rsc->id, pcmk__xe_history_key(xml_op), node->details->id); break; case pcmk__digest_match: @@ -4063,7 +4135,7 @@ static void order_after_remote_fencing(pcmk_action_t *action, pcmk_resource_t *remote_conn, pcmk_scheduler_t *scheduler) { - pcmk_node_t *remote_node = pe_find_node(scheduler->nodes, remote_conn->id); + pcmk_node_t *remote_node = pcmk_find_node(scheduler, remote_conn->id); if (remote_node) { pcmk_action_t *fence = pe_fence_op(remote_node, NULL, TRUE, NULL, @@ -4102,7 +4174,7 @@ should_ignore_failure_timeout(const pcmk_resource_t *rsc, const char *task, && (interval_ms != 0) && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)) { - pcmk_node_t *remote_node = pe_find_node(rsc->cluster->nodes, rsc->id); + pcmk_node_t *remote_node = pcmk_find_node(rsc->cluster, rsc->id); if (remote_node && !remote_node->details->remote_was_fenced) { if (is_last_failure) { @@ -4143,18 +4215,20 @@ check_operation_expiry(struct action_history *history) const char *clear_reason = NULL; if (history->execution_status == PCMK_EXEC_NOT_INSTALLED) { - pe_rsc_trace(history->rsc, - "Resource history entry %s on %s is not expired: " - "Not Installed does not expire", - history->id, pe__node_name(history->node)); + pcmk__rsc_trace(history->rsc, + "Resource history entry %s on %s is not expired: " + "Not Installed does not expire", + history->id, pcmk__node_name(history->node)); return false; // "Not installed" must always be cleared manually } if ((history->rsc->failure_timeout > 0) - && (crm_element_value_epoch(history->xml, XML_RSC_OP_LAST_CHANGE, + && (crm_element_value_epoch(history->xml, PCMK_XA_LAST_RC_CHANGE, &last_run) == 0)) { - // Resource has a failure-timeout, and history entry has a timestamp + /* Resource has a PCMK_META_FAILURE_TIMEOUT and history entry has a + * timestamp + */ time_t now = get_effective_time(history->rsc->cluster); time_t last_failure = 0; @@ -4202,10 +4276,10 @@ check_operation_expiry(struct action_history *history) * fail count should be expired too), so this is really just a * failsafe. */ - pe_rsc_trace(history->rsc, - "Resource history entry %s on %s is not expired: " - "Unexpired fail count", - history->id, pe__node_name(history->node)); + pcmk__rsc_trace(history->rsc, + "Resource history entry %s on %s is not " + "expired: Unexpired fail count", + history->id, pcmk__node_name(history->node)); expired = false; } @@ -4259,10 +4333,10 @@ check_operation_expiry(struct action_history *history) case PCMK_OCF_DEGRADED: case PCMK_OCF_DEGRADED_PROMOTED: // Don't expire probes that return these values - pe_rsc_trace(history->rsc, - "Resource history entry %s on %s is not expired: " - "Probe result", - history->id, pe__node_name(history->node)); + pcmk__rsc_trace(history->rsc, + "Resource history entry %s on %s is not " + "expired: Probe result", + history->id, pcmk__node_name(history->node)); expired = false; break; } @@ -4275,7 +4349,7 @@ int pe__target_rc_from_xml(const xmlNode *xml_op) { int target_rc = 0; - const char *key = crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY); + const char *key = crm_element_value(xml_op, PCMK__XA_TRANSITION_KEY); if (key == NULL) { return -1; @@ -4301,7 +4375,7 @@ update_resource_state(struct action_history *history, int exit_status, bool clear_past_failure = false; if ((exit_status == PCMK_OCF_NOT_INSTALLED) - || (!pe_rsc_is_bundled(history->rsc) + || (!pcmk__is_bundled(history->rsc) && pcmk_xe_mask_probe_failure(history->xml))) { history->rsc->role = pcmk_role_stopped; @@ -4311,7 +4385,7 @@ update_resource_state(struct action_history *history, int exit_status, } else if (pcmk__str_eq(history->task, PCMK_ACTION_MONITOR, pcmk__str_none)) { if ((last_failure != NULL) - && pcmk__str_eq(history->key, pe__xe_history_key(last_failure), + && pcmk__str_eq(history->key, pcmk__xe_history_key(last_failure), pcmk__str_none)) { clear_past_failure = true; } @@ -4335,7 +4409,9 @@ update_resource_state(struct action_history *history, int exit_status, } else if (pcmk__str_eq(history->task, PCMK_ACTION_DEMOTE, pcmk__str_none)) { if (*on_fail == pcmk_on_fail_demote) { - // Demote clears an error only if on-fail=demote + /* Demote clears an error only if + * PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE + */ clear_past_failure = true; } history->rsc->role = pcmk_role_unpromoted; @@ -4350,8 +4426,8 @@ update_resource_state(struct action_history *history, int exit_status, unpack_migrate_to_success(history); } else if (history->rsc->role < pcmk_role_started) { - pe_rsc_trace(history->rsc, "%s active on %s", - history->rsc->id, pe__node_name(history->node)); + pcmk__rsc_trace(history->rsc, "%s active on %s", + history->rsc->id, pcmk__node_name(history->node)); set_active(history->rsc); } @@ -4364,9 +4440,10 @@ update_resource_state(struct action_history *history, int exit_status, case pcmk_on_fail_ban: case pcmk_on_fail_standby_node: case pcmk_on_fail_fence_node: - pe_rsc_trace(history->rsc, - "%s (%s) is not cleared by a completed %s", - history->rsc->id, fail2text(*on_fail), history->task); + pcmk__rsc_trace(history->rsc, + "%s (%s) is not cleared by a completed %s", + history->rsc->id, pcmk_on_fail_text(*on_fail), + history->task); break; case pcmk_on_fail_block: @@ -4435,19 +4512,21 @@ can_affect_state(struct action_history *history) static int unpack_action_result(struct action_history *history) { - if ((crm_element_value_int(history->xml, XML_LRM_ATTR_OPSTATUS, + if ((crm_element_value_int(history->xml, PCMK__XA_OP_STATUS, &(history->execution_status)) < 0) || (history->execution_status < PCMK_EXEC_PENDING) || (history->execution_status > PCMK_EXEC_MAX) || (history->execution_status == PCMK_EXEC_CANCELLED)) { - crm_err("Ignoring resource history entry %s for %s on %s " - "with invalid " XML_LRM_ATTR_OPSTATUS " '%s'", - history->id, history->rsc->id, pe__node_name(history->node), - pcmk__s(crm_element_value(history->xml, XML_LRM_ATTR_OPSTATUS), - "")); + pcmk__config_err("Ignoring resource history entry %s for %s on %s " + "with invalid " PCMK__XA_OP_STATUS " '%s'", + history->id, history->rsc->id, + pcmk__node_name(history->node), + pcmk__s(crm_element_value(history->xml, + PCMK__XA_OP_STATUS), + "")); return pcmk_rc_unpack_error; } - if ((crm_element_value_int(history->xml, XML_LRM_ATTR_RC, + if ((crm_element_value_int(history->xml, PCMK__XA_RC_CODE, &(history->exit_status)) < 0) || (history->exit_status < 0) || (history->exit_status > CRM_EX_MAX)) { #if 0 @@ -4455,18 +4534,19 @@ unpack_action_result(struct action_history *history) * change behavior, it should be done at a major or minor series * release. */ - crm_err("Ignoring resource history entry %s for %s on %s " - "with invalid " XML_LRM_ATTR_RC " '%s'", - history->id, history->rsc->id, pe__node_name(history->node), - pcmk__s(crm_element_value(history->xml, XML_LRM_ATTR_RC), - "")); + pcmk__config_err("Ignoring resource history entry %s for %s on %s " + "with invalid " PCMK__XA_RC_CODE " '%s'", + history->id, history->rsc->id, + pcmk__node_name(history->node), + pcmk__s(crm_element_value(history->xml, + PCMK__XA_RC_CODE), + "")); return pcmk_rc_unpack_error; #else history->exit_status = CRM_EX_ERROR; #endif } - history->exit_reason = crm_element_value(history->xml, - XML_LRM_ATTR_EXIT_REASON); + history->exit_reason = crm_element_value(history->xml, PCMK_XA_EXIT_REASON); return pcmk_rc_ok; } @@ -4483,7 +4563,7 @@ unpack_action_result(struct action_history *history) static int process_expired_result(struct action_history *history, int orig_exit_status) { - if (!pe_rsc_is_bundled(history->rsc) + if (!pcmk__is_bundled(history->rsc) && pcmk_xe_mask_probe_failure(history->xml) && (orig_exit_status != history->expected_exit_status)) { @@ -4493,7 +4573,7 @@ process_expired_result(struct action_history *history, int orig_exit_status) crm_trace("Ignoring resource history entry %s for probe of %s on %s: " "Masked failure expired", history->id, history->rsc->id, - pe__node_name(history->node)); + pcmk__node_name(history->node)); return pcmk_rc_ok; } @@ -4505,7 +4585,7 @@ process_expired_result(struct action_history *history, int orig_exit_status) crm_notice("Ignoring resource history entry %s for %s of %s on %s: " "Expired failure", history->id, history->task, history->rsc->id, - pe__node_name(history->node)); + pcmk__node_name(history->node)); return pcmk_rc_ok; } @@ -4516,15 +4596,15 @@ process_expired_result(struct action_history *history, int orig_exit_status) * * @TODO We should skip this if there is a newer successful monitor. * Also, this causes rescheduling only if the history entry - * has an op-digest (which the expire-non-blocked-failure + * has a PCMK__XA_OP_DIGEST (which the expire-non-blocked-failure * scheduler regression test doesn't, but that may not be a * realistic scenario in production). */ crm_notice("Rescheduling %s-interval %s of %s on %s " "after failure expired", pcmk__readable_interval(history->interval_ms), history->task, - history->rsc->id, pe__node_name(history->node)); - crm_xml_add(history->xml, XML_LRM_ATTR_RESTART_DIGEST, + history->rsc->id, pcmk__node_name(history->node)); + crm_xml_add(history->xml, PCMK__XA_OP_RESTART_DIGEST, "calculated-failure-timeout"); return pcmk_rc_ok; } @@ -4554,14 +4634,14 @@ mask_probe_failure(struct action_history *history, int orig_exit_status, crm_notice("Treating probe result '%s' for %s on %s as 'not running'", services_ocf_exitcode_str(orig_exit_status), history->rsc->id, - pe__node_name(history->node)); + pcmk__node_name(history->node)); update_resource_state(history, history->expected_exit_status, last_failure, on_fail); - crm_xml_add(history->xml, XML_ATTR_UNAME, history->node->details->uname); + crm_xml_add(history->xml, PCMK_XA_UNAME, history->node->details->uname); record_failed_op(history); - resource_location(ban_rsc, history->node, -INFINITY, "masked-probe-failure", - history->rsc->cluster); + resource_location(ban_rsc, history->node, -PCMK_SCORE_INFINITY, + "masked-probe-failure", history->rsc->cluster); } /*! @@ -4573,7 +4653,8 @@ mask_probe_failure(struct action_history *history, int orig_exit_status, * \return true if \p last_failure is failure of pending action in \p history, * otherwise false * \note Both \p history and \p last_failure must come from the same - * lrm_resource block, as node and resource are assumed to be the same. + * \c PCMK__XE_LRM_RESOURCE block, as node and resource are assumed to be + * the same. */ static bool failure_is_newer(const struct action_history *history, @@ -4588,21 +4669,21 @@ failure_is_newer(const struct action_history *history, } if (!pcmk__str_eq(history->task, - crm_element_value(last_failure, XML_LRM_ATTR_TASK), + crm_element_value(last_failure, PCMK_XA_OPERATION), pcmk__str_none)) { return false; // last_failure is for different action } - if ((crm_element_value_ms(last_failure, XML_LRM_ATTR_INTERVAL_MS, + if ((crm_element_value_ms(last_failure, PCMK_META_INTERVAL, &failure_interval_ms) != pcmk_ok) || (history->interval_ms != failure_interval_ms)) { return false; // last_failure is for action with different interval } - if ((pcmk__scan_ll(crm_element_value(history->xml, XML_RSC_OP_LAST_CHANGE), + if ((pcmk__scan_ll(crm_element_value(history->xml, PCMK_XA_LAST_RC_CHANGE), &this_change, 0LL) != pcmk_rc_ok) || (pcmk__scan_ll(crm_element_value(last_failure, - XML_RSC_OP_LAST_CHANGE), + PCMK_XA_LAST_RC_CHANGE), &failure_change, 0LL) != pcmk_rc_ok) || (failure_change < this_change)) { return false; // Failure is not known to be newer @@ -4636,7 +4717,7 @@ process_pending_action(struct action_history *history, } if (strcmp(history->task, PCMK_ACTION_START) == 0) { - pe__set_resource_flags(history->rsc, pcmk_rsc_start_pending); + pcmk__set_rsc_flags(history->rsc, pcmk_rsc_start_pending); set_active(history->rsc); } else if (strcmp(history->task, PCMK_ACTION_PROMOTE) == 0) { @@ -4651,8 +4732,8 @@ process_pending_action(struct action_history *history, pcmk_node_t *target = NULL; migrate_target = crm_element_value(history->xml, - XML_LRM_ATTR_MIGRATE_TARGET); - target = pe_find_node(history->rsc->cluster->nodes, migrate_target); + PCMK__META_MIGRATE_TARGET); + target = pcmk_find_node(history->rsc->cluster, migrate_target); if (target != NULL) { stop_action(history->rsc, target, FALSE); } @@ -4700,27 +4781,28 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, CRM_CHECK(rsc && node && xml_op, return); - history.id = ID(xml_op); + history.id = pcmk__xe_id(xml_op); if (history.id == NULL) { - crm_err("Ignoring resource history entry for %s on %s without ID", - rsc->id, pe__node_name(node)); + pcmk__config_err("Ignoring resource history entry for %s on %s " + "without ID", rsc->id, pcmk__node_name(node)); return; } // Task and interval - history.task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + history.task = crm_element_value(xml_op, PCMK_XA_OPERATION); if (history.task == NULL) { - crm_err("Ignoring resource history entry %s for %s on %s without " - XML_LRM_ATTR_TASK, history.id, rsc->id, pe__node_name(node)); + pcmk__config_err("Ignoring resource history entry %s for %s on %s " + "without " PCMK_XA_OPERATION, + history.id, rsc->id, pcmk__node_name(node)); return; } - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, - &(history.interval_ms)); + crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &(history.interval_ms)); if (!can_affect_state(&history)) { - pe_rsc_trace(rsc, - "Ignoring resource history entry %s for %s on %s " - "with irrelevant action '%s'", - history.id, rsc->id, pe__node_name(node), history.task); + pcmk__rsc_trace(rsc, + "Ignoring resource history entry %s for %s on %s " + "with irrelevant action '%s'", + history.id, rsc->id, pcmk__node_name(node), + history.task); return; } @@ -4729,19 +4811,20 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, } history.expected_exit_status = pe__target_rc_from_xml(xml_op); - history.key = pe__xe_history_key(xml_op); - crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &(history.call_id)); + history.key = pcmk__xe_history_key(xml_op); + crm_element_value_int(xml_op, PCMK__XA_CALL_ID, &(history.call_id)); - pe_rsc_trace(rsc, "Unpacking %s (%s call %d on %s): %s (%s)", - history.id, history.task, history.call_id, pe__node_name(node), - pcmk_exec_status_str(history.execution_status), - crm_exit_str(history.exit_status)); + pcmk__rsc_trace(rsc, "Unpacking %s (%s call %d on %s): %s (%s)", + history.id, history.task, history.call_id, + pcmk__node_name(node), + pcmk_exec_status_str(history.execution_status), + crm_exit_str(history.exit_status)); if (node->details->unclean) { - pe_rsc_trace(rsc, - "%s is running on %s, which is unclean (further action " - "depends on value of stop's on-fail attribute)", - rsc->id, pe__node_name(node)); + pcmk__rsc_trace(rsc, + "%s is running on %s, which is unclean (further action " + "depends on value of stop's on-fail attribute)", + rsc->id, pcmk__node_name(node)); } expired = check_operation_expiry(&history); @@ -4753,7 +4836,7 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, goto done; } - if (!pe_rsc_is_bundled(rsc) && pcmk_xe_mask_probe_failure(xml_op)) { + if (!pcmk__is_bundled(rsc) && pcmk_xe_mask_probe_failure(xml_op)) { mask_probe_failure(&history, old_rc, *last_failure, on_fail); goto done; } @@ -4778,7 +4861,7 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, crm_warn("Cannot ignore failed %s of %s on %s: " "Resource agent doesn't exist " CRM_XS " status=%d rc=%d id=%s", - history.task, rsc->id, pe__node_name(node), + history.task, rsc->id, pcmk__node_name(node), history.execution_status, history.exit_status, history.id); /* Also for printing it as "FAILED" by marking it as @@ -4786,14 +4869,14 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, */ *on_fail = pcmk_on_fail_ban; } - resource_location(parent, node, -INFINITY, "hard-error", - rsc->cluster); + resource_location(parent, node, -PCMK_SCORE_INFINITY, + "hard-error", rsc->cluster); unpack_rsc_op_failure(&history, failure_strategy, fail_role, last_failure, on_fail); goto done; case PCMK_EXEC_NOT_CONNECTED: - if (pe__is_guest_or_remote_node(node) + if (pcmk__is_pacemaker_remote_node(node) && pcmk_is_set(node->details->remote_rsc->flags, pcmk_rsc_managed)) { /* We should never get into a situation where a managed remote @@ -4802,8 +4885,8 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, * fail-safe in case a bug or unusual circumstances do lead to * that, ensure the remote connection is considered failed. */ - pe__set_resource_flags(node->details->remote_rsc, - pcmk_rsc_failed|pcmk_rsc_stop_if_failed); + pcmk__set_rsc_flags(node->details->remote_rsc, + pcmk_rsc_failed|pcmk_rsc_stop_if_failed); } break; // Not done, do error handling @@ -4830,14 +4913,14 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, CRM_XS " %s", history.task, services_ocf_exitcode_str(history.exit_status), (pcmk__str_empty(history.exit_reason)? "" : ": "), - pcmk__s(history.exit_reason, ""), rsc->id, pe__node_name(node), - last_change_s, history.id); + pcmk__s(history.exit_reason, ""), rsc->id, + pcmk__node_name(node), last_change_s, history.id); free(last_change_s); update_resource_state(&history, history.expected_exit_status, *last_failure, on_fail); - crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname); - pe__set_resource_flags(rsc, pcmk_rsc_ignore_failure); + crm_xml_add(xml_op, PCMK_XA_UNAME, node->details->uname); + pcmk__set_rsc_flags(rsc, pcmk_rsc_ignore_failure); record_failed_op(&history); @@ -4859,28 +4942,30 @@ unpack_rsc_op(pcmk_resource_t *rsc, pcmk_node_t *node, xmlNode *xml_op, do_crm_log(log_level, "Preventing %s from restarting on %s because " "of hard failure (%s%s%s) " CRM_XS " %s", - parent->id, pe__node_name(node), + parent->id, pcmk__node_name(node), services_ocf_exitcode_str(history.exit_status), (pcmk__str_empty(history.exit_reason)? "" : ": "), pcmk__s(history.exit_reason, ""), history.id); - resource_location(parent, node, -INFINITY, "hard-error", - rsc->cluster); + resource_location(parent, node, -PCMK_SCORE_INFINITY, + "hard-error", rsc->cluster); } else if (history.execution_status == PCMK_EXEC_ERROR_FATAL) { - crm_err("Preventing %s from restarting anywhere because " - "of fatal failure (%s%s%s) " CRM_XS " %s", - parent->id, services_ocf_exitcode_str(history.exit_status), - (pcmk__str_empty(history.exit_reason)? "" : ": "), - pcmk__s(history.exit_reason, ""), history.id); - resource_location(parent, NULL, -INFINITY, "fatal-error", - rsc->cluster); + pcmk__sched_err("Preventing %s from restarting anywhere because " + "of fatal failure (%s%s%s) " CRM_XS " %s", + parent->id, + services_ocf_exitcode_str(history.exit_status), + (pcmk__str_empty(history.exit_reason)? "" : ": "), + pcmk__s(history.exit_reason, ""), history.id); + resource_location(parent, NULL, -PCMK_SCORE_INFINITY, + "fatal-error", rsc->cluster); } } done: - pe_rsc_trace(rsc, "%s role on %s after %s is %s (next %s)", - rsc->id, pe__node_name(node), history.id, - role2text(rsc->role), role2text(rsc->next_role)); + pcmk__rsc_trace(rsc, "%s role on %s after %s is %s (next %s)", + rsc->id, pcmk__node_name(node), history.id, + pcmk_role_text(rsc->role), + pcmk_role_text(rsc->next_role)); } static void @@ -4891,55 +4976,54 @@ add_node_attrs(const xmlNode *xml_obj, pcmk_node_t *node, bool overwrite, pe_rule_eval_data_t rule_data = { .node_hash = NULL, - .role = pcmk_role_unknown, .now = scheduler->now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; - g_hash_table_insert(node->details->attrs, - strdup(CRM_ATTR_UNAME), strdup(node->details->uname)); + pcmk__insert_dup(node->details->attrs, + CRM_ATTR_UNAME, node->details->uname); - g_hash_table_insert(node->details->attrs, strdup(CRM_ATTR_ID), - strdup(node->details->id)); + pcmk__insert_dup(node->details->attrs, CRM_ATTR_ID, node->details->id); if (pcmk__str_eq(node->details->id, scheduler->dc_uuid, pcmk__str_casei)) { scheduler->dc_node = node; node->details->is_dc = TRUE; - g_hash_table_insert(node->details->attrs, - strdup(CRM_ATTR_IS_DC), strdup(XML_BOOLEAN_TRUE)); + pcmk__insert_dup(node->details->attrs, + CRM_ATTR_IS_DC, PCMK_VALUE_TRUE); } else { - g_hash_table_insert(node->details->attrs, - strdup(CRM_ATTR_IS_DC), strdup(XML_BOOLEAN_FALSE)); + pcmk__insert_dup(node->details->attrs, + CRM_ATTR_IS_DC, PCMK_VALUE_FALSE); } - cluster_name = g_hash_table_lookup(scheduler->config_hash, "cluster-name"); + cluster_name = g_hash_table_lookup(scheduler->config_hash, + PCMK_OPT_CLUSTER_NAME); if (cluster_name) { - g_hash_table_insert(node->details->attrs, strdup(CRM_ATTR_CLUSTER_NAME), - strdup(cluster_name)); + pcmk__insert_dup(node->details->attrs, CRM_ATTR_CLUSTER_NAME, + cluster_name); } - pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_ATTR_SETS, &rule_data, + pe__unpack_dataset_nvpairs(xml_obj, PCMK_XE_INSTANCE_ATTRIBUTES, &rule_data, node->details->attrs, NULL, overwrite, scheduler); - pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_UTILIZATION, &rule_data, + pe__unpack_dataset_nvpairs(xml_obj, PCMK_XE_UTILIZATION, &rule_data, node->details->utilization, NULL, FALSE, scheduler); - if (pe_node_attribute_raw(node, CRM_ATTR_SITE_NAME) == NULL) { - const char *site_name = pe_node_attribute_raw(node, "site-name"); + if (pcmk__node_attr(node, CRM_ATTR_SITE_NAME, NULL, + pcmk__rsc_node_current) == NULL) { + const char *site_name = pcmk__node_attr(node, "site-name", NULL, + pcmk__rsc_node_current); if (site_name) { - g_hash_table_insert(node->details->attrs, - strdup(CRM_ATTR_SITE_NAME), - strdup(site_name)); + pcmk__insert_dup(node->details->attrs, + CRM_ATTR_SITE_NAME, site_name); } else if (cluster_name) { /* Default to cluster-name if unset */ - g_hash_table_insert(node->details->attrs, - strdup(CRM_ATTR_SITE_NAME), - strdup(cluster_name)); + pcmk__insert_dup(node->details->attrs, + CRM_ATTR_SITE_NAME, cluster_name); } } } @@ -4961,13 +5045,12 @@ extract_operations(const char *node, const char *rsc, xmlNode * rsc_entry, gbool op_list = NULL; sorted_op_list = NULL; - for (rsc_op = pcmk__xe_first_child(rsc_entry); + for (rsc_op = pcmk__xe_first_child(rsc_entry, NULL, NULL, NULL); rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op)) { - if (pcmk__str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, - pcmk__str_none)) { - crm_xml_add(rsc_op, "resource", rsc); - crm_xml_add(rsc_op, XML_ATTR_UNAME, node); + if (pcmk__xe_is(rsc_op, PCMK__XE_LRM_RSC_OP)) { + crm_xml_add(rsc_op, PCMK_XA_RESOURCE, rsc); + crm_xml_add(rsc_op, PCMK_XA_UNAME, node); op_list = g_list_prepend(op_list, rsc_op); } } @@ -4994,11 +5077,11 @@ extract_operations(const char *node, const char *rsc, xmlNode * rsc_entry, gbool counter++; if (start_index < stop_index) { - crm_trace("Skipping %s: not active", ID(rsc_entry)); + crm_trace("Skipping %s: not active", pcmk__xe_id(rsc_entry)); break; } else if (counter < start_index) { - crm_trace("Skipping %s: old", ID(rsc_op)); + crm_trace("Skipping %s: old", pcmk__xe_id(rsc_op)); continue; } op_list = g_list_append(op_list, rsc_op); @@ -5016,28 +5099,31 @@ find_operations(const char *rsc, const char *node, gboolean active_filter, GList *intermediate = NULL; xmlNode *tmp = NULL; - xmlNode *status = find_xml_node(scheduler->input, XML_CIB_TAG_STATUS, TRUE); + xmlNode *status = pcmk__xe_first_child(scheduler->input, PCMK_XE_STATUS, + NULL, NULL); pcmk_node_t *this_node = NULL; xmlNode *node_state = NULL; - for (node_state = pcmk__xe_first_child(status); node_state != NULL; - node_state = pcmk__xe_next(node_state)) { + CRM_CHECK(status != NULL, return NULL); - if (pcmk__str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, pcmk__str_none)) { - const char *uname = crm_element_value(node_state, XML_ATTR_UNAME); + for (node_state = pcmk__xe_first_child(status, NULL, NULL, NULL); + node_state != NULL; node_state = pcmk__xe_next(node_state)) { + + if (pcmk__xe_is(node_state, PCMK__XE_NODE_STATE)) { + const char *uname = crm_element_value(node_state, PCMK_XA_UNAME); if (node != NULL && !pcmk__str_eq(uname, node, pcmk__str_casei)) { continue; } - this_node = pe_find_node(scheduler->nodes, uname); + this_node = pcmk_find_node(scheduler, uname); if(this_node == NULL) { CRM_LOG_ASSERT(this_node != NULL); continue; - } else if (pe__is_guest_or_remote_node(this_node)) { + } else if (pcmk__is_pacemaker_remote_node(this_node)) { determine_remote_online_status(scheduler, this_node); } else { @@ -5052,16 +5138,17 @@ find_operations(const char *rsc, const char *node, gboolean active_filter, */ xmlNode *lrm_rsc = NULL; - tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE); - - for (lrm_rsc = pcmk__xe_first_child(tmp); lrm_rsc != NULL; - lrm_rsc = pcmk__xe_next(lrm_rsc)) { + tmp = pcmk__xe_first_child(node_state, PCMK__XE_LRM, NULL, + NULL); + tmp = pcmk__xe_first_child(tmp, PCMK__XE_LRM_RESOURCES, NULL, + NULL); - if (pcmk__str_eq((const char *)lrm_rsc->name, - XML_LRM_TAG_RESOURCE, pcmk__str_none)) { + for (lrm_rsc = pcmk__xe_first_child(tmp, NULL, NULL, NULL); + lrm_rsc != NULL; lrm_rsc = pcmk__xe_next(lrm_rsc)) { - const char *rsc_id = crm_element_value(lrm_rsc, XML_ATTR_ID); + if (pcmk__xe_is(lrm_rsc, PCMK__XE_LRM_RESOURCE)) { + const char *rsc_id = crm_element_value(lrm_rsc, + PCMK_XA_ID); if (rsc != NULL && !pcmk__str_eq(rsc_id, rsc, pcmk__str_casei)) { continue; diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index 4055d6d..d8d9c2e 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.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. * @@ -13,7 +13,7 @@ #include <stdbool.h> #include <crm/crm.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/pengine/rules.h> #include <crm/pengine/internal.h> @@ -35,7 +35,7 @@ gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); bool pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node) { - if (pe__is_guest_node(node)) { + if (pcmk__is_guest_or_bundle_node(node)) { /* Guest nodes are fenced by stopping their container resource. We can * do that if the container's host is either online or fenceable. */ @@ -68,11 +68,11 @@ pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node) } else if(node->details->online) { crm_notice("We can fence %s without quorum because they're in our membership", - pe__node_name(node)); + pcmk__node_name(node)); return true; } - crm_trace("Cannot fence %s", pe__node_name(node)); + crm_trace("Cannot fence %s", pcmk__node_name(node)); return false; } @@ -92,8 +92,7 @@ pe__copy_node(const pcmk_node_t *this_node) CRM_ASSERT(this_node != NULL); - new_node = calloc(1, sizeof(pcmk_node_t)); - CRM_ASSERT(new_node != NULL); + new_node = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); new_node->rsc_discover_mode = this_node->rsc_discover_mode; new_node->weight = this_node->weight; @@ -221,12 +220,12 @@ pe__log_node_weights(const char *file, const char *function, int line, "%s: %s allocation score on %s: %s", LOG_TRACE, line, 0, comment, rsc->id, - pe__node_name(node), + pcmk__node_name(node), pcmk_readable_score(node->weight)); } else { qb_log_from_external_source(function, file, "%s: %s = %s", LOG_TRACE, line, 0, - comment, pe__node_name(node), + comment, pcmk__node_name(node), pcmk_readable_score(node->weight)); } } @@ -350,10 +349,10 @@ resource_node_score(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match); } match->weight = pcmk__add_scores(match->weight, score); - pe_rsc_trace(rsc, - "Enabling %s preference (%s) for %s on %s (now %s)", - tag, pcmk_readable_score(score), rsc->id, pe__node_name(node), - pcmk_readable_score(match->weight)); + pcmk__rsc_trace(rsc, + "Enabling %s preference (%s) for %s on %s (now %s)", + tag, pcmk_readable_score(score), rsc->id, + pcmk__node_name(node), pcmk_readable_score(match->weight)); } void @@ -382,10 +381,10 @@ resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, } } - if (node == NULL && score == -INFINITY) { + if ((node == NULL) && (score == -PCMK_SCORE_INFINITY)) { if (rsc->allocated_to) { crm_info("Deallocating %s from %s", - rsc->id, pe__node_name(rsc->allocated_to)); + rsc->id, pcmk__node_name(rsc->allocated_to)); free(rsc->allocated_to); rsc->allocated_to = NULL; } @@ -411,18 +410,26 @@ gboolean get_target_role(const pcmk_resource_t *rsc, enum rsc_role_e *role) { enum rsc_role_e local_role = pcmk_role_unknown; - const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + const char *value = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); CRM_CHECK(role != NULL, return FALSE); - if (pcmk__str_eq(value, "started", pcmk__str_null_matches | pcmk__str_casei) - || pcmk__str_eq("default", value, pcmk__str_casei)) { + if (pcmk__str_eq(value, PCMK_ROLE_STARTED, + pcmk__str_null_matches|pcmk__str_casei)) { + return FALSE; + } + if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) { + // @COMPAT Deprecated since 2.1.8 + pcmk__config_warn("Support for setting " PCMK_META_TARGET_ROLE + " to the explicit value '" PCMK_VALUE_DEFAULT + "' is deprecated and will be removed in a " + "future release (just leave it unset)"); return FALSE; } - local_role = text2role(value); + local_role = pcmk_parse_role(value); if (local_role == pcmk_role_unknown) { - pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s " + pcmk__config_err("Ignoring '" PCMK_META_TARGET_ROLE "' for %s " "because '%s' is not valid", rsc->id, value); return FALSE; @@ -435,7 +442,7 @@ get_target_role(const pcmk_resource_t *rsc, enum rsc_role_e *role) } } else { - pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s " + pcmk__config_err("Ignoring '" PCMK_META_TARGET_ROLE "' for %s " "because '%s' only makes sense for promotable " "clones", rsc->id, value); return FALSE; @@ -478,14 +485,14 @@ order_actions(pcmk_action_t *lh_action, pcmk_action_t *rh_action, } } - wrapper = calloc(1, sizeof(pcmk__related_action_t)); + wrapper = pcmk__assert_alloc(1, sizeof(pcmk__related_action_t)); wrapper->action = rh_action; wrapper->type = flags; list = lh_action->actions_after; list = g_list_prepend(list, wrapper); lh_action->actions_after = list; - wrapper = calloc(1, sizeof(pcmk__related_action_t)); + wrapper = pcmk__assert_alloc(1, sizeof(pcmk__related_action_t)); wrapper->action = lh_action; wrapper->type = flags; list = rh_action->actions_before; @@ -524,7 +531,7 @@ ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler) ticket = calloc(1, sizeof(pcmk_ticket_t)); if (ticket == NULL) { - crm_err("Cannot allocate ticket '%s'", ticket_id); + pcmk__sched_err("Cannot allocate ticket '%s'", ticket_id); return NULL; } @@ -545,13 +552,16 @@ ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler) const char * rsc_printable_id(const pcmk_resource_t *rsc) { - return pcmk_is_set(rsc->flags, pcmk_rsc_unique)? rsc->id : ID(rsc->xml); + if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) { + return rsc->id; + } + return pcmk__xe_id(rsc->xml); } void pe__clear_resource_flags_recursive(pcmk_resource_t *rsc, uint64_t flags) { - pe__clear_resource_flags(rsc, flags); + pcmk__clear_rsc_flags(rsc, flags); for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { pe__clear_resource_flags_recursive((pcmk_resource_t *) gIter->data, flags); @@ -570,7 +580,7 @@ pe__clear_resource_flags_on_all(pcmk_scheduler_t *scheduler, uint64_t flag) void pe__set_resource_flags_recursive(pcmk_resource_t *rsc, uint64_t flags) { - pe__set_resource_flags(rsc, flags); + pcmk__set_rsc_flags(rsc, flags); for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { pe__set_resource_flags_recursive((pcmk_resource_t *) gIter->data, flags); @@ -626,6 +636,7 @@ add_tag_ref(GHashTable * tags, const char * tag_name, const char * obj_ref) if (tag == NULL) { tag = calloc(1, sizeof(pcmk_tag_t)); if (tag == NULL) { + pcmk__sched_err("Could not allocate memory for tag %s", tag_name); return FALSE; } tag->id = strdup(tag_name); @@ -665,7 +676,8 @@ add_tag_ref(GHashTable * tags, const char * tag_name, const char * obj_ref) bool pe__shutdown_requested(const pcmk_node_t *node) { - const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN); + const char *shutdown = pcmk__node_attr(node, PCMK__NODE_ATTR_SHUTDOWN, NULL, + pcmk__rsc_node_current); return !pcmk__str_eq(shutdown, "0", pcmk__str_null_matches); } @@ -727,9 +739,10 @@ pe__resource_is_disabled(const pcmk_resource_t *rsc) const char *target_role = NULL; CRM_CHECK(rsc != NULL, return false); - target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE); if (target_role) { - enum rsc_role_e target_role_e = text2role(target_role); + // If invalid, we've already logged an error when unpacking + enum rsc_role_e target_role_e = pcmk_parse_role(target_role); if ((target_role_e == pcmk_role_stopped) || ((target_role_e == pcmk_role_unpromoted) @@ -754,7 +767,8 @@ bool pe__rsc_running_on_only(const pcmk_resource_t *rsc, const pcmk_node_t *node) { return (rsc != NULL) && pcmk__list_of_1(rsc->running_on) - && pe__same_node((const pcmk_node_t *) rsc->running_on->data, node); + && pcmk__same_node((const pcmk_node_t *) rsc->running_on->data, + node); } bool @@ -809,7 +823,7 @@ pe__build_node_name_list(pcmk_scheduler_t *scheduler, const char *s) */ nodes = g_list_prepend(nodes, strdup("*")); } else { - pcmk_node_t *node = pe_find_node(scheduler->nodes, s); + pcmk_node_t *node = pcmk_find_node(scheduler, s); if (node) { /* The given string was a valid uname for a node. Return a @@ -871,12 +885,14 @@ pe__failed_probe_for_rsc(const pcmk_resource_t *rsc, const char *name) const pcmk_resource_t *parent = pe__const_top_resource(rsc, false); const char *rsc_id = rsc->id; - if (parent->variant == pcmk_rsc_variant_clone) { + if (pcmk__is_clone(parent)) { rsc_id = pe__clone_child_id(parent); } - for (xmlNode *xml_op = pcmk__xml_first_child(rsc->cluster->failed); xml_op != NULL; - xml_op = pcmk__xml_next(xml_op)) { + for (xmlNode *xml_op = pcmk__xe_first_child(rsc->cluster->failed, NULL, + NULL, NULL); + xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) { + const char *value = NULL; char *op_id = NULL; @@ -888,12 +904,12 @@ pe__failed_probe_for_rsc(const pcmk_resource_t *rsc, const char *name) /* This resource operation was not run on the given node. Note that if name is * NULL, this will always succeed. */ - value = crm_element_value(xml_op, XML_LRM_ATTR_TARGET); + value = crm_element_value(xml_op, PCMK__META_ON_NODE); if (value == NULL || !pcmk__str_eq(value, name, pcmk__str_casei|pcmk__str_null_matches)) { continue; } - if (!parse_op_key(pe__xe_history_key(xml_op), &op_id, NULL, NULL)) { + if (!parse_op_key(pcmk__xe_history_key(xml_op), &op_id, NULL, NULL)) { continue; // This history entry is missing an operation key } diff --git a/lib/services/Makefile.am b/lib/services/Makefile.am index 5a19003..69c8a2c 100644 --- a/lib/services/Makefile.am +++ b/lib/services/Makefile.am @@ -14,7 +14,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include lib_LTLIBRARIES = libcrmservice.la noinst_HEADERS = $(wildcard *.h) -libcrmservice_la_LDFLAGS = -version-info 32:0:4 +libcrmservice_la_LDFLAGS = -version-info 32:1:4 libcrmservice_la_CFLAGS = libcrmservice_la_CFLAGS += $(CFLAGS_HARDENED_LIB) diff --git a/lib/services/services.c b/lib/services/services.c index e438443..94a8afc 100644 --- a/lib/services/services.c +++ b/lib/services/services.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the Pacemaker project contributors + * Copyright 2010-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -26,7 +26,7 @@ #include <crm/services.h> #include <crm/services_internal.h> #include <crm/stonith-ng.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "services_private.h" #include "services_ocf.h" #include "services_lsb.h" @@ -357,7 +357,7 @@ services_action_create_generic(const char *exec, const char *args[]) { svc_action_t *op = new_action(); - CRM_ASSERT(op != NULL); + pcmk__mem_assert(op); op->opaque->exec = strdup(exec); op->opaque->args[0] = strdup(exec); @@ -415,10 +415,8 @@ services_alert_create(const char *id, const char *exec, int timeout, { svc_action_t *action = services_action_create_generic(exec, NULL); - action->id = strdup(id); - action->standard = strdup(PCMK_RESOURCE_CLASS_ALERT); - CRM_ASSERT((action->id != NULL) && (action->standard != NULL)); - + action->id = pcmk__str_copy(id); + action->standard = pcmk__str_copy(PCMK_RESOURCE_CLASS_ALERT); action->timeout = timeout; action->params = params; action->sequence = sequence; diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index c7792f0..971d38c 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -51,6 +51,7 @@ static void close_pipe(int fildes[]); struct sigchld_data_s { sigset_t mask; // Signals to block now (including SIGCHLD) sigset_t old_mask; // Previous set of blocked signals + bool ignored; // If SIGCHLD for another child has been ignored }; // Initialize SIGCHLD data and prepare for use @@ -68,6 +69,9 @@ sigchld_setup(struct sigchld_data_s *data) CRM_XS " source=sigprocmask", pcmk_rc_str(errno)); return false; } + + data->ignored = false; + return true; } @@ -98,7 +102,7 @@ sigchld_close(int fd) // Return true if SIGCHLD was received from polled fd static bool -sigchld_received(int fd) +sigchld_received(int fd, int pid, struct sigchld_data_s *data) { struct signalfd_siginfo fdsi; ssize_t s; @@ -112,7 +116,18 @@ sigchld_received(int fd) CRM_XS " source=read", pcmk_rc_str(errno)); } else if (fdsi.ssi_signo == SIGCHLD) { - return true; + if (fdsi.ssi_pid == pid) { + return true; + + } else { + /* This SIGCHLD is for another child. We have to ignore it here but + * will still need to resend it after this synchronous action has + * completed and SIGCHLD has been restored to be handled by the + * previous SIGCHLD handler, so that it will be handled. + */ + data->ignored = true; + return false; + } } return false; } @@ -127,6 +142,12 @@ sigchld_cleanup(struct sigchld_data_s *data) crm_warn("Could not clean up after child process completion: %s", pcmk_rc_str(errno)); } + + // Resend any ignored SIGCHLD for other children so that they'll be handled. + if (data->ignored && kill(getpid(), SIGCHLD) != 0) { + crm_warn("Could not resend ignored SIGCHLD to ourselves: %s", + pcmk_rc_str(errno)); + } } #else // HAVE_SYS_SIGNALFD_H not defined @@ -137,6 +158,7 @@ struct sigchld_data_s { int pipe_fd[2]; // Pipe file descriptors struct sigaction sa; // Signal handling info (with SIGCHLD) struct sigaction old_sa; // Previous signal handling info + bool ignored; // If SIGCHLD for another child has been ignored }; // We need a global to use in the signal handler @@ -187,6 +209,8 @@ sigchld_setup(struct sigchld_data_s *data) CRM_XS " source=sigaction", pcmk_rc_str(errno)); } + data->ignored = false; + // Remember data for use in signal handler last_sigchld_data = data; return true; @@ -207,7 +231,7 @@ sigchld_close(int fd) } static bool -sigchld_received(int fd) +sigchld_received(int fd, int pid, struct sigchld_data_s *data) { char ch; @@ -230,6 +254,12 @@ sigchld_cleanup(struct sigchld_data_s *data) } close_pipe(data->pipe_fd); + + // Resend any ignored SIGCHLD for other children so that they'll be handled. + if (data->ignored && kill(getpid(), SIGCHLD) != 0) { + crm_warn("Could not resend ignored SIGCHLD to ourselves: %s", + pcmk_rc_str(errno)); + } } #endif @@ -1054,7 +1084,8 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) svc_read_output(op->opaque->stderr_fd, op, TRUE); } - if ((fds[2].revents & POLLIN) && sigchld_received(fds[2].fd)) { + if ((fds[2].revents & POLLIN) + && sigchld_received(fds[2].fd, op->pid, data)) { wait_rc = waitpid(op->pid, &status, WNOHANG); if ((wait_rc > 0) || ((wait_rc < 0) && (errno == ECHILD))) { @@ -1067,6 +1098,17 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) CRM_XS " source=waitpid", op->id, op->pid, wait_reason); wait_rc = 0; // Act as if process is still running + +#ifndef HAVE_SYS_SIGNALFD_H + } else { + /* The child hasn't exited, so this SIGCHLD could be for + * another child. We have to ignore it here but will still + * need to resend it after this synchronous action has + * completed and SIGCHLD has been restored to be handled by + * the previous handler, so that it will be handled. + */ + data->ignored = true; +#endif } } diff --git a/lib/services/services_lsb.c b/lib/services/services_lsb.c index 9ad7025..83587f2 100644 --- a/lib/services/services_lsb.c +++ b/lib/services/services_lsb.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the Pacemaker project contributors + * Copyright 2010-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,63 +18,67 @@ #include <sys/stat.h> #include <crm/crm.h> +#include <crm/common/xml.h> #include <crm/services.h> #include "services_private.h" #include "services_lsb.h" +// @TODO Use XML string constants and maybe a real XML object #define lsb_metadata_template \ - "<?xml version='1.0'?>\n" \ - "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n" \ - "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \ - " <version>1.0</version>\n" \ - " <longdesc lang='en'>\n" \ - "%s" \ - " </longdesc>\n" \ - " <shortdesc lang='en'>%s</shortdesc>\n" \ - " <parameters>\n" \ - " </parameters>\n" \ - " <actions>\n" \ - " <action name='meta-data' timeout='5' />\n" \ - " <action name='start' timeout='15' />\n" \ - " <action name='stop' timeout='15' />\n" \ - " <action name='status' timeout='15' />\n" \ - " <action name='restart' timeout='15' />\n" \ - " <action name='force-reload' timeout='15' />\n" \ - " <action name='monitor' timeout='15' interval='15' />\n" \ - " </actions>\n" \ - " <special tag='LSB'>\n" \ - " <Provides>%s</Provides>\n" \ - " <Required-Start>%s</Required-Start>\n" \ - " <Required-Stop>%s</Required-Stop>\n" \ - " <Should-Start>%s</Should-Start>\n" \ - " <Should-Stop>%s</Should-Stop>\n" \ - " <Default-Start>%s</Default-Start>\n" \ - " <Default-Stop>%s</Default-Stop>\n" \ - " </special>\n" \ - "</resource-agent>\n" + "<?xml " PCMK_XA_VERSION "='1.0'?>\n" \ + "<" PCMK_XE_RESOURCE_AGENT " " \ + PCMK_XA_NAME "='%s' " \ + PCMK_XA_VERSION "='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \ + " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \ + " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>\n" \ + "%s" \ + " </" PCMK_XE_LONGDESC ">\n" \ + " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>" \ + "%s" \ + "</" PCMK_XE_SHORTDESC ">\n" \ + " <" PCMK_XE_PARAMETERS "/>\n" \ + " <" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_META_DATA "'" \ + " " PCMK_META_TIMEOUT "='5s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_START "'" \ + " " PCMK_META_TIMEOUT "='15s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_STOP "'" \ + " " PCMK_META_TIMEOUT "='15s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_STATUS "'" \ + " " PCMK_META_TIMEOUT "='15s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='restart'" \ + " " PCMK_META_TIMEOUT "='15s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='force-reload'" \ + " " PCMK_META_TIMEOUT "='15s' />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "'" \ + " " PCMK_META_TIMEOUT "='15s'" \ + " " PCMK_META_INTERVAL "='15s' />\n" \ + " </" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "='LSB'>\n" \ + " <Provides>%s</Provides>\n" \ + " <Required-Start>%s</Required-Start>\n" \ + " <Required-Stop>%s</Required-Stop>\n" \ + " <Should-Start>%s</Should-Start>\n" \ + " <Should-Stop>%s</Should-Stop>\n" \ + " <Default-Start>%s</Default-Start>\n" \ + " <Default-Stop>%s</Default-Stop>\n" \ + " </" PCMK_XE_SPECIAL ">\n" \ + "</" PCMK_XE_RESOURCE_AGENT ">\n" /* See "Comment Conventions for Init Scripts" in the LSB core specification at: * http://refspecs.linuxfoundation.org/lsb.shtml */ -#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO" -#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO" -#define PROVIDES "# Provides:" -#define REQ_START "# Required-Start:" -#define REQ_STOP "# Required-Stop:" -#define SHLD_START "# Should-Start:" -#define SHLD_STOP "# Should-Stop:" -#define DFLT_START "# Default-Start:" -#define DFLT_STOP "# Default-Stop:" -#define SHORT_DSCR "# Short-Description:" -#define DESCRIPTION "# Description:" - -#define lsb_meta_helper_free_value(m) \ - do { \ - if ((m) != NULL) { \ - xmlFree(m); \ - (m) = NULL; \ - } \ - } while(0) +#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO" +#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO" +#define PROVIDES "# Provides:" +#define REQUIRED_START "# Required-Start:" +#define REQUIRED_STOP "# Required-Stop:" +#define SHOULD_START "# Should-Start:" +#define SHOULD_STOP "# Should-Stop:" +#define DEFAULT_START "# Default-Start:" +#define DEFAULT_STOP "# Default-Stop:" +#define SHORT_DESC "# Short-Description:" +#define DESCRIPTION "# Description:" /*! * \internal @@ -87,10 +91,13 @@ * \return TRUE if value was set, FALSE otherwise */ static inline gboolean -lsb_meta_helper_get_value(const char *line, char **value, const char *prefix) +lsb_meta_helper_get_value(const char *line, gchar **value, const char *prefix) { - if (!*value && pcmk__starts_with(line, prefix)) { - *value = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST line+strlen(prefix)); + /* @TODO Perhaps update later to use pcmk__xml_needs_escape(). Involves many + * extra variables in the caller. + */ + if ((*value == NULL) && pcmk__starts_with(line, prefix)) { + *value = pcmk__xml_escape(line + strlen(prefix), pcmk__xml_escape_text); return TRUE; } return FALSE; @@ -102,15 +109,15 @@ services__get_lsb_metadata(const char *type, char **output) char ra_pathname[PATH_MAX] = { 0, }; FILE *fp = NULL; char buffer[1024] = { 0, }; - char *provides = NULL; - char *req_start = NULL; - char *req_stop = NULL; - char *shld_start = NULL; - char *shld_stop = NULL; - char *dflt_start = NULL; - char *dflt_stop = NULL; - char *s_dscrpt = NULL; - char *xml_l_dscrpt = NULL; + gchar *provides = NULL; + gchar *required_start = NULL; + gchar *required_stop = NULL; + gchar *should_start = NULL; + gchar *should_stop = NULL; + gchar *default_start = NULL; + gchar *default_stop = NULL; + gchar *short_desc = NULL; + gchar *long_desc = NULL; bool in_header = FALSE; if (type[0] == '/') { @@ -142,30 +149,31 @@ services__get_lsb_metadata(const char *type, char **output) if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) { continue; } - if (lsb_meta_helper_get_value(buffer, &req_start, REQ_START)) { + if (lsb_meta_helper_get_value(buffer, &required_start, + REQUIRED_START)) { continue; } - if (lsb_meta_helper_get_value(buffer, &req_stop, REQ_STOP)) { + if (lsb_meta_helper_get_value(buffer, &required_stop, REQUIRED_STOP)) { continue; } - if (lsb_meta_helper_get_value(buffer, &shld_start, SHLD_START)) { + if (lsb_meta_helper_get_value(buffer, &should_start, SHOULD_START)) { continue; } - if (lsb_meta_helper_get_value(buffer, &shld_stop, SHLD_STOP)) { + if (lsb_meta_helper_get_value(buffer, &should_stop, SHOULD_STOP)) { continue; } - if (lsb_meta_helper_get_value(buffer, &dflt_start, DFLT_START)) { + if (lsb_meta_helper_get_value(buffer, &default_start, DEFAULT_START)) { continue; } - if (lsb_meta_helper_get_value(buffer, &dflt_stop, DFLT_STOP)) { + if (lsb_meta_helper_get_value(buffer, &default_stop, DEFAULT_STOP)) { continue; } - if (lsb_meta_helper_get_value(buffer, &s_dscrpt, SHORT_DSCR)) { + if (lsb_meta_helper_get_value(buffer, &short_desc, SHORT_DESC)) { continue; } /* Long description may cross multiple lines */ - if ((xml_l_dscrpt == NULL) // haven't already found long description + if ((long_desc == NULL) // Haven't already found long description && pcmk__starts_with(buffer, DESCRIPTION)) { bool processed_line = TRUE; GString *desc = g_string_sized_new(2048); @@ -192,9 +200,7 @@ services__get_lsb_metadata(const char *type, char **output) } // Make long description safe to use in XML - xml_l_dscrpt = - (char *) xmlEncodeEntitiesReentrant(NULL, - (pcmkXmlStr) desc->str); + long_desc = pcmk__xml_escape(desc->str, pcmk__xml_escape_text); g_string_free(desc, TRUE); if (processed_line) { @@ -214,28 +220,25 @@ services__get_lsb_metadata(const char *type, char **output) fclose(fp); *output = crm_strdup_printf(lsb_metadata_template, type, - (xml_l_dscrpt? xml_l_dscrpt : type), - (s_dscrpt? s_dscrpt : type), - (provides? provides : ""), - (req_start? req_start : ""), - (req_stop? req_stop : ""), - (shld_start? shld_start : ""), - (shld_stop? shld_stop : ""), - (dflt_start? dflt_start : ""), - (dflt_stop? dflt_stop : "")); - - lsb_meta_helper_free_value(xml_l_dscrpt); - lsb_meta_helper_free_value(s_dscrpt); - lsb_meta_helper_free_value(provides); - lsb_meta_helper_free_value(req_start); - lsb_meta_helper_free_value(req_stop); - lsb_meta_helper_free_value(shld_start); - lsb_meta_helper_free_value(shld_stop); - lsb_meta_helper_free_value(dflt_start); - lsb_meta_helper_free_value(dflt_stop); - - crm_trace("Created fake metadata: %llu", - (unsigned long long) strlen(*output)); + pcmk__s(long_desc, type), + pcmk__s(short_desc, type), + pcmk__s(provides, ""), + pcmk__s(required_start, ""), + pcmk__s(required_stop, ""), + pcmk__s(should_start, ""), + pcmk__s(should_stop, ""), + pcmk__s(default_start, ""), + pcmk__s(default_stop, "")); + + g_free(long_desc); + g_free(short_desc); + g_free(provides); + g_free(required_start); + g_free(required_stop); + g_free(should_start); + g_free(should_stop); + g_free(default_start); + g_free(default_stop); return pcmk_ok; } diff --git a/lib/services/services_nagios.c b/lib/services/services_nagios.c index 10759b5..f4b1320 100644 --- a/lib/services/services_nagios.c +++ b/lib/services/services_nagios.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the Pacemaker project contributors + * Copyright 2010-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -25,7 +25,7 @@ #include <sys/resource.h> #include "crm/crm.h" -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include "crm/common/mainloop.h" #include "crm/services.h" @@ -73,7 +73,7 @@ services__nagios_prepare(svc_action_t *op) return E2BIG; } - if (pcmk__str_eq(key, XML_ATTR_CRM_VERSION, pcmk__str_casei) + if (pcmk__str_eq(key, PCMK_XA_CRM_FEATURE_SET, pcmk__str_casei) || strstr(key, CRM_META "_")) { continue; } @@ -203,7 +203,7 @@ services__get_nagios_metadata(const char *type, char **output) } else { crm_trace("Reading %d bytes from file", length); - *output = calloc(1, (length + 1)); + *output = pcmk__assert_alloc(1, (length + 1)); read_len = fread(*output, 1, length, file_strm); if (read_len != length) { crm_err("Calculated and read bytes differ: %d vs. %d", diff --git a/lib/services/systemd.c b/lib/services/systemd.c index ecac86c..0563c49 100644 --- a/lib/services/systemd.c +++ b/lib/services/systemd.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. * @@ -9,6 +9,7 @@ #include <crm_internal.h> #include <crm/crm.h> +#include <crm/common/xml.h> #include <crm/services.h> #include <crm/services_internal.h> #include <crm/common/mainloop.h> @@ -663,25 +664,35 @@ systemd_unit_exists(const char *name) return FALSE; } -#define METADATA_FORMAT \ - "<?xml version=\"1.0\"?>\n" \ - "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" \ - "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \ - " <version>1.1</version>\n" \ - " <longdesc lang=\"en\">\n" \ - " %s\n" \ - " </longdesc>\n" \ - " <shortdesc lang=\"en\">systemd unit file for %s</shortdesc>\n" \ - " <parameters/>\n" \ - " <actions>\n" \ - " <action name=\"start\" timeout=\"100\" />\n" \ - " <action name=\"stop\" timeout=\"100\" />\n" \ - " <action name=\"status\" timeout=\"100\" />\n" \ - " <action name=\"monitor\" timeout=\"100\" interval=\"60\"/>\n" \ - " <action name=\"meta-data\" timeout=\"5\" />\n" \ - " </actions>\n" \ - " <special tag=\"systemd\"/>\n" \ - "</resource-agent>\n" +// @TODO Use XML string constants and maybe a real XML object +#define METADATA_FORMAT \ + "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \ + "<" PCMK_XE_RESOURCE_AGENT " " \ + PCMK_XA_NAME "=\"%s\" " \ + PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \ + " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \ + " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \ + " %s\n" \ + " </" PCMK_XE_LONGDESC ">\n" \ + " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \ + "systemd unit file for %s" \ + "</" PCMK_XE_SHORTDESC ">\n" \ + " <" PCMK_XE_PARAMETERS "/>\n" \ + " <" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \ + " " PCMK_META_TIMEOUT "=\"100s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \ + " " PCMK_META_TIMEOUT "=\"100s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \ + " " PCMK_META_TIMEOUT "=\"100s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \ + " " PCMK_META_TIMEOUT "=\"100s\"" \ + " " PCMK_META_INTERVAL "=\"60s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \ + " " PCMK_META_TIMEOUT "=\"5s\" />\n" \ + " </" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"systemd\"/>\n" \ + "</" PCMK_XE_RESOURCE_AGENT ">\n" static char * systemd_unit_metadata(const char *name, int timeout) @@ -690,8 +701,6 @@ systemd_unit_metadata(const char *name, int timeout) char *desc = NULL; char *path = NULL; - char *escaped = NULL; - if (invoke_unit_by_name(name, NULL, &path) == pcmk_rc_ok) { /* TODO: Worth a making blocking call for? Probably not. Possibly if cached. */ desc = systemd_get_property(path, "Description", NULL, NULL, NULL, @@ -700,12 +709,18 @@ systemd_unit_metadata(const char *name, int timeout) desc = crm_strdup_printf("Systemd unit file for %s", name); } - escaped = crm_xml_escape(desc); + if (pcmk__xml_needs_escape(desc, pcmk__xml_escape_text)) { + gchar *escaped = pcmk__xml_escape(desc, pcmk__xml_escape_text); + + meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name); + g_free(escaped); + + } else { + meta = crm_strdup_printf(METADATA_FORMAT, name, desc, name); + } - meta = crm_strdup_printf(METADATA_FORMAT, name, escaped, name); free(desc); free(path); - free(escaped); return meta; } diff --git a/lib/services/upstart.c b/lib/services/upstart.c index 2306e73..b6f58f4 100644 --- a/lib/services/upstart.c +++ b/lib/services/upstart.c @@ -1,7 +1,7 @@ /* * Original copyright 2010 Senko Rasic <senko.rasic@dobarkod.hr> * and Ante Karamatic <ivoks@init.hr> - * Later changes copyright 2012-2023 the Pacemaker project contributors + * Later changes copyright 2012-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,6 +14,7 @@ #include <stdio.h> #include <crm/crm.h> +#include <crm/common/xml.h> #include <crm/services.h> #include <crm/common/mainloop.h> @@ -370,26 +371,38 @@ parse_status_result(const char *name, const char *state, void *userdata) } } -#define METADATA_FORMAT \ - "<?xml version=\"1.0\"?>\n" \ - "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" \ - "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \ - " <version>1.1</version>\n" \ - " <longdesc lang=\"en\">\n" \ - " Upstart agent for controlling the system %s service\n" \ - " </longdesc>\n" \ - " <shortdesc lang=\"en\">Upstart job for %s</shortdesc>\n" \ - " <parameters/>\n" \ - " <actions>\n" \ - " <action name=\"start\" timeout=\"15\" />\n" \ - " <action name=\"stop\" timeout=\"15\" />\n" \ - " <action name=\"status\" timeout=\"15\" />\n" \ - " <action name=\"restart\" timeout=\"15\" />\n" \ - " <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n" \ - " <action name=\"meta-data\" timeout=\"5\" />\n" \ - " </actions>\n" \ - " <special tag=\"upstart\"/>\n" \ - "</resource-agent>\n" +// @TODO Use XML string constants and maybe a real XML object +#define METADATA_FORMAT \ + "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \ + "<" PCMK_XE_RESOURCE_AGENT " " \ + PCMK_XA_NAME "=\"%s\" " \ + PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \ + " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \ + " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \ + " Upstart agent for controlling the system %s service\n" \ + " </" PCMK_XE_LONGDESC ">\n" \ + " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \ + "Upstart job for %s" \ + "</" PCMK_XE_SHORTDESC ">\n" \ + " <" PCMK_XE_PARAMETERS "/>\n" \ + " <" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \ + " " PCMK_META_TIMEOUT "=\"15s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \ + " " PCMK_META_TIMEOUT "=\"15s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \ + " " PCMK_META_TIMEOUT "=\"15s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"restart\"" \ + " " PCMK_META_TIMEOUT "=\"15s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \ + " " PCMK_META_TIMEOUT "=\"15s\"" \ + " " PCMK_META_INTERVAL "=\"15s\"" \ + " " PCMK_META_START_DELAY "=\"15s\" />\n" \ + " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \ + " " PCMK_META_TIMEOUT "=\"5s\" />\n" \ + " </" PCMK_XE_ACTIONS ">\n" \ + " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"upstart\"/>\n" \ + "</" PCMK_XE_RESOURCE_AGENT ">\n" static char * upstart_job_metadata(const char *name) |