summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/cib/Makefile.am2
-rw-r--r--lib/cib/cib_attrs.c255
-rw-r--r--lib/cib/cib_client.c35
-rw-r--r--lib/cib/cib_file.c90
-rw-r--r--lib/cib/cib_native.c45
-rw-r--r--lib/cib/cib_ops.c350
-rw-r--r--lib/cib/cib_remote.c50
-rw-r--r--lib/cib/cib_utils.c383
-rw-r--r--lib/cluster/Makefile.am6
-rw-r--r--lib/cluster/cluster.c528
-rw-r--r--lib/cluster/corosync.c175
-rw-r--r--lib/cluster/cpg.c472
-rw-r--r--lib/cluster/crmcluster_private.h26
-rw-r--r--lib/cluster/election.c67
-rw-r--r--lib/cluster/membership.c790
-rw-r--r--lib/cluster/tests/Makefile.am12
-rw-r--r--lib/cluster/tests/cluster/Makefile.am (renamed from lib/pengine/tests/rules/Makefile.am)6
-rw-r--r--lib/cluster/tests/cluster/pcmk_cluster_set_destroy_fn_test.c79
-rw-r--r--lib/cluster/tests/cpg/Makefile.am19
-rw-r--r--lib/cluster/tests/cpg/pcmk_cpg_set_confchg_fn_test.c98
-rw-r--r--lib/cluster/tests/cpg/pcmk_cpg_set_deliver_fn_test.c94
-rw-r--r--lib/common/Makefile.am22
-rw-r--r--lib/common/acl.c136
-rw-r--r--lib/common/actions.c281
-rw-r--r--lib/common/agents.c3
-rw-r--r--lib/common/alerts.c20
-rw-r--r--lib/common/attrs.c116
-rw-r--r--lib/common/cib.c100
-rw-r--r--lib/common/crmcommon_private.h160
-rw-r--r--lib/common/digest.c90
-rw-r--r--lib/common/health.c39
-rw-r--r--lib/common/io.c15
-rw-r--r--lib/common/ipc_attrd.c258
-rw-r--r--lib/common/ipc_client.c39
-rw-r--r--lib/common/ipc_common.c4
-rw-r--r--lib/common/ipc_controld.c113
-rw-r--r--lib/common/ipc_pacemakerd.c69
-rw-r--r--lib/common/ipc_schedulerd.c30
-rw-r--r--lib/common/ipc_server.c88
-rw-r--r--lib/common/iso8601.c233
-rw-r--r--lib/common/logging.c20
-rw-r--r--lib/common/mainloop.c33
-rw-r--r--lib/common/messages.c122
-rw-r--r--lib/common/mock.c85
-rw-r--r--lib/common/mock_private.h13
-rw-r--r--lib/common/nodes.c145
-rw-r--r--lib/common/nvpair.c161
-rw-r--r--lib/common/options.c1489
-rw-r--r--lib/common/options_display.c493
-rw-r--r--lib/common/output.c41
-rw-r--r--lib/common/output_html.c120
-rw-r--r--lib/common/output_log.c80
-rw-r--r--lib/common/output_none.c8
-rw-r--r--lib/common/output_text.c108
-rw-r--r--lib/common/output_xml.c320
-rw-r--r--lib/common/patchset.c821
-rw-r--r--lib/common/patchset_display.c73
-rw-r--r--lib/common/probes.c84
-rw-r--r--lib/common/remote.c35
-rw-r--r--lib/common/resources.c67
-rw-r--r--lib/common/results.c8
-rw-r--r--lib/common/roles.c88
-rw-r--r--lib/common/rules.c1512
-rw-r--r--lib/common/scheduler.c97
-rw-r--r--lib/common/schemas.c1561
-rw-r--r--lib/common/scores.c57
-rw-r--r--lib/common/strings.c259
-rw-r--r--lib/common/tests/Makefile.am8
-rw-r--r--lib/common/tests/acl/xml_acl_denied_test.c10
-rw-r--r--lib/common/tests/acl/xml_acl_enabled_test.c10
-rw-r--r--lib/common/tests/actions/Makefile.am10
-rw-r--r--lib/common/tests/actions/copy_in_properties_test.c62
-rw-r--r--lib/common/tests/actions/expand_plus_plus_test.c256
-rw-r--r--lib/common/tests/actions/fix_plus_plus_recursive_test.c47
-rw-r--r--lib/common/tests/actions/pcmk_xe_is_probe_test.c43
-rw-r--r--lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c150
-rw-r--r--lib/common/tests/health/pcmk__parse_health_strategy_test.c2
-rw-r--r--lib/common/tests/health/pcmk__validate_health_strategy_test.c2
-rw-r--r--lib/common/tests/io/pcmk__full_path_test.c10
-rw-r--r--lib/common/tests/iso8601/Makefile.am6
-rw-r--r--lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c243
-rw-r--r--lib/common/tests/iso8601/pcmk__readable_interval_test.c4
-rw-r--r--lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c80
-rw-r--r--lib/common/tests/nodes/Makefile.am23
-rw-r--r--lib/common/tests/nodes/pcmk__find_node_in_list_test.c53
-rw-r--r--lib/common/tests/nodes/pcmk__xe_add_node_test.c71
-rw-r--r--lib/common/tests/nodes/pcmk_foreach_active_resource_test.c149
-rw-r--r--lib/common/tests/nodes/pcmk_node_is_clean_test.c54
-rw-r--r--lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c54
-rw-r--r--lib/common/tests/nodes/pcmk_node_is_online_test.c54
-rw-r--r--lib/common/tests/nodes/pcmk_node_is_pending_test.c54
-rw-r--r--lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c54
-rw-r--r--lib/common/tests/nvpair/Makefile.am7
-rw-r--r--lib/common/tests/nvpair/crm_meta_name_test.c (renamed from lib/common/tests/utils/crm_meta_name_test.c)10
-rw-r--r--lib/common/tests/nvpair/crm_meta_value_test.c (renamed from lib/common/tests/utils/crm_meta_value_test.c)18
-rw-r--r--lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c10
-rw-r--r--lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c11
-rw-r--r--lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c108
-rw-r--r--lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c12
-rw-r--r--lib/common/tests/probes/Makefile.am18
-rw-r--r--lib/common/tests/probes/pcmk_is_probe_test.c (renamed from lib/common/tests/actions/pcmk_is_probe_test.c)2
-rw-r--r--lib/common/tests/probes/pcmk_xe_is_probe_test.c54
-rw-r--r--lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c333
-rw-r--r--lib/common/tests/procfs/pcmk__procfs_pid2path_test.c8
-rw-r--r--lib/common/tests/resources/Makefile.am17
-rw-r--r--lib/common/tests/resources/pcmk_resource_id_test.c36
-rw-r--r--lib/common/tests/resources/pcmk_resource_is_managed_test.c46
-rw-r--r--lib/common/tests/rules/Makefile.am29
-rw-r--r--lib/common/tests/rules/pcmk__cmp_by_type_test.c102
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c831
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_condition_test.c197
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_date_expression_test.c684
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_date_spec_test.c231
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_op_expression_test.c207
-rw-r--r--lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c227
-rw-r--r--lib/common/tests/rules/pcmk__parse_combine_test.c52
-rw-r--r--lib/common/tests/rules/pcmk__parse_comparison_test.c72
-rw-r--r--lib/common/tests/rules/pcmk__parse_source_test.c62
-rw-r--r--lib/common/tests/rules/pcmk__parse_type_test.c127
-rw-r--r--lib/common/tests/rules/pcmk__replace_submatches_test.c81
-rw-r--r--lib/common/tests/rules/pcmk__unpack_duration_test.c120
-rw-r--r--lib/common/tests/rules/pcmk_evaluate_rule_test.c379
-rw-r--r--lib/common/tests/scheduler/Makefile.am19
-rw-r--r--lib/common/tests/scheduler/pcmk_get_dc_test.c47
-rw-r--r--lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c34
-rw-r--r--lib/common/tests/scheduler/pcmk_has_quorum_test.c36
-rw-r--r--lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c71
-rw-r--r--lib/common/tests/schemas/Makefile.am88
-rw-r--r--lib/common/tests/schemas/crm_schema_init_test.c152
-rw-r--r--lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c158
-rw-r--r--lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c121
-rw-r--r--lib/common/tests/schemas/pcmk__find_x_0_schema_test.c100
-rw-r--r--lib/common/tests/schemas/pcmk__get_schema_test.c81
-rw-r--r--lib/common/tests/schemas/pcmk__schema_files_later_than_test.c106
-rw-r--r--lib/common/tests/scores/char2score_test.c14
-rw-r--r--lib/common/tests/scores/pcmk__add_scores_test.c69
-rw-r--r--lib/common/tests/scores/pcmk_readable_score_test.c10
-rw-r--r--lib/common/tests/strings/Makefile.am3
-rw-r--r--lib/common/tests/strings/crm_get_msec_test.c30
-rw-r--r--lib/common/tests/strings/crm_str_to_boolean_test.c16
-rw-r--r--lib/common/tests/strings/pcmk__char_in_any_str_test.c46
-rw-r--r--lib/common/tests/strings/pcmk__compress_test.c11
-rw-r--r--lib/common/tests/strings/pcmk__str_update_test.c3
-rw-r--r--lib/common/tests/utils/Makefile.am11
-rw-r--r--lib/common/tests/utils/compare_version_test.c5
-rw-r--r--lib/common/tests/utils/pcmk__realloc_test.c69
-rw-r--r--lib/common/tests/utils/pcmk_hostname_test.c56
-rw-r--r--lib/common/tests/xml/Makefile.am11
-rw-r--r--lib/common/tests/xml/crm_xml_init_test.c230
-rw-r--r--lib/common/tests/xml/pcmk__xe_copy_attrs_test.c188
-rw-r--r--lib/common/tests/xml/pcmk__xe_first_child_test.c (renamed from lib/common/tests/xml/pcmk__xe_match_test.c)52
-rw-r--r--lib/common/tests/xml/pcmk__xe_foreach_child_test.c20
-rw-r--r--lib/common/tests/xml/pcmk__xe_set_score_test.c188
-rw-r--r--lib/common/tests/xml/pcmk__xml_escape_test.c213
-rw-r--r--lib/common/tests/xml/pcmk__xml_needs_escape_test.c337
-rw-r--r--lib/common/tests/xpath/pcmk__xpath_node_id_test.c29
-rw-r--r--lib/common/unittest.c128
-rw-r--r--lib/common/utils.c145
-rw-r--r--lib/common/watchdog.c45
-rw-r--r--lib/common/xml.c2825
-rw-r--r--lib/common/xml_attr.c31
-rw-r--r--lib/common/xml_display.c21
-rw-r--r--lib/common/xml_io.c840
-rw-r--r--lib/common/xpath.c85
-rw-r--r--lib/fencing/Makefile.am2
-rw-r--r--lib/fencing/st_actions.c46
-rw-r--r--lib/fencing/st_client.c277
-rw-r--r--lib/fencing/st_lha.c115
-rw-r--r--lib/fencing/st_output.c66
-rw-r--r--lib/fencing/st_rhcs.c65
-rw-r--r--lib/lrmd/Makefile.am2
-rw-r--r--lib/lrmd/lrmd_alerts.c4
-rw-r--r--lib/lrmd/lrmd_client.c310
-rw-r--r--lib/lrmd/lrmd_output.c39
-rw-r--r--lib/lrmd/proxy_common.c86
-rw-r--r--lib/pacemaker/Makefile.am10
-rw-r--r--lib/pacemaker/libpacemaker_private.h77
-rw-r--r--lib/pacemaker/pcmk_acl.c43
-rw-r--r--lib/pacemaker/pcmk_agents.c8
-rw-r--r--lib/pacemaker/pcmk_cluster_queries.c29
-rw-r--r--lib/pacemaker/pcmk_fence.c22
-rw-r--r--lib/pacemaker/pcmk_graph_consumer.c270
-rw-r--r--lib/pacemaker/pcmk_graph_logging.c20
-rw-r--r--lib/pacemaker/pcmk_graph_producer.c191
-rw-r--r--lib/pacemaker/pcmk_injections.c282
-rw-r--r--lib/pacemaker/pcmk_options.c153
-rw-r--r--lib/pacemaker/pcmk_output.c679
-rw-r--r--lib/pacemaker/pcmk_resource.c113
-rw-r--r--lib/pacemaker/pcmk_result_code.c4
-rw-r--r--lib/pacemaker/pcmk_rule.c134
-rw-r--r--lib/pacemaker/pcmk_sched_actions.c458
-rw-r--r--lib/pacemaker/pcmk_sched_bundle.c171
-rw-r--r--lib/pacemaker/pcmk_sched_clone.c111
-rw-r--r--lib/pacemaker/pcmk_sched_colocation.c508
-rw-r--r--lib/pacemaker/pcmk_sched_constraints.c120
-rw-r--r--lib/pacemaker/pcmk_sched_fencing.c61
-rw-r--r--lib/pacemaker/pcmk_sched_group.c147
-rw-r--r--lib/pacemaker/pcmk_sched_instances.c324
-rw-r--r--lib/pacemaker/pcmk_sched_location.c542
-rw-r--r--lib/pacemaker/pcmk_sched_migration.c181
-rw-r--r--lib/pacemaker/pcmk_sched_nodes.c63
-rw-r--r--lib/pacemaker/pcmk_sched_ordering.c485
-rw-r--r--lib/pacemaker/pcmk_sched_primitive.c457
-rw-r--r--lib/pacemaker/pcmk_sched_probes.c119
-rw-r--r--lib/pacemaker/pcmk_sched_promotable.c328
-rw-r--r--lib/pacemaker/pcmk_sched_recurring.c205
-rw-r--r--lib/pacemaker/pcmk_sched_remote.c78
-rw-r--r--lib/pacemaker/pcmk_sched_resource.c89
-rw-r--r--lib/pacemaker/pcmk_sched_tickets.c156
-rw-r--r--lib/pacemaker/pcmk_sched_utilization.c34
-rw-r--r--lib/pacemaker/pcmk_scheduler.c141
-rw-r--r--lib/pacemaker/pcmk_setup.c78
-rw-r--r--lib/pacemaker/pcmk_simulate.c121
-rw-r--r--lib/pacemaker/pcmk_status.c18
-rw-r--r--lib/pacemaker/pcmk_ticket.c553
-rw-r--r--lib/pacemaker/pcmk_verify.c153
-rw-r--r--lib/pacemaker/tests/Makefile.am11
-rw-r--r--lib/pacemaker/tests/pcmk_resource/Makefile.am20
-rw-r--r--lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c156
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/Makefile.am27
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk__get_ticket_state_test.c178
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_constraints_test.c130
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c170
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_get_attr_test.c150
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_info_test.c138
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_remove_attr_test.c231
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c281
-rw-r--r--lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_state_test.c156
-rw-r--r--lib/pengine/Makefile.am23
-rw-r--r--lib/pengine/bundle.c445
-rw-r--r--lib/pengine/clone.c250
-rw-r--r--lib/pengine/common.c603
-rw-r--r--lib/pengine/complex.c532
-rw-r--r--lib/pengine/failcounts.c93
-rw-r--r--lib/pengine/group.c80
-rw-r--r--lib/pengine/native.c404
-rw-r--r--lib/pengine/pe_actions.c607
-rw-r--r--lib/pengine/pe_digest.c138
-rw-r--r--lib/pengine/pe_health.c27
-rw-r--r--lib/pengine/pe_notif.c122
-rw-r--r--lib/pengine/pe_output.c1317
-rw-r--r--lib/pengine/pe_status_private.h2
-rw-r--r--lib/pengine/remote.c104
-rw-r--r--lib/pengine/rules.c1232
-rw-r--r--lib/pengine/rules_alerts.c87
-rw-r--r--lib/pengine/status.c112
-rw-r--r--lib/pengine/tests/Makefile.am6
-rw-r--r--lib/pengine/tests/native/native_find_rsc_test.c14
-rw-r--r--lib/pengine/tests/native/pe_base_name_eq_test.c6
-rw-r--r--lib/pengine/tests/rules/pe_cron_range_satisfied_test.c165
-rw-r--r--lib/pengine/tests/status/Makefile.am3
-rw-r--r--lib/pengine/tests/status/pe_find_node_any_test.c10
-rw-r--r--lib/pengine/tests/status/pe_find_node_id_test.c10
-rw-r--r--lib/pengine/tests/status/pe_find_node_test.c51
-rw-r--r--lib/pengine/tests/status/set_working_set_defaults_test.c7
-rw-r--r--lib/pengine/unpack.c1545
-rw-r--r--lib/pengine/utils.c90
-rw-r--r--lib/services/Makefile.am2
-rw-r--r--lib/services/services.c12
-rw-r--r--lib/services/services_linux.c50
-rw-r--r--lib/services/services_lsb.c191
-rw-r--r--lib/services/services_nagios.c8
-rw-r--r--lib/services/systemd.c65
-rw-r--r--lib/services/upstart.c55
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 "&amp;"
+#define PCMK__XML_ENTITY_GT "&gt;"
+#define PCMK__XML_ENTITY_LT "&lt;"
+#define PCMK__XML_ENTITY_QUOT "&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 = "&#x0A;abc&#x0A;def&#x0A;";
+ 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 = "&#x09;abc&#x09;def&#x09;";
+ 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 = "&#x0D;abc&#x0D;def&#x0D;";
+ 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 = "&#x07;&#x7F;&#x1B;";
+
+ 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 &lt; 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, "&lt;");
- break;
- case '>':
- copy = replace_text(copy, index, &length, "&gt;");
- break;
- case '"':
- copy = replace_text(copy, index, &length, "&quot;");
- break;
- case '\'':
- copy = replace_text(copy, index, &length, "&apos;");
- break;
- case '&':
- copy = replace_text(copy, index, &length, "&amp;");
- 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, "&lt;");
+ break;
+ case '>':
+ copy = replace_text(copy, &index, &length, "&gt;");
+ break;
+ case '"':
+ copy = replace_text(copy, &index, &length, "&quot;");
+ break;
+ case '\'':
+ copy = replace_text(copy, &index, &length, "&apos;");
+ break;
+ case '&':
+ copy = replace_text(copy, &index, &length, "&amp;");
+ 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 **) &copy->rsc_id, event->rsc_id);
- pcmk__str_update((char **) &copy->op_type, event->op_type);
- pcmk__str_update((char **) &copy->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 **) &copy->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 **) &copy->remote_nodename, event->remote_nodename);
- pcmk__str_update((char **) &copy->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(&regex, 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(&regex, 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(&regex);
}
}
@@ -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)